#include <sys/select.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <ctype.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include "conf.h"
#include "hunt.h"
#include "server.h"
int allow_severity = LOG_INFO;
int deny_severity = LOG_WARNING;
struct spawn *Spawn = NULL;
static void stplayer(PLAYER *, int);
static void stmonitor(PLAYER *);
static IDENT * get_ident(struct sockaddr *, int, u_long, char *, char);
void
answer_first(void)
{
struct sockaddr sockstruct;
int newsock;
socklen_t socklen;
struct spawn *sp;
socklen = sizeof sockstruct;
newsock = accept4(Socket, (struct sockaddr *) &sockstruct, &socklen,
SOCK_NONBLOCK);
if (newsock < 0) {
logit(LOG_ERR, "accept");
return;
}
sp = calloc(1, sizeof *sp);
if (sp == NULL) {
logit(LOG_ERR, "calloc");
close(newsock);
return;
}
memcpy(&sp->source, &sockstruct, sizeof sp->source);
sp->sourcelen = socklen;
if (socklen > sizeof Spawn->source)
logx(LOG_WARNING,
"struct sockaddr is not big enough! (%d > %zu)",
socklen, sizeof Spawn->source);
sp->fd = newsock;
FD_SET(sp->fd, &Fds_mask);
if (sp->fd >= Num_fds)
Num_fds = sp->fd + 1;
sp->reading_msg = 0;
sp->inlen = 0;
if ((sp->next = Spawn) != NULL)
Spawn->prevnext = &sp->next;
sp->prevnext = &Spawn;
Spawn = sp;
}
int
answer_next(struct spawn *sp)
{
PLAYER *pp;
char *cp1, *cp2;
u_int32_t version;
FILE *conn;
int len;
char teamstr[] = "[x]";
if (sp->reading_msg) {
len = read(sp->fd, sp->msg + sp->msglen,
sizeof sp->msg - sp->msglen);
if (len < 0)
goto error;
sp->msglen += len;
if (len && sp->msglen < sizeof sp->msg)
return FALSE;
teamstr[1] = sp->team;
outyx(ALL_PLAYERS, HEIGHT, 0, "%s%s: %.*s",
sp->name,
sp->team == ' ' ? "": teamstr,
sp->msglen,
sp->msg);
ce(ALL_PLAYERS);
sendcom(ALL_PLAYERS, REFRESH);
sendcom(ALL_PLAYERS, READY, 0);
flush(ALL_PLAYERS);
goto close_it;
}
len = read(sp->fd, sp->inbuf + sp->inlen,
sizeof sp->inbuf - sp->inlen);
if (len <= 0)
goto error;
sp->inlen += len;
if (sp->inlen < sizeof sp->inbuf)
return FALSE;
cp1 = sp->inbuf;
memcpy(&sp->uid, cp1, sizeof (u_int32_t));
cp1+= sizeof(u_int32_t);
memcpy(sp->name, cp1, NAMELEN);
cp1+= NAMELEN;
memcpy(&sp->team, cp1, sizeof (u_int8_t));
cp1+= sizeof(u_int8_t);
memcpy(&sp->enter_status, cp1, sizeof (u_int32_t));
cp1+= sizeof(u_int32_t);
memcpy(sp->ttyname, cp1, NAMELEN);
cp1+= NAMELEN;
memcpy(&sp->mode, cp1, sizeof (u_int32_t));
cp1+= sizeof(u_int32_t);
sp->uid = ntohl(sp->uid);
sp->enter_status = ntohl(sp->enter_status);
sp->mode = ntohl(sp->mode);
sp->name[NAMELEN] = '\0';
for (cp1 = cp2 = sp->name; *cp1 != '\0'; cp1++)
if (isprint((unsigned char)*cp1) || *cp1 == ' ')
*cp2++ = *cp1;
*cp2 = '\0';
if (sp->team < '1' || sp->team > '9')
sp->team = ' ';
version = htonl((u_int32_t) HUNT_VERSION);
(void) write(sp->fd, &version, sizeof version);
if (sp->mode == C_MESSAGE) {
sp->msglen = 0;
sp->reading_msg = 1;
return FALSE;
}
conn = fdopen(sp->fd, "w");
if (sp->mode == C_MONITOR) {
if (conf_monitor && End_monitor < &Monitor[MAXMON]) {
pp = End_monitor++;
if (sp->team == ' ')
sp->team = '*';
} else {
fprintf(conn, "Too many monitors\n");
fflush(conn);
logx(LOG_NOTICE, "too many monitors");
goto close_it;
}
} else {
if (End_player < &Player[MAXPL])
pp = End_player++;
else {
fprintf(conn, "Too many players\n");
fflush(conn);
logx(LOG_NOTICE, "too many players");
goto close_it;
}
}
pp->p_ident = get_ident(&sp->source, sp->sourcelen, sp->uid,
sp->name, sp->team);
pp->p_output = conn;
pp->p_death[0] = '\0';
pp->p_fd = sp->fd;
pp->p_y = 0;
pp->p_x = 0;
if (sp->mode == C_MONITOR)
stmonitor(pp);
else
stplayer(pp, sp->enter_status);
return TRUE;
error:
if (len < 0)
logit(LOG_WARNING, "read");
else
logx(LOG_WARNING, "lost connection to new client");
close_it:
*sp->prevnext = sp->next;
if (sp->next) sp->next->prevnext = sp->prevnext;
FD_CLR(sp->fd, &Fds_mask);
close(sp->fd);
free(sp);
return FALSE;
}
static void
stmonitor(PLAYER *pp)
{
memcpy(pp->p_maze, Maze, sizeof pp->p_maze);
drawmaze(pp);
outyx(ALL_PLAYERS,
STAT_MON_ROW + 1 + (pp - Monitor), STAT_NAME_COL,
"%5.5s%c%-10.10s %c", " ",
stat_char(pp), pp->p_ident->i_name, pp->p_ident->i_team);
sendcom(pp, REFRESH);
sendcom(pp, READY, 0);
flush(pp);
}
static void
stplayer(PLAYER *newpp, int enter_status)
{
int x, y;
PLAYER *pp;
int len;
Nplayer++;
for (y = 0; y < UBOUND; y++)
for (x = 0; x < WIDTH; x++)
newpp->p_maze[y][x] = Maze[y][x];
for ( ; y < DBOUND; y++) {
for (x = 0; x < LBOUND; x++)
newpp->p_maze[y][x] = Maze[y][x];
for ( ; x < RBOUND; x++)
newpp->p_maze[y][x] = SPACE;
for ( ; x < WIDTH; x++)
newpp->p_maze[y][x] = Maze[y][x];
}
for ( ; y < HEIGHT; y++)
for (x = 0; x < WIDTH; x++)
newpp->p_maze[y][x] = Maze[y][x];
do {
x = rand_num(WIDTH - 1) + 1;
y = rand_num(HEIGHT - 1) + 1;
} while (Maze[y][x] != SPACE);
newpp->p_over = SPACE;
newpp->p_x = x;
newpp->p_y = y;
newpp->p_undershot = FALSE;
if (enter_status == Q_FLY && conf_fly) {
newpp->p_flying = rand_num(conf_flytime);
newpp->p_flyx = 2 * rand_num(conf_flystep + 1) - conf_flystep;
newpp->p_flyy = 2 * rand_num(conf_flystep + 1) - conf_flystep;
newpp->p_face = FLYER;
} else {
newpp->p_flying = -1;
newpp->p_face = rand_dir();
}
newpp->p_damage = 0;
newpp->p_damcap = conf_maxdam;
newpp->p_nchar = 0;
newpp->p_ncount = 0;
newpp->p_nexec = 0;
newpp->p_ammo = conf_ishots;
newpp->p_nboots = 0;
if (enter_status == Q_SCAN && conf_scan) {
newpp->p_scan = conf_scanlen * Nplayer;
newpp->p_cloak = 0;
} else if (conf_cloak) {
newpp->p_scan = 0;
newpp->p_cloak = conf_cloaklen;
} else {
newpp->p_scan = 0;
newpp->p_cloak = 0;
}
newpp->p_ncshot = 0;
do {
x = rand_num(WIDTH - 1) + 1;
y = rand_num(HEIGHT - 1) + 1;
} while (Maze[y][x] != SPACE);
Maze[y][x] = GMINE;
for (pp = Monitor; pp < End_monitor; pp++)
check(pp, y, x);
do {
x = rand_num(WIDTH - 1) + 1;
y = rand_num(HEIGHT - 1) + 1;
} while (Maze[y][x] != SPACE);
Maze[y][x] = MINE;
for (pp = Monitor; pp < End_monitor; pp++)
check(pp, y, x);
(void) snprintf(Buf, sizeof Buf, "%5.2f%c%-10.10s %c",
newpp->p_ident->i_score, stat_char(newpp),
newpp->p_ident->i_name, newpp->p_ident->i_team);
len = strlen(Buf);
y = STAT_PLAY_ROW + 1 + (newpp - Player);
for (pp = Player; pp < End_player; pp++) {
if (pp != newpp) {
pp->p_ammo += conf_nshots;
newpp->p_ammo += conf_nshots;
outyx(pp, y, STAT_NAME_COL, Buf, len);
ammo_update(pp);
}
}
for (pp = Monitor; pp < End_monitor; pp++)
outyx(pp, y, STAT_NAME_COL, Buf, len);
drawmaze(newpp);
drawplayer(newpp, TRUE);
look(newpp);
if (enter_status == Q_FLY && conf_fly)
showexpl(newpp->p_y, newpp->p_x, FLYER);
sendcom(newpp, REFRESH);
sendcom(newpp, READY, 0);
flush(newpp);
}
int
rand_dir(void)
{
switch (rand_num(4)) {
case 0:
return LEFTS;
case 1:
return RIGHT;
case 2:
return BELOW;
case 3:
return ABOVE;
}
return(-1);
}
static IDENT *
get_ident(struct sockaddr *sa, int salen, u_long uid, char *name, char team)
{
IDENT *ip;
static IDENT punt;
u_int32_t machine;
if (sa->sa_family == AF_INET)
machine = ntohl((u_long)((struct sockaddr_in *)sa)->sin_addr.s_addr);
else
machine = 0;
for (ip = Scores; ip != NULL; ip = ip->i_next)
if (ip->i_machine == machine
&& ip->i_uid == uid
&& strncmp(ip->i_name, name, NAMELEN) == 0)
break;
if (ip != NULL) {
if (ip->i_team != team) {
logx(LOG_INFO, "player %s %s team %c",
name,
team == ' ' ? "left" : ip->i_team == ' ' ?
"joined" : "changed to",
team == ' ' ? ip->i_team : team);
ip->i_team = team;
}
if (ip->i_entries < conf_scoredecay)
ip->i_entries++;
else
ip->i_kills = (ip->i_kills * (conf_scoredecay - 1))
/ conf_scoredecay;
ip->i_score = ip->i_kills / (double) ip->i_entries;
}
else {
ip = malloc(sizeof (IDENT));
if (ip == NULL) {
logit(LOG_ERR, "malloc");
ip = &punt;
}
ip->i_machine = machine;
ip->i_team = team;
ip->i_uid = uid;
strlcpy(ip->i_name, name, sizeof ip->i_name);
ip->i_kills = 0;
ip->i_entries = 1;
ip->i_score = 0;
ip->i_absorbed = 0;
ip->i_faced = 0;
ip->i_shot = 0;
ip->i_robbed = 0;
ip->i_slime = 0;
ip->i_missed = 0;
ip->i_ducked = 0;
ip->i_gkills = ip->i_bkills = ip->i_deaths = 0;
ip->i_stillb = ip->i_saved = 0;
ip->i_next = Scores;
Scores = ip;
logx(LOG_INFO, "new player: %s%s%c%s",
name,
team == ' ' ? "" : " (team ",
team,
team == ' ' ? "" : ")");
}
return ip;
}
void
answer_info(FILE *fp)
{
struct spawn *sp;
char buf[128];
const char *bf;
struct sockaddr_in *sa;
if (Spawn == NULL)
return;
fprintf(fp, "\nSpawning connections:\n");
for (sp = Spawn; sp; sp = sp->next) {
sa = (struct sockaddr_in *)&sp->source;
bf = inet_ntop(AF_INET, &sa->sin_addr, buf, sizeof buf);
if (!bf) {
logit(LOG_WARNING, "inet_ntop");
bf = "?";
}
fprintf(fp, "fd %d: state %d, from %s:%d\n",
sp->fd, sp->inlen + (sp->reading_msg ? sp->msglen : 0),
bf, sa->sin_port);
}
}