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