games: Remove (void) casts.
[dragonfly.git] / games / hunt / huntd / answer.c
blob436a760591c8f7496eb072277ea18974bd898c03
1 /*-
2 * Copyright (c) 1983-2003, Regents of the University of California.
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
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
17 * permission.
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 $
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
39 #include <ctype.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 #include <stdio.h>
45 #include <syslog.h>
46 #include <string.h>
47 #include <tcpd.h>
49 #include "hunt.h"
50 #include "server.h"
51 #include "conf.h"
53 /* Exported symbols for hosts_access(): */
54 int allow_severity = LOG_INFO;
55 int deny_severity = LOG_WARNING;
58 /* List of spawning connections: */
59 struct spawn *Spawn = NULL;
61 static void stplayer(PLAYER *, int);
62 static void stmonitor(PLAYER *);
63 static IDENT * get_ident(struct sockaddr *, int, uid_t, char *, char);
65 void
66 answer_first(void)
68 struct sockaddr sockstruct;
69 int newsock;
70 socklen_t socklen;
71 int flags;
72 struct request_info ri;
73 struct spawn *sp;
75 /* Answer the call to hunt: */
76 socklen = sizeof sockstruct;
77 newsock = accept(Socket, (struct sockaddr *) &sockstruct, &socklen);
78 if (newsock < 0) {
79 logit(LOG_ERR, "accept");
80 return;
83 /* Check for access permissions: */
84 request_init(&ri, RQ_DAEMON, "huntd", RQ_FILE, newsock, 0);
85 fromhost(&ri);
86 if (hosts_access(&ri) == 0) {
87 logx(LOG_INFO, "rejected connection from %s", eval_client(&ri));
88 close(newsock);
89 return;
92 /* Remember this spawning connection: */
93 sp = (struct spawn *)malloc(sizeof *sp);
94 if (sp == NULL) {
95 logit(LOG_ERR, "malloc");
96 close(newsock);
97 return;
99 memset(sp, '\0', sizeof *sp);
101 /* Keep the calling machine's source addr for ident purposes: */
102 memcpy(&sp->source, &sockstruct, sizeof sp->source);
103 sp->sourcelen = socklen;
105 /* Warn if we lose connection info: */
106 if (socklen > sizeof Spawn->source)
107 logx(LOG_WARNING,
108 "struct sockaddr is not big enough! (%d > %zu)",
109 socklen, sizeof Spawn->source);
112 * Turn off blocking I/O, so a slow or dead terminal won't stop
113 * the game. All subsequent reads check how many bytes they read.
115 flags = fcntl(newsock, F_GETFL, 0);
116 flags |= O_NDELAY;
117 fcntl(newsock, F_SETFL, flags);
119 /* Start listening to the spawning connection */
120 sp->fd = newsock;
121 FD_SET(sp->fd, &Fds_mask);
122 if (sp->fd >= Num_fds)
123 Num_fds = sp->fd + 1;
125 sp->reading_msg = 0;
126 sp->inlen = 0;
128 /* Add to the spawning list */
129 if ((sp->next = Spawn) != NULL)
130 Spawn->prevnext = &sp->next;
131 sp->prevnext = &Spawn;
132 Spawn = sp;
136 answer_next(struct spawn *sp)
138 PLAYER *pp;
139 char *cp1, *cp2;
140 u_int32_t version;
141 FILE *conn;
142 int len;
143 char teamstr[] = "[x]";
145 if (sp->reading_msg) {
146 /* Receive a message from a player */
147 len = read(sp->fd, sp->msg + sp->msglen,
148 sizeof sp->msg - sp->msglen);
149 if (len < 0)
150 goto error;
151 sp->msglen += len;
152 if (len && sp->msglen < (int)(sizeof sp->msg))
153 return FALSE;
155 teamstr[1] = sp->team;
156 outyx(ALL_PLAYERS, HEIGHT, 0, "%s%s: %.*s",
157 sp->name,
158 sp->team == ' ' ? "": teamstr,
159 sp->msglen,
160 sp->msg);
161 ce(ALL_PLAYERS);
162 sendcom(ALL_PLAYERS, REFRESH);
163 sendcom(ALL_PLAYERS, READY, 0);
164 flush(ALL_PLAYERS);
165 goto close_it;
168 /* Fill the buffer */
169 len = read(sp->fd, sp->inbuf + sp->inlen,
170 sizeof sp->inbuf - sp->inlen);
171 if (len <= 0)
172 goto error;
173 sp->inlen += len;
174 if (sp->inlen < (int)(sizeof sp->inbuf))
175 return FALSE;
177 /* Extract values from the buffer */
178 cp1 = sp->inbuf;
179 memcpy(&sp->uid, cp1, sizeof (u_int32_t));
180 cp1+= sizeof(u_int32_t);
181 memcpy(sp->name, cp1, NAMELEN);
182 cp1+= NAMELEN;
183 memcpy(&sp->team, cp1, sizeof (u_int8_t));
184 cp1+= sizeof(u_int8_t);
185 memcpy(&sp->enter_status, cp1, sizeof (u_int32_t));
186 cp1+= sizeof(u_int32_t);
187 memcpy(sp->ttyname, cp1, NAMELEN);
188 cp1+= NAMELEN;
189 memcpy(&sp->mode, cp1, sizeof (u_int32_t));
190 cp1+= sizeof(u_int32_t);
192 /* Convert data from network byte order: */
193 sp->uid = ntohl(sp->uid);
194 sp->enter_status = ntohl(sp->enter_status);
195 sp->mode = ntohl(sp->mode);
198 * Make sure the name contains only printable characters
199 * since we use control characters for cursor control
200 * between driver and player processes
202 sp->name[NAMELEN] = '\0';
203 for (cp1 = cp2 = sp->name; *cp1 != '\0'; cp1++)
204 if (isprint(*cp1) || *cp1 == ' ')
205 *cp2++ = *cp1;
206 *cp2 = '\0';
208 /* Make sure team name is valid */
209 if (sp->team < '1' || sp->team > '9')
210 sp->team = ' ';
212 /* Tell the other end this server's hunt driver version: */
213 version = htonl((u_int32_t) HUNT_VERSION);
214 write(sp->fd, &version, sizeof version);
216 if (sp->mode == C_MESSAGE) {
217 /* The clients only wants to send a message: */
218 sp->msglen = 0;
219 sp->reading_msg = 1;
220 return FALSE;
223 /* Use a stdio file descriptor from now on: */
224 conn = fdopen(sp->fd, "w");
226 /* The player is a monitor: */
227 if (sp->mode == C_MONITOR) {
228 if (conf_monitor && End_monitor < &Monitor[MAXMON]) {
229 pp = End_monitor++;
230 if (sp->team == ' ')
231 sp->team = '*';
232 } else {
233 /* Too many monitors */
234 fprintf(conn, "Too many monitors\n");
235 fflush(conn);
236 logx(LOG_NOTICE, "too many monitors");
237 goto close_it;
240 /* The player is a normal hunter: */
241 } else {
242 if (End_player < &Player[MAXPL])
243 pp = End_player++;
244 else {
245 fprintf(conn, "Too many players\n");
246 fflush(conn);
247 /* Too many players */
248 logx(LOG_NOTICE, "too many players");
249 goto close_it;
253 /* Find the player's running scorecard */
254 pp->p_ident = get_ident(&sp->source, sp->sourcelen, sp->uid,
255 sp->name, sp->team);
256 pp->p_output = conn;
257 pp->p_death[0] = '\0';
258 pp->p_fd = sp->fd;
260 /* No idea where the player starts: */
261 pp->p_y = 0;
262 pp->p_x = 0;
264 /* Mode-specific initialisation: */
265 if (sp->mode == C_MONITOR)
266 stmonitor(pp);
267 else
268 stplayer(pp, sp->enter_status);
270 /* And, they're off! Caller should remove and free sp. */
271 return TRUE;
273 error:
274 if (len < 0)
275 logit(LOG_WARNING, "read");
276 else
277 logx(LOG_WARNING, "lost connection to new client");
279 close_it:
280 /* Destroy the spawn */
281 *sp->prevnext = sp->next;
282 if (sp->next) sp->next->prevnext = sp->prevnext;
283 FD_CLR(sp->fd, &Fds_mask);
284 close(sp->fd);
285 free(sp);
286 return FALSE;
289 /* Start a monitor: */
290 static void
291 stmonitor(PLAYER *pp)
294 /* Monitors get to see the entire maze: */
295 memcpy(pp->p_maze, Maze, sizeof pp->p_maze);
296 drawmaze(pp);
298 /* Put the monitor's name near the bottom right on all screens: */
299 outyx(ALL_PLAYERS,
300 STAT_MON_ROW + 1 + (pp - Monitor), STAT_NAME_COL,
301 "%5.5s%c%-10.10s %c", " ",
302 stat_char(pp), pp->p_ident->i_name, pp->p_ident->i_team);
304 /* Ready the monitor: */
305 sendcom(pp, REFRESH);
306 sendcom(pp, READY, 0);
307 flush(pp);
310 /* Start a player: */
311 static void
312 stplayer(PLAYER *newpp, int enter_status)
314 int x, y;
315 PLAYER *pp;
316 int len;
318 Nplayer++;
320 for (y = 0; y < UBOUND; y++)
321 for (x = 0; x < WIDTH; x++)
322 newpp->p_maze[y][x] = Maze[y][x];
323 for ( ; y < DBOUND; y++) {
324 for (x = 0; x < LBOUND; x++)
325 newpp->p_maze[y][x] = Maze[y][x];
326 for ( ; x < RBOUND; x++)
327 newpp->p_maze[y][x] = SPACE;
328 for ( ; x < WIDTH; x++)
329 newpp->p_maze[y][x] = Maze[y][x];
331 for ( ; y < HEIGHT; y++)
332 for (x = 0; x < WIDTH; x++)
333 newpp->p_maze[y][x] = Maze[y][x];
335 /* Drop the new player somewhere in the maze: */
336 do {
337 x = rand_num(WIDTH - 1) + 1;
338 y = rand_num(HEIGHT - 1) + 1;
339 } while (Maze[y][x] != SPACE);
340 newpp->p_over = SPACE;
341 newpp->p_x = x;
342 newpp->p_y = y;
343 newpp->p_undershot = FALSE;
345 /* Send them flying if needed */
346 if (enter_status == Q_FLY && conf_fly) {
347 newpp->p_flying = rand_num(conf_flytime);
348 newpp->p_flyx = 2 * rand_num(conf_flystep + 1) - conf_flystep;
349 newpp->p_flyy = 2 * rand_num(conf_flystep + 1) - conf_flystep;
350 newpp->p_face = FLYER;
351 } else {
352 newpp->p_flying = -1;
353 newpp->p_face = rand_dir();
356 /* Initialize the new player's attributes: */
357 newpp->p_damage = 0;
358 newpp->p_damcap = conf_maxdam;
359 newpp->p_nchar = 0;
360 newpp->p_ncount = 0;
361 newpp->p_nexec = 0;
362 newpp->p_ammo = conf_ishots;
363 newpp->p_nboots = 0;
365 /* Decide on what cloak/scan status to enter with */
366 if (enter_status == Q_SCAN && conf_scan) {
367 newpp->p_scan = conf_scanlen * Nplayer;
368 newpp->p_cloak = 0;
369 } else if (conf_cloak) {
370 newpp->p_scan = 0;
371 newpp->p_cloak = conf_cloaklen;
372 } else {
373 newpp->p_scan = 0;
374 newpp->p_cloak = 0;
376 newpp->p_ncshot = 0;
379 * For each new player, place a large mine and
380 * a small mine somewhere in the maze:
382 do {
383 x = rand_num(WIDTH - 1) + 1;
384 y = rand_num(HEIGHT - 1) + 1;
385 } while (Maze[y][x] != SPACE);
386 Maze[y][x] = GMINE;
387 for (pp = Monitor; pp < End_monitor; pp++)
388 check(pp, y, x);
390 do {
391 x = rand_num(WIDTH - 1) + 1;
392 y = rand_num(HEIGHT - 1) + 1;
393 } while (Maze[y][x] != SPACE);
394 Maze[y][x] = MINE;
395 for (pp = Monitor; pp < End_monitor; pp++)
396 check(pp, y, x);
398 /* Create a score line for the new player: */
399 snprintf(Buf, sizeof Buf, "%5.2f%c%-10.10s %c",
400 newpp->p_ident->i_score, stat_char(newpp),
401 newpp->p_ident->i_name, newpp->p_ident->i_team);
402 len = strlen(Buf);
403 y = STAT_PLAY_ROW + 1 + (newpp - Player);
404 for (pp = Player; pp < End_player; pp++) {
405 if (pp != newpp) {
406 /* Give everyone a few more shots: */
407 pp->p_ammo += conf_nshots;
408 newpp->p_ammo += conf_nshots;
409 outyx(pp, y, STAT_NAME_COL, Buf, len);
410 ammo_update(pp);
413 for (pp = Monitor; pp < End_monitor; pp++)
414 outyx(pp, y, STAT_NAME_COL, Buf, len);
416 /* Show the new player what they can see and where they are: */
417 drawmaze(newpp);
418 drawplayer(newpp, TRUE);
419 look(newpp);
421 /* Make sure that the position they enter in will be erased: */
422 if (enter_status == Q_FLY && conf_fly)
423 showexpl(newpp->p_y, newpp->p_x, FLYER);
425 /* Ready the new player: */
426 sendcom(newpp, REFRESH);
427 sendcom(newpp, READY, 0);
428 flush(newpp);
432 * rand_dir:
433 * Return a random direction
436 rand_dir(void)
438 switch (rand_num(4)) {
439 case 0:
440 return LEFTS;
441 case 1:
442 return RIGHT;
443 case 2:
444 return BELOW;
445 case 3:
446 return ABOVE;
448 /* NOTREACHED */
449 return(-1);
453 * get_ident:
454 * Get the score structure of a player
456 static IDENT *
457 get_ident(struct sockaddr *sa, int salen __unused, uid_t uid, char *name,
458 char team)
460 IDENT *ip;
461 static IDENT punt;
462 u_int32_t machine;
464 if (sa->sa_family == AF_INET)
465 machine = ntohl((u_long)((struct sockaddr_in *)sa)->sin_addr.s_addr);
466 else
467 machine = 0;
469 for (ip = Scores; ip != NULL; ip = ip->i_next)
470 if (ip->i_machine == machine
471 && ip->i_uid == uid
472 /* && ip->i_team == team */
473 && strncmp(ip->i_name, name, NAMELEN) == 0)
474 break;
476 if (ip != NULL) {
477 if (ip->i_team != team) {
478 logx(LOG_INFO, "player %s %s team %c",
479 name,
480 team == ' ' ? "left" : ip->i_team == ' ' ?
481 "joined" : "changed to",
482 team == ' ' ? ip->i_team : team);
483 ip->i_team = team;
485 if (ip->i_entries < conf_scoredecay)
486 ip->i_entries++;
487 else
488 ip->i_kills = (ip->i_kills * (conf_scoredecay - 1))
489 / conf_scoredecay;
490 ip->i_score = ip->i_kills / (double) ip->i_entries;
492 else {
493 /* Alloc new entry -- it is released in clear_scores() */
494 ip = (IDENT *) malloc(sizeof (IDENT));
495 if (ip == NULL) {
496 logit(LOG_ERR, "malloc");
497 /* Fourth down, time to punt */
498 ip = &punt;
500 ip->i_machine = machine;
501 ip->i_team = team;
502 ip->i_uid = uid;
503 strlcpy(ip->i_name, name, sizeof ip->i_name);
504 ip->i_kills = 0;
505 ip->i_entries = 1;
506 ip->i_score = 0;
507 ip->i_absorbed = 0;
508 ip->i_faced = 0;
509 ip->i_shot = 0;
510 ip->i_robbed = 0;
511 ip->i_slime = 0;
512 ip->i_missed = 0;
513 ip->i_ducked = 0;
514 ip->i_gkills = ip->i_bkills = ip->i_deaths = 0;
515 ip->i_stillb = ip->i_saved = 0;
516 ip->i_next = Scores;
517 Scores = ip;
519 logx(LOG_INFO, "new player: %s%s%c%s",
520 name,
521 team == ' ' ? "" : " (team ",
522 team,
523 team == ' ' ? "" : ")");
526 return ip;
529 void
530 answer_info(FILE *fp)
532 struct spawn *sp;
533 char buf[128];
534 const char *bf;
535 struct sockaddr_in *sa;
537 if (Spawn == NULL)
538 return;
539 fprintf(fp, "\nSpawning connections:\n");
540 for (sp = Spawn; sp; sp = sp->next) {
541 sa = (struct sockaddr_in *)&sp->source;
542 bf = inet_ntop(AF_INET, &sa->sin_addr, buf, sizeof buf);
543 if (!bf) {
544 logit(LOG_WARNING, "inet_ntop");
545 bf = "?";
547 fprintf(fp, "fd %d: state %d, from %s:%d\n",
548 sp->fd, sp->inlen + (sp->reading_msg ? sp->msglen : 0),
549 bf, sa->sin_port);