tetrinet/server.c

967 lines
24 KiB
C

/* Tetrinet for Linux, by Andrew Church <achurch@achurch.org>
* This program is public domain.
*
* Tetrinet server code
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <netinet/in.h>
/* Due to glibc brokenness, we can't blindly include this. Yet another
* reason to not use glibc. */
/* #include <netinet/protocols.h> */
#include <signal.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include "tetrinet.h"
#include "tetris.h"
#include "server.h"
#include "sockets.h"
/*************************************************************************/
static int linuxmode = 0; /* 1: don't try to be compatible with Windows */
static int ipv6_only = 0; /* 1: only use IPv6 (when available) */
static int quit = 0;
static int listen_sock = -1;
#ifdef HAVE_IPV6
static int listen_sock6 = -1;
#endif
static int player_socks[6] = {-1,-1,-1,-1,-1,-1};
static unsigned char player_ips[6][4];
static int player_modes[6];
/* Which players have already lost in the current game? */
static int player_lost[6];
/* We re-use a lot of variables from the main code */
/*************************************************************************/
/*************************************************************************/
/* Convert a 2-byte hex value to an integer. */
int xtoi(const char *buf)
{
int val;
if (buf[0] <= '9')
val = (buf[0] - '0') << 4;
else
val = (toupper(buf[0]) - 'A' + 10) << 4;
if (buf[1] <= '9')
val |= buf[1] - '0';
else
val |= toupper(buf[1]) - 'A' + 10;
return val;
}
/*************************************************************************/
/* Return a string containing the winlist in a format suitable for sending
* to clients.
*/
static char *winlist_str()
{
static char buf[1024];
char *s;
int i;
s = buf;
for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
s += snprintf(s, sizeof(buf)-(s-buf),
linuxmode ? " %c%s;%d;%d" : " %c%s;%d",
winlist[i].team ? 't' : 'p',
winlist[i].name, winlist[i].points, winlist[i].games);
}
return buf;
}
/*************************************************************************/
/*************************************************************************/
/* Read the configuration file. */
void read_config(void)
{
char buf[1024], *s, *t;
FILE *f;
int i;
s = getenv("HOME");
if (!s)
s = "/etc";
snprintf(buf, sizeof(buf), "%s/.tetrinet", s);
if (!(f = fopen(buf, "r")))
return;
while (fgets(buf, sizeof(buf), f)) {
s = strtok(buf, " ");
if (!s) {
continue;
} else if (strcmp(s, "linuxmode") == 0) {
if ((s = strtok(NULL, " ")))
linuxmode = atoi(s);
} else if (strcmp(s, "ipv6_only") == 0) {
if ((s = strtok(NULL, " ")))
ipv6_only = atoi(s);
} else if (strcmp(s, "averagelevels") == 0) {
if ((s = strtok(NULL, " ")))
level_average = atoi(s);
} else if (strcmp(s, "classic") == 0) {
if ((s = strtok(NULL, " ")))
old_mode = atoi(s);
} else if (strcmp(s, "initiallevel") == 0) {
if ((s = strtok(NULL, " ")))
initial_level = atoi(s);
} else if (strcmp(s, "levelinc") == 0) {
if ((s = strtok(NULL, " ")))
level_inc = atoi(s);
} else if (strcmp(s, "linesperlevel") == 0) {
if ((s = strtok(NULL, " ")))
lines_per_level = atoi(s);
} else if (strcmp(s, "pieces") == 0) {
i = 0;
while (i < 7 && (s = strtok(NULL, " ")))
piecefreq[i++] = atoi(s);
} else if (strcmp(s, "specialcapacity") == 0) {
if ((s = strtok(NULL, " ")))
special_capacity = atoi(s);
} else if (strcmp(s, "specialcount") == 0) {
if ((s = strtok(NULL, " ")))
special_count = atoi(s);
} else if (strcmp(s, "speciallines") == 0) {
if ((s = strtok(NULL, " ")))
special_lines = atoi(s);
} else if (strcmp(s, "specials") == 0) {
i = 0;
while (i < 9 && (s = strtok(NULL, " ")))
specialfreq[i++] = atoi(s);
} else if (strcmp(s, "winlist") == 0) {
i = 0;
while (i < MAXWINLIST && (s = strtok(NULL, " "))) {
t = strchr(s, ';');
if (!t)
break;
*t++ = 0;
strncpy(winlist[i].name, s, sizeof(winlist[i].name)-1);
winlist[i].name[sizeof(winlist[i].name)-1] = 0;
s = t;
t = strchr(s, ';');
if (!t) {
*winlist[i].name = 0;
break;
}
winlist[i].team = atoi(s);
s = t+1;
t = strchr(s, ';');
if (!t) {
*winlist[i].name = 0;
break;
}
winlist[i].points = atoi(s);
winlist[i].games = atoi(t+1);
i++;
}
}
}
fclose(f);
}
/*************************************************************************/
/* Re-write the configuration file. */
void write_config(void)
{
char buf[1024], *s;
FILE *f;
int i;
s = getenv("HOME");
if (!s)
s = "/etc";
snprintf(buf, sizeof(buf), "%s/.tetrinet", s);
if (!(f = fopen(buf, "w")))
return;
fprintf(f, "winlist");
for (i = 0; i < MAXSAVEWINLIST && *winlist[i].name; i++) {
fprintf(f, " %s;%d;%d;%d", winlist[i].name, winlist[i].team,
winlist[i].points, winlist[i].games);
}
fputc('\n', f);
fprintf(f, "classic %d\n", old_mode);
fprintf(f, "initiallevel %d\n", initial_level);
fprintf(f, "linesperlevel %d\n", lines_per_level);
fprintf(f, "levelinc %d\n", level_inc);
fprintf(f, "averagelevels %d\n", level_average);
fprintf(f, "speciallines %d\n", special_lines);
fprintf(f, "specialcount %d\n", special_count);
fprintf(f, "specialcapacity %d\n", special_capacity);
fprintf(f, "pieces");
for (i = 0; i < 7; i++)
fprintf(f, " %d", piecefreq[i]);
fputc('\n', f);
fprintf(f, "specials");
for (i = 0; i < 9; i++)
fprintf(f, " %d", specialfreq[i]);
fputc('\n', f);
fprintf(f, "linuxmode %d\n", linuxmode);
fprintf(f, "ipv6_only %d\n", ipv6_only);
fclose(f);
}
/*************************************************************************/
/*************************************************************************/
/* Send a message to a single player. */
static void send_to(int player, const char *format, ...)
{
va_list args;
char buf[1024];
va_start(args, format);
vsnprintf(buf, sizeof(buf), format, args);
if (player_socks[player-1] >= 0)
sockprintf(player_socks[player-1], "%s", buf);
}
/*************************************************************************/
/* Send a message to all players. */
static void send_to_all(const char *format, ...)
{
va_list args;
char buf[1024];
int i;
va_start(args, format);
vsnprintf(buf, sizeof(buf), format, args);
for (i = 0; i < 6; i++) {
if (player_socks[i] >= 0)
sockprintf(player_socks[i], "%s", buf);
}
}
/*************************************************************************/
/* Send a message to all players but the given one. */
static void send_to_all_but(int player, const char *format, ...)
{
va_list args;
char buf[1024];
int i;
va_start(args, format);
vsnprintf(buf, sizeof(buf), format, args);
for (i = 0; i < 6; i++) {
if (i+1 != player && player_socks[i] >= 0)
sockprintf(player_socks[i], "%s", buf);
}
}
/*************************************************************************/
/* Send a message to all players but those on the same team as the given
* player.
*/
static void send_to_all_but_team(int player, const char *format, ...)
{
va_list args;
char buf[1024];
int i;
char *team = teams[player-1];
va_start(args, format);
vsnprintf(buf, sizeof(buf), format, args);
for (i = 0; i < 6; i++) {
if (i+1 != player && player_socks[i] >= 0 &&
(!team || !teams[i] || strcmp(teams[i], team) != 0))
sockprintf(player_socks[i], "%s", buf);
}
}
/*************************************************************************/
/*************************************************************************/
/* Add points to a given player's [team's] winlist entry, or make a new one
* if they rank.
*/
static void add_points(int player, int points)
{
int i;
if (!players[player-1])
return;
for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
if (!winlist[i].team && !teams[player-1]
&& strcmp(winlist[i].name, players[player-1]) == 0)
break;
if (winlist[i].team && teams[player-1]
&& strcmp(winlist[i].name, teams[player-1]) == 0)
break;
}
if (i == MAXWINLIST) {
for (i = 0; i < MAXWINLIST && winlist[i].points >= points; i++)
;
}
if (i == MAXWINLIST)
return;
if (!*winlist[i].name) {
if (teams[player-1]) {
strncpy(winlist[i].name, teams[player-1], sizeof(winlist[i].name)-1);
winlist[i].name[sizeof(winlist[i].name)-1] = 0;
winlist[i].team = 1;
} else {
strncpy(winlist[i].name, players[player-1], sizeof(winlist[i].name)-1);
winlist[i].name[sizeof(winlist[i].name)-1] = 0;
winlist[i].team = 0;
}
}
winlist[i].points += points;
}
/*************************************************************************/
/* Add a game to a given player's [team's] winlist entry. */
static void add_game(int player)
{
int i;
if (!players[player-1])
return;
for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
if (!winlist[i].team && !teams[player-1]
&& strcmp(winlist[i].name, players[player-1]) == 0)
break;
if (winlist[i].team && teams[player-1]
&& strcmp(winlist[i].name, teams[player-1]) == 0)
break;
}
if (i == MAXWINLIST || !*winlist[i].name)
return;
winlist[i].games++;
}
/*************************************************************************/
/* Sort the winlist. */
static void sort_winlist()
{
int i, j, best, bestindex;
for (i = 0; i < MAXWINLIST && *winlist[i].name; i++) {
best = winlist[i].points;
bestindex = i;
for (j = i+1; j < MAXWINLIST && *winlist[j].name; j++) {
if (winlist[j].points > best) {
best = winlist[j].points;
bestindex = j;
}
}
if (bestindex != i) {
WinInfo tmp;
memcpy(&tmp, &winlist[i], sizeof(WinInfo));
memcpy(&winlist[i], &winlist[bestindex], sizeof(WinInfo));
memcpy(&winlist[bestindex], &tmp, sizeof(WinInfo));
}
}
}
/*************************************************************************/
/* Take care of a player losing (which may end the game). */
static void player_loses(int player)
{
int i, j, order, end = 1, winner = -1, second = -1, third = -1;
if (player < 1 || player > 6 || player_socks[player-1] < 0)
return;
order = 0;
for (i = 1; i <= 6; i++) {
if (player_lost[i-1] > order)
order = player_lost[i-1];
}
player_lost[player-1] = order+1;
for (i = 1; i <= 6; i++) {
if (player_socks[i-1] >= 0 && !player_lost[i-1]) {
if (winner < 0) {
winner = i;
} else if (!teams[winner-1] || !teams[i-1]
|| strcasecmp(teams[winner-1],teams[i-1]) != 0) {
end = 0;
break;
}
}
}
if (end) {
send_to_all("endgame");
playing_game = 0;
/* Catch the case where no players are left (1-player game) */
if (winner > 0) {
send_to_all("playerwon %d", winner);
add_points(winner, 3);
order = 0;
for (i = 1; i <= 6; i++) {
if (player_lost[i-1] > order
&& (!teams[winner-1] || !teams[i-1]
|| strcasecmp(teams[winner-1],teams[i-1]) != 0)) {
order = player_lost[i-1];
second = i;
}
}
if (order) {
add_points(second, 2);
player_lost[second-1] = 0;
}
order = 0;
for (i = 1; i <= 6; i++) {
if (player_lost[i-1] > order
&& (!teams[winner-1] || !teams[i-1]
|| strcasecmp(teams[winner-1],teams[i-1]) != 0)
&& (!teams[second-1] || !teams[i-1]
|| strcasecmp(teams[second-1],teams[i-1]) != 0)) {
order = player_lost[i-1];
third = i;
}
}
if (order)
add_points(third, 1);
for (i = 1; i <= 6; i++) {
if (teams[i-1]) {
for (j = 1; j < i; j++) {
if (teams[j-1] && strcasecmp(teams[i-1],teams[j-1])==0)
break;
}
if (j < i)
continue;
}
if (player_socks[i-1] >= 0)
add_game(i);
}
}
sort_winlist();
write_config();
send_to_all("winlist %s", winlist_str());
}
/* One more possibility: the only player playing left the game, which
* means there are now no players left. */
if (!players[0] && !players[1] && !players[2] && !players[3]
&& !players[4] && !players[5])
playing_game = 0;
}
/*************************************************************************/
/*************************************************************************/
/* Parse a line from a client. Destroys the buffer it's given as a side
* effect. Return 0 if the command is unknown (or bad syntax), else 1.
*/
static int server_parse(int player, char *buf)
{
char *cmd, *s, *t;
int i, tetrifast = 0;
cmd = strtok(buf, " ");
if (!cmd) {
return 1;
} else if (strcmp(cmd, "tetrisstart") == 0) {
newplayer:
s = strtok(NULL, " ");
t = strtok(NULL, " ");
if (!t)
return 0;
for (i = 1; i <= 6; i++) {
if (players[i-1] && strcasecmp(s, players[i-1]) == 0) {
send_to(player, "noconnecting Nickname already exists on server!");
return 0;
}
}
players[player-1] = strdup(s);
if (teams[player-1])
free(teams[player-1]);
teams[player-1] = NULL;
player_modes[player-1] = tetrifast;
send_to(player, "%s %d", tetrifast ? ")#)(!@(*3" : "playernum", player);
send_to(player, "winlist %s", winlist_str());
for (i = 1; i <= 6; i++) {
if (i != player && players[i-1]) {
send_to(player, "playerjoin %d %s", i, players[i-1]);
send_to(player, "team %d %s", i, teams[i-1] ? teams[i-1] : "");
}
}
if (playing_game) {
send_to(player, "ingame");
player_lost[player-1] = 1;
}
send_to_all_but(player, "playerjoin %d %s", player, players[player-1]);
} else if (strcmp(cmd, "tetrifaster") == 0) {
tetrifast = 1;
goto newplayer;
} else if (strcmp(cmd, "team") == 0) {
s = strtok(NULL, " ");
t = strtok(NULL, "");
if (!s || atoi(s) != player)
return 0;
if (teams[player])
free(teams[player]);
if (t)
teams[player] = strdup(t);
else
teams[player] = NULL;
send_to_all_but(player, "team %d %s", player, t ? t : "");
} else if (strcmp(cmd, "pline") == 0) {
s = strtok(NULL, " ");
t = strtok(NULL, "");
if (!s || atoi(s) != player)
return 0;
if (!t)
t = "";
send_to_all_but(player, "pline %d %s", player, t);
} else if (strcmp(cmd, "plineact") == 0) {
s = strtok(NULL, " ");
t = strtok(NULL, "");
if (!s || atoi(s) != player)
return 0;
if (!t)
t = "";
send_to_all_but(player, "plineact %d %s", player, t);
} else if (strcmp(cmd, "startgame") == 0) {
int total;
char piecebuf[101], specialbuf[101];
for (i = 1; i < player; i++) {
if (player_socks[i-1] >= 0)
return 1;
}
s = strtok(NULL, " ");
t = strtok(NULL, " ");
if (!s)
return 1;
i = atoi(s);
if ((i && playing_game) || (!i && !playing_game))
return 1;
if (!i) { /* end game */
send_to_all("endgame");
playing_game = 0;
return 1;
}
total = 0;
for (i = 0; i < 7; i++) {
if (piecefreq[i])
memset(piecebuf+total, '1'+i, piecefreq[i]);
total += piecefreq[i];
}
piecebuf[100] = 0;
if (total != 100) {
send_to_all("plineact 0 cannot start game: Piece frequencies do not total 100 percent!");
return 1;
}
total = 0;
for (i = 0; i < 9; i++) {
if (specialfreq[i])
memset(specialbuf+total, '1'+i, specialfreq[i]);
total += specialfreq[i];
}
specialbuf[100] = 0;
if (total != 100) {
send_to_all("plineact 0 cannot start game: Special frequencies do not total 100 percent!");
return 1;
}
playing_game = 1;
game_paused = 0;
for (i = 1; i <= 6; i++) {
if (player_socks[i-1] < 0)
continue;
/* XXX First parameter is stack height */
send_to(i, "%s %d %d %d %d %d %d %d %s %s %d %d",
player_modes[i-1] ? "*******" : "newgame",
0, initial_level, lines_per_level, level_inc,
special_lines, special_count, special_capacity,
piecebuf, specialbuf, level_average, old_mode);
}
memset(player_lost, 0, sizeof(player_lost));
} else if (strcmp(cmd, "pause") == 0) {
if (!playing_game)
return 1;
s = strtok(NULL, " ");
if (!s)
return 1;
i = atoi(s);
if (i)
i = 1; /* to make sure it's not anything else */
if ((i && game_paused) || (!i && !game_paused))
return 1;
game_paused = i;
send_to_all("pause %d", i);
} else if (strcmp(cmd, "playerlost") == 0) {
if (!(s = strtok(NULL, " ")) || atoi(s) != player)
return 1;
player_loses(player);
} else if (strcmp(cmd, "f") == 0) { /* field */
if (!(s = strtok(NULL, " ")) || atoi(s) != player)
return 1;
if (!(s = strtok(NULL, "")))
s = "";
send_to_all_but(player, "f %d %s", player, s);
} else if (strcmp(cmd, "lvl") == 0) {
if (!(s = strtok(NULL, " ")) || atoi(s) != player)
return 1;
if (!(s = strtok(NULL, " ")))
return 1;
levels[player] = atoi(s);
send_to_all_but(player, "lvl %d %d", player, levels[player]);
} else if (strcmp(cmd, "sb") == 0) {
int from, to;
char *type;
if (!(s = strtok(NULL, " ")))
return 1;
to = atoi(s);
if (!(type = strtok(NULL, " ")))
return 1;
if (!(s = strtok(NULL, " ")))
return 1;
from = atoi(s);
if (from != player)
return 1;
if (to < 0 || to > 6 || player_socks[to-1] < 0 || player_lost[to-1])
return 1;
if (to == 0)
send_to_all_but_team(player, "sb %d %s %d", to, type, from);
else
send_to_all_but(player, "sb %d %s %d", to, type, from);
} else if (strcmp(cmd, "gmsg") == 0) {
if (!(s = strtok(NULL, "")))
return 1;
send_to_all("gmsg %s", s);
} else { /* unrecognized command */
return 0;
}
return 1;
}
/*************************************************************************/
/*************************************************************************/
static void sigcatcher(int sig)
{
if (sig == SIGHUP) {
read_config();
signal(SIGHUP, sigcatcher);
send_to_all("winlist %s", winlist_str());
} else if (sig == SIGTERM || sig == SIGINT) {
quit = 1;
signal(sig, SIG_IGN);
}
}
/*************************************************************************/
/* Returns 0 on success, desired program exit code on failure */
static int init()
{
struct sockaddr_in sin;
#ifdef HAVE_IPV6
struct sockaddr_in6 sin6;
#endif
int i;
/* Set up some sensible defaults */
*winlist[0].name = 0;
old_mode = 1;
initial_level = 1;
lines_per_level = 2;
level_inc = 1;
level_average = 1;
special_lines = 1;
special_count = 1;
special_capacity = 18;
piecefreq[0] = 14;
piecefreq[1] = 14;
piecefreq[2] = 15;
piecefreq[3] = 14;
piecefreq[4] = 14;
piecefreq[5] = 14;
piecefreq[6] = 15;
specialfreq[0] = 18;
specialfreq[1] = 18;
specialfreq[2] = 3;
specialfreq[3] = 12;
specialfreq[4] = 0;
specialfreq[5] = 16;
specialfreq[6] = 3;
specialfreq[7] = 12;
specialfreq[8] = 18;
/* (Try to) read the config file */
read_config();
/* Catch some signals */
signal(SIGHUP, sigcatcher);
signal(SIGINT, sigcatcher);
signal(SIGTERM, sigcatcher);
/* Set up a listen socket */
if (!ipv6_only)
listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listen_sock >= 0){
i = 1;
if (setsockopt(listen_sock,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(i))==0) {
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(31457);
if (bind(listen_sock, (struct sockaddr *)&sin, sizeof(sin)) == 0) {
if (listen(listen_sock, 5) == 0) {
goto ipv4_success;
}
}
}
i = errno;
close(listen_sock);
errno = i;
listen_sock = -1;
}
ipv4_success:
#ifdef HAVE_IPV6
/* Set up an IPv6 listen socket if possible */
listen_sock6 = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (listen_sock6 >= 0) {
i = 1;
if (setsockopt(listen_sock6,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(i))==0) {
memset(&sin6, 0, sizeof(sin6));
sin6.sin6_family = AF_INET6;
sin6.sin6_port = htons(31457);
if (bind(listen_sock6,(struct sockaddr *)&sin6,sizeof(sin6))==0) {
if (listen(listen_sock6, 5) == 0) {
goto ipv6_success;
}
}
}
i = errno;
close(listen_sock6);
errno = i;
listen_sock6 = -1;
}
ipv6_success:
#else /* !HAVE_IPV6 */
if (ipv6_only) {
fprintf(stderr,"ipv6_only specified but IPv6 support not available\n");
return 1;
}
#endif /* HAVE_IPV6 */
if (listen_sock < 0
#ifdef HAVE_IPV6
&& listen_sock6 < 0
#endif
) {
return 1;
}
return 0;
}
/*************************************************************************/
static void check_sockets()
{
fd_set fds;
int i, fd, maxfd;
FD_ZERO(&fds);
if (listen_sock >= 0)
FD_SET(listen_sock, &fds);
maxfd = listen_sock;
#ifdef HAVE_IPV6
if (listen_sock6 >= 0)
FD_SET(listen_sock6, &fds);
if (listen_sock6 > maxfd)
maxfd = listen_sock6;
#endif
for (i = 0; i < 6; i++) {
if (player_socks[i] != -1) {
if (player_socks[i] < 0)
fd = (~player_socks[i]) - 1;
else
fd = player_socks[i];
FD_SET(fd, &fds);
if (fd > maxfd)
maxfd = fd;
}
}
if (select(maxfd+1, &fds, NULL, NULL, NULL) <= 0)
return;
if (listen_sock >= 0 && FD_ISSET(listen_sock, &fds)) {
struct sockaddr_in sin;
int len = sizeof(sin);
fd = accept(listen_sock, (struct sockaddr *)&sin, &len);
if (fd >= 0) {
for (i = 0; i < 6 && player_socks[i] != -1; i++)
;
if (i == 6) {
sockprintf(fd, "noconnecting Too many players on server!");
close(fd);
} else {
player_socks[i] = ~(fd+1);
memcpy(player_ips[i], &sin.sin_addr, 4);
}
}
} /* if (FD_ISSET(listen_sock)) */
#ifdef HAVE_IPV6
if (listen_sock6 >= 0 && FD_ISSET(listen_sock6, &fds)) {
struct sockaddr_in6 sin6;
int len = sizeof(sin6);
fd = accept(listen_sock6, (struct sockaddr *)&sin6, &len);
if (fd >= 0) {
for (i = 0; i < 6 && player_socks[i] != -1; i++)
;
if (i == 6) {
sockprintf(fd, "noconnecting Too many players on server!");
close(fd);
} else {
player_socks[i] = ~(fd+1);
memcpy(player_ips[i], (char *)(&sin6.sin6_addr)+12, 4);
}
}
} /* if (FD_ISSET(listen_sock6)) */
#endif
for (i = 0; i < 6; i++) {
char buf[1024];
if (player_socks[i] == -1)
continue;
if (player_socks[i] < 0)
fd = (~player_socks[i]) - 1;
else
fd = player_socks[i];
if (!FD_ISSET(fd, &fds))
continue;
sgets(buf, sizeof(buf), fd);
if (player_socks[i] < 0) {
/* Messy decoding stuff */
char iphashbuf[16], newbuf[1024];
unsigned char *ip;
int j, c, d;
if (strlen(buf) < 2*13) { /* "tetrisstart " + initial byte */
close(fd);
player_socks[i] = -1;
continue;
}
ip = player_ips[i];
sprintf(iphashbuf, "%d", ip[0]*54 + ip[1]*41 + ip[2]*29 + ip[3]*17);
c = xtoi(buf);
for (j = 2; buf[j] && buf[j+1]; j += 2) {
int temp;
temp = d = xtoi(buf+j);
d ^= iphashbuf[((j/2)-1) % strlen(iphashbuf)];
d += 255 - c;
d %= 255;
newbuf[j/2-1] = d;
c = temp;
}
newbuf[j/2-1] = 0;
if (strncmp(newbuf, "tetrisstart ", 12) != 0) {
close(fd);
player_socks[i] = -1;
continue;
}
/* Buffers should be the same size, but let's be paranoid */
strncpy(buf, newbuf, sizeof(buf));
buf[sizeof(buf)-1] = 0;
player_socks[i] = fd; /* Has now registered */
} /* if client not registered */
if (!server_parse(i+1, buf)) {
close(fd);
player_socks[i] = -1;
if (players[i]) {
send_to_all("playerleave %d", i+1);
if (playing_game)
player_loses(i+1);
free(players[i]);
players[i] = NULL;
if (teams[i]) {
free(teams[i]);
teams[i] = NULL;
}
}
}
} /* for each player socket */
}
/*************************************************************************/
#ifdef SERVER_ONLY
int main()
#else
int server_main()
#endif
{
int i;
if ((i = init()) != 0)
return i;
while (!quit)
check_sockets();
write_config();
if (listen_sock >= 0)
close(listen_sock);
#ifdef HAVE_IPV6
if (listen_sock6 >= 0)
close(listen_sock6);
#endif
for (i = 0; i < 6; i++)
close(player_socks[i]);
return 0;
}
/*************************************************************************/