b30ba02a21e2bf3d4c424d646666e6964b973097
[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
20 #define NOEXT
21 #include "netris.h"
22
23 #include <stdlib.h>
24 #include <stdbool.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 #include "util.h"
36
37 #define HEADER_SIZE sizeof(netint4[3])
38
39 static struct option options[] = {
40         { "wait",        0, 0, 'w' },
41         { "port",        1, 0, 'p' },
42         { "quadra",      1, 0, 'q' },
43         { "min-players", 1, 0, 'm' },
44         { "max-players", 1, 0, 'x' },
45         { "continuous",  1, 0, 'c' },
46         { "speed",       1, 0, 'i' },
47         { "seed",        1, 0, 's' },
48         { "verbose",     0, 0, 'v' },
49         { "info",        0, 0, 'H' },
50         { "help",        0, 0, 'h' },
51         { 0,             0, 0,  0  }
52 };
53
54 static char minplayers = 2;
55 static char maxplayers = 8;
56 static char playercount;
57 static char verbose = 0;
58
59 struct sockaddr_in addr;
60
61 MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event);
62 static EventGenRec netGen[MAX_SCREENS] = {
63         { NULL, 0, FT_read, -1, NetGenFunc, EM_net, 0, "\0", 0, HEADER_SIZE }
64 };
65
66 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event);
67 static EventGenRec alarmGen = {
68         &alarmGen, 0, FT_read, -1, AlarmGenFunc, EM_alarm
69 };
70
71 static MyEventType ConnGenFunc(EventGenRec *gen, MyEvent *event);
72 static EventGenRec connGen = {
73         NULL, 0, FT_read, -1, ConnGenFunc, EM_connect
74 };
75
76 static EventGenRec *nextGen = &alarmGen;
77
78 static sigjmp_buf close_env;
79
80
81 void SendPacketTo(short playa, short uid, NetPacketType type, int size, void *data)
82 { //send to someone
83         netint4 header[3];
84
85         if (netGen[playa].fd >= 0) {
86                 if (verbose)
87                         fprintf(stderr, ": send %d from %d to %d\n", type, uid, playa);
88                 header[0] = hton4(uid);
89                 header[1] = hton4(type);
90                 header[2] = hton4(size + HEADER_SIZE);
91                 if (MyWrite(netGen[playa].fd, header, HEADER_SIZE) != HEADER_SIZE)
92                         die("write (header)");
93                 if (size > 0 && data && MyWrite(netGen[playa].fd, data, size) != size)
94                         die("write");
95         }
96 }
97
98 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event)
99 {
100         return E_alarm;
101 }
102
103 void SCloseNet(short playa)
104 { //kick some connection's ass!
105         MyEvent event;
106
107         if (netGen[playa].fd >= 0) {
108                 if (Players[playa].alive >= 0) {
109                         SendPacketTo(playa, 0, NP_endConn, 0, NULL);
110                         do {} while (WaitMyEvent(&event, EM_net) != E_lostConn);
111                 } //say bye to player
112                 close(netGen[playa].fd);
113                 netGen[playa].fd = -1;
114         }
115         if (netGen[playa].next)
116                 RemoveEventGen(&netGen[playa]);
117 }
118
119 void CloseNets(void)
120 { //nou oogjes dicht en snaveltjes toe
121         int i;
122
123         fprintf(stderr, "- Closing connections...\n");
124         for (i = 1; i < MAX_SCREENS; i++)
125                 SCloseNet(i); // bye everybuddy
126         fprintf(stderr, "* All Done\n\n");
127 }
128
129 MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event)
130 { //receive
131         int result;
132         short uid, type, size;
133         netint4 *data = (netint4*)&(gen->buf);
134
135         result = MyRead(gen->fd, gen->buf + gen->bufSize,
136                 gen->bufGoal - gen->bufSize);
137         if (result < 0) {
138                 fprintf(stderr, "- Closed connection to player #%d\n", gen->player);
139                 return E_lostConn;
140         }
141         gen->bufSize += result;
142         if (gen->bufSize < gen->bufGoal)
143                 return E_none;
144         // *ugly* memcpy(data, gen->buf, sizeof(data));
145         uid  = ntoh4(data[0]);
146         type = ntoh4(data[1]);
147         size = ntoh4(data[2]);
148         gen->bufGoal = size;
149         if (gen->bufSize < gen->bufGoal)
150                 return E_none;
151         gen->bufSize = 0;
152         gen->bufGoal = HEADER_SIZE;
153         event->u.net.sender = gen->player;
154         event->u.net.uid = uid;
155         event->u.net.type = type;
156         event->u.net.size = size - HEADER_SIZE;
157         event->u.net.data = gen->buf + HEADER_SIZE;
158         if (type == NP_endConn) {
159                 fprintf(stderr, "- Quit player #%d\n", gen->player);
160                 return E_lostConn;
161         } //client sent quit signal
162         return E_net;
163 }
164
165
166 static MyEventType ConnGenFunc(EventGenRec *gen, MyEvent *event)
167 {
168         int addrLen;
169         struct linger val2;
170         int new;
171
172         addrLen = sizeof(addr);
173         for (new = 1; new <= MAX_SCREENS; new++)
174                 if (netGen[new].fd < 0) break;
175         if (new > maxplayers) return;
176
177         if ((
178                 netGen[new].fd = accept(gen->fd, (struct sockaddr *)&addr, &addrLen)
179         ) < 0)
180                 die("accept");
181         fprintf(stderr, "+ Connection: %s\n", inet_ntoa(addr.sin_addr));
182         val2.l_onoff = 1;
183         val2.l_linger = 0;
184         setsockopt(netGen[new].fd, SOL_SOCKET, SO_LINGER,(void *)&val2,
185                 sizeof(val2));
186         AddEventGen(&netGen[new]);
187         netGen[new].player = event->u.net.uid = new;
188         { //new connection; netGen already initialized in GenFunc
189                 struct hostent *host;
190
191                 sprintf(Players[new].host, "%s", inet_ntoa(addr.sin_addr));
192                 if (addr.sin_family == AF_INET) {
193                         host = gethostbyaddr(
194                                 (void *)&addr.sin_addr, sizeof(struct in_addr), AF_INET
195                         );
196                         if (host) {
197                                 strncpy(Players[new].host, host->h_name,
198                                         sizeof(Players[new].host) - 1);
199                                 Players[new].host[sizeof(Players[new].host) - 1] = 0;
200                         } //set player's hostname
201                 }
202         } //E_connect
203         return E_connect;
204 }
205
206 void CountPlayers(void)
207 { //count number of players/teams
208         int i, j;
209         playercount = 0;
210         for (i = 1; i < MAX_SCREENS; i++) if (Players[i].alive > 0) {
211                 if (Players[i].team < 128) for (j = 1; j < i; j++) {
212                         if (Players[j].alive > 0 && Players[j].team == Players[i].team) {
213                                 playercount--; // player of same team counted before
214                                 break;
215                         }
216                 }
217                 playercount++;
218         } //player alive
219 }
220
221 int StartServer(void)
222 {
223         MyEvent event;
224         netint2 currentpiece[MAX_SCREENS];
225         int playersReady = 0;
226         bool paused = 1;
227         int i;
228         char teams[10][7] = {
229                 "", "Green", "Cyan", "Blue", "Purple",
230                 "Red", "Grey", "White", "*Orange"
231         };
232
233         do {
234                 switch (WaitMyEvent(&event, EM_any)) {
235                         case E_lostConn: //client went away :(
236                                 Players[event.u.net.sender].alive = -1;
237                                 for (i = 1; i < MAX_SCREENS; i++)
238                                         if (Players[i].alive >= 0)
239                                                 SendPacketTo(i, event.u.net.sender,
240                                                         NP_part, sizeof(Players[0].alive),
241                                                         &Players[event.u.net.sender].alive);
242                                 SCloseNet(event.u.net.sender);
243                                 break; //NP_endConn
244                         case E_net:
245                                 if (verbose) fprintf(stderr, ": %d sent %d\n",
246                                         netGen[event.u.net.sender].fd, event.u.net.type);
247                                 switch(event.u.net.type) {
248                                 case NP_hello:
249 //                                      if (event.u.net.type != NP_hello) ByeClient(new);
250                                 {
251                                         netint4 versiondata[2];
252                                         char data[255];
253                                         int major;
254                                         int protocolVersion;
255
256                                         memcpy(versiondata, event.u.net.data, sizeof(versiondata));
257                                         major = ntoh4(versiondata[0]);
258                                         protocolVersion = ntoh4(versiondata[1]);
259                                         if (major != MAJOR_VERSION
260                                          || protocolVersion != PROTOCOL_VERSION) {
261                                                 snprintf(data, sizeof(data),
262                                                         "Version mismatch: received %d.%d",
263                                                         major, protocolVersion);
264                                                 fprintf(stderr, "= Wrong version player #%d (%s)\n",
265                                                         event.u.net.sender, data);
266                                                 SendPacketTo(event.u.net.sender, 0, NP_error,
267                                                         strlen(data)+1, data);
268                                                 SCloseNet(event.u.net.sender);
269                                         } //version mismatch
270                                         fprintf(stderr, "* Accepted player #%d\n",
271                                                 event.u.net.sender);
272                                         break;
273                                 } //NP_hello
274                                 case NP_newPlayer:
275                                 //receive player details and return other players
276                                         memcpy(&Players[event.u.net.sender],
277                                                 event.u.net.data, event.u.net.size);
278                                         if (Players[event.u.net.sender].team < 1
279                                          || Players[event.u.net.sender].team > 7) {
280                                                 int team;
281
282                                                 for (team = 1; team < 7; team++) {
283                                                         for (i = 1; i < MAX_SCREENS; i++)
284                                                                 if (Players[i].alive > 0 && Players[i].team == team)
285                                                                         break; //team in use
286                                                         if (i == MAX_SCREENS) break;
287                                                 } //find unused team
288                                                 Players[event.u.net.sender].team = team;
289                                                 SendPacketTo(event.u.net.sender, event.u.net.sender, NP_team,
290                                                         sizeof(Players[event.u.net.sender].team),
291                                                         &Players[event.u.net.sender].team);
292                                         } //invalid team
293                                         if (Game.started < 2)
294                                                 Players[event.u.net.sender].flags |= SCF_paused;
295                                         if (!Game.continuous && Game.started >= 2) {
296                                                 char data[40];
297                                                 strcpy(data, "Can't join: Game has already started");
298                                                 fprintf(stderr, "- Can't join player #%d in "
299                                                         "non-continuous game\n", event.u.net.sender);
300                                                 SendPacketTo(event.u.net.sender, 0, NP_error,
301                                                         strlen(data)+1, data);
302 //                                              SCloseNet(event.u.net.sender, 0);
303                                                 break;
304                                         } //can't join started game
305                                         {
306                                                 static struct {
307                                                         int playerflags;
308                                                         int gravity;    //1
309                                                         int started;    //2
310                                                         int continuous; //3
311                                                         long seed;              //4
312                                                         int initspeed;  //5
313                                                 } data;
314
315                                                 memcpy(&data, &Players[event.u.net.sender].flags,
316                                                         sizeof(data.playerflags));
317                                                 memcpy(&data.gravity, &Game.gravity,
318                                                         sizeof(data) - sizeof(data.playerflags));
319                                                 SendPacketTo(event.u.net.sender, 0, NP_gamedata,
320                                                         sizeof(data), &data);
321                                         } //send game options
322                                         for (i = 1; i < MAX_SCREENS; i++)
323                                                 if (netGen[i].fd >= 0 && i != event.u.net.sender) {
324                                                         SendPacketTo(event.u.net.sender, i, NP_newPlayer,
325                                                                 sizeof(_Player), &Players[i]);
326                                                         SendPacketTo(event.u.net.sender, i, NP_newPiece,
327                                                                 sizeof(Players[i].curShape), &Players[i].curShape);
328                                                         SendPacketTo(i, event.u.net.sender, NP_newPlayer,
329                                                                 sizeof(_Player), &Players[event.u.net.sender]);
330                                                 } //send (to) players
331                                         fprintf(stderr, "> Joined player #%d: %s <%s> (%s)\n",
332                                                 event.u.net.sender,
333                                                 Players[event.u.net.sender].name,
334                                                 Players[event.u.net.sender].host,
335                                                 teams[Players[event.u.net.sender].team]);
336                                         if (++playersReady >= minplayers) {
337                                                 if (Game.started > 1)
338                                                         SendPacketTo(event.u.net.sender, 0,
339                                                                 NP_start, 0, NULL);
340 /*                                              else {
341                                                         fprintf(stderr, "* Starting game (%010d)\n",
342                                                                 Game.seed);
343                                                         for (i = 1; i < MAX_SCREENS; i++)
344                                                                 SendPacketTo(i, 0, NP_start, 0, NULL);
345                                                         Game.started++;
346                                                 } //first goahead (to all)*/
347                                         } //give go ahead
348                                         break; //NP_playerdata
349                                 case NP_newPiece:
350                                         memcpy(&Players[event.u.net.sender].curShape,
351                                                 event.u.net.data, sizeof(Players[0].curShape));
352                                         goto sendtoall;
353                                 case NP_argghhh:
354                                         Players[event.u.net.sender].alive = 0;
355                                         fprintf(stderr, "< Player #%d died\n",
356                                                 event.u.net.sender);
357                                         //check for unpaused game
358                                 case NP_pause:
359                                 {
360                                         Players[event.u.net.sender].flags ^= SCF_paused;
361                                         paused = Game.started < 1;
362                                         for (i = 1; i < MAX_SCREENS; i++)
363                                                 if (Players[i].alive > 0)
364                                                         paused |= (Players[i].flags & SCF_paused) != 0;
365                                         fprintf(stderr, "* Player #%d (un)paused (pause=%d)\n",
366                                                 event.u.net.sender, paused);
367                                         goto sendtoall;
368                                 } //NP_pause
369                                 default: //relay data to all players
370                                 sendtoall:
371 //                                      if (event.u.net.type >= NP_pause)
372                                         if (event.u.net.type >= NP_rotright
373                                          && Game.started < 2)
374                                                 break;
375                                         for (i = 1; i < MAX_SCREENS; i++)
376                                                 if (i != event.u.net.sender)
377                                                 if (event.u.net.type != NP_giveJunk
378                                                  || Players[i].team != Players[event.u.net.sender].team)
379                                                         SendPacketTo(i, event.u.net.sender,
380                                                                 event.u.net.type, event.u.net.size,
381                                                                 event.u.net.data);
382                                         break; //>=NP_paused
383                                 }
384                                 break; //E_net
385                         case E_connect:
386                         { //new connection; netGen already initialized in GenFunc
387                                 char serverdata[255];
388
389                                 sprintf(serverdata, "Netris server %s", version_string);
390                                 SendPacketTo(event.u.net.uid, event.u.net.uid, NP_hello,
391                                         strlen(serverdata)+1, serverdata);
392                                 break;
393                         } //E_connect
394                 } //event
395                 CountPlayers();
396                 if (Game.started < 1) {
397                         if (playercount >= 2) {
398                                 fprintf(stderr, "* Game (%010d) ready to start\n", Game.seed);
399                                 Game.started++;
400                         } //give goahead
401                 } //game not yet started
402                 else {
403                         if (playercount < 2) {
404                                 fprintf(stderr, "* Stopping game\n");
405                                 if (Game.seed) Game.seed++;
406                                 if (Game.started > 1) for (i = 1; i < MAX_SCREENS; i++) {
407                                         if (Players[i].alive >= 0) {
408                                                 Players[i].alive = 1;
409                                                 Players[i].flags |= SCF_paused;
410                                                 SendPacketTo(i, 0, NP_stop,
411                                                         sizeof(Game.seed), &Game.seed);
412                                         } //transmit game stop and set players not ready
413                                 }
414                                 paused = 1;
415                                 Game.started = 0;
416                         } //too few players for game
417                         if (Game.started == 1 && !paused) {
418                                 Game.started++;
419                                 fprintf(stderr, "* Game starts\n");
420                                 for (i = 1; i < MAX_SCREENS; i++)
421                                         if (Players[i].alive > 0)
422                                                 SendPacketTo(i, 0, NP_start, 0, NULL);
423                         } //everybody ready to start
424                 } //game (ready to) start(ed)
425         } while (1);
426         fprintf(stderr, "* Exiting server\n");
427 }
428
429
430 void SHeader(void)
431 {
432         fprintf(stderr,
433                 "NETRIS Server %s\t(c) 2002 Shiar <shiar@shiar.org>\n\n",
434                 version_string);
435 }
436
437 void SUsage(void)
438 {
439         SHeader();
440         fprintf(stderr,
441                 "Usage: netris <options>\n"
442                 "\n"
443                 "  -h, --help\t\tPrint this usage information\n"
444                 "  -H, --info\t\tShow distribution and warranty information\n"
445                 "\n"
446                 "  -p, --port %d\tSet port number\n"
447                 "\n"
448                 "  -s, --seed <seed>\tStart with given random seed\n"
449                 "  -i, --speed %.2f\tSet the initial step-down interval, in seconds\n"
450                 "  -m, --min-players %d\tNumber of players required before starting the game\n"
451                 "  -x, --max-players %d\tMaximum number of players allowed in the game\n"
452                 "  -c, --continuous\tDon'n quit the game\n"
453                 "\n",
454                 DEFAULT_PORT, DEFAULT_INTERVAL/1e6, minplayers, maxplayers
455         );
456 }
457
458 void HandleOption(char tag, char *value)
459 {
460         switch (tag) {
461         case 'v':  //verbose
462                 verbose = 1;
463                 break;
464         case 'p':  //port
465                 port = atoi(value);
466                 break;
467         case 'c':  //min-players
468                 Game.continuous = atoi(value);
469                 break;
470         case 'm':  //min-players
471                 minplayers = atoi(value);
472                 break;
473         case 'x':  //max-players
474                 maxplayers = MIN(atoi(value), MAX_SCREENS);
475                 break;
476         case 'q':  //quadra-style gravity
477                 Game.gravity ^= 1;
478                 break;
479         case 'i':  //speed (of level 1)
480                 Game.initspeed = atof(value) * 1e6;
481                 break;
482         case 's':  //seed
483                 Game.seed = atoi(value);
484                 break;
485         case 'H':  //info
486                 SHeader();
487                 DistInfo();
488                 exit(0);
489         case 'h':  //help
490                 SUsage();
491                 exit(0);
492         default:
493                 break;
494         }
495 }
496
497 void ReadConf(char *filename)
498 {
499         FILE *file_in;
500         char buf[513];
501         int i;
502         char *ch;
503         char tag[81], value[81];
504
505         file_in = fopen(filename, "r");
506         if (file_in) {
507                 while (fgets(buf, 512, file_in) != NULL) {
508                         if ((ch = strchr(buf, '#')))
509                                 *ch = '\0'; // truncate string from # char
510                         for (i = strlen(buf)-1; i >= 0; i--)
511                                 if (buf[i] == ' '  || buf[i] == '\t'
512                                  || buf[i] == '\n' || buf[i] == 13)
513                                         buf[i] = '\0';
514                                 else break;
515
516                         sscanf(buf, "%80[^= \t] = %80[^\n]", tag, value);
517                         for (i = 0; options[i].name; i++){
518                                 if (!strcasecmp(options[i].name, tag)) {
519                                         HandleOption(options[i].val, value);
520                                         break;
521                                 }
522                         }
523                 }
524                 fclose(file_in);
525         } //read file
526         else {
527                 fprintf(stderr, "Unable to open config file %s.\n", filename);
528         } //defaults
529
530 }
531
532 void CatchInt(int sig)
533 {
534         siglongjmp(close_env, 1);
535 }
536
537 int main(int argc, char **argv)
538 {
539         char ch;
540
541         if (sigsetjmp(close_env, 1)) exit(0);
542         signal(SIGINT, CatchInt);
543         port = DEFAULT_PORT;
544         maxplayers = 8;
545         Game.initspeed = DEFAULT_INTERVAL;
546         Game.seed = time(0);
547         Game.gravity = 0;
548         {
549                 int i;
550
551                 for (i = 1; i < MAX_SCREENS; i++)
552                         Players[i].alive = -1;
553         }
554
555 //      if (getopt(argc, argv, "f:") == 'f')
556 //              ReadConf(optarg);
557 //      else
558         ReadConf(CONFIG_FILE);
559         while ((ch = getopt_long(
560                 argc, argv, "hHvqp:i:s:c:m:x:", options, NULL
561         )) != -1)
562                 HandleOption(ch, optarg);
563         if (optind < argc) {
564                 SUsage();
565                 exit(1);
566         }
567 //      WriteConf();
568
569         SHeader();
570
571         {
572                 int i;
573
574                 for (i = 1; i < MAX_SCREENS; i++)
575                         memcpy(&netGen[i], &netGen[0], sizeof(EventGenRec));
576         } //setup netGen var
577
578         AtExit(CloseNets);
579
580         {
581                 int val1;
582
583                 memset(&addr, 0, sizeof(addr));
584                 addr.sin_family = AF_INET;
585                 addr.sin_port = htons(port);
586                 addr.sin_addr.s_addr = htonl(INADDR_ANY);
587                 if ((connGen.fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
588                         die("socket");
589                 val1 = 1;
590                 setsockopt(connGen.fd, SOL_SOCKET, SO_REUSEADDR,
591                         (void *)&val1, sizeof(val1));
592                 if (bind(connGen.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
593                         die("bind");
594                 if (listen(connGen.fd, 1) < 0)
595                         die("listen");
596
597                 AddEventGen(&connGen);
598         } //setup listening sock
599
600         StartServer(); //server loop
601
602         return 0;
603 }
604