unofficial version 0.7: multiplayer improvements
[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           "  -i, --speed <sec>\tSet the initial step-down interval, in seconds\n"
83           "  -l, --level <lvl>\tBegin at a higher level (can be used as handicap)\n"
84           "  -k, --keys <keys>\tRemap keys (default is \"%s\" for cursors)\n"
85           "  -d, --dropmode\tDrops go into drop mode\n"
86           "  -D, --instadrop\tInstant drop\n"
87           "\n"
88           "  -r, --robot <cmd>\tExecute program to control the game instead of keyboard\n"
89           "  -F, --fair-robot\tUse fair robot interface\n"
90           "\n", DEFAULT_PORT, DEFAULT_KEYS);
91 }
92
93 ExtFunc void DistInfo(void)
94 {
95         Header();
96         fprintf(stderr,
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           "\n");
111 } //DistInfo
112
113 ExtFunc void Rules(void)
114 {
115         Header();
116         fprintf(stderr,
117           "One player mode\n"
118           "---------------\n"
119           "Good old Tetris. Scoring is like on the GameBoy version (so try to\n"
120           "remove as many lines at once as you can). After removing ten lines\n"
121           "you go to the next level, which will be faster thus making the game\n"
122           "harder to play.\n"
123           "\n"
124           "Two player mode\n"
125           "---------------\n"
126           "It's just like normal T*tris except that when you clear more than\n"
127           "one row with a single piece, the other player receives penalty lines\n"
128           "For clearing 2, 3 or 4 rows, respectively 1, 2 or 4 junk rows will\n"
129           "be added to the bottom of your opponent's board respectively.\n"
130           "The junk rows have exactly one empty column, which will line up for\n"
131           "multiple rows.\n"
132           "\n"
133           "The longest surviving player wins the game.\n"
134           "\n");
135 } //Rules
136
137 ///////////// RANDOM /////////////
138
139 ExtFunc void InitUtil(void)
140 {
141         SRandom(time(0));
142         if (sigsetjmp(close_env, 1)) exit(0);
143         signal(SIGINT, CatchInt);
144         ResetBaseTime();
145 } //InitUtil
146
147 /*
148  * My really crappy random number generator follows
149  * Should be more than sufficient for our purposes though
150  */
151 ExtFunc void SRandom(int seed)
152 {
153         Game.seed = seed;
154         myRandSeed = seed % 31751 + 1;
155 } //SRandom
156
157 ExtFunc int Random(int min, int max1)
158 { //return a random value
159         myRandSeed = (myRandSeed * 31751 + 15437) % 32767;
160         return myRandSeed % (max1 - min) + min;
161 } //Random
162
163 ///////////// I/O /////////////
164
165 ExtFunc int MyRead(int fd, void *data, int len)
166 {
167         int result, left;
168
169         left = len;
170         while (left > 0) {
171                 result = read(fd, data, left);
172                 if (result > 0) {
173                         data = ((char *)data) + result;
174                         left -= result;
175                 }
176                 else if (errno != EINTR)
177                         return result;
178         }
179         return len;
180 } //MyRead
181
182 ExtFunc int MyWrite(int fd, void *data, int len)
183 {
184         int result, left;
185
186         left = len;
187         while (left > 0) {
188                 result = write(fd, data, left);
189                 if (result > 0) {
190                         data = ((char *)data) + result;
191                         left -= result;
192                 }
193                 else if (errno != EINTR)
194                         return result;
195         }
196         return len;
197 } //MyWrite
198
199 ///////////// TIME /////////////
200
201 ExtFunc void NormalizeTime(struct timeval *tv)
202 {
203         tv->tv_sec += tv->tv_usec / 1000000;
204         tv->tv_usec %= 1000000;
205         if (tv->tv_usec < 0) {
206                 tv->tv_usec += 1000000;
207                 --tv->tv_sec;
208         }
209 }
210
211 ExtFunc void CatchInt(int sig)
212 {
213         siglongjmp(close_env, 1);
214 }
215
216 ExtFunc void CatchAlarm(int sig)
217 {
218         alarmGen.ready = 1;
219         signal(SIGALRM, CatchAlarm);
220 }
221
222 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event)
223 {
224         return E_alarm;
225 }
226
227 ExtFunc void SetTimeval(struct timeval *tv, long usec)
228 {
229         tv->tv_sec = usec / 1000000;
230         tv->tv_usec = usec % 1000000;
231 } //SetTimeval
232
233 ExtFunc long GetTimeval(struct timeval *tv)
234 {
235         return tv->tv_sec * 1000000 + tv->tv_usec;
236 } //GetTimeval
237
238 ExtFunc long AbsTimeval(void)
239 {
240         struct timeval tv;
241
242         gettimeofday(&tv, NULL);
243         return GetTimeval(&tv);
244 } //CurTimeval
245
246 ExtFunc void ResetBaseTime(void)
247 {
248         baseTimeval = AbsTimeval();
249 } //ResetBaseTime
250
251 ExtFunc void PauseTime(void)
252 {
253         baseTimeval -= AbsTimeval();
254 } //PauseTime
255
256 ExtFunc void ResumeTime(void)
257 {
258         baseTimeval += AbsTimeval();
259 } //ResumeTime
260
261 ExtFunc long CurTimeval(void)
262 {
263         struct timeval tv;
264
265         gettimeofday(&tv, NULL);
266         return GetTimeval(&tv) - baseTimeval;
267 } //CurTimeval
268
269 static long SetITimer1(long interval, long value)
270 {
271         struct itimerval it, old;
272
273         SetTimeval(&it.it_interval, interval);
274         SetTimeval(&it.it_value, value);
275         if (setitimer(ITIMER_REAL, &it, &old) < 0)
276                 die("setitimer");
277         signal(SIGALRM, CatchAlarm);
278         return GetTimeval(&old.it_value);
279 }
280
281 ExtFunc long SetITimer(long interval, long value)
282 {
283         long old;
284
285         old = SetITimer1(0, 0);
286         alarmGen.ready = 0;
287         SetITimer1(interval, value);
288         return old;
289 }
290
291 ///////////// ... /////////////
292
293 ExtFunc volatile void die(char *msg)
294 {
295         perror(msg);
296         exit(1);
297 }
298
299 ExtFunc volatile void fatal(char *msg)
300 {
301         fprintf(stderr, "%s\n", msg);
302         exit(1);
303 }
304
305 ExtFunc void BlockSignals(MySigSet *saved, ...)
306 {
307         MySigSet set;
308         va_list args;
309         int sig;
310
311         va_start(args, saved);
312 #ifdef HAS_SIGPROCMASK
313         sigemptyset(&set);
314 #else
315         set = 0;
316 #endif
317         while ((sig = va_arg(args, int))) {
318 #ifdef HAS_SIGPROCMASK
319                 sigaddset(&set, sig);
320 #else
321                 sig |= sigmask(sig);
322 #endif
323         }
324 #ifdef HAS_SIGPROCMASK
325         sigprocmask(SIG_BLOCK, &set, saved);
326 #else
327         *saved = sigblock(set);
328 #endif
329         va_end(args);
330 }
331
332 ExtFunc void RestoreSignals(MySigSet *saved, MySigSet *set)
333 {
334 #ifdef HAS_SIGPROCMASK
335         sigprocmask(SIG_SETMASK, set, saved);
336 #else
337         if (saved)
338                 *saved = sigsetmask(*set);
339         else
340                 sigsetmask(*set);
341 #endif
342 }
343
344 ///////////// EVENTS /////////////
345
346 ExtFunc void AddEventGen(EventGenRec *gen)
347 {
348         assert(gen->next == NULL);
349         gen->next = nextGen->next;
350         nextGen->next = gen;
351 } //AddEventGen
352
353 ExtFunc void RemoveEventGen(EventGenRec *gen)
354 {
355         // assert(gen->next != NULL);   /* Be more forgiving, for SIGINTs */
356         if (gen->next) {
357                 while (nextGen->next != gen)
358                         nextGen = nextGen->next;
359                 nextGen->next = gen->next;
360                 gen->next = NULL;
361         }
362 } //RemoveEventGen
363
364 ExtFunc MyEventType WaitMyEvent(MyEvent *event, int mask)
365 { //poll
366         int i, retry = 0;
367         fd_set fds[FT_len];
368         EventGenRec *gen;
369         int result, anyReady, anySet;
370         struct timeval tv;
371
372         for (;;) {
373                 for (i = 0; i < FT_len; ++i)
374                         FD_ZERO(&fds[i]);
375                 anyReady = anySet = 0;
376                 gen = nextGen;
377                 do {
378                         if (gen->mask & mask) {
379                                 if (gen->ready)
380                                         anyReady = 1;
381                                 if (gen->fd >= 0) {
382                                         FD_SET(gen->fd, &fds[gen->fdType]);
383                                         anySet = 1;
384                                 }
385                         }
386                         gen = gen->next;
387                 } while (gen != nextGen);
388                 if (anySet) {
389                         tv.tv_sec = 0;
390                         tv.tv_usec = (retry && !anyReady) ? 500000 : 0;
391                         result = select(FD_SETSIZE, &fds[FT_read], &fds[FT_write],
392                                         &fds[FT_except], anyReady ? &tv : NULL);
393                 }
394                 else {
395                         if (retry && !anyReady)
396                                 sleep(1);
397                         result = 0;
398                 }
399                 gen = nextGen;
400                 do {
401                         if ((gen->mask & mask)
402                                         && (gen->ready || (result > 0 && gen->fd >= 0
403                                                 && FD_ISSET(gen->fd, &fds[gen->fdType])))) {
404                                 gen->ready = 0;
405                                 event->type = gen->func(gen, event);
406                                 if (event->type != E_none) {
407                                         nextGen = gen->next;
408                                         return event->type;
409                                 }
410                         }
411                         gen = gen->next;
412                 } while (gen != nextGen);
413                 retry = 1;
414         }
415 } //WaitMyEvent
416
417 /*
418  * vi: ts=4 ai
419  * vim: noai si
420  */