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