2 * Copyright (c) 1983-2003, Regents of the University of California.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the University of California, San Francisco nor
15 * the names of its contributors may be used to endorse or promote
16 * products derived from this software without specific prior written
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 * $OpenBSD: answer.c,v 1.11 2007/11/06 10:22:29 chl Exp $
32 * $NetBSD: answer.c,v 1.3 1997/10/10 16:32:50 lukem Exp $
33 * $DragonFly: src/games/hunt/huntd/answer.c,v 1.2 2008/09/04 16:12:51 swildner Exp $
36 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
54 /* Exported symbols for hosts_access(): */
55 int allow_severity = LOG_INFO;
56 int deny_severity = LOG_WARNING;
59 /* List of spawning connections: */
60 struct spawn *Spawn = NULL;
62 static void stplayer(PLAYER *, int);
63 static void stmonitor(PLAYER *);
64 static IDENT * get_ident(struct sockaddr *, int, uid_t, char *, char);
69 struct sockaddr sockstruct;
73 struct request_info ri;
76 /* Answer the call to hunt: */
77 socklen = sizeof sockstruct;
78 newsock = accept(Socket, (struct sockaddr *) &sockstruct, &socklen);
80 logit(LOG_ERR, "accept");
84 /* Check for access permissions: */
85 request_init(&ri, RQ_DAEMON, "huntd", RQ_FILE, newsock, 0);
87 if (hosts_access(&ri) == 0) {
88 logx(LOG_INFO, "rejected connection from %s", eval_client(&ri));
93 /* Remember this spawning connection: */
94 sp = (struct spawn *)malloc(sizeof *sp);
96 logit(LOG_ERR, "malloc");
100 memset(sp, '\0', sizeof *sp);
102 /* Keep the calling machine's source addr for ident purposes: */
103 memcpy(&sp->source, &sockstruct, sizeof sp->source);
104 sp->sourcelen = socklen;
106 /* Warn if we lose connection info: */
107 if (socklen > sizeof Spawn->source)
109 "struct sockaddr is not big enough! (%d > %zu)",
110 socklen, sizeof Spawn->source);
113 * Turn off blocking I/O, so a slow or dead terminal won't stop
114 * the game. All subsequent reads check how many bytes they read.
116 flags = fcntl(newsock, F_GETFL, 0);
118 (void) fcntl(newsock, F_SETFL, flags);
120 /* Start listening to the spawning connection */
122 FD_SET(sp->fd, &Fds_mask);
123 if (sp->fd >= Num_fds)
124 Num_fds = sp->fd + 1;
129 /* Add to the spawning list */
130 if ((sp->next = Spawn) != NULL)
131 Spawn->prevnext = &sp->next;
132 sp->prevnext = &Spawn;
137 answer_next(struct spawn *sp)
144 char teamstr[] = "[x]";
146 if (sp->reading_msg) {
147 /* Receive a message from a player */
148 len = read(sp->fd, sp->msg + sp->msglen,
149 sizeof sp->msg - sp->msglen);
153 if (len && sp->msglen < (int)(sizeof sp->msg))
156 teamstr[1] = sp->team;
157 outyx(ALL_PLAYERS, HEIGHT, 0, "%s%s: %.*s",
159 sp->team == ' ' ? "": teamstr,
163 sendcom(ALL_PLAYERS, REFRESH);
164 sendcom(ALL_PLAYERS, READY, 0);
169 /* Fill the buffer */
170 len = read(sp->fd, sp->inbuf + sp->inlen,
171 sizeof sp->inbuf - sp->inlen);
175 if (sp->inlen < (int)(sizeof sp->inbuf))
178 /* Extract values from the buffer */
180 memcpy(&sp->uid, cp1, sizeof (u_int32_t));
181 cp1+= sizeof(u_int32_t);
182 memcpy(sp->name, cp1, NAMELEN);
184 memcpy(&sp->team, cp1, sizeof (u_int8_t));
185 cp1+= sizeof(u_int8_t);
186 memcpy(&sp->enter_status, cp1, sizeof (u_int32_t));
187 cp1+= sizeof(u_int32_t);
188 memcpy(sp->ttyname, cp1, NAMELEN);
190 memcpy(&sp->mode, cp1, sizeof (u_int32_t));
191 cp1+= sizeof(u_int32_t);
193 /* Convert data from network byte order: */
194 sp->uid = ntohl(sp->uid);
195 sp->enter_status = ntohl(sp->enter_status);
196 sp->mode = ntohl(sp->mode);
199 * Make sure the name contains only printable characters
200 * since we use control characters for cursor control
201 * between driver and player processes
203 sp->name[NAMELEN] = '\0';
204 for (cp1 = cp2 = sp->name; *cp1 != '\0'; cp1++)
205 if (isprint(*cp1) || *cp1 == ' ')
209 /* Make sure team name is valid */
210 if (sp->team < '1' || sp->team > '9')
213 /* Tell the other end this server's hunt driver version: */
214 version = htonl((u_int32_t) HUNT_VERSION);
215 (void) write(sp->fd, &version, sizeof version);
217 if (sp->mode == C_MESSAGE) {
218 /* The clients only wants to send a message: */
224 /* Use a stdio file descriptor from now on: */
225 conn = fdopen(sp->fd, "w");
227 /* The player is a monitor: */
228 if (sp->mode == C_MONITOR) {
229 if (conf_monitor && End_monitor < &Monitor[MAXMON]) {
234 /* Too many monitors */
235 fprintf(conn, "Too many monitors\n");
237 logx(LOG_NOTICE, "too many monitors");
241 /* The player is a normal hunter: */
243 if (End_player < &Player[MAXPL])
246 fprintf(conn, "Too many players\n");
248 /* Too many players */
249 logx(LOG_NOTICE, "too many players");
254 /* Find the player's running scorecard */
255 pp->p_ident = get_ident(&sp->source, sp->sourcelen, sp->uid,
258 pp->p_death[0] = '\0';
261 /* No idea where the player starts: */
265 /* Mode-specific initialisation: */
266 if (sp->mode == C_MONITOR)
269 stplayer(pp, sp->enter_status);
271 /* And, they're off! Caller should remove and free sp. */
276 logit(LOG_WARNING, "read");
278 logx(LOG_WARNING, "lost connection to new client");
281 /* Destroy the spawn */
282 *sp->prevnext = sp->next;
283 if (sp->next) sp->next->prevnext = sp->prevnext;
284 FD_CLR(sp->fd, &Fds_mask);
290 /* Start a monitor: */
292 stmonitor(PLAYER *pp)
295 /* Monitors get to see the entire maze: */
296 memcpy(pp->p_maze, Maze, sizeof pp->p_maze);
299 /* Put the monitor's name near the bottom right on all screens: */
301 STAT_MON_ROW + 1 + (pp - Monitor), STAT_NAME_COL,
302 "%5.5s%c%-10.10s %c", " ",
303 stat_char(pp), pp->p_ident->i_name, pp->p_ident->i_team);
305 /* Ready the monitor: */
306 sendcom(pp, REFRESH);
307 sendcom(pp, READY, 0);
311 /* Start a player: */
313 stplayer(PLAYER *newpp, int enter_status)
321 for (y = 0; y < UBOUND; y++)
322 for (x = 0; x < WIDTH; x++)
323 newpp->p_maze[y][x] = Maze[y][x];
324 for ( ; y < DBOUND; y++) {
325 for (x = 0; x < LBOUND; x++)
326 newpp->p_maze[y][x] = Maze[y][x];
327 for ( ; x < RBOUND; x++)
328 newpp->p_maze[y][x] = SPACE;
329 for ( ; x < WIDTH; x++)
330 newpp->p_maze[y][x] = Maze[y][x];
332 for ( ; y < HEIGHT; y++)
333 for (x = 0; x < WIDTH; x++)
334 newpp->p_maze[y][x] = Maze[y][x];
336 /* Drop the new player somewhere in the maze: */
338 x = rand_num(WIDTH - 1) + 1;
339 y = rand_num(HEIGHT - 1) + 1;
340 } while (Maze[y][x] != SPACE);
341 newpp->p_over = SPACE;
344 newpp->p_undershot = FALSE;
346 /* Send them flying if needed */
347 if (enter_status == Q_FLY && conf_fly) {
348 newpp->p_flying = rand_num(conf_flytime);
349 newpp->p_flyx = 2 * rand_num(conf_flystep + 1) - conf_flystep;
350 newpp->p_flyy = 2 * rand_num(conf_flystep + 1) - conf_flystep;
351 newpp->p_face = FLYER;
353 newpp->p_flying = -1;
354 newpp->p_face = rand_dir();
357 /* Initialize the new player's attributes: */
359 newpp->p_damcap = conf_maxdam;
363 newpp->p_ammo = conf_ishots;
366 /* Decide on what cloak/scan status to enter with */
367 if (enter_status == Q_SCAN && conf_scan) {
368 newpp->p_scan = conf_scanlen * Nplayer;
370 } else if (conf_cloak) {
372 newpp->p_cloak = conf_cloaklen;
380 * For each new player, place a large mine and
381 * a small mine somewhere in the maze:
384 x = rand_num(WIDTH - 1) + 1;
385 y = rand_num(HEIGHT - 1) + 1;
386 } while (Maze[y][x] != SPACE);
388 for (pp = Monitor; pp < End_monitor; pp++)
392 x = rand_num(WIDTH - 1) + 1;
393 y = rand_num(HEIGHT - 1) + 1;
394 } while (Maze[y][x] != SPACE);
396 for (pp = Monitor; pp < End_monitor; pp++)
399 /* Create a score line for the new player: */
400 (void) snprintf(Buf, sizeof Buf, "%5.2f%c%-10.10s %c",
401 newpp->p_ident->i_score, stat_char(newpp),
402 newpp->p_ident->i_name, newpp->p_ident->i_team);
404 y = STAT_PLAY_ROW + 1 + (newpp - Player);
405 for (pp = Player; pp < End_player; pp++) {
407 /* Give everyone a few more shots: */
408 pp->p_ammo += conf_nshots;
409 newpp->p_ammo += conf_nshots;
410 outyx(pp, y, STAT_NAME_COL, Buf, len);
414 for (pp = Monitor; pp < End_monitor; pp++)
415 outyx(pp, y, STAT_NAME_COL, Buf, len);
417 /* Show the new player what they can see and where they are: */
419 drawplayer(newpp, TRUE);
422 /* Make sure that the position they enter in will be erased: */
423 if (enter_status == Q_FLY && conf_fly)
424 showexpl(newpp->p_y, newpp->p_x, FLYER);
426 /* Ready the new player: */
427 sendcom(newpp, REFRESH);
428 sendcom(newpp, READY, 0);
434 * Return a random direction
439 switch (rand_num(4)) {
455 * Get the score structure of a player
458 get_ident(struct sockaddr *sa, int salen __unused, uid_t uid, char *name,
465 if (sa->sa_family == AF_INET)
466 machine = ntohl((u_long)((struct sockaddr_in *)sa)->sin_addr.s_addr);
470 for (ip = Scores; ip != NULL; ip = ip->i_next)
471 if (ip->i_machine == machine
473 /* && ip->i_team == team */
474 && strncmp(ip->i_name, name, NAMELEN) == 0)
478 if (ip->i_team != team) {
479 logx(LOG_INFO, "player %s %s team %c",
481 team == ' ' ? "left" : ip->i_team == ' ' ?
482 "joined" : "changed to",
483 team == ' ' ? ip->i_team : team);
486 if (ip->i_entries < conf_scoredecay)
489 ip->i_kills = (ip->i_kills * (conf_scoredecay - 1))
491 ip->i_score = ip->i_kills / (double) ip->i_entries;
494 /* Alloc new entry -- it is released in clear_scores() */
495 ip = (IDENT *) malloc(sizeof (IDENT));
497 logit(LOG_ERR, "malloc");
498 /* Fourth down, time to punt */
501 ip->i_machine = machine;
504 strlcpy(ip->i_name, name, sizeof ip->i_name);
515 ip->i_gkills = ip->i_bkills = ip->i_deaths = 0;
516 ip->i_stillb = ip->i_saved = 0;
520 logx(LOG_INFO, "new player: %s%s%c%s",
522 team == ' ' ? "" : " (team ",
524 team == ' ' ? "" : ")");
531 answer_info(FILE *fp)
536 struct sockaddr_in *sa;
540 fprintf(fp, "\nSpawning connections:\n");
541 for (sp = Spawn; sp; sp = sp->next) {
542 sa = (struct sockaddr_in *)&sp->source;
543 bf = inet_ntop(AF_INET, &sa->sin_addr, buf, sizeof buf);
545 logit(LOG_WARNING, "inet_ntop");
548 fprintf(fp, "fd %d: state %d, from %s:%d\n",
549 sp->fd, sp->inlen + (sp->reading_msg ? sp->msglen : 0),