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