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