0.10-pb2 (pb1 was done outside of CVS yet)
[tetrinet.git] / server.c
blob35dbf56d467ebd9c009c11599c40593d34d0796d
1 /* Tetrinet for Linux, by Andrew Church <achurch@achurch.org>
2 * This program is public domain.
4 * Tetrinet server code
5 */
7 #include <stdarg.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <errno.h>
12 #include <sys/types.h>
13 #include <netinet/in.h>
14 /* Due to glibc brokenness, we can't blindly include this. Yet another
15 * reason to not use glibc. */
16 /* #include <netinet/protocols.h> */
17 #include <signal.h>
18 #include <sys/socket.h>
19 #include <sys/time.h>
20 #include "tetrinet.h"
21 #include "tetris.h"
22 #include "server.h"
23 #include "sockets.h"
25 /*************************************************************************/
27 static int linuxmode = 0; /* 1: don't try to be compatible with Windows */
28 static int ipv6_only = 0; /* 1: only use IPv6 (when available) */
30 static int quit = 0;
32 static int listen_sock = -1;
33 #ifdef HAVE_IPV6
34 static int listen_sock6 = -1;
35 #endif
36 static int player_socks[6] = {-1,-1,-1,-1,-1,-1};
37 static unsigned char player_ips[6][4];
39 /* Which players have already lost in the current game? */
40 static int player_lost[6];
42 /* We re-use a lot of variables from the main code */
44 /*************************************************************************/
45 /*************************************************************************/
47 /* Convert a 2-byte hex value to an integer. */
49 int xtoi(const char *buf)
51 int val;
53 if (buf[0] <= '9')
54 val = (buf[0] - '0') << 4;
55 else
56 val = (toupper(buf[0]) - 'A' + 10) << 4;
57 if (buf[1] <= '9')
58 val |= buf[1] - '0';
59 else
60 val |= toupper(buf[1]) - 'A' + 10;
61 return val;
64 /*************************************************************************/
66 /* Return a string containing the winlist in a format suitable for sending
67 * to clients.
70 static char *winlist_str()
72 static char buf[1024];
73 char *s;
74 int i;
76 s = buf;
77 for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
78 s += snprintf(s, sizeof(buf)-(s-buf),
79 linuxmode ? " %c%s;%d;%d" : " %c%s;%d",
80 winlist[i].team ? 't' : 'p',
81 winlist[i].name, winlist[i].points, winlist[i].games);
83 return buf;
86 /*************************************************************************/
87 /*************************************************************************/
89 /* Read the configuration file. */
91 void read_config(void)
93 char buf[1024], *s, *t;
94 FILE *f;
95 int i;
97 s = getenv("HOME");
98 if (!s)
99 s = "/etc";
100 snprintf(buf, sizeof(buf), "%s/.tetrinet", s);
101 if (!(f = fopen(buf, "r")))
102 return;
103 while (fgets(buf, sizeof(buf), f)) {
104 s = strtok(buf, " ");
105 if (!s) {
106 continue;
107 } else if (strcmp(s, "linuxmode") == 0) {
108 if (s = strtok(NULL, " "))
109 linuxmode = atoi(s);
110 } else if (strcmp(s, "ipv6_only") == 0) {
111 if (s = strtok(NULL, " "))
112 ipv6_only = atoi(s);
113 } else if (strcmp(s, "averagelevels") == 0) {
114 if (s = strtok(NULL, " "))
115 level_average = atoi(s);
116 } else if (strcmp(s, "classic") == 0) {
117 if (s = strtok(NULL, " "))
118 old_mode = atoi(s);
119 } else if (strcmp(s, "initiallevel") == 0) {
120 if (s = strtok(NULL, " "))
121 initial_level = atoi(s);
122 } else if (strcmp(s, "levelinc") == 0) {
123 if (s = strtok(NULL, " "))
124 level_inc = atoi(s);
125 } else if (strcmp(s, "linesperlevel") == 0) {
126 if (s = strtok(NULL, " "))
127 lines_per_level = atoi(s);
128 } else if (strcmp(s, "pieces") == 0) {
129 i = 0;
130 while (i < 7 && (s = strtok(NULL, " ")))
131 piecefreq[i++] = atoi(s);
132 } else if (strcmp(s, "specialcapacity") == 0) {
133 if (s = strtok(NULL, " "))
134 special_capacity = atoi(s);
135 } else if (strcmp(s, "specialcount") == 0) {
136 if (s = strtok(NULL, " "))
137 special_count = atoi(s);
138 } else if (strcmp(s, "speciallines") == 0) {
139 if (s = strtok(NULL, " "))
140 special_lines = atoi(s);
141 } else if (strcmp(s, "specials") == 0) {
142 i = 0;
143 while (i < 9 && (s = strtok(NULL, " ")))
144 specialfreq[i++] = atoi(s);
145 } else if (strcmp(s, "winlist") == 0) {
146 i = 0;
147 while (i < MAXWINLIST && (s = strtok(NULL, " "))) {
148 t = strchr(s, ';');
149 if (!t)
150 break;
151 *t++ = 0;
152 strncpy(winlist[i].name, s, sizeof(winlist[i].name)-1);
153 winlist[i].name[sizeof(winlist[i].name)-1] = 0;
154 s = t;
155 t = strchr(s, ';');
156 if (!t) {
157 *winlist[i].name = 0;
158 break;
160 winlist[i].team = atoi(s);
161 s = t+1;
162 t = strchr(s, ';');
163 if (!t) {
164 *winlist[i].name = 0;
165 break;
167 winlist[i].points = atoi(s);
168 winlist[i].games = atoi(t+1);
169 i++;
173 fclose(f);
176 /*************************************************************************/
178 /* Re-write the configuration file. */
180 void write_config(void)
182 char buf[1024], *s;
183 FILE *f;
184 int i;
186 s = getenv("HOME");
187 if (!s)
188 s = "/etc";
189 snprintf(buf, sizeof(buf), "%s/.tetrinet", s);
190 if (!(f = fopen(buf, "w")))
191 return;
193 fprintf(f, "winlist");
194 for (i = 0; i < MAXSAVEWINLIST && *winlist[i].name; i++) {
195 fprintf(f, " %s;%d;%d;%d", winlist[i].name, winlist[i].team,
196 winlist[i].points, winlist[i].games);
198 fputc('\n', f);
200 fprintf(f, "classic %d\n", old_mode);
202 fprintf(f, "initiallevel %d\n", initial_level);
203 fprintf(f, "linesperlevel %d\n", lines_per_level);
204 fprintf(f, "levelinc %d\n", level_inc);
205 fprintf(f, "averagelevels %d\n", level_average);
207 fprintf(f, "speciallines %d\n", special_lines);
208 fprintf(f, "specialcount %d\n", special_count);
209 fprintf(f, "specialcapacity %d\n", special_capacity);
211 fprintf(f, "pieces");
212 for (i = 0; i < 7; i++)
213 fprintf(f, " %d", piecefreq[i]);
214 fputc('\n', f);
216 fprintf(f, "specials");
217 for (i = 0; i < 9; i++)
218 fprintf(f, " %d", specialfreq[i]);
219 fputc('\n', f);
221 fprintf(f, "linuxmode %d\n", linuxmode);
222 fprintf(f, "ipv6_only %d\n", ipv6_only);
224 fclose(f);
227 /*************************************************************************/
228 /*************************************************************************/
230 /* Send a message to a single player. */
232 static void send_to(int player, const char *format, ...)
234 va_list args;
235 char buf[1024];
236 int i;
238 va_start(args, format);
239 vsnprintf(buf, sizeof(buf), format, args);
240 if (player_socks[player-1] >= 0)
241 sockprintf(player_socks[player-1], "%s", buf);
244 /*************************************************************************/
246 /* Send a message to all players. */
248 static void send_to_all(const char *format, ...)
250 va_list args;
251 char buf[1024];
252 int i;
254 va_start(args, format);
255 vsnprintf(buf, sizeof(buf), format, args);
256 for (i = 0; i < 6; i++) {
257 if (player_socks[i] >= 0)
258 sockprintf(player_socks[i], "%s", buf);
262 /*************************************************************************/
264 /* Send a message to all players but the given one. */
266 static void send_to_all_but(int player, const char *format, ...)
268 va_list args;
269 char buf[1024];
270 int i;
272 va_start(args, format);
273 vsnprintf(buf, sizeof(buf), format, args);
274 for (i = 0; i < 6; i++) {
275 if (i+1 != player && player_socks[i] >= 0)
276 sockprintf(player_socks[i], "%s", buf);
280 /*************************************************************************/
282 /* Send a message to all players but those on the same team as the given
283 * player.
286 static void send_to_all_but_team(int player, const char *format, ...)
288 va_list args;
289 char buf[1024];
290 int i;
291 char *team = teams[player-1];
293 va_start(args, format);
294 vsnprintf(buf, sizeof(buf), format, args);
295 for (i = 0; i < 6; i++) {
296 if (i+1 != player && player_socks[i] >= 0 &&
297 (!team || !teams[i] || strcmp(teams[i], team) != 0))
298 sockprintf(player_socks[i], "%s", buf);
302 /*************************************************************************/
303 /*************************************************************************/
305 /* Add points to a given player's [team's] winlist entry, or make a new one
306 * if they rank.
309 static void add_points(int player, int points)
311 int i;
313 if (!players[player-1])
314 return;
315 for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
316 if (!winlist[i].team && !teams[player-1]
317 && strcmp(winlist[i].name, players[player-1]) == 0)
318 break;
319 if (winlist[i].team && teams[player-1]
320 && strcmp(winlist[i].name, teams[player-1]) == 0)
321 break;
323 if (i == MAXWINLIST) {
324 for (i = 0; i < MAXWINLIST && winlist[i].points >= points; i++)
327 if (i == MAXWINLIST)
328 return;
329 if (!*winlist[i].name) {
330 if (teams[player-1]) {
331 strncpy(winlist[i].name, teams[player-1], sizeof(winlist[i].name)-1);
332 winlist[i].name[sizeof(winlist[i].name)-1] = 0;
333 winlist[i].team = 1;
334 } else {
335 strncpy(winlist[i].name, players[player-1], sizeof(winlist[i].name)-1);
336 winlist[i].name[sizeof(winlist[i].name)-1] = 0;
337 winlist[i].team = 0;
340 winlist[i].points += points;
343 /*************************************************************************/
345 /* Add a game to a given player's [team's] winlist entry. */
347 static void add_game(int player)
349 int i;
351 if (!players[player-1])
352 return;
353 for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
354 if (!winlist[i].team && !teams[player-1]
355 && strcmp(winlist[i].name, players[player-1]) == 0)
356 break;
357 if (winlist[i].team && teams[player-1]
358 && strcmp(winlist[i].name, teams[player-1]) == 0)
359 break;
361 if (i == MAXWINLIST || !*winlist[i].name)
362 return;
363 winlist[i].games++;
366 /*************************************************************************/
368 /* Sort the winlist. */
370 static void sort_winlist()
372 int i, j, best, bestindex;
374 for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
375 best = winlist[i].points;
376 bestindex = i;
377 for (j = i+1; j < MAXWINLIST && *winlist[j].name; j++) {
378 if (winlist[j].points > best) {
379 best = winlist[j].points;
380 bestindex = j;
383 if (bestindex != i) {
384 WinInfo tmp;
385 memcpy(&tmp, &winlist[i], sizeof(WinInfo));
386 memcpy(&winlist[i], &winlist[bestindex], sizeof(WinInfo));
387 memcpy(&winlist[bestindex], &tmp, sizeof(WinInfo));
392 /*************************************************************************/
394 /* Take care of a player losing (which may end the game). */
396 static void player_loses(int player)
398 int i, j, order, end = 1, winner = -1, second, third;
400 if (player < 1 || player > 6 || player_socks[player-1] < 0)
401 return;
402 order = 0;
403 for (i = 1; i <= 6; i++) {
404 if (player_lost[i-1] > order)
405 order = player_lost[i-1];
407 player_lost[player-1] = order+1;
408 for (i = 1; i <= 6; i++) {
409 if (player_socks[i-1] >= 0 && !player_lost[i-1]) {
410 if (winner < 0) {
411 winner = i;
412 } else if (!teams[winner-1] || !teams[i-1]
413 || strcasecmp(teams[winner-1],teams[i-1]) != 0) {
414 end = 0;
415 break;
419 if (end) {
420 send_to_all("endgame");
421 playing_game = 0;
422 /* Catch the case where no players are left (1-player game) */
423 if (winner > 0) {
424 send_to_all("playerwon %d", winner);
425 add_points(winner, 3);
426 order = 0;
427 for (i = 1; i <= 6; i++) {
428 if (player_lost[i-1] > order
429 && (!teams[winner-1] || !teams[i-1]
430 || strcasecmp(teams[winner-1],teams[i-1]) != 0)) {
431 order = player_lost[i-1];
432 second = i;
435 if (order) {
436 add_points(second, 2);
437 player_lost[second-1] = 0;
439 order = 0;
440 for (i = 1; i <= 6; i++) {
441 if (player_lost[i-1] > order
442 && (!teams[winner-1] || !teams[i-1]
443 || strcasecmp(teams[winner-1],teams[i-1]) != 0)
444 && (!teams[second-1] || !teams[i-1]
445 || strcasecmp(teams[second-1],teams[i-1]) != 0)) {
446 order = player_lost[i-1];
447 third = i;
450 if (order)
451 add_points(third, 1);
452 for (i = 1; i <= 6; i++) {
453 if (teams[i-1]) {
454 for (j = 1; j < i; j++) {
455 if (teams[j-1] && strcasecmp(teams[i-1],teams[j-1])==0)
456 break;
458 if (j < i)
459 continue;
461 if (player_socks[i-1] >= 0)
462 add_game(i);
465 sort_winlist();
466 write_config();
467 send_to_all("winlist %s", winlist_str());
469 /* One more possibility: the only player playing left the game, which
470 * means there are now no players left. */
471 if (!players[0] && !players[1] && !players[2] && !players[3]
472 && !players[4] && !players[5])
473 playing_game = 0;
476 /*************************************************************************/
477 /*************************************************************************/
479 /* Parse a line from a client. Destroys the buffer it's given as a side
480 * effect. Return 0 if the command is unknown (or bad syntax), else 1.
483 static int server_parse(int player, char *buf)
485 char *cmd, *s, *t;
486 int i;
488 cmd = strtok(buf, " ");
490 if (!cmd) {
491 return 1;
493 } else if (strcmp(cmd, "tetrisstart") == 0) {
494 s = strtok(NULL, " ");
495 t = strtok(NULL, " ");
496 if (!t)
497 return 0;
498 for (i = 1; i <= 6; i++) {
499 if (players[i-1] && strcasecmp(s, players[i-1]) == 0) {
500 send_to(player, "noconnecting Nickname already exists on server!");
501 return 0;
504 players[player-1] = strdup(s);
505 if (teams[player-1])
506 free(teams[player-1]);
507 teams[player-1] = NULL;
508 send_to(player, "playernum %d", player);
509 send_to(player, "winlist %s", winlist_str());
510 for (i = 1; i <= 6; i++) {
511 if (i != player && players[i-1]) {
512 send_to(player, "playerjoin %d %s", i, players[i-1]);
513 send_to(player, "team %d %s", i, teams[i-1] ? teams[i-1] : "");
516 if (playing_game) {
517 send_to(player, "ingame");
518 player_lost[player-1] = 1;
520 send_to_all_but(player, "playerjoin %d %s", player, players[player-1]);
522 } else if (strcmp(cmd, "team") == 0) {
523 s = strtok(NULL, " ");
524 t = strtok(NULL, "");
525 if (!s || atoi(s) != player)
526 return 0;
527 if (teams[player])
528 free(teams[player]);
529 if (t)
530 teams[player] = strdup(t);
531 else
532 teams[player] = NULL;
533 send_to_all_but(player, "team %d %s", player, t ? t : "");
535 } else if (strcmp(cmd, "pline") == 0) {
536 s = strtok(NULL, " ");
537 t = strtok(NULL, "");
538 if (!s || atoi(s) != player)
539 return 0;
540 if (!t)
541 t = "";
542 send_to_all_but(player, "pline %d %s", player, t);
544 } else if (strcmp(cmd, "plineact") == 0) {
545 s = strtok(NULL, " ");
546 t = strtok(NULL, "");
547 if (!s || atoi(s) != player)
548 return 0;
549 if (!t)
550 t = "";
551 send_to_all_but(player, "plineact %d %s", player, t);
553 } else if (strcmp(cmd, "startgame") == 0) {
554 int total;
555 char piecebuf[101], specialbuf[101];
557 for (i = 1; i < player; i++) {
558 if (player_socks[i-1] >= 0)
559 return 1;
561 s = strtok(NULL, " ");
562 t = strtok(NULL, " ");
563 if (!s)
564 return 1;
565 i = atoi(s);
566 if ((i && playing_game) || (!i && !playing_game))
567 return 1;
568 if (!i) { /* end game */
569 send_to_all("endgame");
570 playing_game = 0;
571 return 1;
573 total = 0;
574 for (i = 0; i < 7; i++) {
575 if (piecefreq[i])
576 memset(piecebuf+total, '1'+i, piecefreq[i]);
577 total += piecefreq[i];
579 piecebuf[100] = 0;
580 if (total != 100) {
581 send_to_all("plineact 0 cannot start game: Piece frequencies do not total 100 percent!");
582 return 1;
584 total = 0;
585 for (i = 0; i < 9; i++) {
586 if (specialfreq[i])
587 memset(specialbuf+total, '1'+i, specialfreq[i]);
588 total += specialfreq[i];
590 specialbuf[100] = 0;
591 if (total != 100) {
592 send_to_all("plineact 0 cannot start game: Special frequencies do not total 100 percent!");
593 return 1;
595 playing_game = 1;
596 game_paused = 0;
597 for (i = 1; i <= 6; i++) {
598 if (player_socks[i-1] < 0)
599 continue;
600 /* XXX First parameter is stack height */
601 send_to(i, "newgame %d %d %d %d %d %d %d %s %s %d %d",
602 0, initial_level, lines_per_level, level_inc,
603 special_lines, special_count, special_capacity,
604 piecebuf, specialbuf, level_average, old_mode);
606 memset(player_lost, 0, sizeof(player_lost));
608 } else if (strcmp(cmd, "pause") == 0) {
609 if (!playing_game)
610 return 1;
611 s = strtok(NULL, " ");
612 if (!s)
613 return 1;
614 i = atoi(s);
615 if (i)
616 i = 1; /* to make sure it's not anything else */
617 if ((i && game_paused) || (!i && !game_paused))
618 return 1;
619 game_paused = i;
620 send_to_all("pause %d", i);
622 } else if (strcmp(cmd, "playerlost") == 0) {
623 if (!(s = strtok(NULL, " ")) || atoi(s) != player)
624 return 1;
625 player_loses(player);
627 } else if (strcmp(cmd, "f") == 0) { /* field */
628 if (!(s = strtok(NULL, " ")) || atoi(s) != player)
629 return 1;
630 if (!(s = strtok(NULL, "")))
631 s = "";
632 send_to_all_but(player, "f %d %s", player, s);
634 } else if (strcmp(cmd, "lvl") == 0) {
635 if (!(s = strtok(NULL, " ")) || atoi(s) != player)
636 return 1;
637 if (!(s = strtok(NULL, " ")))
638 return 1;
639 levels[player] = atoi(s);
640 send_to_all_but(player, "lvl %d %d", player, levels[player]);
642 } else if (strcmp(cmd, "sb") == 0) {
643 int from, to;
644 char *type;
646 if (!(s = strtok(NULL, " ")))
647 return 1;
648 to = atoi(s);
649 if (!(type = strtok(NULL, " ")))
650 return 1;
651 if (!(s = strtok(NULL, " ")))
652 return 1;
653 from = atoi(s);
654 if (from != player)
655 return 1;
656 if (to < 0 || to > 6 || player_socks[to-1] < 0 || player_lost[to-1])
657 return 1;
658 if (to == 0)
659 send_to_all_but_team(player, "sb %d %s %d", to, type, from);
660 else
661 send_to_all_but(player, "sb %d %s %d", to, type, from);
663 } else if (strcmp(cmd, "gmsg") == 0) {
664 if (!(s = strtok(NULL, "")))
665 return 1;
666 send_to_all("gmsg %s", s);
668 } else { /* unrecognized command */
669 return 0;
673 return 1;
676 /*************************************************************************/
677 /*************************************************************************/
679 static void sigcatcher(int sig)
681 if (sig == SIGHUP) {
682 read_config();
683 signal(SIGHUP, sigcatcher);
684 send_to_all("winlist %s", winlist_str());
685 } else if (sig == SIGTERM || sig == SIGINT) {
686 quit = 1;
687 signal(sig, SIG_IGN);
691 /*************************************************************************/
693 /* Returns 0 on success, desired program exit code on failure */
695 static int init()
697 struct sockaddr_in sin;
698 #ifdef HAVE_IPV6
699 struct sockaddr_in6 sin6;
700 #endif
701 int i;
703 /* Set up some sensible defaults */
704 *winlist[0].name = 0;
705 old_mode = 1;
706 initial_level = 1;
707 lines_per_level = 2;
708 level_inc = 1;
709 level_average = 1;
710 special_lines = 1;
711 special_count = 1;
712 special_capacity = 18;
713 piecefreq[0] = 14;
714 piecefreq[1] = 14;
715 piecefreq[2] = 15;
716 piecefreq[3] = 14;
717 piecefreq[4] = 14;
718 piecefreq[5] = 14;
719 piecefreq[6] = 15;
720 specialfreq[0] = 18;
721 specialfreq[1] = 18;
722 specialfreq[2] = 3;
723 specialfreq[3] = 12;
724 specialfreq[4] = 0;
725 specialfreq[5] = 16;
726 specialfreq[6] = 3;
727 specialfreq[7] = 12;
728 specialfreq[8] = 18;
730 /* (Try to) read the config file */
731 read_config();
733 /* Catch some signals */
734 signal(SIGHUP, sigcatcher);
735 signal(SIGINT, sigcatcher);
736 signal(SIGTERM, sigcatcher);
738 /* Set up a listen socket */
739 if (!ipv6_only)
740 listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
741 if (listen_sock >= 0){
742 i = 1;
743 if (setsockopt(listen_sock,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(i))==0) {
744 memset(&sin, 0, sizeof(sin));
745 sin.sin_family = AF_INET;
746 sin.sin_port = htons(31457);
747 if (bind(listen_sock, (struct sockaddr *)&sin, sizeof(sin)) == 0) {
748 if (listen(listen_sock, 5) == 0) {
749 goto ipv4_success;
753 i = errno;
754 close(listen_sock);
755 errno = i;
756 listen_sock = -1;
758 ipv4_success:
760 #ifdef HAVE_IPV6
761 /* Set up an IPv6 listen socket if possible */
762 listen_sock6 = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
763 if (listen_sock6 >= 0) {
764 i = 1;
765 if (setsockopt(listen_sock6,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(i))==0) {
766 memset(&sin6, 0, sizeof(sin6));
767 sin6.sin6_family = AF_INET6;
768 sin6.sin6_port = htons(31457);
769 if (bind(listen_sock6,(struct sockaddr *)&sin6,sizeof(sin6))==0) {
770 if (listen(listen_sock6, 5) == 0) {
771 goto ipv6_success;
775 i = errno;
776 close(listen_sock6);
777 errno = i;
778 listen_sock6 = -1;
780 ipv6_success:
781 #else /* !HAVE_IPV6 */
782 if (ipv6_only) {
783 fprintf(stderr,"ipv6_only specified but IPv6 support not available\n");
784 return 1;
786 #endif /* HAVE_IPV6 */
788 if (listen_sock < 0
789 #ifdef HAVE_IPV6
790 && listen_sock6 < 0
791 #endif
793 return 1;
796 return 0;
799 /*************************************************************************/
801 static void check_sockets()
803 fd_set fds;
804 int i, fd, maxfd;
806 FD_ZERO(&fds);
807 if (listen_sock >= 0)
808 FD_SET(listen_sock, &fds);
809 maxfd = listen_sock;
810 #ifdef HAVE_IPV6
811 if (listen_sock6 >= 0)
812 FD_SET(listen_sock6, &fds);
813 if (listen_sock6 > maxfd)
814 maxfd = listen_sock6;
815 #endif
816 for (i = 0; i < 6; i++) {
817 if (player_socks[i] != -1) {
818 if (player_socks[i] < 0)
819 fd = (~player_socks[i]) - 1;
820 else
821 fd = player_socks[i];
822 FD_SET(fd, &fds);
823 if (fd > maxfd)
824 maxfd = fd;
828 if (select(maxfd+1, &fds, NULL, NULL, NULL) <= 0)
829 return;
831 if (listen_sock >= 0 && FD_ISSET(listen_sock, &fds)) {
832 struct sockaddr_in sin;
833 int len = sizeof(sin);
834 fd = accept(listen_sock, (struct sockaddr *)&sin, &len);
835 if (fd >= 0) {
836 for (i = 0; i < 6 && player_socks[i] != -1; i++)
838 if (i == 6) {
839 sockprintf(fd, "noconnecting Too many players on server!");
840 close(fd);
841 } else {
842 player_socks[i] = ~(fd+1);
843 memcpy(player_ips[i], &sin.sin_addr, 4);
846 } /* if (FD_ISSET(listen_sock)) */
848 #ifdef HAVE_IPV6
849 if (listen_sock6 >= 0 && FD_ISSET(listen_sock6, &fds)) {
850 struct sockaddr_in6 sin6;
851 int len = sizeof(sin6);
852 fd = accept(listen_sock6, (struct sockaddr *)&sin6, &len);
853 if (fd >= 0) {
854 for (i = 0; i < 6 && player_socks[i] != -1; i++)
856 if (i == 6) {
857 sockprintf(fd, "noconnecting Too many players on server!");
858 close(fd);
859 } else {
860 player_socks[i] = ~(fd+1);
861 memcpy(player_ips[i], (char *)(&sin6.sin6_addr)+12, 4);
864 } /* if (FD_ISSET(listen_sock6)) */
865 #endif
867 for (i = 0; i < 6; i++) {
868 char buf[1024];
870 if (player_socks[i] == -1)
871 continue;
872 if (player_socks[i] < 0)
873 fd = (~player_socks[i]) - 1;
874 else
875 fd = player_socks[i];
876 if (!FD_ISSET(fd, &fds))
877 continue;
878 sgets(buf, sizeof(buf), fd);
879 if (player_socks[i] < 0) {
880 /* Messy decoding stuff */
881 char iphashbuf[16], newbuf[1024];
882 unsigned char *ip;
883 int j, c, d;
885 if (strlen(buf) < 2*13) { /* "tetrisstart " + initial byte */
886 close(fd);
887 player_socks[i] = -1;
888 continue;
890 ip = player_ips[i];
891 sprintf(iphashbuf, "%d", ip[0]*54 + ip[1]*41 + ip[2]*29 + ip[3]*17);
892 c = xtoi(buf);
893 for (j = 2; buf[j] && buf[j+1]; j += 2) {
894 int temp;
895 temp = d = xtoi(buf+j);
896 d ^= iphashbuf[((j/2)-1) % strlen(iphashbuf)];
897 d += 255 - c;
898 d %= 255;
899 newbuf[j/2-1] = d;
900 c = temp;
902 newbuf[j/2-1] = 0;
903 if (strncmp(newbuf, "tetrisstart ", 12) != 0) {
904 close(fd);
905 player_socks[i] = -1;
906 continue;
908 /* Buffers should be the same size, but let's be paranoid */
909 strncpy(buf, newbuf, sizeof(buf));
910 buf[sizeof(buf)-1] = 0;
911 player_socks[i] = fd; /* Has now registered */
912 } /* if client not registered */
913 if (!server_parse(i+1, buf)) {
914 close(fd);
915 player_socks[i] = -1;
916 if (players[i]) {
917 send_to_all("playerleave %d", i+1);
918 if (playing_game)
919 player_loses(i+1);
920 free(players[i]);
921 players[i] = NULL;
922 if (teams[i]) {
923 free(teams[i]);
924 teams[i] = NULL;
928 } /* for each player socket */
931 /*************************************************************************/
933 #ifdef SERVER_ONLY
934 int main()
935 #else
936 int server_main()
937 #endif
939 int i;
941 if ((i = init()) != 0)
942 return i;
943 while (!quit)
944 check_sockets();
945 write_config();
946 if (listen_sock >= 0)
947 close(listen_sock);
948 #ifdef HAVE_IPV6
949 if (listen_sock6 >= 0)
950 close(listen_sock6);
951 #endif
952 for (i = 0; i < 6; i++)
953 close(player_socks[i]);
954 return 0;
957 /*************************************************************************/