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.
27 #include <sys/types.h>
28 #include <netinet/in.h>
30 #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 static 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 }
67 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event);
68 static EventGenRec alarmGen = {
69 &alarmGen, 0, FT_read, -1, AlarmGenFunc, EM_alarm
72 static MyEventType ConnGenFunc(EventGenRec *gen, MyEvent *event);
73 static EventGenRec connGen = {
74 NULL, 0, FT_read, -1, ConnGenFunc, EM_connect
77 static EventGenRec *nextGen = &alarmGen;
79 static sigjmp_buf close_env;
82 static void SendPacketTo(short player, short uid, NetPacketType type, int size, void *data)
86 if (netGen[player].fd >= 0) {
88 fprintf(stderr, MSG_SERVER_DBG_SEND "\n", type, uid, player);
89 header[0] = hton4(uid);
90 header[1] = hton4(type);
91 header[2] = hton4(size + HEADER_SIZE);
92 if (MyWrite(netGen[player].fd, header, HEADER_SIZE) != HEADER_SIZE)
93 die("write (header)");
94 if (size > 0 && data && MyWrite(netGen[player].fd, data, size) != size)
99 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event)
104 static void SCloseNet(short player)
105 { //kick some connection's ass!
108 if (netGen[player].fd >= 0) {
109 if (Players[player].alive >= 0) {
110 SendPacketTo(player, 0, NP_endConn, 0, NULL);
111 do {} while (WaitMyEvent(&event, EM_net) != E_lostConn);
112 } //say bye to player
113 close(netGen[player].fd);
114 netGen[player].fd = -1;
116 if (netGen[player].next)
117 RemoveEventGen(&netGen[player]);
120 static void CloseNets(void)
121 { //nou oogjes dicht en snaveltjes toe
124 fprintf(stderr, MSG_SERVER_CLOSE_ALL "\n");
125 for (i = 1; i < MAX_SCREENS; i++)
126 SCloseNet(i); // bye everybuddy
127 fprintf(stderr, MSG_SERVER_CLOSE_ALL_DONE "\n\n");
130 static MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event)
133 short uid, type, size;
134 netint4 *data = (netint4*)&(gen->buf);
136 result = MyRead(gen->fd, gen->buf + gen->bufSize,
137 gen->bufGoal - gen->bufSize);
139 fprintf(stderr, MSG_SERVER_PLAYER_CLOSED "\n", gen->player);
142 gen->bufSize += result;
143 if (gen->bufSize < gen->bufGoal)
145 // *ugly* memcpy(data, gen->buf, sizeof(data));
146 uid = ntoh4(data[0]);
147 type = ntoh4(data[1]);
148 size = ntoh4(data[2]);
150 if (gen->bufSize < gen->bufGoal)
153 gen->bufGoal = HEADER_SIZE;
154 event->u.net.sender = gen->player;
155 event->u.net.uid = uid;
156 event->u.net.type = type;
157 event->u.net.size = size - HEADER_SIZE;
158 event->u.net.data = gen->buf + HEADER_SIZE;
159 if (type == NP_endConn) {
160 fprintf(stderr, MSG_SERVER_PLAYER_QUIT "\n", gen->player);
162 } //client sent quit signal
167 static MyEventType ConnGenFunc(EventGenRec *gen, MyEvent *event)
173 addrLen = sizeof(addr);
174 for (new = 1; new <= MAX_SCREENS; new++)
175 if (netGen[new].fd < 0) break;
176 if (new > maxplayers) return;
179 netGen[new].fd = accept(gen->fd, (struct sockaddr *)&addr, &addrLen)
182 fprintf(stderr, MSG_SERVER_CONNECT "\n", inet_ntoa(addr.sin_addr));
185 setsockopt(netGen[new].fd, SOL_SOCKET, SO_LINGER,(void *)&val2,
187 AddEventGen(&netGen[new]);
188 netGen[new].player = event->u.net.uid = new;
189 { //new connection; netGen already initialized in GenFunc
190 struct hostent *host;
192 sprintf(Players[new].host, "%s", inet_ntoa(addr.sin_addr));
193 if (addr.sin_family == AF_INET) {
194 host = gethostbyaddr(
195 (void *)&addr.sin_addr, sizeof(struct in_addr), AF_INET
198 strncpy(Players[new].host, host->h_name,
199 sizeof(Players[new].host) - 1);
200 Players[new].host[sizeof(Players[new].host) - 1] = 0;
201 } //set player's hostname
207 static void CountPlayers(void)
208 { //count number of players/teams
211 for (i = 1; i < MAX_SCREENS; i++) if (Players[i].alive > 0) {
212 if (Players[i].team < 128) for (j = 1; j < i; j++) {
213 if (Players[j].alive > 0 && Players[j].team == Players[i].team) {
214 playercount--; // player of same team counted before
222 static int StartServer(void)
225 netint2 currentpiece[MAX_SCREENS];
226 int playersReady = 0;
231 switch (WaitMyEvent(&event, EM_any)) {
232 case E_lostConn: //client went away :(
233 Players[event.u.net.sender].alive = -1;
234 for (i = 1; i < MAX_SCREENS; i++)
235 if (Players[i].alive >= 0)
236 SendPacketTo(i, event.u.net.sender,
237 NP_part, sizeof(Players[0].alive),
238 &Players[event.u.net.sender].alive);
239 SCloseNet(event.u.net.sender);
242 if (verbose) fprintf(stderr, MSG_SERVER_DBG_RECV "\n",
243 netGen[event.u.net.sender].fd, event.u.net.type);
244 switch(event.u.net.type) {
246 // if (event.u.net.type != NP_hello) ByeClient(new);
248 netint4 versiondata[2];
253 memcpy(versiondata, event.u.net.data, sizeof(versiondata));
254 major = ntoh4(versiondata[0]);
255 protocolVersion = ntoh4(versiondata[1]);
256 if (major != MAJOR_VERSION
257 || protocolVersion != PROTOCOL_VERSION) {
258 snprintf(data, sizeof(data),
259 "Version mismatch: received %d.%d",
260 major, protocolVersion);
261 fprintf(stderr, MSG_SERVER_PLAYER_EVERSION "\n",
262 event.u.net.sender, data);
263 SendPacketTo(event.u.net.sender, 0, NP_error,
264 strlen(data)+1, data);
265 SCloseNet(event.u.net.sender);
267 fprintf(stderr, MSG_SERVER_PLAYER_ACCEPT "\n",
272 //receive player details and return other players
273 memcpy(&Players[event.u.net.sender],
274 event.u.net.data, event.u.net.size);
275 if (Players[event.u.net.sender].team < 1
276 || Players[event.u.net.sender].team > 7) {
279 for (team = 1; team < 7; team++) {
280 for (i = 1; i < MAX_SCREENS; i++)
281 if (Players[i].alive > 0 && Players[i].team == team)
283 if (i == MAX_SCREENS) break;
285 Players[event.u.net.sender].team = team;
286 SendPacketTo(event.u.net.sender, event.u.net.sender, NP_team,
287 sizeof(Players[event.u.net.sender].team),
288 &Players[event.u.net.sender].team);
290 if (Game.started < 2)
291 Players[event.u.net.sender].flags |= SCF_paused;
292 if (!Game.continuous && Game.started >= 2) {
294 strcpy(data, "Can't join: Game has already started");
295 fprintf(stderr, MSG_SERVER_PLAYER_JOIN_ESTARTED,
297 SendPacketTo(event.u.net.sender, 0, NP_error,
298 strlen(data)+1, data);
299 // SCloseNet(event.u.net.sender, 0);
301 } //can't join started game
313 memcpy(&data, &Players[event.u.net.sender].flags,
314 sizeof(data.playerflags));
315 memcpy(&data.gravity, &Game.gravity,
316 sizeof(data) - sizeof(data.playerflags));
317 SendPacketTo(event.u.net.sender, 0, NP_gamedata,
318 sizeof(data), &data);
319 } //send game options
320 for (i = 1; i < MAX_SCREENS; i++)
321 if (netGen[i].fd >= 0 && i != event.u.net.sender) {
322 SendPacketTo(event.u.net.sender, i, NP_newPlayer,
323 sizeof(player_t), &Players[i]);
324 SendPacketTo(event.u.net.sender, i, NP_newPiece,
325 sizeof(Players[i].curShape), &Players[i].curShape);
326 SendPacketTo(i, event.u.net.sender, NP_newPlayer,
327 sizeof(player_t), &Players[event.u.net.sender]);
328 } //send (to) players
329 fprintf(stderr, MSG_SERVER_PLAYER_JOIN "\n",
331 Players[event.u.net.sender].name,
332 Players[event.u.net.sender].host,
333 teamname[Players[event.u.net.sender].team]);
334 if (++playersReady >= minplayers) {
335 if (Game.started > 1)
336 SendPacketTo(event.u.net.sender, 0,
339 fprintf(stderr, "* Starting game (%010d)\n",
341 for (i = 1; i < MAX_SCREENS; i++)
342 SendPacketTo(i, 0, NP_start, 0, NULL);
344 } //first goahead (to all)*/
346 break; //NP_playerdata
348 memcpy(&Players[event.u.net.sender].curShape,
349 event.u.net.data, sizeof(Players[0].curShape));
352 Players[event.u.net.sender].alive = 0;
353 fprintf(stderr, MSG_SERVER_PLAYER_DIE "\n",
355 //check for unpaused game
358 Players[event.u.net.sender].flags ^= SCF_paused;
359 paused = Game.started < 1;
360 for (i = 1; i < MAX_SCREENS; i++)
361 if (Players[i].alive > 0)
362 paused |= (Players[i].flags & SCF_paused) != 0;
363 fprintf(stderr, MSG_SERVER_PLAYER_PAUSE "\n",
364 event.u.net.sender, paused);
367 default: //relay data to all players
369 // if (event.u.net.type >= NP_pause)
370 if (event.u.net.type >= NP_rotright
373 for (i = 1; i < MAX_SCREENS; i++)
374 if (i != event.u.net.sender)
375 if (event.u.net.type != NP_giveJunk
376 || Players[i].team != Players[event.u.net.sender].team)
377 SendPacketTo(i, event.u.net.sender,
378 event.u.net.type, event.u.net.size,
384 { //new connection; netGen already initialized in GenFunc
385 char serverdata[255];
387 sprintf(serverdata, "Netris server %s", version_string);
388 SendPacketTo(event.u.net.uid, event.u.net.uid, NP_hello,
389 strlen(serverdata)+1, serverdata);
394 if (Game.started < 1) {
395 if (playercount >= 2) {
396 fprintf(stderr, MSG_SERVER_GAME_READY "\n", Game.seed);
399 } //game not yet started
401 if (playercount < 2) {
402 fprintf(stderr, MSG_SERVER_GAME_STOP "\n");
403 if (Game.seed) Game.seed++;
404 if (Game.started > 1) for (i = 1; i < MAX_SCREENS; i++) {
405 if (Players[i].alive >= 0) {
406 Players[i].alive = 1;
407 Players[i].flags |= SCF_paused;
408 SendPacketTo(i, 0, NP_stop,
409 sizeof(Game.seed), &Game.seed);
410 } //transmit game stop and set players not ready
414 } //too few players for game
415 if (Game.started == 1 && !paused) {
417 fprintf(stderr, MSG_SERVER_GAME_START "\n");
418 for (i = 1; i < MAX_SCREENS; i++)
419 if (Players[i].alive > 0)
420 SendPacketTo(i, 0, NP_start, 0, NULL);
421 } //everybody ready to start
422 } //game (ready to) start(ed)
424 fprintf(stderr, "* Exiting server\n");
428 static void SHeader(void)
430 fprintf(stderr, MSG_SERVER_TITLE "\n\n", version_string);
433 static void SUsage(void)
437 "Usage: netris <options>\n"
439 " -h, --help\t\tPrint this usage information\n"
440 " -H, --info\t\tShow distribution and warranty information\n"
442 " -p, --port %d\tSet port number\n"
444 " -s, --seed <seed>\tStart with given random seed\n"
445 " -i, --speed %.2f\tSet the initial step-down interval, in seconds\n"
446 " -m, --min-players %d\tNumber of players required before starting the game\n"
447 " -x, --max-players %d\tMaximum number of players allowed in the game\n"
448 " -c, --continuous\tDon'n quit the game\n"
450 DEFAULT_PORT, DEFAULT_INTERVAL/1e6, minplayers, maxplayers
454 static void HandleOption(char tag, char *value)
463 case 'c': //min-players
464 Game.continuous = atoi(value);
466 case 'm': //min-players
467 minplayers = atoi(value);
469 case 'x': //max-players
470 maxplayers = MIN(atoi(value), MAX_SCREENS);
472 case 'q': //quadra-style gravity
475 case 'i': //speed (of level 1)
476 Game.initspeed = atof(value) * 1e6;
479 Game.seed = atoi(value);
493 static void ReadConf(char *filename)
499 char tag[81], value[81];
501 file_in = fopen(filename, "r");
503 while (fgets(buf, 512, file_in) != NULL) {
504 if ((ch = strchr(buf, '#')))
505 *ch = '\0'; // truncate string from # char
506 for (i = strlen(buf)-1; i >= 0; i--)
507 if (buf[i] == ' ' || buf[i] == '\t'
508 || buf[i] == '\n' || buf[i] == 13)
512 sscanf(buf, "%80[^= \t] = %80[^\n]", tag, value);
513 for (i = 0; options[i].name; i++){
514 if (!strcasecmp(options[i].name, tag)) {
515 HandleOption(options[i].val, value);
523 fprintf(stderr, "Unable to open config file %s.\n", filename);
528 static void CatchInt(int sig)
530 siglongjmp(close_env, 1);
533 int main(int argc, char **argv)
538 if (sigsetjmp(close_env, 1)) exit(0);
539 signal(SIGINT, CatchInt);
542 Game.initspeed = DEFAULT_INTERVAL;
544 for (i = 0; i < sizeof(Game.shapes) / sizeof(Game.shapes[0]); i++)
550 for (i = 1; i < MAX_SCREENS; i++)
551 Players[i].alive = -1;
554 // if (getopt(argc, argv, "f:") == 'f')
557 ReadConf(CONFIG_FILE);
558 while ((ch = getopt_long(
559 argc, argv, "hHvqp:i:s:c:m:x:", options, NULL
561 HandleOption(ch, optarg);
573 for (i = 1; i < MAX_SCREENS; i++)
574 memcpy(&netGen[i], &netGen[0], sizeof(EventGenRec));
582 memset(&addr, 0, sizeof(addr));
583 addr.sin_family = AF_INET;
584 addr.sin_port = htons(port);
585 addr.sin_addr.s_addr = htonl(INADDR_ANY);
586 if ((connGen.fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
589 setsockopt(connGen.fd, SOL_SOCKET, SO_REUSEADDR,
590 (void *)&val1, sizeof(val1));
591 if (bind(connGen.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
593 if (listen(connGen.fd, 1) < 0)
596 AddEventGen(&connGen);
597 } //setup listening sock
599 StartServer(); //server loop