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 $
28 #include <sys/types.h>
29 #include <netinet/in.h>
31 #include <sys/socket.h>
38 #define HEADER_SIZE sizeof(netint4[3])
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' },
55 static char minplayers = 2;
56 static char maxplayers = 8;
57 static char playercount;
58 static char verbose = 0;
60 struct sockaddr_in addr;
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 } };
66 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event);
67 static EventGenRec alarmGen =
68 { &alarmGen, 0, FT_read, -1, AlarmGenFunc, EM_alarm };
70 static MyEventType ConnGenFunc(EventGenRec *gen, MyEvent *event);
71 static EventGenRec connGen =
72 { NULL, 0, FT_read, -1, ConnGenFunc, EM_connect };
74 static EventGenRec *nextGen = &alarmGen;
76 static sigjmp_buf close_env;
79 void SendPacketTo(short playa, short uid, NetPacketType type, int size, void *data)
83 if (netGen[playa].fd >= 0) {
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)
96 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event)
101 void SCloseNet(short playa)
102 { //kick some connection's ass!
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;
113 if (netGen[playa].next)
114 RemoveEventGen(&netGen[playa]);
118 { //nou oogjes dicht en snaveltjes toe
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");
127 MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event)
130 short uid, type, size;
131 netint4 *data = (netint4*)&(gen->buf);
133 result = MyRead(gen->fd, gen->buf + gen->bufSize,
134 gen->bufGoal - gen->bufSize);
136 fprintf(stderr, "- Closed connection to player #%d\n", gen->player);
139 gen->bufSize += result;
140 if (gen->bufSize < gen->bufGoal)
142 // *ugly* memcpy(data, gen->buf, sizeof(data));
143 uid = ntoh4(data[0]);
144 type = ntoh4(data[1]);
145 size = ntoh4(data[2]);
147 if (gen->bufSize < gen->bufGoal)
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);
159 } //client sent quit signal
164 static MyEventType ConnGenFunc(EventGenRec *gen, MyEvent *event)
170 addrLen = sizeof(addr);
171 for (new = 1; new <= MAX_SCREENS; new++)
172 if (netGen[new].fd < 0) break;
173 if (new > maxplayers) return;
175 if ((netGen[new].fd =
176 accept(gen->fd, (struct sockaddr *)&addr, &addrLen)) < 0)
178 fprintf(stderr, "+ Connection: %s\n", inet_ntoa(addr.sin_addr));
181 setsockopt(netGen[new].fd, SOL_SOCKET, SO_LINGER,(void *)&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;
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);
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
202 void CountPlayers(void)
203 { //count number of players/teams
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)) {
211 } //player of same team counted before
216 int StartServer(void)
219 netint2 currentpiece[MAX_SCREENS];
220 int playersReady = 0;
223 char teams[10][7] = { "", "Green", "Cyan", "Blue", "Purple",
224 "Red", "Grey", "White", "*Orange" };
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);
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) {
242 // if (event.u.net.type != NP_hello) ByeClient(new);
244 netint4 versiondata[2];
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);
264 fprintf(stderr, "* Accepted player #%d\n",
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) {
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))
280 if (i==MAX_SCREENS) break;
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);
287 if (Game.started < 2)
288 Players[event.u.net.sender].flags |= SCF_paused;
289 if (!Game.continuous && Game.started >= 2) {
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);
298 } //can't join started game
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",
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,
335 fprintf(stderr, "* Starting game (%010d)\n",
337 for (i = 1; i < MAX_SCREENS; i++)
338 SendPacketTo(i, 0, NP_start, 0, NULL);
340 } //first goahead (to all)*/
342 break; //NP_playerdata
344 memcpy(&Players[event.u.net.sender].curShape,
345 event.u.net.data, sizeof(Players[0].curShape));
348 Players[event.u.net.sender].alive = 0;
349 fprintf(stderr, "< Player #%d died\n",
351 //check for unpaused game
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;
364 default: //relay data to all players
366 // if (event.u.net.type >= NP_pause)
367 if (event.u.net.type >= NP_rotright
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,
381 { //new connection; netGen already initialized in GenFunc
382 char serverdata[255];
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);
391 if (Game.started < 1) {
392 if (playercount > 1) {
393 fprintf(stderr, "* Game (%010d) ready to start\n", Game.seed);
396 } //game not yet started
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
410 } //too few players for game
411 if (Game.started == 1 && !paused) {
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)
420 fprintf(stderr, "* Exiting server\n");
427 "NETRIS Server %s\t(c) 2002 Shiar <shiar@shiar.org>\n\n",
435 "Usage: netris <options>\n"
437 " -h, --help\t\tPrint this usage information\n"
438 " -H, --info\t\tShow distribution and warranty information\n"
440 " -p, --port <port>\tSet port number (default is %d)\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"
454 file_out = fopen(CONFIG_FILE, "w");
455 if (file_out == NULL) {
456 perror("Error writing config file");
460 fprintf(file_out, "### NETRIS %s Config file ###\n\n", version_string);
463 fprintf(stderr, "Wrote new game configuration to %s\n", CONFIG_FILE);
466 void HandleOption(char tag, char *value)
475 case 'c': //min-players
476 Game.continuous = atoi(value);
478 case 'm': //min-players
479 minplayers = atoi(value);
481 case 'x': //max-players
482 maxplayers = atoi(value);
483 if (maxplayers >= MAX_SCREENS)
484 maxplayers = MAX_SCREENS;
486 case 'q': //quadra-style gravity
489 case 'i': //speed (of level 1)
490 Game.initspeed = atof(value) * 1e6;
493 Game.seed = atoi(value);
505 void ReadConf(char *filename)
511 char tag[81], value[81];
513 file_in = fopen(filename, "r");
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)
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);
535 fprintf(stderr, "Unable to open config file %s.\n", filename);
540 void CatchInt(int sig)
542 siglongjmp(close_env, 1);
545 int main(int argc, char **argv)
549 if (sigsetjmp(close_env, 1)) exit(0);
550 signal(SIGINT, CatchInt);
553 Game.initspeed = DEFAULT_INTERVAL;
559 for (i = 1; i < MAX_SCREENS; i++)
560 Players[i].alive = -1;
563 // if (getopt(argc, argv, "f:") == 'f')
566 ReadConf(CONFIG_FILE);
567 while ((ch = getopt_long(
568 argc, argv, "hHvqp:i:s:c:m:x:", options, NULL
570 HandleOption(ch, optarg);
582 for (i = 1; i < MAX_SCREENS; i++)
583 memcpy(&netGen[i], &netGen[0], sizeof(EventGenRec));
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)
598 setsockopt(connGen.fd, SOL_SOCKET, SO_REUSEADDR,
599 (void *)&val1, sizeof(val1));
600 if (bind(connGen.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
602 if (listen(connGen.fd, 1) < 0)
605 AddEventGen(&connGen);
606 } //setup listening sock
608 StartServer(); //server loop