ChangeLog update
[tetrinet.git] / server.c
blobedf228563db17db9fd5e8366fdefbf47bdefc6aa
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];
38 static int player_modes[6];
40 /* Which players have already lost in the current game? */
41 static int player_lost[6];
43 /* We re-use a lot of variables from the main code */
45 /*************************************************************************/
46 /*************************************************************************/
48 /* Convert a 2-byte hex value to an integer. */
50 int xtoi(const char *buf)
52 int val;
54 if (buf[0] <= '9')
55 val = (buf[0] - '0') << 4;
56 else
57 val = (toupper(buf[0]) - 'A' + 10) << 4;
58 if (buf[1] <= '9')
59 val |= buf[1] - '0';
60 else
61 val |= toupper(buf[1]) - 'A' + 10;
62 return val;
65 /*************************************************************************/
67 /* Return a string containing the winlist in a format suitable for sending
68 * to clients.
71 static char *winlist_str()
73 static char buf[1024];
74 char *s;
75 int i;
77 s = buf;
78 for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
79 s += snprintf(s, sizeof(buf)-(s-buf),
80 linuxmode ? " %c%s;%d;%d" : " %c%s;%d",
81 winlist[i].team ? 't' : 'p',
82 winlist[i].name, winlist[i].points, winlist[i].games);
84 return buf;
87 /*************************************************************************/
88 /*************************************************************************/
90 /* Read the configuration file. */
92 void read_config(void)
94 char buf[1024], *s, *t;
95 FILE *f;
96 int i;
98 s = getenv("HOME");
99 if (!s)
100 s = "/etc";
101 snprintf(buf, sizeof(buf), "%s/.tetrinet", s);
102 if (!(f = fopen(buf, "r")))
103 return;
104 while (fgets(buf, sizeof(buf), f)) {
105 s = strtok(buf, " ");
106 if (!s) {
107 continue;
108 } else if (strcmp(s, "linuxmode") == 0) {
109 if (s = strtok(NULL, " "))
110 linuxmode = atoi(s);
111 } else if (strcmp(s, "ipv6_only") == 0) {
112 if (s = strtok(NULL, " "))
113 ipv6_only = atoi(s);
114 } else if (strcmp(s, "averagelevels") == 0) {
115 if (s = strtok(NULL, " "))
116 level_average = atoi(s);
117 } else if (strcmp(s, "classic") == 0) {
118 if (s = strtok(NULL, " "))
119 old_mode = atoi(s);
120 } else if (strcmp(s, "initiallevel") == 0) {
121 if (s = strtok(NULL, " "))
122 initial_level = atoi(s);
123 } else if (strcmp(s, "levelinc") == 0) {
124 if (s = strtok(NULL, " "))
125 level_inc = atoi(s);
126 } else if (strcmp(s, "linesperlevel") == 0) {
127 if (s = strtok(NULL, " "))
128 lines_per_level = atoi(s);
129 } else if (strcmp(s, "pieces") == 0) {
130 i = 0;
131 while (i < 7 && (s = strtok(NULL, " ")))
132 piecefreq[i++] = atoi(s);
133 } else if (strcmp(s, "specialcapacity") == 0) {
134 if (s = strtok(NULL, " "))
135 special_capacity = atoi(s);
136 } else if (strcmp(s, "specialcount") == 0) {
137 if (s = strtok(NULL, " "))
138 special_count = atoi(s);
139 } else if (strcmp(s, "speciallines") == 0) {
140 if (s = strtok(NULL, " "))
141 special_lines = atoi(s);
142 } else if (strcmp(s, "specials") == 0) {
143 i = 0;
144 while (i < 9 && (s = strtok(NULL, " ")))
145 specialfreq[i++] = atoi(s);
146 } else if (strcmp(s, "winlist") == 0) {
147 i = 0;
148 while (i < MAXWINLIST && (s = strtok(NULL, " "))) {
149 t = strchr(s, ';');
150 if (!t)
151 break;
152 *t++ = 0;
153 strncpy(winlist[i].name, s, sizeof(winlist[i].name)-1);
154 winlist[i].name[sizeof(winlist[i].name)-1] = 0;
155 s = t;
156 t = strchr(s, ';');
157 if (!t) {
158 *winlist[i].name = 0;
159 break;
161 winlist[i].team = atoi(s);
162 s = t+1;
163 t = strchr(s, ';');
164 if (!t) {
165 *winlist[i].name = 0;
166 break;
168 winlist[i].points = atoi(s);
169 winlist[i].games = atoi(t+1);
170 i++;
174 fclose(f);
177 /*************************************************************************/
179 /* Re-write the configuration file. */
181 void write_config(void)
183 char buf[1024], *s;
184 FILE *f;
185 int i;
187 s = getenv("HOME");
188 if (!s)
189 s = "/etc";
190 snprintf(buf, sizeof(buf), "%s/.tetrinet", s);
191 if (!(f = fopen(buf, "w")))
192 return;
194 fprintf(f, "winlist");
195 for (i = 0; i < MAXSAVEWINLIST && *winlist[i].name; i++) {
196 fprintf(f, " %s;%d;%d;%d", winlist[i].name, winlist[i].team,
197 winlist[i].points, winlist[i].games);
199 fputc('\n', f);
201 fprintf(f, "classic %d\n", old_mode);
203 fprintf(f, "initiallevel %d\n", initial_level);
204 fprintf(f, "linesperlevel %d\n", lines_per_level);
205 fprintf(f, "levelinc %d\n", level_inc);
206 fprintf(f, "averagelevels %d\n", level_average);
208 fprintf(f, "speciallines %d\n", special_lines);
209 fprintf(f, "specialcount %d\n", special_count);
210 fprintf(f, "specialcapacity %d\n", special_capacity);
212 fprintf(f, "pieces");
213 for (i = 0; i < 7; i++)
214 fprintf(f, " %d", piecefreq[i]);
215 fputc('\n', f);
217 fprintf(f, "specials");
218 for (i = 0; i < 9; i++)
219 fprintf(f, " %d", specialfreq[i]);
220 fputc('\n', f);
222 fprintf(f, "linuxmode %d\n", linuxmode);
223 fprintf(f, "ipv6_only %d\n", ipv6_only);
225 fclose(f);
228 /*************************************************************************/
229 /*************************************************************************/
231 /* Send a message to a single player. */
233 static void send_to(int player, const char *format, ...)
235 va_list args;
236 char buf[1024];
237 int i;
239 va_start(args, format);
240 vsnprintf(buf, sizeof(buf), format, args);
241 if (player_socks[player-1] >= 0)
242 sockprintf(player_socks[player-1], "%s", buf);
245 /*************************************************************************/
247 /* Send a message to all players. */
249 static void send_to_all(const char *format, ...)
251 va_list args;
252 char buf[1024];
253 int i;
255 va_start(args, format);
256 vsnprintf(buf, sizeof(buf), format, args);
257 for (i = 0; i < 6; i++) {
258 if (player_socks[i] >= 0)
259 sockprintf(player_socks[i], "%s", buf);
263 /*************************************************************************/
265 /* Send a message to all players but the given one. */
267 static void send_to_all_but(int player, const char *format, ...)
269 va_list args;
270 char buf[1024];
271 int i;
273 va_start(args, format);
274 vsnprintf(buf, sizeof(buf), format, args);
275 for (i = 0; i < 6; i++) {
276 if (i+1 != player && player_socks[i] >= 0)
277 sockprintf(player_socks[i], "%s", buf);
281 /*************************************************************************/
283 /* Send a message to all players but those on the same team as the given
284 * player.
287 static void send_to_all_but_team(int player, const char *format, ...)
289 va_list args;
290 char buf[1024];
291 int i;
292 char *team = teams[player-1];
294 va_start(args, format);
295 vsnprintf(buf, sizeof(buf), format, args);
296 for (i = 0; i < 6; i++) {
297 if (i+1 != player && player_socks[i] >= 0 &&
298 (!team || !teams[i] || strcmp(teams[i], team) != 0))
299 sockprintf(player_socks[i], "%s", buf);
303 /*************************************************************************/
304 /*************************************************************************/
306 /* Add points to a given player's [team's] winlist entry, or make a new one
307 * if they rank.
310 static void add_points(int player, int points)
312 int i;
314 if (!players[player-1])
315 return;
316 for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
317 if (!winlist[i].team && !teams[player-1]
318 && strcmp(winlist[i].name, players[player-1]) == 0)
319 break;
320 if (winlist[i].team && teams[player-1]
321 && strcmp(winlist[i].name, teams[player-1]) == 0)
322 break;
324 if (i == MAXWINLIST) {
325 for (i = 0; i < MAXWINLIST && winlist[i].points >= points; i++)
328 if (i == MAXWINLIST)
329 return;
330 if (!*winlist[i].name) {
331 if (teams[player-1]) {
332 strncpy(winlist[i].name, teams[player-1], sizeof(winlist[i].name)-1);
333 winlist[i].name[sizeof(winlist[i].name)-1] = 0;
334 winlist[i].team = 1;
335 } else {
336 strncpy(winlist[i].name, players[player-1], sizeof(winlist[i].name)-1);
337 winlist[i].name[sizeof(winlist[i].name)-1] = 0;
338 winlist[i].team = 0;
341 winlist[i].points += points;
344 /*************************************************************************/
346 /* Add a game to a given player's [team's] winlist entry. */
348 static void add_game(int player)
350 int i;
352 if (!players[player-1])
353 return;
354 for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
355 if (!winlist[i].team && !teams[player-1]
356 && strcmp(winlist[i].name, players[player-1]) == 0)
357 break;
358 if (winlist[i].team && teams[player-1]
359 && strcmp(winlist[i].name, teams[player-1]) == 0)
360 break;
362 if (i == MAXWINLIST || !*winlist[i].name)
363 return;
364 winlist[i].games++;
367 /*************************************************************************/
369 /* Sort the winlist. */
371 static void sort_winlist()
373 int i, j, best, bestindex;
375 for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
376 best = winlist[i].points;
377 bestindex = i;
378 for (j = i+1; j < MAXWINLIST && *winlist[j].name; j++) {
379 if (winlist[j].points > best) {
380 best = winlist[j].points;
381 bestindex = j;
384 if (bestindex != i) {
385 WinInfo tmp;
386 memcpy(&tmp, &winlist[i], sizeof(WinInfo));
387 memcpy(&winlist[i], &winlist[bestindex], sizeof(WinInfo));
388 memcpy(&winlist[bestindex], &tmp, sizeof(WinInfo));
393 /*************************************************************************/
395 /* Take care of a player losing (which may end the game). */
397 static void player_loses(int player)
399 int i, j, order, end = 1, winner = -1, second, third;
401 if (player < 1 || player > 6 || player_socks[player-1] < 0)
402 return;
403 order = 0;
404 for (i = 1; i <= 6; i++) {
405 if (player_lost[i-1] > order)
406 order = player_lost[i-1];
408 player_lost[player-1] = order+1;
409 for (i = 1; i <= 6; i++) {
410 if (player_socks[i-1] >= 0 && !player_lost[i-1]) {
411 if (winner < 0) {
412 winner = i;
413 } else if (!teams[winner-1] || !teams[i-1]
414 || strcasecmp(teams[winner-1],teams[i-1]) != 0) {
415 end = 0;
416 break;
420 if (end) {
421 send_to_all("endgame");
422 playing_game = 0;
423 /* Catch the case where no players are left (1-player game) */
424 if (winner > 0) {
425 send_to_all("playerwon %d", winner);
426 add_points(winner, 3);
427 order = 0;
428 for (i = 1; i <= 6; i++) {
429 if (player_lost[i-1] > order
430 && (!teams[winner-1] || !teams[i-1]
431 || strcasecmp(teams[winner-1],teams[i-1]) != 0)) {
432 order = player_lost[i-1];
433 second = i;
436 if (order) {
437 add_points(second, 2);
438 player_lost[second-1] = 0;
440 order = 0;
441 for (i = 1; i <= 6; i++) {
442 if (player_lost[i-1] > order
443 && (!teams[winner-1] || !teams[i-1]
444 || strcasecmp(teams[winner-1],teams[i-1]) != 0)
445 && (!teams[second-1] || !teams[i-1]
446 || strcasecmp(teams[second-1],teams[i-1]) != 0)) {
447 order = player_lost[i-1];
448 third = i;
451 if (order)
452 add_points(third, 1);
453 for (i = 1; i <= 6; i++) {
454 if (teams[i-1]) {
455 for (j = 1; j < i; j++) {
456 if (teams[j-1] && strcasecmp(teams[i-1],teams[j-1])==0)
457 break;
459 if (j < i)
460 continue;
462 if (player_socks[i-1] >= 0)
463 add_game(i);
466 sort_winlist();
467 write_config();
468 send_to_all("winlist %s", winlist_str());
470 /* One more possibility: the only player playing left the game, which
471 * means there are now no players left. */
472 if (!players[0] && !players[1] && !players[2] && !players[3]
473 && !players[4] && !players[5])
474 playing_game = 0;
477 /*************************************************************************/
478 /*************************************************************************/
480 /* Parse a line from a client. Destroys the buffer it's given as a side
481 * effect. Return 0 if the command is unknown (or bad syntax), else 1.
484 static int server_parse(int player, char *buf)
486 char *cmd, *s, *t;
487 int i, tetrifast = 0;
489 cmd = strtok(buf, " ");
491 if (!cmd) {
492 return 1;
494 } else if (strcmp(cmd, "tetrisstart") == 0) {
495 newplayer:
496 s = strtok(NULL, " ");
497 t = strtok(NULL, " ");
498 if (!t)
499 return 0;
500 for (i = 1; i <= 6; i++) {
501 if (players[i-1] && strcasecmp(s, players[i-1]) == 0) {
502 send_to(player, "noconnecting Nickname already exists on server!");
503 return 0;
506 players[player-1] = strdup(s);
507 if (teams[player-1])
508 free(teams[player-1]);
509 teams[player-1] = NULL;
510 player_modes[player-1] = tetrifast;
511 send_to(player, "%s %d", tetrifast ? ")#)(!@(*3" : "playernum", player);
512 send_to(player, "winlist %s", winlist_str());
513 for (i = 1; i <= 6; i++) {
514 if (i != player && players[i-1]) {
515 send_to(player, "playerjoin %d %s", i, players[i-1]);
516 send_to(player, "team %d %s", i, teams[i-1] ? teams[i-1] : "");
519 if (playing_game) {
520 send_to(player, "ingame");
521 player_lost[player-1] = 1;
523 send_to_all_but(player, "playerjoin %d %s", player, players[player-1]);
525 } else if (strcmp(cmd, "tetrifaster") == 0) {
526 tetrifast = 1;
527 goto newplayer;
529 } else if (strcmp(cmd, "team") == 0) {
530 s = strtok(NULL, " ");
531 t = strtok(NULL, "");
532 if (!s || atoi(s) != player)
533 return 0;
534 if (teams[player])
535 free(teams[player]);
536 if (t)
537 teams[player] = strdup(t);
538 else
539 teams[player] = NULL;
540 send_to_all_but(player, "team %d %s", player, t ? t : "");
542 } else if (strcmp(cmd, "pline") == 0) {
543 s = strtok(NULL, " ");
544 t = strtok(NULL, "");
545 if (!s || atoi(s) != player)
546 return 0;
547 if (!t)
548 t = "";
549 send_to_all_but(player, "pline %d %s", player, t);
551 } else if (strcmp(cmd, "plineact") == 0) {
552 s = strtok(NULL, " ");
553 t = strtok(NULL, "");
554 if (!s || atoi(s) != player)
555 return 0;
556 if (!t)
557 t = "";
558 send_to_all_but(player, "plineact %d %s", player, t);
560 } else if (strcmp(cmd, "startgame") == 0) {
561 int total;
562 char piecebuf[101], specialbuf[101];
564 for (i = 1; i < player; i++) {
565 if (player_socks[i-1] >= 0)
566 return 1;
568 s = strtok(NULL, " ");
569 t = strtok(NULL, " ");
570 if (!s)
571 return 1;
572 i = atoi(s);
573 if ((i && playing_game) || (!i && !playing_game))
574 return 1;
575 if (!i) { /* end game */
576 send_to_all("endgame");
577 playing_game = 0;
578 return 1;
580 total = 0;
581 for (i = 0; i < 7; i++) {
582 if (piecefreq[i])
583 memset(piecebuf+total, '1'+i, piecefreq[i]);
584 total += piecefreq[i];
586 piecebuf[100] = 0;
587 if (total != 100) {
588 send_to_all("plineact 0 cannot start game: Piece frequencies do not total 100 percent!");
589 return 1;
591 total = 0;
592 for (i = 0; i < 9; i++) {
593 if (specialfreq[i])
594 memset(specialbuf+total, '1'+i, specialfreq[i]);
595 total += specialfreq[i];
597 specialbuf[100] = 0;
598 if (total != 100) {
599 send_to_all("plineact 0 cannot start game: Special frequencies do not total 100 percent!");
600 return 1;
602 playing_game = 1;
603 game_paused = 0;
604 for (i = 1; i <= 6; i++) {
605 if (player_socks[i-1] < 0)
606 continue;
607 /* XXX First parameter is stack height */
608 send_to(i, "%s %d %d %d %d %d %d %d %s %s %d %d",
609 player_modes[i-1] ? "*******" : "newgame",
610 0, initial_level, lines_per_level, level_inc,
611 special_lines, special_count, special_capacity,
612 piecebuf, specialbuf, level_average, old_mode);
614 memset(player_lost, 0, sizeof(player_lost));
616 } else if (strcmp(cmd, "pause") == 0) {
617 if (!playing_game)
618 return 1;
619 s = strtok(NULL, " ");
620 if (!s)
621 return 1;
622 i = atoi(s);
623 if (i)
624 i = 1; /* to make sure it's not anything else */
625 if ((i && game_paused) || (!i && !game_paused))
626 return 1;
627 game_paused = i;
628 send_to_all("pause %d", i);
630 } else if (strcmp(cmd, "playerlost") == 0) {
631 if (!(s = strtok(NULL, " ")) || atoi(s) != player)
632 return 1;
633 player_loses(player);
635 } else if (strcmp(cmd, "f") == 0) { /* field */
636 if (!(s = strtok(NULL, " ")) || atoi(s) != player)
637 return 1;
638 if (!(s = strtok(NULL, "")))
639 s = "";
640 send_to_all_but(player, "f %d %s", player, s);
642 } else if (strcmp(cmd, "lvl") == 0) {
643 if (!(s = strtok(NULL, " ")) || atoi(s) != player)
644 return 1;
645 if (!(s = strtok(NULL, " ")))
646 return 1;
647 levels[player] = atoi(s);
648 send_to_all_but(player, "lvl %d %d", player, levels[player]);
650 } else if (strcmp(cmd, "sb") == 0) {
651 int from, to;
652 char *type;
654 if (!(s = strtok(NULL, " ")))
655 return 1;
656 to = atoi(s);
657 if (!(type = strtok(NULL, " ")))
658 return 1;
659 if (!(s = strtok(NULL, " ")))
660 return 1;
661 from = atoi(s);
662 if (from != player)
663 return 1;
664 if (to < 0 || to > 6 || player_socks[to-1] < 0 || player_lost[to-1])
665 return 1;
666 if (to == 0)
667 send_to_all_but_team(player, "sb %d %s %d", to, type, from);
668 else
669 send_to_all_but(player, "sb %d %s %d", to, type, from);
671 } else if (strcmp(cmd, "gmsg") == 0) {
672 if (!(s = strtok(NULL, "")))
673 return 1;
674 send_to_all("gmsg %s", s);
676 } else { /* unrecognized command */
677 return 0;
681 return 1;
684 /*************************************************************************/
685 /*************************************************************************/
687 static void sigcatcher(int sig)
689 if (sig == SIGHUP) {
690 read_config();
691 signal(SIGHUP, sigcatcher);
692 send_to_all("winlist %s", winlist_str());
693 } else if (sig == SIGTERM || sig == SIGINT) {
694 quit = 1;
695 signal(sig, SIG_IGN);
699 /*************************************************************************/
701 /* Returns 0 on success, desired program exit code on failure */
703 static int init()
705 struct sockaddr_in sin;
706 #ifdef HAVE_IPV6
707 struct sockaddr_in6 sin6;
708 #endif
709 int i;
711 /* Set up some sensible defaults */
712 *winlist[0].name = 0;
713 old_mode = 1;
714 initial_level = 1;
715 lines_per_level = 2;
716 level_inc = 1;
717 level_average = 1;
718 special_lines = 1;
719 special_count = 1;
720 special_capacity = 18;
721 piecefreq[0] = 14;
722 piecefreq[1] = 14;
723 piecefreq[2] = 15;
724 piecefreq[3] = 14;
725 piecefreq[4] = 14;
726 piecefreq[5] = 14;
727 piecefreq[6] = 15;
728 specialfreq[0] = 18;
729 specialfreq[1] = 18;
730 specialfreq[2] = 3;
731 specialfreq[3] = 12;
732 specialfreq[4] = 0;
733 specialfreq[5] = 16;
734 specialfreq[6] = 3;
735 specialfreq[7] = 12;
736 specialfreq[8] = 18;
738 /* (Try to) read the config file */
739 read_config();
741 /* Catch some signals */
742 signal(SIGHUP, sigcatcher);
743 signal(SIGINT, sigcatcher);
744 signal(SIGTERM, sigcatcher);
746 /* Set up a listen socket */
747 if (!ipv6_only)
748 listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
749 if (listen_sock >= 0){
750 i = 1;
751 if (setsockopt(listen_sock,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(i))==0) {
752 memset(&sin, 0, sizeof(sin));
753 sin.sin_family = AF_INET;
754 sin.sin_port = htons(31457);
755 if (bind(listen_sock, (struct sockaddr *)&sin, sizeof(sin)) == 0) {
756 if (listen(listen_sock, 5) == 0) {
757 goto ipv4_success;
761 i = errno;
762 close(listen_sock);
763 errno = i;
764 listen_sock = -1;
766 ipv4_success:
768 #ifdef HAVE_IPV6
769 /* Set up an IPv6 listen socket if possible */
770 listen_sock6 = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
771 if (listen_sock6 >= 0) {
772 i = 1;
773 if (setsockopt(listen_sock6,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(i))==0) {
774 memset(&sin6, 0, sizeof(sin6));
775 sin6.sin6_family = AF_INET6;
776 sin6.sin6_port = htons(31457);
777 if (bind(listen_sock6,(struct sockaddr *)&sin6,sizeof(sin6))==0) {
778 if (listen(listen_sock6, 5) == 0) {
779 goto ipv6_success;
783 i = errno;
784 close(listen_sock6);
785 errno = i;
786 listen_sock6 = -1;
788 ipv6_success:
789 #else /* !HAVE_IPV6 */
790 if (ipv6_only) {
791 fprintf(stderr,"ipv6_only specified but IPv6 support not available\n");
792 return 1;
794 #endif /* HAVE_IPV6 */
796 if (listen_sock < 0
797 #ifdef HAVE_IPV6
798 && listen_sock6 < 0
799 #endif
801 return 1;
804 return 0;
807 /*************************************************************************/
809 static void check_sockets()
811 fd_set fds;
812 int i, fd, maxfd;
814 FD_ZERO(&fds);
815 if (listen_sock >= 0)
816 FD_SET(listen_sock, &fds);
817 maxfd = listen_sock;
818 #ifdef HAVE_IPV6
819 if (listen_sock6 >= 0)
820 FD_SET(listen_sock6, &fds);
821 if (listen_sock6 > maxfd)
822 maxfd = listen_sock6;
823 #endif
824 for (i = 0; i < 6; i++) {
825 if (player_socks[i] != -1) {
826 if (player_socks[i] < 0)
827 fd = (~player_socks[i]) - 1;
828 else
829 fd = player_socks[i];
830 FD_SET(fd, &fds);
831 if (fd > maxfd)
832 maxfd = fd;
836 if (select(maxfd+1, &fds, NULL, NULL, NULL) <= 0)
837 return;
839 if (listen_sock >= 0 && FD_ISSET(listen_sock, &fds)) {
840 struct sockaddr_in sin;
841 int len = sizeof(sin);
842 fd = accept(listen_sock, (struct sockaddr *)&sin, &len);
843 if (fd >= 0) {
844 for (i = 0; i < 6 && player_socks[i] != -1; i++)
846 if (i == 6) {
847 sockprintf(fd, "noconnecting Too many players on server!");
848 close(fd);
849 } else {
850 player_socks[i] = ~(fd+1);
851 memcpy(player_ips[i], &sin.sin_addr, 4);
854 } /* if (FD_ISSET(listen_sock)) */
856 #ifdef HAVE_IPV6
857 if (listen_sock6 >= 0 && FD_ISSET(listen_sock6, &fds)) {
858 struct sockaddr_in6 sin6;
859 int len = sizeof(sin6);
860 fd = accept(listen_sock6, (struct sockaddr *)&sin6, &len);
861 if (fd >= 0) {
862 for (i = 0; i < 6 && player_socks[i] != -1; i++)
864 if (i == 6) {
865 sockprintf(fd, "noconnecting Too many players on server!");
866 close(fd);
867 } else {
868 player_socks[i] = ~(fd+1);
869 memcpy(player_ips[i], (char *)(&sin6.sin6_addr)+12, 4);
872 } /* if (FD_ISSET(listen_sock6)) */
873 #endif
875 for (i = 0; i < 6; i++) {
876 char buf[1024];
878 if (player_socks[i] == -1)
879 continue;
880 if (player_socks[i] < 0)
881 fd = (~player_socks[i]) - 1;
882 else
883 fd = player_socks[i];
884 if (!FD_ISSET(fd, &fds))
885 continue;
886 sgets(buf, sizeof(buf), fd);
887 if (player_socks[i] < 0) {
888 /* Messy decoding stuff */
889 char iphashbuf[16], newbuf[1024];
890 unsigned char *ip;
891 int j, c, d;
893 if (strlen(buf) < 2*13) { /* "tetrisstart " + initial byte */
894 close(fd);
895 player_socks[i] = -1;
896 continue;
898 ip = player_ips[i];
899 sprintf(iphashbuf, "%d", ip[0]*54 + ip[1]*41 + ip[2]*29 + ip[3]*17);
900 c = xtoi(buf);
901 for (j = 2; buf[j] && buf[j+1]; j += 2) {
902 int temp;
903 temp = d = xtoi(buf+j);
904 d ^= iphashbuf[((j/2)-1) % strlen(iphashbuf)];
905 d += 255 - c;
906 d %= 255;
907 newbuf[j/2-1] = d;
908 c = temp;
910 newbuf[j/2-1] = 0;
911 if (strncmp(newbuf, "tetrisstart ", 12) != 0) {
912 close(fd);
913 player_socks[i] = -1;
914 continue;
916 /* Buffers should be the same size, but let's be paranoid */
917 strncpy(buf, newbuf, sizeof(buf));
918 buf[sizeof(buf)-1] = 0;
919 player_socks[i] = fd; /* Has now registered */
920 } /* if client not registered */
921 if (!server_parse(i+1, buf)) {
922 close(fd);
923 player_socks[i] = -1;
924 if (players[i]) {
925 send_to_all("playerleave %d", i+1);
926 if (playing_game)
927 player_loses(i+1);
928 free(players[i]);
929 players[i] = NULL;
930 if (teams[i]) {
931 free(teams[i]);
932 teams[i] = NULL;
936 } /* for each player socket */
939 /*************************************************************************/
941 #ifdef SERVER_ONLY
942 int main()
943 #else
944 int server_main()
945 #endif
947 int i;
949 if ((i = init()) != 0)
950 return i;
951 while (!quit)
952 check_sockets();
953 write_config();
954 if (listen_sock >= 0)
955 close(listen_sock);
956 #ifdef HAVE_IPV6
957 if (listen_sock6 >= 0)
958 close(listen_sock6);
959 #endif
960 for (i = 0; i < 6; i++)
961 close(player_socks[i]);
962 return 0;
965 /*************************************************************************/