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 { "connections",1, 0, 'c' },
41 { "speed", 1, 0, 'i' },
42 { "seed", 1, 0, 's' },
43 { "info", 0, 0, 'H' },
44 { "help", 0, 0, 'h' },
48 static char Connections = 2;
50 static char *gameNames[GT_len] = { "OnePlayer", "ClassicTwo" };
52 ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event);
54 EventGenRec netGen[MAX_SCREENS] = {
55 { NULL, 0, FT_read, -1, NetGenFunc, EM_net, 0, "\0", 0, HEADER_SIZE } };
57 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event);
58 static EventGenRec alarmGen =
59 { &alarmGen, 0, FT_read, -1, AlarmGenFunc, EM_alarm };
60 static EventGenRec *nextGen = &alarmGen;
62 static sigjmp_buf close_env;
64 ExtFunc volatile void die(char *msg)
70 ExtFunc int MyRead(int fd, void *data, int len)
76 result = read(fd, data, left);
78 data = ((char *)data) + result;
81 else if (errno != EINTR)
87 ExtFunc int MyWrite(int fd, void *data, int len)
93 result = write(fd, data, left);
95 data = ((char *)data) + result;
98 else if (errno != EINTR)
104 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event)
109 ExtFunc void AddEventGen(EventGenRec *gen)
111 assert(gen->next == NULL);
112 assert(nextGen->next != (void*)0xffffffff);
113 gen->next = nextGen->next;
117 ExtFunc void RemoveEventGen(EventGenRec *gen)
119 // assert(gen->next != NULL); /* Be more forgiving, for SIGINTs */
121 while (nextGen->next != gen)
122 nextGen = nextGen->next;
123 nextGen->next = gen->next;
128 ExtFunc MyEventType WaitMyEvent(MyEvent *event, int mask)
133 int result, anyReady, anySet;
137 for (i = 0; i < FT_len; ++i)
139 anyReady = anySet = 0;
142 if (gen->mask & mask) {
146 FD_SET(gen->fd, &fds[gen->fdType]);
151 } while (gen != nextGen);
154 tv.tv_usec = (retry && !anyReady) ? 500000 : 0;
155 result = select(FD_SETSIZE, &fds[FT_read], &fds[FT_write],
156 &fds[FT_except], anyReady ? &tv : NULL);
159 if (retry && !anyReady)
165 if ((gen->mask & mask)
166 && (gen->ready || (result > 0 && gen->fd >= 0
167 && FD_ISSET(gen->fd, &fds[gen->fdType])))) {
169 event->type = gen->func(gen, event);
170 if (event->type != E_none) {
176 } while (gen != nextGen);
181 ExtFunc void ByeClient(int playa)
183 fprintf(stderr, "Close connection #%d\n", playa);
184 close(netGen[playa].fd);
185 netGen[playa].fd = -1;
188 ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event)
191 short uid, type, size;
192 netint4 *data = (netint4*)&(gen->buf);
194 result = MyRead(gen->fd, gen->buf + gen->bufSize,
195 gen->bufGoal - gen->bufSize);
197 ByeClient(gen->player);
201 gen->bufSize += result;
202 if (gen->bufSize < gen->bufGoal)
204 // *ugly* memcpy(data, gen->buf, sizeof(data));
205 uid = ntoh4(data[0]);
206 type = ntoh4(data[1]);
207 size = ntoh4(data[2]);
209 if (gen->bufSize < gen->bufGoal)
212 gen->bufGoal = HEADER_SIZE;
213 event->u.net.sender = gen->player;
214 event->u.net.uid = uid;
215 event->u.net.type = type;
216 event->u.net.size = size - HEADER_SIZE;
217 event->u.net.data = gen->buf + HEADER_SIZE;
218 if (type == NP_endConn)
219 ByeClient(gen->player);
223 ExtFunc void SendPacketTo(short playa, short uid, NetPacketType type, int size, void *data)
227 if (netGen[playa].fd >= 0) {
228 header[0] = hton4(uid);
229 header[1] = hton4(type);
230 header[2] = hton4(size + HEADER_SIZE);
231 if (MyWrite(netGen[playa].fd, header, HEADER_SIZE) != HEADER_SIZE)
232 die("write (header)");
233 if (size > 0 && data && MyWrite(netGen[playa].fd, data, size) != size)
239 ExtFunc void AtExit(void (*handler)(void))
242 on_exit((void *)handler, NULL);
248 ExtFunc void SCloseNet(short playa)
249 { //kick some connection's ass!
252 if (netGen[playa].fd >= 0) {
253 SendPacketTo(playa, 0, NP_endConn, 0, NULL);
254 do{} while (WaitMyEvent(&event, EM_net) != E_lostConn);
255 close(netGen[playa].fd);
256 netGen[playa].fd = -1;
258 if (netGen[playa].next)
259 RemoveEventGen(&netGen[playa]);
262 ExtFunc void CloseNets(void)
263 { //nou oogjes dicht en snaveltjes toe
266 for (i = 1; i <= totalPlayers; i++)
270 ExtFunc int WaitForConnection(short port)
272 struct sockaddr_in addr;
273 struct hostent *host;
281 for (i = 1; i < MAX_SCREENS; i++)
282 memcpy(&netGen[i], &netGen[0], sizeof(EventGenRec));
284 memset(&addr, 0, sizeof(addr));
285 addr.sin_family = AF_INET;
286 addr.sin_port = htons(port);
287 addr.sin_addr.s_addr = htonl(INADDR_ANY);
288 if ((sockListen = socket(AF_INET, SOCK_STREAM, 0)) < 0)
291 setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR,
292 (void *)&val1, sizeof(val1));
293 if (bind(sockListen, (struct sockaddr *)&addr, sizeof(addr)) < 0)
295 if (listen(sockListen, 1) < 0)
298 addrLen = sizeof(addr);
299 for (i = 1; i <= Connections; i++) {
300 if ((netGen[i].fd = accept(sockListen, (struct sockaddr *)&addr, &addrLen)) < 0)
302 fprintf(stderr, "Connection: %s\n", inet_ntoa(addr.sin_addr));
305 setsockopt(netGen[i].fd, SOL_SOCKET, SO_LINGER, (void *)&val2, sizeof(val2));
306 sprintf(Players[i].host, "%s", inet_ntoa(addr.sin_addr));
307 if (addr.sin_family == AF_INET) {
308 host = gethostbyaddr((void *)&addr.sin_addr,
309 sizeof(struct in_addr), AF_INET);
311 strncpy(Players[i].host, host->h_name, sizeof(Players[i].host) - 1);
312 Players[i].host[sizeof(Players[i].host) - 1] = 0;
315 AddEventGen(&netGen[i]);
316 netGen[i].player = i;
321 do {} while ((WaitMyEvent(&event, EM_net) != E_net) ||
322 (event.u.net.sender != i));
323 if (event.u.net.type != NP_hello) ByeClient(i);
325 netint4 versiondata[2];
328 memcpy(versiondata, event.u.net.data, sizeof(versiondata));
329 major = ntoh4(versiondata[0]);
330 protocolVersion = ntoh4(versiondata[1]);
331 if (major != MAJOR_VERSION || protocolVersion != PROTOCOL_VERSION) {
332 snprintf(data, sizeof(data), "Version mismatch: received %d.%d",
333 major, protocolVersion);
334 fprintf(stderr, "Byebye client #%d (%s)\n",
335 event.u.net.sender, data);
336 SendPacketTo(event.u.net.sender, 0, NP_error, strlen(data)+1, data);
337 SCloseNet(event.u.net.sender);
339 fprintf(stderr, "Accepted #%d\n", event.u.net.sender);
345 } //WaitForConnection
347 ExtFunc int StartServer(void)
351 int playersReady = 0;
355 char serverdata[255];
356 for (i = 1; i <= totalPlayers; i++) {
357 sprintf(serverdata, "Netris server %s", version_string);
358 SendPacketTo(i, i, NP_hello, strlen(serverdata)+1, serverdata);
363 if (WaitMyEvent(&event, EM_net) == E_net) {
364 // fprintf(stderr, "in %d: %d\n",
365 // netGen[event.u.net.sender].fd, event.u.net.type);
366 switch(event.u.net.type) {
368 { //client went away :(
369 Players[event.u.net.sender].alive = 0;
370 for (i = 1; i <= totalPlayers; i++)
371 SendPacketTo(i, event.u.net.sender,
372 NP_argghhh, sizeof(Players[0].alive),
373 &Players[event.u.net.sender].alive);
379 { //receive player details and return other players
380 memcpy(&Players[event.u.net.sender],
381 event.u.net.data, event.u.net.size);
382 if (!Players[event.u.net.sender].team)
383 Players[event.u.net.sender].team = 256 - event.u.net.sender;
384 fprintf(stderr, "player %d: %s <%s>\n", event.u.net.sender,
385 event.u.net.data, //Players[event.u.net.sender].name
386 Players[event.u.net.sender].host);
387 SendPacketTo(event.u.net.sender, 0, NP_gamedata,
388 sizeof(Game.seed)+sizeof(Game.initspeed), &Game);
389 for (i = 1; i <= totalPlayers; i++)
390 if (i != event.u.net.sender)
391 SendPacketTo(i, event.u.net.sender, event.u.net.type,
392 sizeof(Player) - sizeof(Players[0].spy),
393 &Players[event.u.net.sender]);
394 if (++playersReady >= totalPlayers) {
395 fprintf(stderr, "Starting game (%010d)\n", Game.seed);
396 for (i = 1; i <= totalPlayers; i++)
397 SendPacketTo(i, 0, NP_goAhead, 0, NULL);
402 { //relay data to all players
403 // if (event.u.net.type >= NP_pause)
404 for (i = 1; i <= totalPlayers; i++)
405 if (i != event.u.net.sender)
406 if (event.u.net.type != NP_giveJunk
407 || Players[i].team != Players[event.u.net.sender].team)
408 SendPacketTo(i, event.u.net.sender, event.u.net.type,
409 event.u.net.size, event.u.net.data);
415 for (i = 1; i <= totalPlayers; i++)
416 if (netGen[i].fd >= 0) playercount++;
417 } while (playercount > 1);
421 ExtFunc void Header(void)
424 "NETRIS Server %s\t(c) 2002 Shiar <shiar@shiar.org>\n\n",
428 ExtFunc void Usage(void)
432 "Usage: netris <options>\n"
434 " -h, --help\t\tPrint this usage information\n"
435 " -H, --info\t\tShow distribution and warranty information\n"
437 " -p, --port <port>\tSet port number (default is %d)\n"
439 " -s, --seed <seed>\tStart with given random seed\n"
440 " -i, --speed <sec>\tSet the initial step-down interval, in seconds\n"
444 ExtFunc void DistInfo(void)
448 "This program is free software; you can redistribute it and/or modify\n"
449 "it under the terms of the GNU General Public License as published by\n"
450 "the Free Software Foundation; either version 2 of the License, or\n"
451 "(at your option) any later version.\n"
453 "This program is distributed in the hope that it will be useful,\n"
454 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
455 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
456 "GNU General Public License for more details.\n"
458 "You should have received a copy of the GNU General Public License\n"
459 "along with this program; if not, write to the Free Software\n"
460 "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n"
464 ExtFunc void WriteConf(void)
468 file_out = fopen(CONFIG_FILE, "w");
469 if (file_out == NULL) {
470 perror("Error writing config file");
474 fprintf(file_out, "### NETRIS %s Config file ###\n\n", version_string);
477 fprintf(stderr, "Wrote new game configuration to %s\n", CONFIG_FILE);
480 ExtFunc void HandleOption(char tag, char *value)
486 case 'c': //connections
487 Connections = atoi(value);
489 case 'i': //speed (of level 1)
490 Game.initspeed = atof(value) * 1e6;
493 Game.seed = atoi(value);
504 ExtFunc void ReadConf(char *filename)
510 char tag[81], value[81];
512 file_in = fopen(filename, "r");
514 while (fgets(buf, 512, file_in) != NULL) {
515 if ((ch = strchr(buf, '#')))
516 *ch = '\0'; // truncate string from # char
517 for (i = strlen(buf)-1; i >= 0; i--)
518 if (buf[i] == ' ' || buf[i] == '\n' || buf[i] == '\t' || buf[i] == 13)
522 sscanf(buf, "%80[^= \t] = %80[^\n]", tag, value);
523 for (i = 0; options[i].name; i++){
524 if (!strcasecmp(options[i].name, tag)) {
525 HandleOption(options[i].val, value);
533 fprintf(stderr, "Unable to open config file %s.\n", filename);
538 ExtFunc void CatchInt(int sig)
540 siglongjmp(close_env, 1);
543 ExtFunc int main(int argc, char **argv)
547 if (sigsetjmp(close_env, 1)) exit(0);
548 signal(SIGINT, CatchInt);
550 Game.initspeed = DEFAULT_INTERVAL;
553 // if (getopt(argc, argv, "f:") == 'f')
556 ReadConf(CONFIG_FILE);
557 while ((ch = getopt_long(argc, argv,
558 "hHp:i:s:c:", options, NULL)) != -1)
559 HandleOption(ch, optarg);
567 WaitForConnection(port);