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 countPlayers = 0;
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 SendPacketTo(playa, 0, NP_endConn, 0, NULL);
170 do{} while (WaitMyEvent(&event, EM_net) != E_lostConn);
171 close(netGen[playa].fd);
172 netGen[playa].fd = -1;
174 if (netGen[playa].next)
175 RemoveEventGen(&netGen[playa]);
178 ExtFunc void CloseNets(void)
179 { //nou oogjes dicht en snaveltjes toe
182 fprintf(stderr, "- Closing connections...\n");
183 for (i = 1; i < MAX_SCREENS; i++)
184 SCloseNet(i); //bye everybuddy
185 fprintf(stderr, "* All Done\n\n");
188 ExtFunc MyEventType WaitMyEvent(MyEvent *event, int mask)
193 int result, anyReady, anySet;
197 for (i = 0; i < FT_len; ++i)
199 anyReady = anySet = 0;
202 if (gen->mask & mask) {
206 FD_SET(gen->fd, &fds[gen->fdType]);
211 } while (gen != nextGen);
214 tv.tv_usec = (retry && !anyReady) ? 500000 : 0;
215 result = select(FD_SETSIZE, &fds[FT_read], &fds[FT_write],
216 &fds[FT_except], anyReady ? &tv : NULL);
219 if (retry && !anyReady)
225 if ((gen->mask & mask)
226 && (gen->ready || (result > 0 && gen->fd >= 0
227 && FD_ISSET(gen->fd, &fds[gen->fdType])))) {
229 event->type = gen->func(gen, event);
230 if (event->type != E_none) {
236 } while (gen != nextGen);
241 ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event)
244 short uid, type, size;
245 netint4 *data = (netint4*)&(gen->buf);
247 result = MyRead(gen->fd, gen->buf + gen->bufSize,
248 gen->bufGoal - gen->bufSize);
250 fprintf(stderr, "- Closed connection to player #%d\n", gen->player);
253 gen->bufSize += result;
254 if (gen->bufSize < gen->bufGoal)
256 // *ugly* memcpy(data, gen->buf, sizeof(data));
257 uid = ntoh4(data[0]);
258 type = ntoh4(data[1]);
259 size = ntoh4(data[2]);
261 if (gen->bufSize < gen->bufGoal)
264 gen->bufGoal = HEADER_SIZE;
265 event->u.net.sender = gen->player;
266 event->u.net.uid = uid;
267 event->u.net.type = type;
268 event->u.net.size = size - HEADER_SIZE;
269 event->u.net.data = gen->buf + HEADER_SIZE;
270 if (type == NP_endConn) {
271 fprintf(stderr, "- Quit player #%d\n", gen->player);
273 } //client sent quit signal
278 static MyEventType ConnGenFunc(EventGenRec *gen, MyEvent *event)
284 addrLen = sizeof(addr);
285 for (new = 1; new <= MAX_SCREENS; new++)
286 if (netGen[new].fd < 0) break;
287 if (new > Game.maxplayers) return;
289 if ((netGen[new].fd =
290 accept(gen->fd, (struct sockaddr *)&addr, &addrLen)) < 0)
292 fprintf(stderr, "+ Connection: %s\n", inet_ntoa(addr.sin_addr));
295 setsockopt(netGen[new].fd, SOL_SOCKET, SO_LINGER,(void *)&val2,
297 AddEventGen(&netGen[new]);
298 netGen[new].player = event->u.net.uid = new;
299 { //new connection; netGen already initialized in GenFunc
300 struct hostent *host;
302 sprintf(Players[new].host, "%s", inet_ntoa(addr.sin_addr));
303 if (addr.sin_family == AF_INET) {
304 host = gethostbyaddr((void *)&addr.sin_addr,
305 sizeof(struct in_addr), AF_INET);
307 strncpy(Players[new].host, host->h_name,
308 sizeof(Players[new].host) - 1);
309 Players[new].host[sizeof(Players[new].host) - 1] = 0;
310 } //set player's hostname
316 ExtFunc int StartServer(void)
319 netint2 currentpiece[MAX_SCREENS];
321 int playersReady = 0;
325 switch (WaitMyEvent(&event, EM_any)) {
326 case E_lostConn: //client went away :(
327 Players[event.u.net.sender].alive = 0;
328 for (i = 1; i < MAX_SCREENS; i++)
329 if (Players[i].alive)
330 SendPacketTo(i, event.u.net.sender,
331 NP_argghhh, sizeof(Players[0].alive),
332 &Players[event.u.net.sender].alive);
333 SCloseNet(event.u.net.sender);
336 if (verbose) fprintf(stderr, ": %d sent %d\n",
337 netGen[event.u.net.sender].fd, event.u.net.type);
338 switch(event.u.net.type) {
340 // if (event.u.net.type != NP_hello) ByeClient(new);
342 netint4 versiondata[2];
346 memcpy(versiondata, event.u.net.data,
347 sizeof(versiondata));
348 major = ntoh4(versiondata[0]);
349 protocolVersion = ntoh4(versiondata[1]);
350 if (major != MAJOR_VERSION
351 || protocolVersion != PROTOCOL_VERSION) {
352 snprintf(data, sizeof(data),
353 "Version mismatch: received %d.%d",
354 major, protocolVersion);
355 fprintf(stderr, "= Wrong version player #%d (%s)\n",
356 event.u.net.sender, data);
357 SendPacketTo(event.u.net.sender, 0, NP_error,
358 strlen(data)+1, data);
359 SCloseNet(event.u.net.sender);
361 fprintf(stderr, "* Accepted player #%d\n",
366 //receive player details and return other players
367 memcpy(&Players[event.u.net.sender],
368 event.u.net.data, event.u.net.size);
369 if (!Players[event.u.net.sender].team)
370 Players[event.u.net.sender].team =
371 256 - event.u.net.sender;
372 if (Game.started < 2)
373 Players[event.u.net.sender].flags |= SCF_paused;
374 if (!Game.continuous && Game.started >= 2) {
376 strcpy(data,"Can't join: Game has already started");
377 fprintf(stderr, "- Can't join player #%d in "
378 "non-continuous game\n", event.u.net.sender);
379 SendPacketTo(event.u.net.sender, 0, NP_error,
380 strlen(data)+1, data);
381 // SCloseNet(event.u.net.sender, 0);
383 } //can't join started game
394 memcpy(&data, &Players[event.u.net.sender].flags,
395 sizeof(data.playerflags));
396 memcpy(&data.maxplayers, &Game,
397 sizeof(data) - sizeof(data.playerflags));
398 SendPacketTo(event.u.net.sender, 0, NP_gamedata,
399 sizeof(data), &data);
400 } //send game options
401 for (i = 1; i < MAX_SCREENS; i++)
402 if (netGen[i].fd >= 0 && i != event.u.net.sender) {
403 SendPacketTo(event.u.net.sender, i,
404 NP_newPlayer, sizeof(Player)
405 - sizeof(Players[0].spy)
406 - sizeof(Players[0].small),
408 SendPacketTo(event.u.net.sender, i,
409 NP_newPiece, sizeof(currentpiece[i]),
411 SendPacketTo(i, event.u.net.sender,
412 NP_newPlayer, sizeof(Player)
413 - sizeof(Players[0].spy)
414 - sizeof(Players[0].small),
415 &Players[event.u.net.sender]);
416 } //send (to) players
417 fprintf(stderr, "> Joined player #%d: %s <%s>\n",
419 Players[event.u.net.sender].name,
420 Players[event.u.net.sender].host);
421 if (++playersReady >= minplayers) {
423 SendPacketTo(event.u.net.sender, 0,
426 fprintf(stderr, "* Starting game (%010d)\n",
428 for (i = 1; i < MAX_SCREENS; i++)
429 SendPacketTo(i, 0, NP_start, 0, NULL);
431 } //first goahead (to all)
433 break; //NP_playerdata
435 memcpy(¤tpiece[event.u.net.sender],
436 event.u.net.data, sizeof(currentpiece[0]));
439 Players[event.u.net.sender].alive = 0;
440 fprintf(stderr, "< Player #%d died\n",
442 //check for unpaused game
446 Players[event.u.net.sender].flags ^= SCF_paused;
447 paused = Game.started < 1;
448 for (i = 1; i < MAX_SCREENS; i++) if (Players[i].alive)
449 paused |= Players[i].flags & SCF_paused;
450 if (paused) paused = 1;
451 else if (Game.started == 1) Game.started++;
454 default: //relay data to all players
456 // if (event.u.net.type >= NP_pause)
457 for (i = 1; i < MAX_SCREENS; i++)
458 if (i != event.u.net.sender)
459 if (event.u.net.type != NP_giveJunk ||
460 Players[i].team != Players[event.u.net.sender].team)
461 SendPacketTo(i, event.u.net.sender,
462 event.u.net.type, event.u.net.size,
468 { //new connection; netGen already initialized in GenFunc
469 char serverdata[255];
471 sprintf(serverdata, "Netris server %s", version_string);
472 SendPacketTo(event.u.net.uid, event.u.net.uid, NP_hello,
473 strlen(serverdata)+1, serverdata);
480 for (i = 1; i < MAX_SCREENS; i++) if (Players[i].alive) {
481 if (Players[i].team < 128) for (j = 1; j < i; j++)
483 && (Players[j].team == Players[i].team)) {
486 } //player of same team counted before
490 } while (Game.started < 2 || playercount > 1 || Game.continuous);
491 fprintf(stderr, "* Exiting server\n");
495 ExtFunc void Header(void)
498 "NETRIS Server %s\t(c) 2002 Shiar <shiar@shiar.org>\n\n",
502 ExtFunc void Usage(void)
506 "Usage: netris <options>\n"
508 " -h, --help\t\tPrint this usage information\n"
509 " -H, --info\t\tShow distribution and warranty information\n"
511 " -p, --port <port>\tSet port number (default is %d)\n"
513 " -s, --seed <seed>\tStart with given random seed\n"
514 " -i, --speed <sec>\tSet the initial step-down interval, in seconds\n"
515 " -m, --min-players <2>\tNumber of players required before starting the game\n"
516 " -x, --max-players <8>\tMaximum number of players allowed in the game\n"
517 " -c, --continuous\tDon'n quit the game\n"
521 ExtFunc void DistInfo(void)
525 "This program is free software; you can redistribute it and/or modify\n"
526 "it under the terms of the GNU General Public License as published by\n"
527 "the Free Software Foundation; either version 2 of the License, or\n"
528 "(at your option) any later version.\n"
530 "This program is distributed in the hope that it will be useful,\n"
531 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
532 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
533 "GNU General Public License for more details.\n"
535 "You should have received a copy of the GNU General Public License\n"
536 "along with this program; if not, write to the Free Software\n"
537 "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n"
541 ExtFunc void WriteConf(void)
545 file_out = fopen(CONFIG_FILE, "w");
546 if (file_out == NULL) {
547 perror("Error writing config file");
551 fprintf(file_out, "### NETRIS %s Config file ###\n\n", version_string);
554 fprintf(stderr, "Wrote new game configuration to %s\n", CONFIG_FILE);
557 ExtFunc void HandleOption(char tag, char *value)
566 case 'c': //min-players
567 Game.continuous = atoi(value);
569 case 'm': //min-players
570 minplayers = atoi(value);
572 case 'x': //max-players
573 Game.maxplayers = atoi(value);
574 if (Game.maxplayers >= MAX_SCREENS)
575 Game.maxplayers = MAX_SCREENS;
577 case 'i': //speed (of level 1)
578 Game.initspeed = atof(value) * 1e6;
581 Game.seed = atoi(value);
592 ExtFunc void ReadConf(char *filename)
598 char tag[81], value[81];
600 file_in = fopen(filename, "r");
602 while (fgets(buf, 512, file_in) != NULL) {
603 if ((ch = strchr(buf, '#')))
604 *ch = '\0'; // truncate string from # char
605 for (i = strlen(buf)-1; i >= 0; i--)
606 if (buf[i] == ' ' || buf[i] == '\t'
607 || buf[i] == '\n' || buf[i] == 13)
611 sscanf(buf, "%80[^= \t] = %80[^\n]", tag, value);
612 for (i = 0; options[i].name; i++){
613 if (!strcasecmp(options[i].name, tag)) {
614 HandleOption(options[i].val, value);
622 fprintf(stderr, "Unable to open config file %s.\n", filename);
627 ExtFunc void CatchInt(int sig)
629 siglongjmp(close_env, 1);
632 ExtFunc int main(int argc, char **argv)
636 if (sigsetjmp(close_env, 1)) exit(0);
637 signal(SIGINT, CatchInt);
640 Game.initspeed = DEFAULT_INTERVAL;
643 // if (getopt(argc, argv, "f:") == 'f')
646 ReadConf(CONFIG_FILE);
647 while ((ch = getopt_long(argc, argv,
648 "hHvp:i:s:c:m:x:", options, NULL)) != -1)
649 HandleOption(ch, optarg);
661 for (i = 1; i < MAX_SCREENS; i++)
662 memcpy(&netGen[i], &netGen[0], sizeof(EventGenRec));
670 memset(&addr, 0, sizeof(addr));
671 addr.sin_family = AF_INET;
672 addr.sin_port = htons(port);
673 addr.sin_addr.s_addr = htonl(INADDR_ANY);
674 if ((connGen.fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
677 setsockopt(connGen.fd, SOL_SOCKET, SO_REUSEADDR,
678 (void *)&val1, sizeof(val1));
679 if (bind(connGen.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
681 if (listen(connGen.fd, 1) < 0)
684 AddEventGen(&connGen);
685 } //setup listening sock
687 StartServer(); //server loop