netris version 0.3
[netris.git] / util.c
1 /*
2  * Netris -- A free networked version of Tetris
3  * Copyright (C) 1994,1995  Mark Weaver <Mark_Weaver@brown.edu>
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  *
19  * $Id: util.c,v 1.26 1995/07/11 08:53:32 mhw Exp $
20  */
21
22 #include "netris.h"
23 #include <stdarg.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <sys/types.h>
28 #include <sys/time.h>
29 #include <netdb.h>
30 #include <errno.h>
31
32 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event);
33
34 static EventGenRec alarmGen =
35                 { &alarmGen, 0, FT_read, -1, AlarmGenFunc, EM_alarm };
36 static EventGenRec *nextGen = &alarmGen;
37
38 static myRandSeed = 1;
39
40 static struct timeval baseTimeval;
41
42 ExtFunc void InitUtil(void)
43 {
44         if (initSeed)
45                 SRandom(initSeed);
46         else
47                 SRandom(time(0));
48         signal(SIGINT, CatchInt);
49         ResetBaseTime();
50 }
51
52 ExtFunc void ResetBaseTime(void)
53 {
54         gettimeofday(&baseTimeval, NULL);
55 }
56
57 ExtFunc void AtExit(void (*handler)(void))
58 {
59 #ifdef HAS_ON_EXIT
60         on_exit((void *)handler, NULL);
61 #else
62         atexit(handler);
63 #endif
64 }
65
66 ExtFunc void Usage(void)
67 {
68         fprintf(stderr,
69                 "Netris version %s (C) 1994,1995  Mark Weaver <Mark_Weaver@brown.edu>\n"
70                 "Usage: netris <options>\n"
71                 "  -h           Print usage information\n"
72                 "  -w           Wait for connection\n"
73                 "  -c <host>    Initiate connection\n"
74                 "  -p <port>    Set port number (default is %d)\n"
75                 "  -k <keys>    Remap keys.  The argument is a string containing\n"
76                 "                 the keys in order: left, rotate, right, drop,\n"
77                 "                 down-faster, toggle-spying, pause\n"
78                 "  -i <sec>     Set the step-down interval, in seconds\n"
79                 "  -r <robot>   Execute <robot> (a command) as a robot controlling\n"
80                 "                 the game instead of the keyboard\n"
81                 "  -F           Use fair robot interface\n"
82                 "  -s <seed>    Start with given random seed\n"
83                 "  -D           Drops go into drop mode\n"
84                 "                 This means that sliding off a cliff after a drop causes\n"
85                 "                 another drop automatically\n"
86                 "  -S           Disable standout mode (inverse/bold) for slow terminals\n"
87                 "  -H           Show distribution and warranty information\n"
88                 "  -R           Show rules\n",
89                 version_string, DEFAULT_PORT);
90 }
91
92 ExtFunc void DistInfo(void)
93 {
94         fprintf(stderr,
95                 "Netris version %s (C) 1994,1995  Mark Weaver <Mark_Weaver@brown.edu>\n"
96                 "\n"
97                 "This program is free software; you can redistribute it and/or modify\n"
98                 "it under the terms of the GNU General Public License as published by\n"
99                 "the Free Software Foundation; either version 2 of the License, or\n"
100                 "(at your option) any later version.\n"
101                 "\n"
102                 "This program is distributed in the hope that it will be useful,\n"
103                 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
104                 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
105                 "GNU General Public License for more details.\n"
106                 "\n"
107                 "You should have received a copy of the GNU General Public License\n"
108                 "along with this program; if not, write to the Free Software\n"
109                 "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n",
110                 version_string);
111 }
112
113 ExtFunc void Rules(void)
114 {
115         fprintf(stderr,
116                 "Netris version %s rules\n"
117                 "\n"
118                 "Two player mode\n"
119                 "---------------\n"
120                 "It's just like normal Tetris except that when you clear more than\n"
121                 "one row with a single piece, the other player's board is moved up\n"
122                 "and junk rows are added to the bottom.  If you clear 2, 3 or 4\n"
123                 "rows, 1, 2 or 4 junk rows are added to your opponent's board,\n"
124                 "respectively.  The junk rows have exactly one empty column.\n"
125                 "For each group of junk rows given, the empty columns will line\n"
126                 "up.  This is intentional.\n"
127                 "\n"
128                 "The longest surviving player wins the game.\n"
129                 "\n"
130                 "One player mode\n"
131                 "---------------\n"
132                 "This mode is currently very boring, because there's no scoring\n"
133                 "and it never gets any faster.  This will be rectified at some point.\n"
134                 "I'm not very motivated to do it right now because I'm sick of one\n"
135                 "player Tetris.\n",
136                 version_string);
137 }
138
139 /*
140  * My really crappy random number generator follows
141  * Should be more than sufficient for our purposes though
142  */
143 ExtFunc void SRandom(int seed)
144 {
145         initSeed = seed;
146         myRandSeed = seed % 31751 + 1;
147 }
148
149 ExtFunc int Random(int min, int max1)
150 {
151         myRandSeed = (myRandSeed * 31751 + 15437) % 32767;
152         return myRandSeed % (max1 - min) + min;
153 }
154
155 ExtFunc int MyRead(int fd, void *data, int len)
156 {
157         int result, left;
158
159         left = len;
160         while (left > 0) {
161                 result = read(fd, data, left);
162                 if (result > 0) {
163                         data = ((char *)data) + result;
164                         left -= result;
165                 }
166                 else if (errno != EINTR)
167                         return result;
168         }
169         return len;
170 }
171
172 ExtFunc int MyWrite(int fd, void *data, int len)
173 {
174         int result, left;
175
176         left = len;
177         while (left > 0) {
178                 result = write(fd, data, left);
179                 if (result > 0) {
180                         data = ((char *)data) + result;
181                         left -= result;
182                 }
183                 else if (errno != EINTR)
184                         return result;
185         }
186         return len;
187 }
188
189 ExtFunc void NormalizeTime(struct timeval *tv)
190 {
191         tv->tv_sec += tv->tv_usec / 1000000;
192         tv->tv_usec %= 1000000;
193         if (tv->tv_usec < 0) {
194                 tv->tv_usec += 1000000;
195                 --tv->tv_sec;
196         }
197 }
198
199 ExtFunc void CatchInt(int sig)
200 {
201         exit(0);
202 }
203
204 ExtFunc void CatchAlarm(int sig)
205 {
206         alarmGen.ready = 1;
207         signal(SIGALRM, CatchAlarm);
208 }
209
210 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event)
211 {
212         return E_alarm;
213 }
214
215 ExtFunc long CurTimeval(void)
216 {
217         struct timeval tv;
218
219         gettimeofday(&tv, NULL);
220         tv.tv_sec -= baseTimeval.tv_sec;
221         tv.tv_usec -= baseTimeval.tv_usec;
222         return GetTimeval(&tv);
223 }
224
225 ExtFunc void SetTimeval(struct timeval *tv, long usec)
226 {
227         tv->tv_sec = usec / 1000000;
228         tv->tv_usec = usec % 1000000;
229 }
230
231 ExtFunc long GetTimeval(struct timeval *tv)
232 {
233         return tv->tv_sec * 1000000 + tv->tv_usec;
234 }
235
236 static long SetITimer1(long interval, long value)
237 {
238         struct itimerval it, old;
239
240         SetTimeval(&it.it_interval, interval);
241         SetTimeval(&it.it_value, value);
242         if (setitimer(ITIMER_REAL, &it, &old) < 0)
243                 die("setitimer");
244         signal(SIGALRM, CatchAlarm);
245         return GetTimeval(&old.it_value);
246 }
247
248 ExtFunc long SetITimer(long interval, long value)
249 {
250         long old;
251
252         old = SetITimer1(0, 0);
253         alarmGen.ready = 0;
254         SetITimer1(interval, value);
255         return old;
256 }
257
258 ExtFunc volatile void die(char *msg)
259 {
260         perror(msg);
261         exit(1);
262 }
263
264 ExtFunc volatile void fatal(char *msg)
265 {
266         fprintf(stderr, "%s\n", msg);
267         exit(1);
268 }
269
270 ExtFunc void BlockSignals(MySigSet *saved, ...)
271 {
272         MySigSet set;
273         va_list args;
274         int sig;
275
276         va_start(args, saved);
277 #ifdef HAS_SIGPROCMASK
278         sigemptyset(&set);
279 #else
280         set = 0;
281 #endif
282         while ((sig = va_arg(args, int))) {
283 #ifdef HAS_SIGPROCMASK
284                 sigaddset(&set, sig);
285 #else
286                 sig |= sigmask(sig);
287 #endif
288         }
289 #ifdef HAS_SIGPROCMASK
290         sigprocmask(SIG_BLOCK, &set, saved);
291 #else
292         *saved = sigblock(set);
293 #endif
294         va_end(args);
295 }
296
297 ExtFunc void RestoreSignals(MySigSet *saved, MySigSet *set)
298 {
299 #ifdef HAS_SIGPROCMASK
300         sigprocmask(SIG_SETMASK, set, saved);
301 #else
302         if (saved)
303                 *saved = sigsetmask(*set);
304         else
305                 sigsetmask(*set);
306 #endif
307 }
308
309 ExtFunc void AddEventGen(EventGenRec *gen)
310 {
311         assert(gen->next == NULL);
312         gen->next = nextGen->next;
313         nextGen->next = gen;
314 }
315
316 ExtFunc void RemoveEventGen(EventGenRec *gen)
317 {
318         assert(gen->next != NULL);
319         while (nextGen->next != gen)
320                 nextGen = nextGen->next;
321         nextGen->next = gen->next;
322         gen->next = NULL;
323 }
324
325 ExtFunc MyEventType WaitMyEvent(MyEvent *event, int mask)
326 {
327         int i, retry = 0;
328         fd_set fds[FT_len];
329         EventGenRec *gen;
330         int result, anyReady, anySet;
331         struct timeval tv;
332
333         /* XXX In certain circumstances, this routine does polling */
334         for (;;) {
335                 for (i = 0; i < FT_len; ++i)
336                         FD_ZERO(&fds[i]);
337                 anyReady = anySet = 0;
338                 gen = nextGen;
339                 do {
340                         if (gen->mask & mask) {
341                                 if (gen->ready)
342                                         anyReady = 1;
343                                 if (gen->fd >= 0) {
344                                         FD_SET(gen->fd, &fds[gen->fdType]);
345                                         anySet = 1;
346                                 }
347                         }
348                         gen = gen->next;
349                 } while (gen != nextGen);
350                 if (anySet) {
351                         tv.tv_sec = 0;
352                         tv.tv_usec = (retry && !anyReady) ? 500000 : 0;
353                         result = select(FD_SETSIZE, &fds[FT_read], &fds[FT_write],
354                                         &fds[FT_except], anyReady ? &tv : NULL);
355                 }
356                 else {
357                         if (retry && !anyReady)
358                                 sleep(1);
359                         result = 0;
360                 }
361                 gen = nextGen;
362                 do {
363                         if ((gen->mask & mask)
364                                         && (gen->ready || (result > 0 && gen->fd >= 0
365                                                 && FD_ISSET(gen->fd, &fds[gen->fdType])))) {
366                                 gen->ready = 0;
367                                 event->type = gen->func(gen, event);
368                                 if (event->type != E_none) {
369                                         nextGen = gen->next;
370                                         return event->type;
371                                 }
372                         }
373                         gen = gen->next;
374                 } while (gen != nextGen);
375                 retry = 1;
376         }
377 }
378