Home | History | Annotate | Line # | Download | only in huntd
driver.c revision 1.27
      1 /*	$NetBSD: driver.c,v 1.27 2014/03/29 20:35:30 dholland 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.27 2014/03/29 20:35:30 dholland Exp $");
     36 #endif /* not lint */
     37 
     38 #include <sys/ioctl.h>
     39 #include <sys/stat.h>
     40 #include <sys/time.h>
     41 #include <err.h>
     42 #include <errno.h>
     43 #include <signal.h>
     44 #include <stdlib.h>
     45 #include <unistd.h>
     46 #include"hunt.h"
     47 
     48 
     49 static SOCKET Daemon;
     50 
     51 #ifdef INTERNET
     52 static int Test_socket;			/* test socket to answer datagrams */
     53 static bool inetd_spawned;		/* invoked via inetd */
     54 static bool standard_port = true;	/* true if listening on standard port */
     55 static u_short	sock_port;		/* port # of tcp listen socket */
     56 static u_short	stat_port;		/* port # of statistics tcp socket */
     57 #define DAEMON_SIZE	(sizeof Daemon)
     58 #else
     59 #define DAEMON_SIZE	(sizeof Daemon - 1)
     60 #endif
     61 
     62 static void clear_scores(void);
     63 static bool havechar(PLAYER *, int);
     64 static void init(void);
     65 static void makeboots(void);
     66 static void send_stats(void);
     67 static void zap(PLAYER *, bool, int);
     68 
     69 
     70 /*
     71  * main:
     72  *	The main program.
     73  */
     74 int
     75 main(int ac, char **av)
     76 {
     77 	PLAYER *pp;
     78 #ifdef INTERNET
     79 	u_short msg;
     80 	short reply;
     81 	socklen_t namelen;
     82 	SOCKET test;
     83 #endif
     84 	static bool first = true;
     85 	static bool server = false;
     86 	int c, i;
     87 	const int linger = 90 * 1000;
     88 
     89 	while ((c = getopt(ac, av, "sp:")) != -1) {
     90 		switch (c) {
     91 		  case 's':
     92 			server = true;
     93 			break;
     94 #ifdef INTERNET
     95 		  case 'p':
     96 			standard_port = false;
     97 			Test_port = atoi(optarg);
     98 			break;
     99 #endif
    100 		  default:
    101 erred:
    102 			fprintf(stderr, "Usage: %s [-s] [-p port]\n", av[0]);
    103 			exit(1);
    104 		}
    105 	}
    106 	if (optind < ac)
    107 		goto erred;
    108 
    109 	init();
    110 
    111 
    112 again:
    113 	do {
    114 		errno = 0;
    115 		while (poll(fdset, 3+MAXPL+MAXMON, INFTIM) < 0)
    116 		{
    117 			if (errno != EINTR)
    118 #ifdef LOG
    119 				syslog(LOG_WARNING, "poll: %m");
    120 #else
    121 				warn("poll");
    122 #endif
    123 			errno = 0;
    124 		}
    125 #ifdef INTERNET
    126 		if (fdset[2].revents & POLLIN) {
    127 			namelen = DAEMON_SIZE;
    128 			(void) recvfrom(Test_socket, &msg, sizeof msg,
    129 				0, (struct sockaddr *) &test, &namelen);
    130 			switch (ntohs(msg)) {
    131 			  case C_MESSAGE:
    132 				if (Nplayer <= 0)
    133 					break;
    134 				reply = htons((u_short) Nplayer);
    135 				(void) sendto(Test_socket, &reply,
    136 					sizeof reply, 0,
    137 					(struct sockaddr *) &test, DAEMON_SIZE);
    138 				break;
    139 			  case C_SCORES:
    140 				reply = htons(stat_port);
    141 				(void) sendto(Test_socket, &reply,
    142 					sizeof reply, 0,
    143 					(struct sockaddr *) &test, DAEMON_SIZE);
    144 				break;
    145 			  case C_PLAYER:
    146 			  case C_MONITOR:
    147 				if (msg == C_MONITOR && Nplayer <= 0)
    148 					break;
    149 				reply = htons(sock_port);
    150 				(void) sendto(Test_socket, &reply,
    151 					sizeof reply, 0,
    152 					(struct sockaddr *) &test, DAEMON_SIZE);
    153 				break;
    154 			}
    155 		}
    156 #endif
    157 		{
    158 			for (pp = Player, i = 0; pp < End_player; pp++, i++)
    159 				if (havechar(pp, i + 3)) {
    160 					execute(pp);
    161 					pp->p_nexec++;
    162 				}
    163 #ifdef MONITOR
    164 			for (pp = Monitor, i = 0; pp < End_monitor; pp++, i++)
    165 				if (havechar(pp, i + MAXPL + 3)) {
    166 					mon_execute(pp);
    167 					pp->p_nexec++;
    168 				}
    169 #endif
    170 			moveshots();
    171 			for (pp = Player, i = 0; pp < End_player; )
    172 				if (pp->p_death[0] != '\0')
    173 					zap(pp, true, i + 3);
    174 				else
    175 					pp++, i++;
    176 #ifdef MONITOR
    177 			for (pp = Monitor, i = 0; pp < End_monitor; )
    178 				if (pp->p_death[0] != '\0')
    179 					zap(pp, false, i + MAXPL + 3);
    180 				else
    181 					pp++, i++;
    182 #endif
    183 		}
    184 		if (fdset[0].revents & POLLIN)
    185 			if (answer()) {
    186 				if (first) {
    187 					/* announce start of game? */
    188 				}
    189 				first = false;
    190 			}
    191 		if (fdset[1].revents & POLLIN)
    192 			send_stats();
    193 		for (pp = Player, i = 0; pp < End_player; pp++, i++) {
    194 			if (fdset[i + 3].revents & POLLIN)
    195 				sendcom(pp, READY, pp->p_nexec);
    196 			pp->p_nexec = 0;
    197 			(void) fflush(pp->p_output);
    198 		}
    199 #ifdef MONITOR
    200 		for (pp = Monitor, i = 0; pp < End_monitor; pp++, i++) {
    201 			if (fdset[i + MAXPL + 3].revents & POLLIN)
    202 				sendcom(pp, READY, pp->p_nexec);
    203 			pp->p_nexec = 0;
    204 			(void) fflush(pp->p_output);
    205 		}
    206 #endif
    207 	} while (Nplayer > 0);
    208 
    209 	if (poll(fdset, 3+MAXPL+MAXMON, linger) > 0) {
    210 		goto again;
    211 	}
    212 	if (server) {
    213 		clear_scores();
    214 		makemaze();
    215 		clearwalls();
    216 #ifdef BOOTS
    217 		makeboots();
    218 #endif
    219 		first = true;
    220 		goto again;
    221 	}
    222 
    223 #ifdef MONITOR
    224 	for (pp = Monitor, i = 0; pp < End_monitor; i++)
    225 		zap(pp, false, i + MAXPL + 3);
    226 #endif
    227 	cleanup(0);
    228 	/* NOTREACHED */
    229 	return(0);
    230 }
    231 
    232 /*
    233  * init:
    234  *	Initialize the global parameters.
    235  */
    236 static void
    237 init(void)
    238 {
    239 	int i;
    240 #ifdef INTERNET
    241 	SOCKET test_port;
    242 	int msg;
    243 	socklen_t len;
    244 #endif
    245 
    246 #ifndef DEBUG
    247 #ifdef TIOCNOTTY
    248 	(void) ioctl(fileno(stdout), TIOCNOTTY, NULL);
    249 #endif
    250 	(void) setpgrp(getpid(), getpid());
    251 	(void) signal(SIGHUP, SIG_IGN);
    252 	(void) signal(SIGINT, SIG_IGN);
    253 	(void) signal(SIGQUIT, SIG_IGN);
    254 	(void) signal(SIGTERM, cleanup);
    255 #endif
    256 
    257 	(void) chdir("/var/tmp");	/* just in case it core dumps */
    258 	(void) umask(0);		/* No privacy at all! */
    259 	(void) signal(SIGPIPE, SIG_IGN);
    260 
    261 #ifdef LOG
    262 	openlog("huntd", LOG_PID, LOG_DAEMON);
    263 #endif
    264 
    265 	/*
    266 	 * Initialize statistics socket
    267 	 */
    268 #ifdef INTERNET
    269 	Daemon.sin_family = SOCK_FAMILY;
    270 	Daemon.sin_addr.s_addr = INADDR_ANY;
    271 	Daemon.sin_port = 0;
    272 #else
    273 	Daemon.sun_family = SOCK_FAMILY;
    274 	(void) strcpy(Daemon.sun_path, Stat_name);
    275 #endif
    276 
    277 	Status = socket(SOCK_FAMILY, SOCK_STREAM, 0);
    278 	if (bind(Status, (struct sockaddr *) &Daemon, DAEMON_SIZE) < 0) {
    279 		if (errno == EADDRINUSE)
    280 			exit(0);
    281 		else {
    282 #ifdef LOG
    283 			syslog(LOG_ERR, "bind: %m");
    284 #else
    285 			warn("bind");
    286 #endif
    287 			cleanup(1);
    288 		}
    289 	}
    290 	(void) listen(Status, 5);
    291 
    292 #ifdef INTERNET
    293 	len = sizeof (SOCKET);
    294 	if (getsockname(Status, (struct sockaddr *) &Daemon, &len) < 0)  {
    295 #ifdef LOG
    296 		syslog(LOG_ERR, "getsockname: %m");
    297 #else
    298 		warn("getsockname");
    299 #endif
    300 		exit(1);
    301 	}
    302 	stat_port = ntohs(Daemon.sin_port);
    303 #endif
    304 
    305 	/*
    306 	 * Initialize main socket
    307 	 */
    308 #ifdef INTERNET
    309 	Daemon.sin_family = SOCK_FAMILY;
    310 	Daemon.sin_addr.s_addr = INADDR_ANY;
    311 	Daemon.sin_port = 0;
    312 #else
    313 	Daemon.sun_family = SOCK_FAMILY;
    314 	(void) strcpy(Daemon.sun_path, Sock_name);
    315 #endif
    316 
    317 	Socket = socket(SOCK_FAMILY, SOCK_STREAM, 0);
    318 #if defined(INTERNET)
    319 	msg = 1;
    320 	if (setsockopt(Socket, SOL_SOCKET, SO_USELOOPBACK, &msg, sizeof msg)<0)
    321 #ifdef LOG
    322 		syslog(LOG_WARNING, "setsockopt loopback %m");
    323 #else
    324 		warn("setsockopt loopback");
    325 #endif
    326 #endif
    327 	if (bind(Socket, (struct sockaddr *) &Daemon, DAEMON_SIZE) < 0) {
    328 		if (errno == EADDRINUSE)
    329 			exit(0);
    330 		else {
    331 #ifdef LOG
    332 			syslog(LOG_ERR, "bind: %m");
    333 #else
    334 			warn("bind");
    335 #endif
    336 			cleanup(1);
    337 		}
    338 	}
    339 	(void) listen(Socket, 5);
    340 
    341 #ifdef INTERNET
    342 	len = sizeof (SOCKET);
    343 	if (getsockname(Socket, (struct sockaddr *) &Daemon, &len) < 0)  {
    344 #ifdef LOG
    345 		syslog(LOG_ERR, "getsockname: %m");
    346 #else
    347 		warn("getsockname");
    348 #endif
    349 		exit(1);
    350 	}
    351 	sock_port = ntohs(Daemon.sin_port);
    352 #endif
    353 
    354 	/*
    355 	 * Initialize minimal poll mask
    356 	 */
    357 	fdset[0].fd = Socket;
    358 	fdset[0].events = POLLIN;
    359 	fdset[1].fd = Status;
    360 	fdset[1].events = POLLIN;
    361 
    362 #ifdef INTERNET
    363 	len = sizeof (SOCKET);
    364 	if (getsockname(0, (struct sockaddr *) &test_port, &len) >= 0
    365 	&& test_port.sin_family == AF_INET) {
    366 		inetd_spawned = true;
    367 		Test_socket = 0;
    368 		if (test_port.sin_port != htons((u_short) Test_port)) {
    369 			standard_port = false;
    370 			Test_port = ntohs(test_port.sin_port);
    371 		}
    372 	} else {
    373 		test_port = Daemon;
    374 		test_port.sin_port = htons((u_short) Test_port);
    375 
    376 		Test_socket = socket(SOCK_FAMILY, SOCK_DGRAM, 0);
    377 		if (bind(Test_socket, (struct sockaddr *) &test_port,
    378 		    DAEMON_SIZE) < 0) {
    379 #ifdef LOG
    380 			syslog(LOG_ERR, "bind: %m");
    381 #else
    382 			warn("bind");
    383 #endif
    384 			exit(1);
    385 		}
    386 		(void) listen(Test_socket, 5);
    387 	}
    388 
    389 	fdset[2].fd = Test_socket;
    390 	fdset[2].events = POLLIN;
    391 #else
    392 	fdset[2].fd = -1;
    393 #endif
    394 
    395 	srandom(time(NULL));
    396 	makemaze();
    397 #ifdef BOOTS
    398 	makeboots();
    399 #endif
    400 
    401 	for (i = 0; i < NASCII; i++)
    402 		See_over[i] = true;
    403 	See_over[DOOR] = false;
    404 	See_over[WALL1] = false;
    405 	See_over[WALL2] = false;
    406 	See_over[WALL3] = false;
    407 #ifdef REFLECT
    408 	See_over[WALL4] = false;
    409 	See_over[WALL5] = false;
    410 #endif
    411 
    412 }
    413 
    414 #ifdef BOOTS
    415 /*
    416  * makeboots:
    417  *	Put the boots in the maze
    418  */
    419 static void
    420 makeboots(void)
    421 {
    422 	int x, y;
    423 	PLAYER *pp;
    424 
    425 	do {
    426 		x = rand_num(WIDTH - 1) + 1;
    427 		y = rand_num(HEIGHT - 1) + 1;
    428 	} while (Maze[y][x] != SPACE);
    429 	Maze[y][x] = BOOT_PAIR;
    430 	for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
    431 		pp->p_flying = -1;
    432 }
    433 #endif
    434 
    435 
    436 /*
    437  * checkdam:
    438  *	Check the damage to the given player, and see if s/he is killed
    439  */
    440 void
    441 checkdam(PLAYER *ouch, PLAYER *gotcha, IDENT *credit, int amt,
    442 	 char this_shot_type)
    443 {
    444 	const char *cp;
    445 
    446 	if (ouch->p_death[0] != '\0')
    447 		return;
    448 #ifdef BOOTS
    449 	if (this_shot_type == SLIME)
    450 		switch (ouch->p_nboots) {
    451 		  default:
    452 			break;
    453 		  case 1:
    454 			amt = (amt + 1) / 2;
    455 			break;
    456 		  case 2:
    457 			if (gotcha != NULL)
    458 				message(gotcha, "He has boots on!");
    459 			return;
    460 		}
    461 #endif
    462 	ouch->p_damage += amt;
    463 	if (ouch->p_damage <= ouch->p_damcap) {
    464 		(void) snprintf(Buf, sizeof(Buf), "%2d", ouch->p_damage);
    465 		cgoto(ouch, STAT_DAM_ROW, STAT_VALUE_COL);
    466 		outstr(ouch, Buf, 2);
    467 		return;
    468 	}
    469 
    470 	/* Someone DIED */
    471 	switch (this_shot_type) {
    472 	  default:
    473 		cp = "Killed";
    474 		break;
    475 #ifdef FLY
    476 	  case FALL:
    477 		cp = "Killed on impact";
    478 		break;
    479 #endif
    480 	  case KNIFE:
    481 		cp = "Stabbed to death";
    482 		ouch->p_ammo = 0;		/* No exploding */
    483 		break;
    484 	  case SHOT:
    485 		cp = "Shot to death";
    486 		break;
    487 	  case GRENADE:
    488 	  case SATCHEL:
    489 	  case BOMB:
    490 		cp = "Bombed";
    491 		break;
    492 	  case MINE:
    493 	  case GMINE:
    494 		cp = "Blown apart";
    495 		break;
    496 #ifdef	OOZE
    497 	  case SLIME:
    498 		cp = "Slimed";
    499 		if (credit != NULL)
    500 			credit->i_slime++;
    501 		break;
    502 #endif
    503 #ifdef	VOLCANO
    504 	  case LAVA:
    505 		cp = "Baked";
    506 		break;
    507 #endif
    508 #ifdef DRONE
    509 	  case DSHOT:
    510 		cp = "Eliminated";
    511 		break;
    512 #endif
    513 	}
    514 	if (credit == NULL) {
    515 		(void) snprintf(ouch->p_death, sizeof(ouch->p_death),
    516 			"| %s by %s |", cp,
    517 			(this_shot_type == MINE || this_shot_type == GMINE) ?
    518 			"a mine" : "act of God");
    519 		return;
    520 	}
    521 
    522 	(void) snprintf(ouch->p_death, sizeof(ouch->p_death),
    523 		"| %s by %s |", cp, credit->i_name);
    524 
    525 	if (ouch == gotcha) {		/* No use killing yourself */
    526 		credit->i_kills--;
    527 		credit->i_bkills++;
    528 	}
    529 	else if (ouch->p_ident->i_team == ' '
    530 	|| ouch->p_ident->i_team != credit->i_team) {
    531 		credit->i_kills++;
    532 		credit->i_gkills++;
    533 	}
    534 	else {
    535 		credit->i_kills--;
    536 		credit->i_bkills++;
    537 	}
    538 	credit->i_score = credit->i_kills / (double) credit->i_entries;
    539 	ouch->p_ident->i_deaths++;
    540 	if (ouch->p_nchar == 0)
    541 		ouch->p_ident->i_stillb++;
    542 	if (gotcha == NULL)
    543 		return;
    544 	gotcha->p_damcap += STABDAM;
    545 	gotcha->p_damage -= STABDAM;
    546 	if (gotcha->p_damage < 0)
    547 		gotcha->p_damage = 0;
    548 	(void) snprintf(Buf, sizeof(Buf), "%2d/%2d", gotcha->p_damage,
    549 			gotcha->p_damcap);
    550 	cgoto(gotcha, STAT_DAM_ROW, STAT_VALUE_COL);
    551 	outstr(gotcha, Buf, 5);
    552 	(void) snprintf(Buf, sizeof(Buf), "%3d",
    553 			(gotcha->p_damcap - MAXDAM) / 2);
    554 	cgoto(gotcha, STAT_KILL_ROW, STAT_VALUE_COL);
    555 	outstr(gotcha, Buf, 3);
    556 	(void) snprintf(Buf, sizeof(Buf), "%5.2f", gotcha->p_ident->i_score);
    557 	for (ouch = Player; ouch < End_player; ouch++) {
    558 		cgoto(ouch, STAT_PLAY_ROW + 1 + (gotcha - Player),
    559 			STAT_NAME_COL);
    560 		outstr(ouch, Buf, 5);
    561 	}
    562 #ifdef MONITOR
    563 	for (ouch = Monitor; ouch < End_monitor; ouch++) {
    564 		cgoto(ouch, STAT_PLAY_ROW + 1 + (gotcha - Player),
    565 			STAT_NAME_COL);
    566 		outstr(ouch, Buf, 5);
    567 	}
    568 #endif
    569 }
    570 
    571 /*
    572  * zap:
    573  *	Kill off a player and take him out of the game.
    574  */
    575 static void
    576 zap(PLAYER *pp, bool was_player, int i)
    577 {
    578 	int n, len;
    579 	BULLET *bp;
    580 	PLAYER *np;
    581 	int x, y;
    582 
    583 	if (was_player) {
    584 		if (pp->p_undershot)
    585 			fixshots(pp->p_y, pp->p_x, pp->p_over);
    586 		drawplayer(pp, false);
    587 		Nplayer--;
    588 	}
    589 
    590 	len = strlen(pp->p_death);	/* Display the cause of death */
    591 	x = (WIDTH - len) / 2;
    592 	cgoto(pp, HEIGHT / 2, x);
    593 	outstr(pp, pp->p_death, len);
    594 	for (n = 1; n < len; n++)
    595 		pp->p_death[n] = '-';
    596 	pp->p_death[0] = '+';
    597 	pp->p_death[len - 1] = '+';
    598 	cgoto(pp, HEIGHT / 2 - 1, x);
    599 	outstr(pp, pp->p_death, len);
    600 	cgoto(pp, HEIGHT / 2 + 1, x);
    601 	outstr(pp, pp->p_death, len);
    602 	cgoto(pp, HEIGHT, 0);
    603 
    604 #ifdef MONITOR
    605 	if (was_player) {
    606 #endif
    607 		for (bp = Bullets; bp != NULL; bp = bp->b_next) {
    608 			if (bp->b_owner == pp)
    609 				bp->b_owner = NULL;
    610 			if (bp->b_x == pp->p_x && bp->b_y == pp->p_y)
    611 				bp->b_over = SPACE;
    612 		}
    613 
    614 		n = rand_num(pp->p_ammo);
    615 		x = rand_num(pp->p_ammo);
    616 		if (x > n)
    617 			n = x;
    618 		if (pp->p_ammo == 0)
    619 			x = 0;
    620 		else if (n == pp->p_ammo - 1) {
    621 			x = pp->p_ammo;
    622 			len = SLIME;
    623 		}
    624 		else {
    625 			for (x = MAXBOMB - 1; x > 0; x--)
    626 				if (n >= shot_req[x])
    627 					break;
    628 			for (y = MAXSLIME - 1; y > 0; y--)
    629 				if (n >= slime_req[y])
    630 					break;
    631 			if (y >= 0 && slime_req[y] > shot_req[x]) {
    632 				x = slime_req[y];
    633 				len = SLIME;
    634 			}
    635 			else if (x != 0) {
    636 				len = shot_type[x];
    637 				x = shot_req[x];
    638 			}
    639 		}
    640 		if (x > 0) {
    641 			(void) add_shot(len, pp->p_y, pp->p_x, pp->p_face, x,
    642 				NULL, true, SPACE);
    643 			(void) snprintf(Buf, sizeof(Buf), "%s detonated.",
    644 				pp->p_ident->i_name);
    645 			for (np = Player; np < End_player; np++)
    646 				message(np, Buf);
    647 #ifdef MONITOR
    648 			for (np = Monitor; np < End_monitor; np++)
    649 				message(np, Buf);
    650 #endif
    651 #ifdef BOOTS
    652 			while (pp->p_nboots-- > 0) {
    653 				for (np = Boot; np < &Boot[NBOOTS]; np++)
    654 					if (np->p_flying < 0)
    655 						break;
    656 				if (np >= &Boot[NBOOTS])
    657 					err(1, "Too many boots");
    658 				np->p_undershot = false;
    659 				np->p_x = pp->p_x;
    660 				np->p_y = pp->p_y;
    661 				np->p_flying = rand_num(20);
    662 				np->p_flyx = 2 * rand_num(6) - 5;
    663 				np->p_flyy = 2 * rand_num(6) - 5;
    664 				np->p_over = SPACE;
    665 				np->p_face = BOOT;
    666 				showexpl(np->p_y, np->p_x, BOOT);
    667 			}
    668 #endif
    669 		}
    670 #ifdef BOOTS
    671 		else if (pp->p_nboots > 0) {
    672 			if (pp->p_nboots == 2)
    673 				Maze[pp->p_y][pp->p_x] = BOOT_PAIR;
    674 			else
    675 				Maze[pp->p_y][pp->p_x] = BOOT;
    676 			if (pp->p_undershot)
    677 				fixshots(pp->p_y, pp->p_x,
    678 					Maze[pp->p_y][pp->p_x]);
    679 		}
    680 #endif
    681 
    682 #ifdef VOLCANO
    683 		volcano += pp->p_ammo - x;
    684 		if (rand_num(100) < volcano / 50) {
    685 			do {
    686 				x = rand_num(WIDTH / 2) + WIDTH / 4;
    687 				y = rand_num(HEIGHT / 2) + HEIGHT / 4;
    688 			} while (Maze[y][x] != SPACE);
    689 			(void) add_shot(LAVA, y, x, LEFTS, volcano,
    690 				NULL, true, SPACE);
    691 			for (np = Player; np < End_player; np++)
    692 				message(np, "Volcano eruption.");
    693 			volcano = 0;
    694 		}
    695 #endif
    696 
    697 #ifdef DRONE
    698 		if (rand_num(100) < 2) {
    699 			do {
    700 				x = rand_num(WIDTH / 2) + WIDTH / 4;
    701 				y = rand_num(HEIGHT / 2) + HEIGHT / 4;
    702 			} while (Maze[y][x] != SPACE);
    703 			add_shot(DSHOT, y, x, rand_dir(),
    704 				shot_req[MINDSHOT +
    705 				rand_num(MAXBOMB - MINDSHOT)],
    706 				NULL, false, SPACE);
    707 		}
    708 #endif
    709 
    710 		sendcom(pp, ENDWIN);
    711 		(void) putc(' ', pp->p_output);
    712 		(void) fclose(pp->p_output);
    713 
    714 		End_player--;
    715 		if (pp != End_player) {
    716 			memcpy(pp, End_player, sizeof (PLAYER));
    717 			fdset[i] = fdset[End_player - Player + 3];
    718 			fdset[End_player - Player + 3].fd = -1;
    719 			(void) snprintf(Buf, sizeof(Buf), "%5.2f%c%-10.10s %c",
    720 				pp->p_ident->i_score, stat_char(pp),
    721 				pp->p_ident->i_name, pp->p_ident->i_team);
    722 			n = STAT_PLAY_ROW + 1 + (pp - Player);
    723 			for (np = Player; np < End_player; np++) {
    724 				cgoto(np, n, STAT_NAME_COL);
    725 				outstr(np, Buf, STAT_NAME_LEN);
    726 			}
    727 #ifdef MONITOR
    728 			for (np = Monitor; np < End_monitor; np++) {
    729 				cgoto(np, n, STAT_NAME_COL);
    730 				outstr(np, Buf, STAT_NAME_LEN);
    731 			}
    732 #endif
    733 		} else
    734 			fdset[i].fd = -1;
    735 
    736 		/* Erase the last player */
    737 		n = STAT_PLAY_ROW + 1 + Nplayer;
    738 		for (np = Player; np < End_player; np++) {
    739 			cgoto(np, n, STAT_NAME_COL);
    740 			ce(np);
    741 		}
    742 #ifdef MONITOR
    743 		for (np = Monitor; np < End_monitor; np++) {
    744 			cgoto(np, n, STAT_NAME_COL);
    745 			ce(np);
    746 		}
    747 	}
    748 	else {
    749 		sendcom(pp, ENDWIN);
    750 		(void) putc(LAST_PLAYER, pp->p_output);
    751 		(void) fclose(pp->p_output);
    752 
    753 		End_monitor--;
    754 		if (pp != End_monitor) {
    755 			memcpy(pp, End_monitor, sizeof (PLAYER));
    756 			fdset[i] = fdset[End_monitor - Monitor + MAXPL + 3];
    757 			fdset[End_monitor - Monitor + MAXPL + 3].fd = -1;
    758 			(void) snprintf(Buf, sizeof(Buf), "%5.5s %-10.10s %c",
    759 				" ",
    760 				pp->p_ident->i_name, pp->p_ident->i_team);
    761 			n = STAT_MON_ROW + 1 + (pp - Player);
    762 			for (np = Player; np < End_player; np++) {
    763 				cgoto(np, n, STAT_NAME_COL);
    764 				outstr(np, Buf, STAT_NAME_LEN);
    765 			}
    766 			for (np = Monitor; np < End_monitor; np++) {
    767 				cgoto(np, n, STAT_NAME_COL);
    768 				outstr(np, Buf, STAT_NAME_LEN);
    769 			}
    770 		} else
    771 			fdset[i].fd = -1;
    772 
    773 		/* Erase the last monitor */
    774 		n = STAT_MON_ROW + 1 + (End_monitor - Monitor);
    775 		for (np = Player; np < End_player; np++) {
    776 			cgoto(np, n, STAT_NAME_COL);
    777 			ce(np);
    778 		}
    779 		for (np = Monitor; np < End_monitor; np++) {
    780 			cgoto(np, n, STAT_NAME_COL);
    781 			ce(np);
    782 		}
    783 	}
    784 #endif
    785 }
    786 
    787 /*
    788  * rand_num:
    789  *	Return a random number in a given range.
    790  */
    791 int
    792 rand_num(int range)
    793 {
    794 	return (range == 0 ? 0 : random() % range);
    795 }
    796 
    797 /*
    798  * havechar:
    799  *	Check to see if we have any characters in the input queue; if
    800  *	we do, read them, stash them away, and return true; else return
    801  *	false.
    802  */
    803 static bool
    804 havechar(PLAYER *pp, int i)
    805 {
    806 
    807 	if (pp->p_ncount < pp->p_nchar)
    808 		return true;
    809 	if (!(fdset[i].revents & POLLIN))
    810 		return false;
    811 check_again:
    812 	errno = 0;
    813 	if ((pp->p_nchar = read(pp->p_fd, pp->p_cbuf, sizeof pp->p_cbuf)) <= 0)
    814 	{
    815 		if (errno == EINTR)
    816 			goto check_again;
    817 		pp->p_cbuf[0] = 'q';
    818 	}
    819 	pp->p_ncount = 0;
    820 	return true;
    821 }
    822 
    823 /*
    824  * cleanup:
    825  *	Exit with the given value, cleaning up any droppings lying around
    826  */
    827 void
    828 cleanup(int eval)
    829 {
    830 	PLAYER *pp;
    831 
    832 	for (pp = Player; pp < End_player; pp++) {
    833 		cgoto(pp, HEIGHT, 0);
    834 		sendcom(pp, ENDWIN);
    835 		(void) putc(LAST_PLAYER, pp->p_output);
    836 		(void) fclose(pp->p_output);
    837 	}
    838 #ifdef MONITOR
    839 	for (pp = Monitor; pp < End_monitor; pp++) {
    840 		cgoto(pp, HEIGHT, 0);
    841 		sendcom(pp, ENDWIN);
    842 		(void) putc(LAST_PLAYER, pp->p_output);
    843 		(void) fclose(pp->p_output);
    844 	}
    845 #endif
    846 	(void) close(Socket);
    847 #ifdef AF_UNIX_HACK
    848 	(void) unlink(Sock_name);
    849 #endif
    850 
    851 	exit(eval);
    852 }
    853 
    854 /*
    855  * send_stats:
    856  *	Print stats to requestor
    857  */
    858 static void
    859 send_stats(void)
    860 {
    861 	IDENT *ip;
    862 	FILE *fp;
    863 	int s;
    864 	SOCKET sockstruct;
    865 	socklen_t socklen;
    866 
    867 	/*
    868 	 * Get the output stream ready
    869 	 */
    870 #ifdef INTERNET
    871 	socklen = sizeof sockstruct;
    872 #else
    873 	socklen = sizeof sockstruct - 1;
    874 #endif
    875 	s = accept(Status, (struct sockaddr *) &sockstruct, &socklen);
    876 	if (s < 0) {
    877 		if (errno == EINTR)
    878 			return;
    879 #ifdef LOG
    880 		syslog(LOG_WARNING, "accept: %m");
    881 #else
    882 		warn("accept");
    883 #endif
    884 		return;
    885 	}
    886 	fp = fdopen(s, "w");
    887 	if (fp == NULL) {
    888 #ifdef LOG
    889 		syslog(LOG_WARNING, "fdopen: %m");
    890 #else
    891 		warn("fdopen");
    892 #endif
    893 		(void) close(s);
    894 		return;
    895 	}
    896 
    897 	/*
    898 	 * Send output to requestor
    899 	 */
    900 	fputs("Name\t\tScore\tDucked\tAbsorb\tFaced\tShot\tRobbed\tMissed\tSlimeK\n", fp);
    901 	for (ip = Scores; ip != NULL; ip = ip->i_next) {
    902 		fprintf(fp, "%s\t", ip->i_name);
    903 		if (strlen(ip->i_name) < 8)
    904 			putc('\t', fp);
    905 		fprintf(fp, "%.2f\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
    906 			ip->i_score, ip->i_ducked, ip->i_absorbed,
    907 			ip->i_faced, ip->i_shot, ip->i_robbed,
    908 			ip->i_missed, ip->i_slime);
    909 	}
    910 	fputs("\n\nName\t\tEnemy\tFriend\tDeaths\tStill\tSaved\n", fp);
    911 	for (ip = Scores; ip != NULL; ip = ip->i_next) {
    912 		if (ip->i_team == ' ') {
    913 			fprintf(fp, "%s\t", ip->i_name);
    914 			if (strlen(ip->i_name) < 8)
    915 				putc('\t', fp);
    916 		}
    917 		else {
    918 			fprintf(fp, "%s[%c]\t", ip->i_name, ip->i_team);
    919 			if (strlen(ip->i_name) + 3 < 8)
    920 				putc('\t', fp);
    921 		}
    922 		fprintf(fp, "%d\t%d\t%d\t%d\t%d\n",
    923 			ip->i_gkills, ip->i_bkills, ip->i_deaths,
    924 			ip->i_stillb, ip->i_saved);
    925 	}
    926 
    927 	(void) fclose(fp);
    928 }
    929 
    930 /*
    931  * clear_scores:
    932  *	Clear out the scores so the next session start clean
    933  */
    934 static void
    935 clear_scores(void)
    936 {
    937 	IDENT *ip, *nextip;
    938 
    939 	for (ip = Scores; ip != NULL; ip = nextip) {
    940 		nextip = ip->i_next;
    941 		(void) free(ip);
    942 	}
    943 	Scores = NULL;
    944 }
    945