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