9297b13a96374dbfe4c44f70a4db165cdd490d65
[netris.git] / server.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: game.c,v 1.39 1999/05/16 06:56:27 mhw Exp $
20  */
21
22 #define NOEXT
23 #include "netris.h"
24 #include <stdlib.h>
25 #include <ctype.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <netinet/in.h>
29 #include <unistd.h>
30 #include <sys/socket.h>
31 #include <netdb.h>
32 #include <errno.h>
33 #include <setjmp.h>
34
35 #define HEADER_SIZE sizeof(netint4[3])
36
37 static struct option options[] = {
38         { "wait",               0, 0, 'w' },
39         { "port",               1, 0, 'p' },
40         { "connections",1, 0, 'c' },
41         { "speed",              1, 0, 'i' },
42         { "seed",               1, 0, 's' },
43         { "info",               0, 0, 'H' },
44         { "help",               0, 0, 'h' },
45         { 0,                    0, 0,  0 }
46 };
47
48 static char Connections = 2;
49
50 static char *gameNames[GT_len] = { "OnePlayer", "ClassicTwo" };
51
52 ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event);
53
54 EventGenRec netGen[MAX_SCREENS] = {
55         { NULL, 0, FT_read, -1, NetGenFunc, EM_net, 0, "\0", 0, HEADER_SIZE } };
56
57 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event);
58 static EventGenRec alarmGen =
59                 { &alarmGen, 0, FT_read, -1, AlarmGenFunc, EM_alarm };
60 static EventGenRec *nextGen = &alarmGen;
61
62 static sigjmp_buf close_env;
63
64 ExtFunc volatile void die(char *msg)
65 {
66         perror(msg);
67         exit(1);
68 } //die
69
70 ExtFunc int MyRead(int fd, void *data, int len)
71 {
72         int result, left;
73
74         left = len;
75         while (left > 0) {
76                 result = read(fd, data, left);
77                 if (result > 0) {
78                         data = ((char *)data) + result;
79                         left -= result;
80                 }
81                 else if (errno != EINTR)
82                         return result;
83         }
84         return len;
85 } //MyRead
86
87 ExtFunc int MyWrite(int fd, void *data, int len)
88 {
89         int result, left;
90
91         left = len;
92         while (left > 0) {
93                 result = write(fd, data, left);
94                 if (result > 0) {
95                         data = ((char *)data) + result;
96                         left -= result;
97                 }
98                 else if (errno != EINTR)
99                         return result;
100         }
101         return len;
102 } //MyWrite
103
104 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event)
105 {
106         return E_alarm;
107 }
108
109 ExtFunc void AddEventGen(EventGenRec *gen)
110 {
111         assert(gen->next == NULL);
112         assert(nextGen->next != (void*)0xffffffff);
113         gen->next = nextGen->next;
114         nextGen->next = gen;
115 } //AddEventGen
116
117 ExtFunc void RemoveEventGen(EventGenRec *gen)
118 {
119         // assert(gen->next != NULL);   /* Be more forgiving, for SIGINTs */
120         if (gen->next) {
121                 while (nextGen->next != gen)
122                         nextGen = nextGen->next;
123                 nextGen->next = gen->next;
124                 gen->next = NULL;
125         }
126 } //RemoveEventGen
127
128 ExtFunc MyEventType WaitMyEvent(MyEvent *event, int mask)
129 { //poll
130         int i, retry = 0;
131         fd_set fds[FT_len];
132         EventGenRec *gen;
133         int result, anyReady, anySet;
134         struct timeval tv;
135
136         for (;;) {
137                 for (i = 0; i < FT_len; ++i)
138                         FD_ZERO(&fds[i]);
139                 anyReady = anySet = 0;
140                 gen = nextGen;
141                 do {
142                         if (gen->mask & mask) {
143                                 if (gen->ready)
144                                         anyReady = 1;
145                                 if (gen->fd >= 0) {
146                                         FD_SET(gen->fd, &fds[gen->fdType]);
147                                         anySet = 1;
148                                 }
149                         }
150                         gen = gen->next;
151                 } while (gen != nextGen);
152                 if (anySet) {
153                         tv.tv_sec = 0;
154                         tv.tv_usec = (retry && !anyReady) ? 500000 : 0;
155                         result = select(FD_SETSIZE, &fds[FT_read], &fds[FT_write],
156                                         &fds[FT_except], anyReady ? &tv : NULL);
157                 }
158                 else {
159                         if (retry && !anyReady)
160                                 sleep(1);
161                         result = 0;
162                 }
163                 gen = nextGen;
164                 do {
165                         if ((gen->mask & mask)
166                                         && (gen->ready || (result > 0 && gen->fd >= 0
167                                                 && FD_ISSET(gen->fd, &fds[gen->fdType])))) {
168                                 gen->ready = 0;
169                                 event->type = gen->func(gen, event);
170                                 if (event->type != E_none) {
171                                         nextGen = gen->next;
172                                         return event->type;
173                                 }
174                         }
175                         gen = gen->next;
176                 } while (gen != nextGen);
177                 retry = 1;
178         }
179 } //WaitMyEvent
180
181 ExtFunc void ByeClient(int playa)
182 { //client went away
183         fprintf(stderr, "Close connection #%d\n", playa);
184         close(netGen[playa].fd);
185         netGen[playa].fd = -1;
186 } //ByeClient
187
188 ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event)
189 { //receive
190         int result;
191         short uid, type, size;
192         netint4 *data = (netint4*)&(gen->buf);
193
194         result = MyRead(gen->fd, gen->buf + gen->bufSize,
195                 gen->bufGoal - gen->bufSize);
196         if (result < 0) {
197                 ByeClient(gen->player);
198                 type = NP_endConn;
199                 return E_net;
200         }
201         gen->bufSize += result;
202         if (gen->bufSize < gen->bufGoal)
203                 return E_none;
204         // *ugly* memcpy(data, gen->buf, sizeof(data));
205         uid = ntoh4(data[0]);
206         type = ntoh4(data[1]);
207         size = ntoh4(data[2]);
208         gen->bufGoal = size;
209         if (gen->bufSize < gen->bufGoal)
210                 return E_none;
211         gen->bufSize = 0;
212         gen->bufGoal = HEADER_SIZE;
213         event->u.net.sender = gen->player;
214         event->u.net.uid = uid;
215         event->u.net.type = type;
216         event->u.net.size = size - HEADER_SIZE;
217         event->u.net.data = gen->buf + HEADER_SIZE;
218         if (type == NP_endConn)
219                 ByeClient(gen->player);
220         return E_net;
221 } //NetGenFunc
222
223 ExtFunc void SendPacketTo(short playa, short uid, NetPacketType type, int size, void *data)
224 { //send to someone
225         netint4 header[3];
226
227         if (netGen[playa].fd >= 0) {
228                 header[0] = hton4(uid);
229                 header[1] = hton4(type);
230                 header[2] = hton4(size + HEADER_SIZE);
231                 if (MyWrite(netGen[playa].fd, header, HEADER_SIZE) != HEADER_SIZE)
232                         die("write (header)");
233                 if (size > 0 && data && MyWrite(netGen[playa].fd, data, size) != size)
234                         die("write");
235         }
236 } //SendPacketTo
237
238
239 ExtFunc void AtExit(void (*handler)(void))
240 {
241 #ifdef HAS_ON_EXIT
242         on_exit((void *)handler, NULL);
243 #else
244         atexit(handler);
245 #endif
246 }
247
248 ExtFunc void SCloseNet(short playa)
249 { //kick some connection's ass!
250         MyEvent event;
251
252         if (netGen[playa].fd >= 0) {
253                 SendPacketTo(playa, 0, NP_endConn, 0, NULL);
254                 do{} while (WaitMyEvent(&event, EM_net) != E_lostConn);
255                 close(netGen[playa].fd);
256                 netGen[playa].fd = -1;
257         }
258         if (netGen[playa].next)
259                 RemoveEventGen(&netGen[playa]);
260 } //SCloseNet
261
262 ExtFunc void CloseNets(void)
263 { //nou oogjes dicht en snaveltjes toe
264         int i;
265
266         for (i = 1; i <= totalPlayers; i++)
267                 SCloseNet(i);
268 } //CloseNets
269
270 ExtFunc int WaitForConnection(short port)
271 {
272         struct sockaddr_in addr;
273         struct hostent *host;
274         int sockListen;
275         int addrLen;
276         int val1;
277         struct linger val2;
278         int i;
279
280         AtExit(CloseNets);
281         for (i = 1; i < MAX_SCREENS; i++)
282                 memcpy(&netGen[i], &netGen[0], sizeof(EventGenRec));
283
284         memset(&addr, 0, sizeof(addr));
285         addr.sin_family = AF_INET;
286         addr.sin_port = htons(port);
287         addr.sin_addr.s_addr = htonl(INADDR_ANY);
288         if ((sockListen = socket(AF_INET, SOCK_STREAM, 0)) < 0)
289                 die("socket");
290         val1 = 1;
291         setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR,
292                         (void *)&val1, sizeof(val1));
293         if (bind(sockListen, (struct sockaddr *)&addr, sizeof(addr)) < 0)
294                 die("bind");
295         if (listen(sockListen, 1) < 0)
296                 die("listen");
297
298         addrLen = sizeof(addr);
299         for (i = 1; i <= Connections; i++) {
300                 if ((netGen[i].fd = accept(sockListen, (struct sockaddr *)&addr, &addrLen)) < 0)
301                         die("accept");
302                 fprintf(stderr, "Connection: %s\n", inet_ntoa(addr.sin_addr));
303                 val2.l_onoff = 1;
304                 val2.l_linger = 0;
305                 setsockopt(netGen[i].fd, SOL_SOCKET, SO_LINGER, (void *)&val2, sizeof(val2));
306                 sprintf(Players[i].host, "%s", inet_ntoa(addr.sin_addr));
307                 if (addr.sin_family == AF_INET) {
308                         host = gethostbyaddr((void *)&addr.sin_addr,
309                                         sizeof(struct in_addr), AF_INET);
310                         if (host) {
311                                 strncpy(Players[i].host, host->h_name, sizeof(Players[i].host) - 1);
312                                 Players[i].host[sizeof(Players[i].host) - 1] = 0;
313                         }
314                 }
315                 AddEventGen(&netGen[i]);
316                 netGen[i].player = i;
317                 totalPlayers++;
318                 {
319                         MyEvent event;
320
321                         do {} while ((WaitMyEvent(&event, EM_net) != E_net) ||
322                                         (event.u.net.sender != i));
323                         if (event.u.net.type != NP_hello) ByeClient(i);
324                         else {
325                                 netint4 versiondata[2];
326                                 char data[255];
327                                 int major;
328                                 memcpy(versiondata, event.u.net.data, sizeof(versiondata));
329                                 major = ntoh4(versiondata[0]);
330                                 protocolVersion = ntoh4(versiondata[1]);
331                                 if (major != MAJOR_VERSION || protocolVersion != PROTOCOL_VERSION) {
332                                         snprintf(data, sizeof(data), "Version mismatch: received %d.%d",
333                                                 major, protocolVersion);
334                                         fprintf(stderr, "Byebye client #%d (%s)\n",
335                                                 event.u.net.sender, data);
336                                         SendPacketTo(event.u.net.sender, 0, NP_error, strlen(data)+1, data);
337                                         SCloseNet(event.u.net.sender);
338                                 } //version mismatch
339                                 fprintf(stderr, "Accepted #%d\n", event.u.net.sender);
340                         } //NP_hello
341                 }
342         }
343         close(sockListen);
344         return 0;
345 } //WaitForConnection
346
347 ExtFunc int StartServer(void)
348 {
349         MyEvent event;
350         int playercount;
351         int playersReady = 0;
352         int i;
353
354         {
355                 char serverdata[255];
356                 for (i = 1; i <= totalPlayers; i++) {
357                         sprintf(serverdata, "Netris server %s", version_string);
358                         SendPacketTo(i, i, NP_hello, strlen(serverdata)+1, serverdata);
359                 }
360         }
361
362         do {
363                 if (WaitMyEvent(&event, EM_net) == E_net) {
364 //                      fprintf(stderr, "in %d: %d\n",
365 //                              netGen[event.u.net.sender].fd, event.u.net.type);
366                         switch(event.u.net.type) {
367                                 case NP_endConn:
368                                 { //client went away :(
369                                         Players[event.u.net.sender].alive = 0;
370                                         for (i = 1; i <= totalPlayers; i++)
371                                                 SendPacketTo(i, event.u.net.sender,
372                                                         NP_argghhh, sizeof(Players[0].alive),
373                                                         &Players[event.u.net.sender].alive);
374                                         break;
375                                 } //NP_endConn
376                                 case NP_hello:
377                                         break;
378                                 case NP_newPlayer:
379                                 { //receive player details and return other players
380                                         memcpy(&Players[event.u.net.sender],
381                                                 event.u.net.data, event.u.net.size);
382                                         if (!Players[event.u.net.sender].team)
383                                                 Players[event.u.net.sender].team = 256 - event.u.net.sender;
384                                         fprintf(stderr, "player %d: %s <%s>\n", event.u.net.sender,
385                                                 event.u.net.data, //Players[event.u.net.sender].name
386                                                 Players[event.u.net.sender].host);
387                                         SendPacketTo(event.u.net.sender, 0, NP_gamedata,
388                                                 sizeof(Game.seed)+sizeof(Game.initspeed), &Game);
389                                         for (i = 1; i <= totalPlayers; i++)
390                                                 if (i != event.u.net.sender)
391                                                         SendPacketTo(i, event.u.net.sender, event.u.net.type,
392                                                                 sizeof(Player) - sizeof(Players[0].spy),
393                                                                 &Players[event.u.net.sender]);
394                                         if (++playersReady >= totalPlayers) {
395                                                 fprintf(stderr, "Starting game (%010d)\n", Game.seed);
396                                                 for (i = 1; i <= totalPlayers; i++)
397                                                         SendPacketTo(i, 0, NP_goAhead, 0, NULL);
398                                         } //give go ahead
399                                         break;
400                                 } //NP_playerdata
401                                 default:
402                                 { //relay data to all players
403         //                              if (event.u.net.type >= NP_pause)
404                                         for (i = 1; i <= totalPlayers; i++)
405                                                 if (i != event.u.net.sender)
406                                                 if (event.u.net.type != NP_giveJunk
407                                                 || Players[i].team != Players[event.u.net.sender].team)
408                                                         SendPacketTo(i, event.u.net.sender, event.u.net.type,
409                                                                 event.u.net.size, event.u.net.data);
410                                         break;
411                                 }
412                         }
413                 } //E_net
414                 playercount = 0;
415                 for (i = 1; i <= totalPlayers; i++)
416                         if (netGen[i].fd >= 0) playercount++;
417         } while (playercount > 1);
418 } //StartServer
419
420
421 ExtFunc void Header(void)
422 {
423         fprintf(stderr,
424           "NETRIS Server %s\t(c) 2002 Shiar <shiar@shiar.org>\n\n",
425           version_string);
426 } //Header
427
428 ExtFunc void Usage(void)
429 {
430         Header();
431         fprintf(stderr,
432           "Usage: netris <options>\n"
433           "\n"
434           "  -h, --help\t\tPrint this usage information\n"
435           "  -H, --info\t\tShow distribution and warranty information\n"
436           "\n"
437           "  -p, --port <port>\tSet port number (default is %d)\n"
438           "\n"
439           "  -s, --seed <seed>\tStart with given random seed\n"
440           "  -i, --speed <sec>\tSet the initial step-down interval, in seconds\n"
441           "\n", DEFAULT_PORT);
442 }
443
444 ExtFunc void DistInfo(void)
445 {
446         Header();
447         fprintf(stderr,
448           "This program is free software; you can redistribute it and/or modify\n"
449           "it under the terms of the GNU General Public License as published by\n"
450           "the Free Software Foundation; either version 2 of the License, or\n"
451           "(at your option) any later version.\n"
452           "\n"
453           "This program is distributed in the hope that it will be useful,\n"
454           "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
455           "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
456           "GNU General Public License for more details.\n"
457           "\n"
458           "You should have received a copy of the GNU General Public License\n"
459           "along with this program; if not, write to the Free Software\n"
460           "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n"
461           "\n");
462 } //DistInfo
463
464 ExtFunc void WriteConf(void)
465 {
466         FILE *file_out;
467
468         file_out = fopen(CONFIG_FILE, "w");
469         if (file_out == NULL) {
470                 perror("Error writing config file");
471                 exit(1);
472         }
473
474         fprintf(file_out, "### NETRIS %s Config file ###\n\n", version_string);
475
476         fclose(file_out);
477         fprintf(stderr, "Wrote new game configuration to %s\n", CONFIG_FILE);
478 } //WriteConf
479
480 ExtFunc void HandleOption(char tag, char *value)
481 {
482         switch (tag) {
483                 case 'p':       //port
484                         port = atoi(value);
485                         break;
486                 case 'c':       //connections
487                         Connections = atoi(value);
488                         break;
489                 case 'i':       //speed (of level 1)
490                         Game.initspeed = atof(value) * 1e6;
491                         break;
492                 case 's':       //seed
493                         Game.seed = atoi(value);
494                         break;
495                 case 'H':       //info
496                         DistInfo(); exit(0);
497                 case 'h':       //help
498                         Usage(); exit(0);
499                 default:
500                         break;
501         }
502 } //HandleParam
503
504 ExtFunc void ReadConf(char *filename)
505 {
506         FILE *file_in;
507         char buf[513];
508         int i;
509         char *ch;
510         char tag[81], value[81];
511
512         file_in = fopen(filename, "r");
513         if (file_in) {
514                 while (fgets(buf, 512, file_in) != NULL) {
515                         if ((ch = strchr(buf, '#')))
516                                 *ch = '\0'; // truncate string from # char
517                         for (i = strlen(buf)-1; i >= 0; i--)
518                                 if (buf[i] == ' ' || buf[i] == '\n' || buf[i] == '\t' || buf[i] == 13)
519                                         buf[i] = '\0';
520                                 else break;
521
522                         sscanf(buf, "%80[^= \t] = %80[^\n]", tag, value);
523                         for (i = 0; options[i].name; i++){
524                                 if (!strcasecmp(options[i].name, tag)) {
525                                         HandleOption(options[i].val, value);
526                                         break;
527                                 }
528                         }
529                 }
530                 fclose(file_in);
531         } //read file
532         else {
533                 fprintf(stderr, "Unable to open config file %s.\n", filename);
534         } //defaults
535
536 } //ReadConf
537
538 ExtFunc void CatchInt(int sig)
539 {
540         siglongjmp(close_env, 1);
541 }
542
543 ExtFunc int main(int argc, char **argv)
544 {
545         char ch;
546
547         if (sigsetjmp(close_env, 1)) exit(0);
548         signal(SIGINT, CatchInt);
549         port = DEFAULT_PORT;
550         Game.initspeed = DEFAULT_INTERVAL;
551         Game.seed = time(0);
552
553 //      if (getopt(argc, argv, "f:") == 'f')
554 //              ReadConf(optarg);
555 //      else
556         ReadConf(CONFIG_FILE);
557         while ((ch = getopt_long(argc, argv,
558                         "hHp:i:s:c:", options, NULL)) != -1)
559                 HandleOption(ch, optarg);
560         if (optind < argc) {
561                 Usage();
562                 exit(1);
563         }
564 //      WriteConf();
565
566         Header();
567         WaitForConnection(port);
568         StartServer();
569         return 0;
570 }
571
572 /*
573  * vi: ts=4 ai
574  * vim: noai si
575  */