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