code cosmetics
[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 #include "util.h"
34
35 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event);
36 static EventGenRec alarmGen = {
37         &alarmGen, 0, FT_read, -1, AlarmGenFunc, EM_alarm
38 };
39 static EventGenRec *nextGen = &alarmGen;
40
41 static int myRandSeed = 1;
42
43 static long baseTimeval;
44
45
46 void AtExit(void (*handler)(void))
47 {
48 #ifdef HAS_ON_EXIT
49         on_exit((void *)handler, NULL);
50 #else
51         atexit(handler);
52 #endif
53 }
54
55 ///////////// HELP MESSAGES /////////////
56
57 void Header(void)
58 {
59         fprintf(stderr,
60                 "NETRIS %s\t(c) 1994-1996,1999 Mark H. Weaver <mhw@netris.org>\n"
61                 "          \t(c) 2002 Shiar <shiar@shiar.org>\n\n",
62                 version_string
63         );
64 }
65
66 void Usage(void)
67 {
68         Header();
69         fprintf(stderr,
70                 "Usage: netris <options>\n"
71                 "\n"
72                 "  -h, --help\t\tPrint this usage information\n"
73                 "  -H, --info\t\tShow distribution and warranty information\n"
74                 "  -R, --rules\t\tShow game rules\n"
75                 "\n"
76                 "  -S, --slowterm\tDisable inverse/bold/color for slow terminals\n"
77                 "  -a, --ascii\t\tUse ascii characters\n"
78                 "  -C, --color=0\t\tDisable color\n"
79                 "\n"
80                 "  -c, --connect <host>\tInitiate connection\n"
81                 "  -p, --port <port>\tSet port number (default is %d)\n"
82                 "\n"
83                 "  -t, --team <team>\tJoin a team (don't receive lines from your teammates)\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
95 void DistInfo(void)
96 {
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         );
113 }
114
115 void Rules(void)
116 {
117         Header();
118         fprintf(stderr,
119                 "One player mode\n"
120                 "---------------\n"
121                 "Good old Tetris. Scoring is like on the GameBoy version (so try to\n"
122                 "remove as many lines at once as you can). After removing ten lines\n"
123                 "you go to the next level, which will be faster thus making the game\n"
124                 "harder to play.\n"
125                 "\n"
126                 "Two player mode\n"
127                 "---------------\n"
128                 "It's just like normal T*tris except that when you clear more than\n"
129                 "one row with a single piece, the other player receives penalty lines\n"
130                 "For clearing 2, 3 or 4 rows, respectively 1, 2 or 4 junk rows will\n"
131                 "be added to the bottom of your opponent's board respectively.\n"
132                 "The junk rows have exactly one empty column, which will line up for\n"
133                 "multiple rows.\n"
134                 "\n"
135                 "The longest surviving player wins the game.\n"
136                 "\n"
137         );
138 }
139
140 ///////////// RANDOM /////////////
141
142 /*
143  * My really crappy random number generator follows
144  * Should be more than sufficient for our purposes though
145  */
146 void SRandom(int seed)
147 {
148         Game.seed = seed;
149         myRandSeed = seed % 31751 + 1;
150 }
151
152 int Random(int min, int max1)
153 { //return a random value
154         myRandSeed = (myRandSeed * 31751 + 15437) % 32767;
155         return myRandSeed % (max1 - min) + min;
156 }
157
158 ///////////// I/O /////////////
159
160 int MyRead(int fd, void *data, int len)
161 {
162         int result, left;
163
164         left = len;
165         while (left > 0) {
166                 result = read(fd, data, left);
167                 if (result > 0) {
168                         data = ((char *)data) + result;
169                         left -= result;
170                 }
171                 else if (errno != EINTR)
172                         return result;
173         }
174         return len;
175 }
176
177 int MyWrite(int fd, void *data, int len)
178 {
179         int result, left;
180
181         left = len;
182         while (left > 0) {
183                 result = write(fd, data, left);
184                 if (result > 0) {
185                         data = ((char *)data) + result;
186                         left -= result;
187                 }
188                 else if (errno != EINTR)
189                         return result;
190         }
191         return len;
192 }
193
194 ///////////// TIME /////////////
195
196 void NormalizeTime(struct timeval *tv)
197 {
198         tv->tv_sec += tv->tv_usec / 1000000;
199         tv->tv_usec %= 1000000;
200         if (tv->tv_usec < 0) {
201                 tv->tv_usec += 1000000;
202                 --tv->tv_sec;
203         }
204 }
205
206 void CatchAlarm(int sig)
207 {
208         alarmGen.ready = 1;
209         signal(SIGALRM, CatchAlarm);
210 }
211
212 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event)
213 {
214         return E_alarm;
215 }
216
217 void SetTimeval(struct timeval *tv, long usec)
218 {
219         tv->tv_sec = usec / 1000000;
220         tv->tv_usec = usec % 1000000;
221 }
222
223 long GetTimeval(struct timeval *tv)
224 {
225         return tv->tv_sec * 1000000 + tv->tv_usec;
226 }
227
228 long AbsTimeval(void)
229 {
230         struct timeval tv;
231
232         gettimeofday(&tv, NULL);
233         return GetTimeval(&tv);
234 }
235
236 void ResetBaseTime(void)
237 { //Reset the timer
238         baseTimeval = AbsTimeval();
239 }
240
241 void PauseTime(void)
242 { //Pause the timer
243         baseTimeval -= AbsTimeval();
244 }
245
246 void ResumeTime(void)
247 { //Unpause timer
248         baseTimeval += AbsTimeval();
249 }
250
251 long CurTimeval(void)
252 {
253         struct timeval tv;
254
255         gettimeofday(&tv, NULL);
256         return GetTimeval(&tv) - baseTimeval;
257 }
258
259 static long SetITimer1(long interval, long value)
260 {
261         struct itimerval it, old;
262
263         SetTimeval(&it.it_interval, interval);
264         SetTimeval(&it.it_value, value);
265         if (setitimer(ITIMER_REAL, &it, &old) < 0)
266                 die("setitimer");
267         signal(SIGALRM, CatchAlarm);
268         return GetTimeval(&old.it_value);
269 }
270
271 long SetITimer(long interval, long value)
272 {
273         long old;
274
275         old = SetITimer1(0, 0);
276         alarmGen.ready = 0;
277         SetITimer1(interval, value);
278         return old;
279 }
280
281 ///////////// ... /////////////
282
283 volatile void die(char *msg)
284 {
285         perror(msg);
286         exit(1);
287 }
288
289 volatile void fatal(char *msg)
290 {
291         fprintf(stderr, "%s\n", msg);
292         exit(1);
293 }
294
295 void BlockSignals(MySigSet *saved, ...)
296 {
297         MySigSet set;
298         va_list args;
299         int sig;
300
301         va_start(args, saved);
302 #ifdef HAS_SIGPROCMASK
303         sigemptyset(&set);
304 #else
305         set = 0;
306 #endif
307         while ((sig = va_arg(args, int))) {
308 #ifdef HAS_SIGPROCMASK
309                 sigaddset(&set, sig);
310 #else
311                 sig |= sigmask(sig);
312 #endif
313         }
314 #ifdef HAS_SIGPROCMASK
315         sigprocmask(SIG_BLOCK, &set, saved);
316 #else
317         *saved = sigblock(set);
318 #endif
319         va_end(args);
320 }
321
322 void RestoreSignals(MySigSet *saved, MySigSet *set)
323 {
324 #ifdef HAS_SIGPROCMASK
325         sigprocmask(SIG_SETMASK, set, saved);
326 #else
327         if (saved)
328                 *saved = sigsetmask(*set);
329         else
330                 sigsetmask(*set);
331 #endif
332 }
333
334 ///////////// EVENTS /////////////
335
336 void AddEventGen(EventGenRec *gen)
337 {
338         assert(gen->next == NULL);
339         gen->next = nextGen->next;
340         nextGen->next = gen;
341 }
342
343 void RemoveEventGen(EventGenRec *gen)
344 {
345         // assert(gen->next != NULL);   /* Be more forgiving, for SIGINTs */
346         if (gen->next) {
347                 while (nextGen->next != gen)
348                         nextGen = nextGen->next;
349                 nextGen->next = gen->next;
350                 gen->next = NULL;
351         }
352 }
353
354 MyEventType WaitMyEvent(MyEvent *event, int mask)
355 { //poll
356         int i, retry = 0;
357         fd_set fds[FT_len];
358         EventGenRec *gen;
359         int result, anyReady, anySet;
360         struct timeval tv;
361
362         for (;;) {
363                 for (i = 0; i < FT_len; ++i)
364                         FD_ZERO(&fds[i]);
365                 anyReady = anySet = 0;
366                 gen = nextGen;
367                 do {
368                         if (gen->mask & mask) {
369                                 if (gen->ready)
370                                         anyReady = 1;
371                                 if (gen->fd >= 0) {
372                                         FD_SET(gen->fd, &fds[gen->fdType]);
373                                         anySet = 1;
374                                 }
375                         }
376                         gen = gen->next;
377                 } while (gen != nextGen);
378                 if (anySet) {
379                         tv.tv_sec = 0;
380                         tv.tv_usec = (retry && !anyReady) ? 500000 : 0;
381                         result = select(FD_SETSIZE, &fds[FT_read], &fds[FT_write],
382                                         &fds[FT_except], anyReady ? &tv : NULL);
383                 }
384                 else {
385                         if (retry && !anyReady)
386                                 sleep(1);
387                         result = 0;
388                 }
389                 gen = nextGen;
390                 do {
391                         if ((gen->mask & mask) && (gen->ready || (
392                                 result > 0 && gen->fd >= 0
393                                 && FD_ISSET(gen->fd, &fds[gen->fdType])
394                         ))) {
395                                 gen->ready = 0;
396                                 event->type = gen->func(gen, event);
397                                 if (event->type != E_none) {
398                                         nextGen = gen->next;
399                                         return event->type;
400                                 }
401                         }
402                         gen = gen->next;
403                 } while (gen != nextGen);
404                 retry = 1;
405         }
406 }
407