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