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