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