Home | History | Annotate | Line # | Download | only in huntd
      1 /*	$NetBSD: driver.c,v 1.36 2021/05/02 12:50:45 rillig Exp $	*/
      2 /*
      3  * Copyright (c) 1983-2003, Regents of the University of California.
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions are
      8  * met:
      9  *
     10  * + Redistributions of source code must retain the above copyright
     11  *   notice, this list of conditions and the following disclaimer.
     12  * + Redistributions in binary form must reproduce the above copyright
     13  *   notice, this list of conditions and the following disclaimer in the
     14  *   documentation and/or other materials provided with the distribution.
     15  * + Neither the name of the University of California, San Francisco nor
     16  *   the names of its contributors may be used to endorse or promote
     17  *   products derived from this software without specific prior written
     18  *   permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
     21  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
     23  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 #include <sys/cdefs.h>
     34 #ifndef lint
     35 __RCSID("$NetBSD: driver.c,v 1.36 2021/05/02 12:50:45 rillig Exp $");
     36 #endif /* not lint */
     37 
     38 #include <sys/types.h>
     39 #include <sys/socket.h>
     40 #include <sys/stat.h>
     41 #include <sys/time.h>
     42 #include <sys/un.h>
     43 
     44 #include <netinet/in.h>
     45 #include <netdb.h>
     46 #include <arpa/inet.h>
     47 #include <net/if.h>
     48 
     49 #include <stdlib.h>
     50 #include <unistd.h>
     51 #include <signal.h>
     52 #include <errno.h>
     53 #include <err.h>
     54 
     55 #include "hunt.h"
     56 #include "pathnames.h"
     57 
     58 /*
     59  * There are three listening sockets in this daemon:
     60  *    - a datagram socket that listens on a well known port
     61  *    - a stream socket for general player connections
     62  *    - a second stream socket that prints the stats/scores
     63  *
     64  * These are (now) named as follows:
     65  *    - contact (contactsock, contactaddr, etc.)
     66  *    - hunt (huntsock, huntaddr, etc.)
     67  *    - stat (statsock, stataddr, etc.)
     68  *
     69  * Until being cleaned up in 2014 the code used an assortment of
     70  * inconsistent and confusing names to refer to the pieces of these:
     71  *
     72  *    test_port -> contactaddr
     73  *    Test_port -> contactport
     74  *    Test_socket -> contactsock
     75  *
     76  *    sock_port -> huntport
     77  *    Socket -> huntsock
     78  *
     79  *    Daemon -> both stataddr and huntaddr
     80  *    stat_port -> statport
     81  *    status -> statsock
     82  *
     83  * It isn't clear to me what purpose contactsocket is supposed to
     84  * serve; maybe it was supposed to avoid asking inetd to support
     85  * tcp/wait sockets? Anyway, we can't really change the protocol at
     86  * this point. (To complicate matters, contactsocket doesn't exist if
     87  * using AF_UNIX sockets; you just connect to the game socket.)
     88  *
     89  * When using internet sockets:
     90  *    - the contact socket listens on INADDR_ANY using the game port
     91  *      (either specified or the default: CONTACT_PORT, currently
     92  *      spelled TEST_PORT)
     93  *    - the hunt socket listens on INADDR_ANY using whatever port
     94  *    - the stat socket listens on INADDR_ANY using whatever port
     95  *
     96  * When using AF_UNIX sockets:
     97  *    - the contact socket isn't used
     98  *    - the hunt socket listens on the game socket (either a specified path
     99  *      or /tmp/hunt)
    100  *    - the stat socket listens on its own socket (huntsocket's path +
    101  *      ".stats")
    102  */
    103 
    104 static bool localmode;			/* true -> AF_UNIX; false -> AF_INET */
    105 static bool inetd_spawned;		/* invoked via inetd? */
    106 static bool standard_port = true;	/* listening on standard port? */
    107 
    108 static struct sockaddr_storage huntaddr;
    109 static struct sockaddr_storage stataddr;
    110 static socklen_t huntaddrlen;
    111 static socklen_t stataddrlen;
    112 
    113 static uint16_t contactport = TEST_PORT;
    114 static uint16_t	huntport;		/* port # of tcp listen socket */
    115 static uint16_t	statport;		/* port # of statistics tcp socket */
    116 
    117 static const char *huntsockpath = PATH_HUNTSOCKET;
    118 static char *statsockpath;
    119 
    120 static int contactsock;			/* socket to answer datagrams */
    121 int huntsock;				/* main socket */
    122 static int statsock;			/* stat socket */
    123 
    124 #ifdef VOLCANO
    125 static int volcano = 0;			/* Explosion size */
    126 #endif
    127 
    128 static void clear_scores(void);
    129 static bool havechar(PLAYER *, int);
    130 static void init(void);
    131 static void makeboots(void);
    132 static void send_stats(void);
    133 static void zap(PLAYER *, bool, int);
    134 
    135 static int
    136 getnum(const char *s, unsigned long *ret)
    137 {
    138 	char *t;
    139 
    140 	errno = 0;
    141 	*ret = strtoul(s, &t, 0);
    142 	if (errno || *t != '\0') {
    143 		return -1;
    144 	}
    145 	return 0;
    146 }
    147 
    148 static __dead void
    149 usage(const char *av0)
    150 {
    151 	fprintf(stderr, "Usage: %s [-s] [-p portnumber|socketpath]\n", av0);
    152 	exit(1);
    153 }
    154 
    155 static void
    156 makeaddr(const char *path, uint16_t port,
    157 	 struct sockaddr_storage *ss, socklen_t *len)
    158 {
    159 	struct sockaddr_in *sin;
    160 	struct sockaddr_un *sun;
    161 
    162 	if (path == NULL) {
    163 		sin = (struct sockaddr_in *)ss;
    164 		sin->sin_family = AF_INET;
    165 		sin->sin_addr.s_addr = INADDR_ANY;
    166 		sin->sin_port = htons(port);
    167 		*len = sizeof(*sin);
    168 	} else {
    169 		sun = (struct sockaddr_un *)ss;
    170 		sun->sun_family = AF_UNIX;
    171 		strlcpy(sun->sun_path, path, sizeof(sun->sun_path));
    172 		*len = SUN_LEN(sun);
    173 	}
    174 }
    175 
    176 static uint16_t
    177 getsockport(int sock)
    178 {
    179 	struct sockaddr_storage addr;
    180 	socklen_t len;
    181 
    182 	len = sizeof(addr);
    183 	if (getsockname(sock, (struct sockaddr *)&addr, &len) < 0)  {
    184 		complain(LOG_ERR, "getsockname");
    185 		exit(1);
    186 	}
    187 	switch (addr.ss_family) {
    188 	    case AF_INET:
    189 		return ntohs(((struct sockaddr_in *)&addr)->sin_port);
    190 	    case AF_INET6:
    191 		return ntohs(((struct sockaddr_in6 *)&addr)->sin6_port);
    192 	    default:
    193 		break;
    194 	}
    195 	return 0;
    196 }
    197 
    198 /*
    199  * main:
    200  *	The main program.
    201  */
    202 int
    203 main(int ac, char **av)
    204 {
    205 	PLAYER *pp;
    206 	unsigned long optargnum;
    207 	uint16_t msg, reply;
    208 	struct sockaddr_storage msgaddr;
    209 	socklen_t msgaddrlen;
    210 	static bool first = true;
    211 	static bool server = false;
    212 	int c, i;
    213 	const int linger = 90 * 1000;
    214 
    215 	while ((c = getopt(ac, av, "sp:")) != -1) {
    216 		switch (c) {
    217 		  case 's':
    218 			server = true;
    219 			break;
    220 		  case 'p':
    221 			standard_port = false;
    222 			if (getnum(optarg, &optargnum) < 0) {
    223 				localmode = true;
    224 				huntsockpath = optarg;
    225 			} else if (optargnum < 0xffff) {
    226 				localmode = false;
    227 				contactport = optargnum;
    228 			} else {
    229 				usage(av[0]);
    230 			}
    231 			break;
    232 		  default:
    233 			usage(av[0]);
    234 		}
    235 	}
    236 	if (optind < ac)
    237 		usage(av[0]);
    238 
    239 	asprintf(&statsockpath, "%s.stats", huntsockpath);
    240 	init();
    241 
    242 
    243 again:
    244 	do {
    245 		errno = 0;
    246 		while (poll(fdset, 3+MAXPL+MAXMON, INFTIM) < 0)
    247 		{
    248 			if (errno != EINTR)
    249 				complain(LOG_WARNING, "poll");
    250 			errno = 0;
    251 		}
    252 		if (!localmode && fdset[2].revents & POLLIN) {
    253 			msgaddrlen = sizeof(msgaddr);
    254 			(void) recvfrom(contactsock, &msg, sizeof msg,
    255 				0, (struct sockaddr *)&msgaddr, &msgaddrlen);
    256 			switch (ntohs(msg)) {
    257 			  case C_MESSAGE:
    258 				if (Nplayer <= 0)
    259 					break;
    260 				reply = htons((u_short) Nplayer);
    261 				(void) sendto(contactsock, &reply,
    262 					sizeof reply, 0,
    263 					(struct sockaddr *)&msgaddr,
    264 					msgaddrlen);
    265 				break;
    266 			  case C_SCORES:
    267 				reply = htons(statport);
    268 				(void) sendto(contactsock, &reply,
    269 					sizeof reply, 0,
    270 					(struct sockaddr *)&msgaddr,
    271 					msgaddrlen);
    272 				break;
    273 			  case C_PLAYER:
    274 			  case C_MONITOR:
    275 				if (msg == C_MONITOR && Nplayer <= 0)
    276 					break;
    277 				reply = htons(huntport);
    278 				(void) sendto(contactsock, &reply,
    279 					sizeof reply, 0,
    280 					(struct sockaddr *)&msgaddr,
    281 					msgaddrlen);
    282 				break;
    283 			}
    284 		}
    285 
    286 		{
    287 			for (pp = Player, i = 0; pp < End_player; pp++, i++)
    288 				if (havechar(pp, i + 3)) {
    289 					execute(pp);
    290 					pp->p_nexec++;
    291 				}
    292 #ifdef MONITOR
    293 			for (pp = Monitor, i = 0; pp < End_monitor; pp++, i++)
    294 				if (havechar(pp, i + MAXPL + 3)) {
    295 					mon_execute(pp);
    296 					pp->p_nexec++;
    297 				}
    298 #endif
    299 			moveshots();
    300 			for (pp = Player, i = 0; pp < End_player; )
    301 				if (pp->p_death[0] != '\0')
    302 					zap(pp, true, i + 3);
    303 				else
    304 					pp++, i++;
    305 #ifdef MONITOR
    306 			for (pp = Monitor, i = 0; pp < End_monitor; )
    307 				if (pp->p_death[0] != '\0')
    308 					zap(pp, false, i + MAXPL + 3);
    309 				else
    310 					pp++, i++;
    311 #endif
    312 		}
    313 		if (fdset[0].revents & POLLIN)
    314 			if (answer()) {
    315 				if (first) {
    316 					/* announce start of game? */
    317 				}
    318 				first = false;
    319 			}
    320 		if (fdset[1].revents & POLLIN)
    321 			send_stats();
    322 		for (pp = Player, i = 0; pp < End_player; pp++, i++) {
    323 			if (fdset[i + 3].revents & POLLIN)
    324 				sendcom(pp, READY, pp->p_nexec);
    325 			pp->p_nexec = 0;
    326 			(void) fflush(pp->p_output);
    327 		}
    328 #ifdef MONITOR
    329 		for (pp = Monitor, i = 0; pp < End_monitor; pp++, i++) {
    330 			if (fdset[i + MAXPL + 3].revents & POLLIN)
    331 				sendcom(pp, READY, pp->p_nexec);
    332 			pp->p_nexec = 0;
    333 			(void) fflush(pp->p_output);
    334 		}
    335 #endif
    336 	} while (Nplayer > 0);
    337 
    338 	if (poll(fdset, 3+MAXPL+MAXMON, linger) > 0) {
    339 		goto again;
    340 	}
    341 	if (server) {
    342 		clear_scores();
    343 		makemaze();
    344 		clearwalls();
    345 #ifdef BOOTS
    346 		makeboots();
    347 #endif
    348 		first = true;
    349 		goto again;
    350 	}
    351 
    352 #ifdef MONITOR
    353 	for (pp = Monitor, i = 0; pp < End_monitor; i++)
    354 		zap(pp, false, i + MAXPL + 3);
    355 #endif
    356 	cleanup(0);
    357 	/* NOTREACHED */
    358 	return(0);
    359 }
    360 
    361 /*
    362  * init:
    363  *	Initialize the global parameters.
    364  */
    365 static void
    366 init(void)
    367 {
    368 	int i;
    369 	struct sockaddr_storage stdinaddr;
    370 	struct sockaddr_storage contactaddr;
    371 	socklen_t contactaddrlen;
    372 	socklen_t len;
    373 
    374 #ifndef DEBUG
    375 #ifdef TIOCNOTTY
    376 	(void) ioctl(fileno(stdout), TIOCNOTTY, NULL);
    377 #endif
    378 	(void) setpgrp(getpid(), getpid());
    379 	(void) signal(SIGHUP, SIG_IGN);
    380 	(void) signal(SIGINT, SIG_IGN);
    381 	(void) signal(SIGQUIT, SIG_IGN);
    382 	(void) signal(SIGTERM, cleanup);
    383 #endif
    384 
    385 	(void) chdir("/var/tmp");	/* just in case it core dumps */
    386 	(void) umask(0);		/* No privacy at all! */
    387 	(void) signal(SIGPIPE, SIG_IGN);
    388 
    389 #ifdef LOG
    390 	openlog("huntd", LOG_PID, LOG_DAEMON);
    391 #endif
    392 
    393 	/*
    394 	 * check for inetd
    395 	 */
    396 	len = sizeof(stdinaddr);
    397 	if (getsockname(STDIN_FILENO, (struct sockaddr *)&stdinaddr,
    398 			&len) >= 0) {
    399 		inetd_spawned = true;
    400 		/* force localmode, assimilate stdin as appropriate */
    401 		if (stdinaddr.ss_family == AF_UNIX) {
    402 			localmode = true;
    403 			contactsock = -1;
    404 			huntsock = STDIN_FILENO;
    405 		}
    406 		else {
    407 			localmode = false;
    408 			contactsock = STDIN_FILENO;
    409 			huntsock = -1;
    410 		}
    411 	} else {
    412 		/* keep value of localmode; no sockets yet */
    413 		contactsock = -1;
    414 		huntsock = -1;
    415 	}
    416 
    417 	/*
    418 	 * initialize contact socket
    419 	 */
    420 	if (!localmode && contactsock < 0) {
    421 		makeaddr(NULL, contactport, &contactaddr, &contactaddrlen);
    422 		contactsock = socket(AF_INET, SOCK_DGRAM, 0);
    423 		if (bind(contactsock, (struct sockaddr *) &contactaddr,
    424 			 contactaddrlen) < 0) {
    425 			complain(LOG_ERR, "bind");
    426 			exit(1);
    427 		}
    428 		(void) listen(contactsock, 5);
    429 	}
    430 
    431 	/*
    432 	 * Initialize main socket
    433 	 */
    434 	if (huntsock < 0) {
    435 		makeaddr(localmode ? huntsockpath : NULL, 0, &huntaddr,
    436 			 &huntaddrlen);
    437 		huntsock = socket(huntaddr.ss_family, SOCK_STREAM, 0);
    438 		if (bind(huntsock, (struct sockaddr *)&huntaddr,
    439 			 huntaddrlen) < 0) {
    440 			if (errno == EADDRINUSE)
    441 				exit(0);
    442 			else {
    443 				complain(LOG_ERR, "bind");
    444 				cleanup(1);
    445 			}
    446 		}
    447 		(void) listen(huntsock, 5);
    448 	}
    449 
    450 	/*
    451 	 * Initialize statistics socket
    452 	 */
    453 	makeaddr(localmode ? statsockpath : NULL, 0, &stataddr, &stataddrlen);
    454 	statsock = socket(stataddr.ss_family, SOCK_STREAM, 0);
    455 	if (bind(statsock, (struct sockaddr *)&stataddr, stataddrlen) < 0) {
    456 		if (errno == EADDRINUSE)
    457 			exit(0);
    458 		else {
    459 			complain(LOG_ERR, "bind");
    460 			cleanup(1);
    461 		}
    462 	}
    463 	(void) listen(statsock, 5);
    464 
    465 	if (!localmode) {
    466 		contactport = getsockport(contactsock);
    467 		statport = getsockport(statsock);
    468 		huntport = getsockport(huntsock);
    469 		if (contactport != TEST_PORT) {
    470 			standard_port = false;
    471 		}
    472 	}
    473 
    474 	/*
    475 	 * Initialize minimal poll mask
    476 	 */
    477 	fdset[0].fd = huntsock;
    478 	fdset[0].events = POLLIN;
    479 	fdset[1].fd = statsock;
    480 	fdset[1].events = POLLIN;
    481 	if (localmode) {
    482 		fdset[2].fd = -1;
    483 		fdset[2].events = 0;
    484 	} else {
    485 		fdset[2].fd = contactsock;
    486 		fdset[2].events = POLLIN;
    487 	}
    488 
    489 	srandom(time(NULL));
    490 	makemaze();
    491 #ifdef BOOTS
    492 	makeboots();
    493 #endif
    494 
    495 	for (i = 0; i < NASCII; i++)
    496 		See_over[i] = true;
    497 	See_over[DOOR] = false;
    498 	See_over[WALL1] = false;
    499 	See_over[WALL2] = false;
    500 	See_over[WALL3] = false;
    501 #ifdef REFLECT
    502 	See_over[WALL4] = false;
    503 	See_over[WALL5] = false;
    504 #endif
    505 
    506 }
    507 
    508 #ifdef BOOTS
    509 /*
    510  * makeboots:
    511  *	Put the boots in the maze
    512  */
    513 static void
    514 makeboots(void)
    515 {
    516 	int x, y;
    517 	PLAYER *pp;
    518 
    519 	do {
    520 		x = rand_num(WIDTH - 1) + 1;
    521 		y = rand_num(HEIGHT - 1) + 1;
    522 	} while (Maze[y][x] != SPACE);
    523 	Maze[y][x] = BOOT_PAIR;
    524 	for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
    525 		pp->p_flying = -1;
    526 }
    527 #endif
    528 
    529 
    530 /*
    531  * checkdam:
    532  *	Check the damage to the given player, and see if s/he is killed
    533  */
    534 void
    535 checkdam(PLAYER *ouch, PLAYER *gotcha, IDENT *credit, int amt,
    536 	 char this_shot_type)
    537 {
    538 	const char *cp;
    539 
    540 	if (ouch->p_death[0] != '\0')
    541 		return;
    542 #ifdef BOOTS
    543 	if (this_shot_type == SLIME)
    544 		switch (ouch->p_nboots) {
    545 		  default:
    546 			break;
    547 		  case 1:
    548 			amt = (amt + 1) / 2;
    549 			break;
    550 		  case 2:
    551 			if (gotcha != NULL)
    552 				message(gotcha, "He has boots on!");
    553 			return;
    554 		}
    555 #endif
    556 	ouch->p_damage += amt;
    557 	if (ouch->p_damage <= ouch->p_damcap) {
    558 		(void) snprintf(Buf, sizeof(Buf), "%2d", ouch->p_damage);
    559 		cgoto(ouch, STAT_DAM_ROW, STAT_VALUE_COL);
    560 		outstr(ouch, Buf, 2);
    561 		return;
    562 	}
    563 
    564 	/* Someone DIED */
    565 	switch (this_shot_type) {
    566 	  default:
    567 		cp = "Killed";
    568 		break;
    569 #ifdef FLY
    570 	  case FALL:
    571 		cp = "Killed on impact";
    572 		break;
    573 #endif
    574 	  case KNIFE:
    575 		cp = "Stabbed to death";
    576 		ouch->p_ammo = 0;		/* No exploding */
    577 		break;
    578 	  case SHOT:
    579 		cp = "Shot to death";
    580 		break;
    581 	  case GRENADE:
    582 	  case SATCHEL:
    583 	  case BOMB:
    584 		cp = "Bombed";
    585 		break;
    586 	  case MINE:
    587 	  case GMINE:
    588 		cp = "Blown apart";
    589 		break;
    590 #ifdef	OOZE
    591 	  case SLIME:
    592 		cp = "Slimed";
    593 		if (credit != NULL)
    594 			credit->i_slime++;
    595 		break;
    596 #endif
    597 #ifdef	VOLCANO
    598 	  case LAVA:
    599 		cp = "Baked";
    600 		break;
    601 #endif
    602 #ifdef DRONE
    603 	  case DSHOT:
    604 		cp = "Eliminated";
    605 		break;
    606 #endif
    607 	}
    608 	if (credit == NULL) {
    609 		(void) snprintf(ouch->p_death, sizeof(ouch->p_death),
    610 			"| %s by %s |", cp,
    611 			(this_shot_type == MINE || this_shot_type == GMINE) ?
    612 			"a mine" : "act of God");
    613 		return;
    614 	}
    615 
    616 	(void) snprintf(ouch->p_death, sizeof(ouch->p_death),
    617 		"| %s by %s |", cp, credit->i_name);
    618 
    619 	if (ouch == gotcha) {		/* No use killing yourself */
    620 		credit->i_kills--;
    621 		credit->i_bkills++;
    622 	}
    623 	else if (ouch->p_ident->i_team == ' '
    624 	|| ouch->p_ident->i_team != credit->i_team) {
    625 		credit->i_kills++;
    626 		credit->i_gkills++;
    627 	}
    628 	else {
    629 		credit->i_kills--;
    630 		credit->i_bkills++;
    631 	}
    632 	credit->i_score = credit->i_kills / (double) credit->i_entries;
    633 	ouch->p_ident->i_deaths++;
    634 	if (ouch->p_nchar == 0)
    635 		ouch->p_ident->i_stillb++;
    636 	if (gotcha == NULL)
    637 		return;
    638 	gotcha->p_damcap += STABDAM;
    639 	gotcha->p_damage -= STABDAM;
    640 	if (gotcha->p_damage < 0)
    641 		gotcha->p_damage = 0;
    642 	(void) snprintf(Buf, sizeof(Buf), "%2d/%2d", gotcha->p_damage,
    643 			gotcha->p_damcap);
    644 	cgoto(gotcha, STAT_DAM_ROW, STAT_VALUE_COL);
    645 	outstr(gotcha, Buf, 5);
    646 	(void) snprintf(Buf, sizeof(Buf), "%3d",
    647 			(gotcha->p_damcap - MAXDAM) / 2);
    648 	cgoto(gotcha, STAT_KILL_ROW, STAT_VALUE_COL);
    649 	outstr(gotcha, Buf, 3);
    650 	(void) snprintf(Buf, sizeof(Buf), "%5.2f", gotcha->p_ident->i_score);
    651 	for (ouch = Player; ouch < End_player; ouch++) {
    652 		cgoto(ouch, STAT_PLAY_ROW + 1 + (gotcha - Player),
    653 			STAT_NAME_COL);
    654 		outstr(ouch, Buf, 5);
    655 	}
    656 #ifdef MONITOR
    657 	for (ouch = Monitor; ouch < End_monitor; ouch++) {
    658 		cgoto(ouch, STAT_PLAY_ROW + 1 + (gotcha - Player),
    659 			STAT_NAME_COL);
    660 		outstr(ouch, Buf, 5);
    661 	}
    662 #endif
    663 }
    664 
    665 /*
    666  * zap:
    667  *	Kill off a player and take him out of the game.
    668  */
    669 static void
    670 zap(PLAYER *pp, bool was_player, int i)
    671 {
    672 	int n, len;
    673 	BULLET *bp;
    674 	PLAYER *np;
    675 	int x, y;
    676 
    677 	if (was_player) {
    678 		if (pp->p_undershot)
    679 			fixshots(pp->p_y, pp->p_x, pp->p_over);
    680 		drawplayer(pp, false);
    681 		Nplayer--;
    682 	}
    683 
    684 	len = strlen(pp->p_death);	/* Display the cause of death */
    685 	x = (WIDTH - len) / 2;
    686 	cgoto(pp, HEIGHT / 2, x);
    687 	outstr(pp, pp->p_death, len);
    688 	for (n = 1; n < len; n++)
    689 		pp->p_death[n] = '-';
    690 	pp->p_death[0] = '+';
    691 	pp->p_death[len - 1] = '+';
    692 	cgoto(pp, HEIGHT / 2 - 1, x);
    693 	outstr(pp, pp->p_death, len);
    694 	cgoto(pp, HEIGHT / 2 + 1, x);
    695 	outstr(pp, pp->p_death, len);
    696 	cgoto(pp, HEIGHT, 0);
    697 
    698 #ifdef MONITOR
    699 	if (was_player) {
    700 #endif
    701 		for (bp = Bullets; bp != NULL; bp = bp->b_next) {
    702 			if (bp->b_owner == pp)
    703 				bp->b_owner = NULL;
    704 			if (bp->b_x == pp->p_x && bp->b_y == pp->p_y)
    705 				bp->b_over = SPACE;
    706 		}
    707 
    708 		n = rand_num(pp->p_ammo);
    709 		x = rand_num(pp->p_ammo);
    710 		if (x > n)
    711 			n = x;
    712 		if (pp->p_ammo == 0)
    713 			x = 0;
    714 		else if (n == pp->p_ammo - 1) {
    715 			x = pp->p_ammo;
    716 			len = SLIME;
    717 		}
    718 		else {
    719 			for (x = MAXBOMB - 1; x > 0; x--)
    720 				if (n >= shot_req[x])
    721 					break;
    722 			for (y = MAXSLIME - 1; y > 0; y--)
    723 				if (n >= slime_req[y])
    724 					break;
    725 			if (y >= 0 && slime_req[y] > shot_req[x]) {
    726 				x = slime_req[y];
    727 				len = SLIME;
    728 			}
    729 			else if (x != 0) {
    730 				len = shot_type[x];
    731 				x = shot_req[x];
    732 			}
    733 		}
    734 		if (x > 0) {
    735 			(void) add_shot(len, pp->p_y, pp->p_x, pp->p_face, x,
    736 				NULL, true, SPACE);
    737 			(void) snprintf(Buf, sizeof(Buf), "%s detonated.",
    738 				pp->p_ident->i_name);
    739 			for (np = Player; np < End_player; np++)
    740 				message(np, Buf);
    741 #ifdef MONITOR
    742 			for (np = Monitor; np < End_monitor; np++)
    743 				message(np, Buf);
    744 #endif
    745 #ifdef BOOTS
    746 			while (pp->p_nboots-- > 0) {
    747 				for (np = Boot; np < &Boot[NBOOTS]; np++)
    748 					if (np->p_flying < 0)
    749 						break;
    750 				if (np >= &Boot[NBOOTS])
    751 					err(1, "Too many boots");
    752 				np->p_undershot = false;
    753 				np->p_x = pp->p_x;
    754 				np->p_y = pp->p_y;
    755 				np->p_flying = rand_num(20);
    756 				np->p_flyx = 2 * rand_num(6) - 5;
    757 				np->p_flyy = 2 * rand_num(6) - 5;
    758 				np->p_over = SPACE;
    759 				np->p_face = BOOT;
    760 				showexpl(np->p_y, np->p_x, BOOT);
    761 			}
    762 #endif
    763 		}
    764 #ifdef BOOTS
    765 		else if (pp->p_nboots > 0) {
    766 			if (pp->p_nboots == 2)
    767 				Maze[pp->p_y][pp->p_x] = BOOT_PAIR;
    768 			else
    769 				Maze[pp->p_y][pp->p_x] = BOOT;
    770 			if (pp->p_undershot)
    771 				fixshots(pp->p_y, pp->p_x,
    772 					Maze[pp->p_y][pp->p_x]);
    773 		}
    774 #endif
    775 
    776 #ifdef VOLCANO
    777 		volcano += pp->p_ammo - x;
    778 		if (rand_num(100) < volcano / 50) {
    779 			do {
    780 				x = rand_num(WIDTH / 2) + WIDTH / 4;
    781 				y = rand_num(HEIGHT / 2) + HEIGHT / 4;
    782 			} while (Maze[y][x] != SPACE);
    783 			(void) add_shot(LAVA, y, x, LEFTS, volcano,
    784 				NULL, true, SPACE);
    785 			for (np = Player; np < End_player; np++)
    786 				message(np, "Volcano eruption.");
    787 			volcano = 0;
    788 		}
    789 #endif
    790 
    791 #ifdef DRONE
    792 		if (rand_num(100) < 2) {
    793 			do {
    794 				x = rand_num(WIDTH / 2) + WIDTH / 4;
    795 				y = rand_num(HEIGHT / 2) + HEIGHT / 4;
    796 			} while (Maze[y][x] != SPACE);
    797 			add_shot(DSHOT, y, x, rand_dir(),
    798 				shot_req[MINDSHOT +
    799 				rand_num(MAXBOMB - MINDSHOT)],
    800 				NULL, false, SPACE);
    801 		}
    802 #endif
    803 
    804 		sendcom(pp, ENDWIN);
    805 		(void) putc(' ', pp->p_output);
    806 		(void) fclose(pp->p_output);
    807 
    808 		End_player--;
    809 		if (pp != End_player) {
    810 			memcpy(pp, End_player, sizeof (PLAYER));
    811 			fdset[i] = fdset[End_player - Player + 3];
    812 			fdset[End_player - Player + 3].fd = -1;
    813 			(void) snprintf(Buf, sizeof(Buf), "%5.2f%c%-10.10s %c",
    814 				pp->p_ident->i_score, stat_char(pp),
    815 				pp->p_ident->i_name, pp->p_ident->i_team);
    816 			n = STAT_PLAY_ROW + 1 + (pp - Player);
    817 			for (np = Player; np < End_player; np++) {
    818 				cgoto(np, n, STAT_NAME_COL);
    819 				outstr(np, Buf, STAT_NAME_LEN);
    820 			}
    821 #ifdef MONITOR
    822 			for (np = Monitor; np < End_monitor; np++) {
    823 				cgoto(np, n, STAT_NAME_COL);
    824 				outstr(np, Buf, STAT_NAME_LEN);
    825 			}
    826 #endif
    827 		} else
    828 			fdset[i].fd = -1;
    829 
    830 		/* Erase the last player */
    831 		n = STAT_PLAY_ROW + 1 + Nplayer;
    832 		for (np = Player; np < End_player; np++) {
    833 			cgoto(np, n, STAT_NAME_COL);
    834 			ce(np);
    835 		}
    836 #ifdef MONITOR
    837 		for (np = Monitor; np < End_monitor; np++) {
    838 			cgoto(np, n, STAT_NAME_COL);
    839 			ce(np);
    840 		}
    841 	}
    842 	else {
    843 		sendcom(pp, ENDWIN);
    844 		(void) putc(LAST_PLAYER, pp->p_output);
    845 		(void) fclose(pp->p_output);
    846 
    847 		End_monitor--;
    848 		if (pp != End_monitor) {
    849 			memcpy(pp, End_monitor, sizeof (PLAYER));
    850 			fdset[i] = fdset[End_monitor - Monitor + MAXPL + 3];
    851 			fdset[End_monitor - Monitor + MAXPL + 3].fd = -1;
    852 			(void) snprintf(Buf, sizeof(Buf), "%5.5s %-10.10s %c",
    853 				" ",
    854 				pp->p_ident->i_name, pp->p_ident->i_team);
    855 			n = STAT_MON_ROW + 1 + (pp - Player);
    856 			for (np = Player; np < End_player; np++) {
    857 				cgoto(np, n, STAT_NAME_COL);
    858 				outstr(np, Buf, STAT_NAME_LEN);
    859 			}
    860 			for (np = Monitor; np < End_monitor; np++) {
    861 				cgoto(np, n, STAT_NAME_COL);
    862 				outstr(np, Buf, STAT_NAME_LEN);
    863 			}
    864 		} else
    865 			fdset[i].fd = -1;
    866 
    867 		/* Erase the last monitor */
    868 		n = STAT_MON_ROW + 1 + (End_monitor - Monitor);
    869 		for (np = Player; np < End_player; np++) {
    870 			cgoto(np, n, STAT_NAME_COL);
    871 			ce(np);
    872 		}
    873 		for (np = Monitor; np < End_monitor; np++) {
    874 			cgoto(np, n, STAT_NAME_COL);
    875 			ce(np);
    876 		}
    877 	}
    878 #endif
    879 }
    880 
    881 /*
    882  * rand_num:
    883  *	Return a random number in a given range.
    884  */
    885 int
    886 rand_num(int range)
    887 {
    888 	return (range == 0 ? 0 : random() % range);
    889 }
    890 
    891 /*
    892  * havechar:
    893  *	Check to see if we have any characters in the input queue; if
    894  *	we do, read them, stash them away, and return true; else return
    895  *	false.
    896  */
    897 static bool
    898 havechar(PLAYER *pp, int i)
    899 {
    900 
    901 	if (pp->p_ncount < pp->p_nchar)
    902 		return true;
    903 	if (!(fdset[i].revents & POLLIN))
    904 		return false;
    905 check_again:
    906 	pp->p_nchar = read(pp->p_fd, pp->p_cbuf, sizeof pp->p_cbuf);
    907 	if (pp->p_nchar < 0 && errno == EINTR) {
    908 		goto check_again;
    909 	} else if (pp->p_nchar <= 0) {
    910 		if (errno == EINTR)
    911 		pp->p_cbuf[0] = 'q';
    912 	}
    913 	pp->p_ncount = 0;
    914 	return true;
    915 }
    916 
    917 /*
    918  * cleanup:
    919  *	Exit with the given value, cleaning up any droppings lying around
    920  */
    921 void
    922 cleanup(int exitval)
    923 {
    924 	PLAYER *pp;
    925 
    926 	for (pp = Player; pp < End_player; pp++) {
    927 		cgoto(pp, HEIGHT, 0);
    928 		sendcom(pp, ENDWIN);
    929 		(void) putc(LAST_PLAYER, pp->p_output);
    930 		(void) fclose(pp->p_output);
    931 	}
    932 #ifdef MONITOR
    933 	for (pp = Monitor; pp < End_monitor; pp++) {
    934 		cgoto(pp, HEIGHT, 0);
    935 		sendcom(pp, ENDWIN);
    936 		(void) putc(LAST_PLAYER, pp->p_output);
    937 		(void) fclose(pp->p_output);
    938 	}
    939 #endif
    940 	(void) close(huntsock);
    941 #ifdef AF_UNIX_HACK
    942 	(void) unlink(huntsockpath);
    943 #endif
    944 
    945 	exit(exitval);
    946 }
    947 
    948 /*
    949  * send_stats:
    950  *	Print stats to requestor
    951  */
    952 static void
    953 send_stats(void)
    954 {
    955 	IDENT *ip;
    956 	FILE *fp;
    957 	int s;
    958 	struct sockaddr_storage newaddr;
    959 	socklen_t socklen;
    960 
    961 	/*
    962 	 * Get the output stream ready
    963 	 */
    964 	socklen = sizeof(newaddr);
    965 	s = accept(statsock, (struct sockaddr *)&newaddr, &socklen);
    966 	if (s < 0) {
    967 		if (errno == EINTR)
    968 			return;
    969 		complain(LOG_WARNING, "accept");
    970 		return;
    971 	}
    972 	fp = fdopen(s, "w");
    973 	if (fp == NULL) {
    974 		complain(LOG_WARNING, "fdopen");
    975 		(void) close(s);
    976 		return;
    977 	}
    978 
    979 	/*
    980 	 * Send output to requestor
    981 	 */
    982 	fputs("Name\t\tScore\tDucked\tAbsorb\tFaced\tShot\tRobbed\tMissed\tSlimeK\n", fp);
    983 	for (ip = Scores; ip != NULL; ip = ip->i_next) {
    984 		fprintf(fp, "%s\t", ip->i_name);
    985 		if (strlen(ip->i_name) < 8)
    986 			putc('\t', fp);
    987 		fprintf(fp, "%.2f\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
    988 			ip->i_score, ip->i_ducked, ip->i_absorbed,
    989 			ip->i_faced, ip->i_shot, ip->i_robbed,
    990 			ip->i_missed, ip->i_slime);
    991 	}
    992 	fputs("\n\nName\t\tEnemy\tFriend\tDeaths\tStill\tSaved\n", fp);
    993 	for (ip = Scores; ip != NULL; ip = ip->i_next) {
    994 		if (ip->i_team == ' ') {
    995 			fprintf(fp, "%s\t", ip->i_name);
    996 			if (strlen(ip->i_name) < 8)
    997 				putc('\t', fp);
    998 		}
    999 		else {
   1000 			fprintf(fp, "%s[%c]\t", ip->i_name, ip->i_team);
   1001 			if (strlen(ip->i_name) + 3 < 8)
   1002 				putc('\t', fp);
   1003 		}
   1004 		fprintf(fp, "%d\t%d\t%d\t%d\t%d\n",
   1005 			ip->i_gkills, ip->i_bkills, ip->i_deaths,
   1006 			ip->i_stillb, ip->i_saved);
   1007 	}
   1008 
   1009 	(void) fclose(fp);
   1010 }
   1011 
   1012 /*
   1013  * clear_scores:
   1014  *	Clear out the scores so the next session start clean
   1015  */
   1016 static void
   1017 clear_scores(void)
   1018 {
   1019 	IDENT *ip, *nextip;
   1020 
   1021 	for (ip = Scores; ip != NULL; ip = nextip) {
   1022 		nextip = ip->i_next;
   1023 		(void) free(ip);
   1024 	}
   1025 	Scores = NULL;
   1026 }
   1027