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