unofficial version 0.6: first major updates
[netris.git] / util.c
1 /*
2  * Netris -- A free networked version of T*tris
3  * Copyright (C) 1994,1995,1996  Mark H. Weaver <mhw@netris.org>
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.29 1999/05/16 06:56:33 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 #include <setjmp.h>
32
33 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event);
34
35 static EventGenRec alarmGen =
36                 { &alarmGen, 0, FT_read, -1, AlarmGenFunc, EM_alarm };
37 static EventGenRec *nextGen = &alarmGen;
38
39 static sigjmp_buf close_env;
40
41 static int myRandSeed = 1;
42
43 static long baseTimeval;
44
45 ExtFunc void AtExit(void (*handler)(void))
46 {
47 #ifdef HAS_ON_EXIT
48         on_exit((void *)handler, NULL);
49 #else
50         atexit(handler);
51 #endif
52 }
53
54 ///////////// HELP MESSAGES /////////////
55
56 ExtFunc void Header(void)
57 {
58         fprintf(stderr,
59           "NETRIS %s\t(c) 1994-1996,1999 Mark H. Weaver <mhw@netris.org>\n"
60           "          \t(c) 2002 Shiar <shiar@shiar.org>\n\n",
61           version_string);
62 } //Header
63
64 ExtFunc void Usage(void)
65 {
66         Header();
67         fprintf(stderr,
68           "Usage: netris <options>\n"
69           "\n"
70           "  -h, --help\t\tPrint this usage information\n"
71           "  -H, --info\t\tShow distribution and warranty information\n"
72           "  -R, --rules\t\tShow game rules\n"
73           "\n"
74           "  -S, --slowterm\tDisable inverse/bold/color for slow terminals\n"
75           "  -a, --ascii\t\tUse ascii characters\n"
76           "  -C, --color=0\t\tDisable color\n"
77           "\n"
78           "  -w, --wait\t\tWait for connection\n"
79           "  -c, --connect <host>\tInitiate connection\n"
80           "  -p, --port <port>\tSet port number (default is %d)\n"
81           "\n"
82           "  -s, --seed <seed>\tStart with given random seed\n"
83           "  -i, --speed <sec>\tSet the initial step-down interval, in seconds\n"
84           "  -l, --level <lvl>\tBegin at a higher level (can be used as handicap)\n"
85           "  -k, --keys <keys>\tRemap keys (default is \"%s\" for cursors)\n"
86           "  -d, --dropmode\tDrops go into drop mode\n"
87           "  -D, --instadrop\tInstant drop\n"
88           "\n"
89           "  -r, --robot <cmd>\tExecute program to control the game instead of keyboard\n"
90           "  -F, --fair-robot\tUse fair robot interface\n"
91           "\n", DEFAULT_PORT, DEFAULT_KEYS);
92 }
93
94 ExtFunc void DistInfo(void)
95 {
96         Header();
97         fprintf(stderr,
98           "This program is free software; you can redistribute it and/or modify\n"
99           "it under the terms of the GNU General Public License as published by\n"
100           "the Free Software Foundation; either version 2 of the License, or\n"
101           "(at your option) any later version.\n"
102           "\n"
103           "This program is distributed in the hope that it will be useful,\n"
104           "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
105           "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
106           "GNU General Public License for more details.\n"
107           "\n"
108           "You should have received a copy of the GNU General Public License\n"
109           "along with this program; if not, write to the Free Software\n"
110           "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n"
111           "\n");
112 } //DistInfo
113
114 ExtFunc void Rules(void)
115 {
116         Header();
117         fprintf(stderr,
118           "One player mode\n"
119           "---------------\n"
120           "Good old Tetris. Scoring is like on the GameBoy version (so try to\n"
121           "remove as many lines at once as you can). After removing ten lines\n"
122           "you go to the next level, which will be faster thus making the game\n"
123           "harder to play.\n"
124           "\n"
125           "Two player mode\n"
126           "---------------\n"
127           "It's just like normal T*tris except that when you clear more than\n"
128           "one row with a single piece, the other player receives penalty lines\n"
129           "For clearing 2, 3 or 4 rows, respectively 1, 2 or 4 junk rows will\n"
130           "be added to the bottom of your opponent's board respectively.\n"
131           "The junk rows have exactly one empty column, which will line up for\n"
132           "multiple rows.\n"
133           "\n"
134           "The longest surviving player wins the game.\n"
135           "\n");
136 } //Rules
137
138 ///////////// RANDOM /////////////
139
140 ExtFunc void InitUtil(void)
141 {
142         if (Game.seed)
143                 SRandom(Game.seed);
144         else
145                 SRandom(time(0));
146         if (sigsetjmp(close_env, 1)) exit(0);
147         signal(SIGINT, CatchInt);
148         ResetBaseTime();
149 } //InitUtil
150
151 /*
152  * My really crappy random number generator follows
153  * Should be more than sufficient for our purposes though
154  */
155 ExtFunc void SRandom(int seed)
156 {
157         Game.seed = seed;
158         myRandSeed = seed % 31751 + 1;
159 } //SRandom
160
161 ExtFunc int Random(int min, int max1)
162 {
163         myRandSeed = (myRandSeed * 31751 + 15437) % 32767;
164         return myRandSeed % (max1 - min) + min;
165 } //Random
166
167 ///////////// I/O /////////////
168
169 ExtFunc int MyRead(int fd, void *data, int len)
170 {
171         int result, left;
172
173         left = len;
174         while (left > 0) {
175                 result = read(fd, data, left);
176                 if (result > 0) {
177                         data = ((char *)data) + result;
178                         left -= result;
179                 }
180                 else if (errno != EINTR)
181                         return result;
182         }
183         return len;
184 } //MyRead
185
186 ExtFunc int MyWrite(int fd, void *data, int len)
187 {
188         int result, left;
189
190         left = len;
191         while (left > 0) {
192                 result = write(fd, data, left);
193                 if (result > 0) {
194                         data = ((char *)data) + result;
195                         left -= result;
196                 }
197                 else if (errno != EINTR)
198                         return result;
199         }
200         return len;
201 } //MyWrite
202
203 ///////////// TIME /////////////
204
205 ExtFunc void NormalizeTime(struct timeval *tv)
206 {
207         tv->tv_sec += tv->tv_usec / 1000000;
208         tv->tv_usec %= 1000000;
209         if (tv->tv_usec < 0) {
210                 tv->tv_usec += 1000000;
211                 --tv->tv_sec;
212         }
213 }
214
215 ExtFunc void CatchInt(int sig)
216 {
217         siglongjmp(close_env, 1);
218 }
219
220 ExtFunc void CatchAlarm(int sig)
221 {
222         alarmGen.ready = 1;
223         signal(SIGALRM, CatchAlarm);
224 }
225
226 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event)
227 {
228         return E_alarm;
229 }
230
231 ExtFunc void SetTimeval(struct timeval *tv, long usec)
232 {
233         tv->tv_sec = usec / 1000000;
234         tv->tv_usec = usec % 1000000;
235 } //SetTimeval
236
237 ExtFunc long GetTimeval(struct timeval *tv)
238 {
239         return tv->tv_sec * 1000000 + tv->tv_usec;
240 } //GetTimeval
241
242 ExtFunc long AbsTimeval(void)
243 {
244         struct timeval tv;
245
246         gettimeofday(&tv, NULL);
247         return GetTimeval(&tv);
248 } //CurTimeval
249
250 ExtFunc void ResetBaseTime(void)
251 {
252         baseTimeval = AbsTimeval();
253 } //ResetBaseTime
254
255 ExtFunc void PauseTime(void)
256 {
257         baseTimeval -= AbsTimeval();
258 } //PauseTime
259
260 ExtFunc void ResumeTime(void)
261 {
262         baseTimeval += AbsTimeval();
263 } //ResumeTime
264
265 ExtFunc long CurTimeval(void)
266 {
267         struct timeval tv;
268
269         gettimeofday(&tv, NULL);
270         return GetTimeval(&tv) - baseTimeval;
271 } //CurTimeval
272
273 static long SetITimer1(long interval, long value)
274 {
275         struct itimerval it, old;
276
277         SetTimeval(&it.it_interval, interval);
278         SetTimeval(&it.it_value, value);
279         if (setitimer(ITIMER_REAL, &it, &old) < 0)
280                 die("setitimer");
281         signal(SIGALRM, CatchAlarm);
282         return GetTimeval(&old.it_value);
283 }
284
285 ExtFunc long SetITimer(long interval, long value)
286 {
287         long old;
288
289         old = SetITimer1(0, 0);
290         alarmGen.ready = 0;
291         SetITimer1(interval, value);
292         return old;
293 }
294
295 ///////////// ... /////////////
296
297 ExtFunc volatile void die(char *msg)
298 {
299         perror(msg);
300         exit(1);
301 }
302
303 ExtFunc volatile void fatal(char *msg)
304 {
305         fprintf(stderr, "%s\n", msg);
306         exit(1);
307 }
308
309 ExtFunc void BlockSignals(MySigSet *saved, ...)
310 {
311         MySigSet set;
312         va_list args;
313         int sig;
314
315         va_start(args, saved);
316 #ifdef HAS_SIGPROCMASK
317         sigemptyset(&set);
318 #else
319         set = 0;
320 #endif
321         while ((sig = va_arg(args, int))) {
322 #ifdef HAS_SIGPROCMASK
323                 sigaddset(&set, sig);
324 #else
325                 sig |= sigmask(sig);
326 #endif
327         }
328 #ifdef HAS_SIGPROCMASK
329         sigprocmask(SIG_BLOCK, &set, saved);
330 #else
331         *saved = sigblock(set);
332 #endif
333         va_end(args);
334 }
335
336 ExtFunc void RestoreSignals(MySigSet *saved, MySigSet *set)
337 {
338 #ifdef HAS_SIGPROCMASK
339         sigprocmask(SIG_SETMASK, set, saved);
340 #else
341         if (saved)
342                 *saved = sigsetmask(*set);
343         else
344                 sigsetmask(*set);
345 #endif
346 }
347
348 ///////////// EVENTS /////////////
349
350 ExtFunc void AddEventGen(EventGenRec *gen)
351 {
352         assert(gen->next == NULL);
353         gen->next = nextGen->next;
354         nextGen->next = gen;
355 } //AddEventGen
356
357 ExtFunc void RemoveEventGen(EventGenRec *gen)
358 {
359         // assert(gen->next != NULL);   /* Be more forgiving, for SIGINTs */
360         if (gen->next) {
361                 while (nextGen->next != gen)
362                         nextGen = nextGen->next;
363                 nextGen->next = gen->next;
364                 gen->next = NULL;
365         }
366 } //RemoveEventGen
367
368 ExtFunc MyEventType WaitMyEvent(MyEvent *event, int mask)
369 { //poll
370         int i, retry = 0;
371         fd_set fds[FT_len];
372         EventGenRec *gen;
373         int result, anyReady, anySet;
374         struct timeval tv;
375
376         for (;;) {
377                 for (i = 0; i < FT_len; ++i)
378                         FD_ZERO(&fds[i]);
379                 anyReady = anySet = 0;
380                 gen = nextGen;
381                 do {
382                         if (gen->mask & mask) {
383                                 if (gen->ready)
384                                         anyReady = 1;
385                                 if (gen->fd >= 0) {
386                                         FD_SET(gen->fd, &fds[gen->fdType]);
387                                         anySet = 1;
388                                 }
389                         }
390                         gen = gen->next;
391                 } while (gen != nextGen);
392                 if (anySet) {
393                         tv.tv_sec = 0;
394                         tv.tv_usec = (retry && !anyReady) ? 500000 : 0;
395                         result = select(FD_SETSIZE, &fds[FT_read], &fds[FT_write],
396                                         &fds[FT_except], anyReady ? &tv : NULL);
397                 }
398                 else {
399                         if (retry && !anyReady)
400                                 sleep(1);
401                         result = 0;
402                 }
403                 gen = nextGen;
404                 do {
405                         if ((gen->mask & mask)
406                                         && (gen->ready || (result > 0 && gen->fd >= 0
407                                                 && FD_ISSET(gen->fd, &fds[gen->fdType])))) {
408                                 gen->ready = 0;
409                                 event->type = gen->func(gen, event);
410                                 if (event->type != E_none) {
411                                         nextGen = gen->next;
412                                         return event->type;
413                                 }
414                         }
415                         gen = gen->next;
416                 } while (gen != nextGen);
417                 retry = 1;
418         }
419 } //WaitMyEvent
420
421 /*
422  * vi: ts=4 ai
423  * vim: noai si
424  */