2 * Netris -- A free networked version of T*tris
3 * Copyright (C) 1994,1995,1996 Mark H. Weaver <mhw@netris.org>
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.
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.
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.
19 * $Id: game.c,v 1.39 1999/05/16 06:56:27 mhw Exp $
27 #include <sys/types.h>
28 #include <netinet/in.h>
30 #include <sys/socket.h>
35 #define HEADER_SIZE sizeof(netint4[3])
37 static struct option options[] = {
38 { "wait", 0, 0, 'w' },
39 { "port", 1, 0, 'p' },
40 { "min-players",1, 0, 'm' },
41 { "max-players",1, 0, 'x' },
42 { "continuous", 1, 0, 'c' },
43 { "speed", 1, 0, 'i' },
44 { "seed", 1, 0, 's' },
45 { "verbose", 0, 0, 'v' },
46 { "info", 0, 0, 'H' },
47 { "help", 0, 0, 'h' },
51 static char minplayers = 2;
52 static char playercount;
53 static char verbose = 0;
55 struct sockaddr_in addr;
57 static char *gameNames[GT_len] = { "OnePlayer", "ClassicTwo" };
59 ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event);
60 static EventGenRec netGen[MAX_SCREENS] = {
61 { NULL, 0, FT_read, -1, NetGenFunc, EM_net, 0, "\0", 0, HEADER_SIZE } };
63 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event);
64 static EventGenRec alarmGen =
65 { &alarmGen, 0, FT_read, -1, AlarmGenFunc, EM_alarm };
67 static MyEventType ConnGenFunc(EventGenRec *gen, MyEvent *event);
68 static EventGenRec connGen =
69 { NULL, 0, FT_read, -1, ConnGenFunc, EM_connect };
71 static EventGenRec *nextGen = &alarmGen;
73 static sigjmp_buf close_env;
76 ExtFunc volatile void die(char *msg)
82 ExtFunc int MyRead(int fd, void *data, int len)
88 result = read(fd, data, left);
90 data = ((char *)data) + result;
93 else if (errno != EINTR)
99 ExtFunc int MyWrite(int fd, void *data, int len)
105 result = write(fd, data, left);
107 data = ((char *)data) + result;
110 else if (errno != EINTR)
116 ExtFunc void SendPacketTo(short playa, short uid, NetPacketType type, int size, void *data)
120 if (netGen[playa].fd >= 0) {
121 header[0] = hton4(uid);
122 header[1] = hton4(type);
123 header[2] = hton4(size + HEADER_SIZE);
124 if (MyWrite(netGen[playa].fd, header, HEADER_SIZE) != HEADER_SIZE)
125 die("write (header)");
126 if (size > 0 && data && MyWrite(netGen[playa].fd, data, size) != size)
131 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event)
136 ExtFunc void AddEventGen(EventGenRec *gen)
138 assert(gen->next == NULL);
139 assert(nextGen->next != (void*)0xffffffff);
140 gen->next = nextGen->next;
144 ExtFunc void RemoveEventGen(EventGenRec *gen)
146 // assert(gen->next != NULL); /* Be more forgiving, for SIGINTs */
148 while (nextGen->next != gen)
149 nextGen = nextGen->next;
150 nextGen->next = gen->next;
155 ExtFunc void AtExit(void (*handler)(void))
156 { //setup something to do at exit (^C)
158 on_exit((void *)handler, NULL);
164 ExtFunc void SCloseNet(short playa)
165 { //kick some connection's ass!
168 if (netGen[playa].fd >= 0) {
169 if (Players[playa].alive >= 0) {
170 SendPacketTo(playa, 0, NP_endConn, 0, NULL);
171 do{} while (WaitMyEvent(&event, EM_net) != E_lostConn);
172 } //say bye to player
173 close(netGen[playa].fd);
174 netGen[playa].fd = -1;
176 if (netGen[playa].next)
177 RemoveEventGen(&netGen[playa]);
180 ExtFunc void CloseNets(void)
181 { //nou oogjes dicht en snaveltjes toe
184 fprintf(stderr, "- Closing connections...\n");
185 for (i = 1; i < MAX_SCREENS; i++)
186 SCloseNet(i); //bye everybuddy
187 fprintf(stderr, "* All Done\n\n");
190 ExtFunc MyEventType WaitMyEvent(MyEvent *event, int mask)
195 int result, anyReady, anySet;
199 for (i = 0; i < FT_len; ++i)
201 anyReady = anySet = 0;
204 if (gen->mask & mask) {
208 FD_SET(gen->fd, &fds[gen->fdType]);
213 } while (gen != nextGen);
216 tv.tv_usec = (retry && !anyReady) ? 500000 : 0;
217 result = select(FD_SETSIZE, &fds[FT_read], &fds[FT_write],
218 &fds[FT_except], anyReady ? &tv : NULL);
221 if (retry && !anyReady)
227 if ((gen->mask & mask)
228 && (gen->ready || (result > 0 && gen->fd >= 0
229 && FD_ISSET(gen->fd, &fds[gen->fdType])))) {
231 event->type = gen->func(gen, event);
232 if (event->type != E_none) {
238 } while (gen != nextGen);
243 ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event)
246 short uid, type, size;
247 netint4 *data = (netint4*)&(gen->buf);
249 result = MyRead(gen->fd, gen->buf + gen->bufSize,
250 gen->bufGoal - gen->bufSize);
252 fprintf(stderr, "- Closed connection to player #%d\n", gen->player);
255 gen->bufSize += result;
256 if (gen->bufSize < gen->bufGoal)
258 // *ugly* memcpy(data, gen->buf, sizeof(data));
259 uid = ntoh4(data[0]);
260 type = ntoh4(data[1]);
261 size = ntoh4(data[2]);
263 if (gen->bufSize < gen->bufGoal)
266 gen->bufGoal = HEADER_SIZE;
267 event->u.net.sender = gen->player;
268 event->u.net.uid = uid;
269 event->u.net.type = type;
270 event->u.net.size = size - HEADER_SIZE;
271 event->u.net.data = gen->buf + HEADER_SIZE;
272 if (type == NP_endConn) {
273 fprintf(stderr, "- Quit player #%d\n", gen->player);
275 } //client sent quit signal
280 static MyEventType ConnGenFunc(EventGenRec *gen, MyEvent *event)
286 addrLen = sizeof(addr);
287 for (new = 1; new <= MAX_SCREENS; new++)
288 if (netGen[new].fd < 0) break;
289 if (new > Game.maxplayers) return;
291 if ((netGen[new].fd =
292 accept(gen->fd, (struct sockaddr *)&addr, &addrLen)) < 0)
294 fprintf(stderr, "+ Connection: %s\n", inet_ntoa(addr.sin_addr));
297 setsockopt(netGen[new].fd, SOL_SOCKET, SO_LINGER,(void *)&val2,
299 AddEventGen(&netGen[new]);
300 netGen[new].player = event->u.net.uid = new;
301 { //new connection; netGen already initialized in GenFunc
302 struct hostent *host;
304 sprintf(Players[new].host, "%s", inet_ntoa(addr.sin_addr));
305 if (addr.sin_family == AF_INET) {
306 host = gethostbyaddr((void *)&addr.sin_addr,
307 sizeof(struct in_addr), AF_INET);
309 strncpy(Players[new].host, host->h_name,
310 sizeof(Players[new].host) - 1);
311 Players[new].host[sizeof(Players[new].host) - 1] = 0;
312 } //set player's hostname
318 ExtFunc void CountPlayers(void)
319 { //count number of players/teams
322 for (i = 1; i < MAX_SCREENS; i++) if (Players[i].alive > 0) {
323 if (Players[i].team < 128) for (j = 1; j < i; j++)
324 if (Players[j].alive > 0 && (Players[j].team == Players[i].team)) {
327 } //player of same team counted before
332 ExtFunc int StartServer(void)
335 netint2 currentpiece[MAX_SCREENS];
336 int playersReady = 0;
339 char teams[10][7] = { "", "Green", "Cyan", "Blue", "Purple",
340 "Red", "Grey", "White", "*Orange" };
343 switch (WaitMyEvent(&event, EM_any)) {
344 case E_lostConn: //client went away :(
345 Players[event.u.net.sender].alive = -1;
346 for (i = 1; i < MAX_SCREENS; i++)
347 if (Players[i].alive >= 0)
348 SendPacketTo(i, event.u.net.sender,
349 NP_part, sizeof(Players[0].alive),
350 &Players[event.u.net.sender].alive);
351 SCloseNet(event.u.net.sender);
354 if (verbose) fprintf(stderr, ": %d sent %d\n",
355 netGen[event.u.net.sender].fd, event.u.net.type);
356 switch(event.u.net.type) {
358 // if (event.u.net.type != NP_hello) ByeClient(new);
360 netint4 versiondata[2];
364 memcpy(versiondata, event.u.net.data,
365 sizeof(versiondata));
366 major = ntoh4(versiondata[0]);
367 protocolVersion = ntoh4(versiondata[1]);
368 if (major != MAJOR_VERSION
369 || protocolVersion != PROTOCOL_VERSION) {
370 snprintf(data, sizeof(data),
371 "Version mismatch: received %d.%d",
372 major, protocolVersion);
373 fprintf(stderr, "= Wrong version player #%d (%s)\n",
374 event.u.net.sender, data);
375 SendPacketTo(event.u.net.sender, 0, NP_error,
376 strlen(data)+1, data);
377 SCloseNet(event.u.net.sender);
379 fprintf(stderr, "* Accepted player #%d\n",
384 //receive player details and return other players
385 memcpy(&Players[event.u.net.sender],
386 event.u.net.data, event.u.net.size);
387 if (Players[event.u.net.sender].team < 1
388 || Players[event.u.net.sender].team > 7) {
389 Players[event.u.net.sender].team =
390 event.u.net.sender % 7 + 1;
391 SendPacketTo(event.u.net.sender,
392 event.u.net.sender, NP_team,
393 sizeof(Players[event.u.net.sender].team),
394 &Players[event.u.net.sender].team);
396 if (Game.started < 2)
397 Players[event.u.net.sender].flags |= SCF_paused;
398 if (!Game.continuous && Game.started >= 2) {
400 strcpy(data, "Can't join: Game has already started");
401 fprintf(stderr, "- Can't join player #%d in "
402 "non-continuous game\n", event.u.net.sender);
403 SendPacketTo(event.u.net.sender, 0, NP_error,
404 strlen(data)+1, data);
405 // SCloseNet(event.u.net.sender, 0);
407 } //can't join started game
418 memcpy(&data, &Players[event.u.net.sender].flags,
419 sizeof(data.playerflags));
420 memcpy(&data.maxplayers, &Game,
421 sizeof(data) - sizeof(data.playerflags));
422 SendPacketTo(event.u.net.sender, 0, NP_gamedata,
423 sizeof(data), &data);
424 } //send game options
425 for (i = 1; i < MAX_SCREENS; i++)
426 if (netGen[i].fd >= 0 && i != event.u.net.sender) {
427 SendPacketTo(event.u.net.sender, i,
428 NP_newPlayer, sizeof(Player)
429 - sizeof(Players[0].spy)
430 - sizeof(Players[0].small),
432 SendPacketTo(event.u.net.sender, i,
433 NP_newPiece, sizeof(currentpiece[i]),
435 SendPacketTo(i, event.u.net.sender,
436 NP_newPlayer, sizeof(Player)
437 - sizeof(Players[0].spy)
438 - sizeof(Players[0].small),
439 &Players[event.u.net.sender]);
440 } //send (to) players
441 fprintf(stderr, "> Joined player #%d: %s <%s> (%s)\n",
443 Players[event.u.net.sender].name,
444 Players[event.u.net.sender].host,
445 teams[Players[event.u.net.sender].team]);
446 if (++playersReady >= minplayers) {
447 if (Game.started > 1)
448 SendPacketTo(event.u.net.sender, 0,
451 fprintf(stderr, "* Starting game (%010d)\n",
453 for (i = 1; i < MAX_SCREENS; i++)
454 SendPacketTo(i, 0, NP_start, 0, NULL);
456 } //first goahead (to all)*/
458 break; //NP_playerdata
460 memcpy(¤tpiece[event.u.net.sender],
461 event.u.net.data, sizeof(currentpiece[0]));
464 Players[event.u.net.sender].alive = 0;
465 fprintf(stderr, "< Player #%d died\n",
467 //check for unpaused game
470 Players[event.u.net.sender].flags ^= SCF_paused;
471 paused = Game.started < 1;
472 for (i = 1; i < MAX_SCREENS; i++)
473 if (Players[i].alive > 0)
474 paused |= Players[i].flags & SCF_paused;
475 fprintf(stderr, "* Player #%d (un)paused (pause=%d)\n",
476 event.u.net.sender, paused);
477 if (paused) paused = 1;
480 default: //relay data to all players
482 // if (event.u.net.type >= NP_pause)
483 if (event.u.net.type >= NP_rotright
486 for (i = 1; i < MAX_SCREENS; i++)
487 if (i != event.u.net.sender)
488 if (event.u.net.type != NP_giveJunk ||
489 Players[i].team != Players[event.u.net.sender].team)
490 SendPacketTo(i, event.u.net.sender,
491 event.u.net.type, event.u.net.size,
497 { //new connection; netGen already initialized in GenFunc
498 char serverdata[255];
500 sprintf(serverdata, "Netris server %s", version_string);
501 SendPacketTo(event.u.net.uid, event.u.net.uid, NP_hello,
502 strlen(serverdata)+1, serverdata);
507 if (Game.started < 1) {
508 if (playercount > 1) {
509 fprintf(stderr, "* Game (%010d) ready to start\n", Game.seed);
512 } //game not yet started
514 if (playercount < 2) {
515 fprintf(stderr, "* Stopping game\n");
516 if (Game.seed) Game.seed++;
517 if (Game.started > 1) for (i = 1; i < MAX_SCREENS; i++)
518 if (Players[i].alive >= 0) {
519 Players[i].alive = 1;
520 Players[i].flags |= SCF_paused;
521 SendPacketTo(i, 0, NP_stop,
522 sizeof(Game.seed), &Game.seed);
523 } //transmit game stop and set players not ready
526 } //too few players for game
527 if (Game.started == 1 && !paused) {
529 fprintf(stderr, "* Game starts\n");
530 for (i = 1; i < MAX_SCREENS; i++)
531 if (Players[i].alive > 0)
532 SendPacketTo(i, 0, NP_start, 0, NULL);
533 } //everybody ready to start
534 } //game (ready to) start(ed)
536 fprintf(stderr, "* Exiting server\n");
540 ExtFunc void Header(void)
543 "NETRIS Server %s\t(c) 2002 Shiar <shiar@shiar.org>\n\n",
547 ExtFunc void Usage(void)
551 "Usage: netris <options>\n"
553 " -h, --help\t\tPrint this usage information\n"
554 " -H, --info\t\tShow distribution and warranty information\n"
556 " -p, --port <port>\tSet port number (default is %d)\n"
558 " -s, --seed <seed>\tStart with given random seed\n"
559 " -i, --speed <sec>\tSet the initial step-down interval, in seconds\n"
560 " -m, --min-players <2>\tNumber of players required before starting the game\n"
561 " -x, --max-players <8>\tMaximum number of players allowed in the game\n"
562 " -c, --continuous\tDon'n quit the game\n"
566 ExtFunc void DistInfo(void)
570 "This program is free software; you can redistribute it and/or modify\n"
571 "it under the terms of the GNU General Public License as published by\n"
572 "the Free Software Foundation; either version 2 of the License, or\n"
573 "(at your option) any later version.\n"
575 "This program is distributed in the hope that it will be useful,\n"
576 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
577 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
578 "GNU General Public License for more details.\n"
580 "You should have received a copy of the GNU General Public License\n"
581 "along with this program; if not, write to the Free Software\n"
582 "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n"
586 ExtFunc void WriteConf(void)
590 file_out = fopen(CONFIG_FILE, "w");
591 if (file_out == NULL) {
592 perror("Error writing config file");
596 fprintf(file_out, "### NETRIS %s Config file ###\n\n", version_string);
599 fprintf(stderr, "Wrote new game configuration to %s\n", CONFIG_FILE);
602 ExtFunc void HandleOption(char tag, char *value)
611 case 'c': //min-players
612 Game.continuous = atoi(value);
614 case 'm': //min-players
615 minplayers = atoi(value);
617 case 'x': //max-players
618 Game.maxplayers = atoi(value);
619 if (Game.maxplayers >= MAX_SCREENS)
620 Game.maxplayers = MAX_SCREENS;
622 case 'i': //speed (of level 1)
623 Game.initspeed = atof(value) * 1e6;
626 Game.seed = atoi(value);
637 ExtFunc void ReadConf(char *filename)
643 char tag[81], value[81];
645 file_in = fopen(filename, "r");
647 while (fgets(buf, 512, file_in) != NULL) {
648 if ((ch = strchr(buf, '#')))
649 *ch = '\0'; // truncate string from # char
650 for (i = strlen(buf)-1; i >= 0; i--)
651 if (buf[i] == ' ' || buf[i] == '\t'
652 || buf[i] == '\n' || buf[i] == 13)
656 sscanf(buf, "%80[^= \t] = %80[^\n]", tag, value);
657 for (i = 0; options[i].name; i++){
658 if (!strcasecmp(options[i].name, tag)) {
659 HandleOption(options[i].val, value);
667 fprintf(stderr, "Unable to open config file %s.\n", filename);
672 ExtFunc void CatchInt(int sig)
674 siglongjmp(close_env, 1);
677 ExtFunc int main(int argc, char **argv)
681 if (sigsetjmp(close_env, 1)) exit(0);
682 signal(SIGINT, CatchInt);
685 Game.initspeed = DEFAULT_INTERVAL;
690 for (i = 1; i < MAX_SCREENS; i++)
691 Players[i].alive = -1;
694 // if (getopt(argc, argv, "f:") == 'f')
697 ReadConf(CONFIG_FILE);
698 while ((ch = getopt_long(argc, argv,
699 "hHvp:i:s:c:m:x:", options, NULL)) != -1)
700 HandleOption(ch, optarg);
712 for (i = 1; i < MAX_SCREENS; i++)
713 memcpy(&netGen[i], &netGen[0], sizeof(EventGenRec));
721 memset(&addr, 0, sizeof(addr));
722 addr.sin_family = AF_INET;
723 addr.sin_port = htons(port);
724 addr.sin_addr.s_addr = htonl(INADDR_ANY);
725 if ((connGen.fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
728 setsockopt(connGen.fd, SOL_SOCKET, SO_REUSEADDR,
729 (void *)&val1, sizeof(val1));
730 if (bind(connGen.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
732 if (listen(connGen.fd, 1) < 0)
735 AddEventGen(&connGen);
736 } //setup listening sock
738 StartServer(); //server loop