xref: /netbsd/games/hunt/huntd/driver.c (revision c4a72b64)
1 /*	$NetBSD: driver.c,v 1.8 2002/09/20 20:54:16 mycroft Exp $	*/
2 /*
3  *  Hunt
4  *  Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
5  *  San Francisco, California
6  */
7 
8 #include <sys/cdefs.h>
9 #ifndef lint
10 __RCSID("$NetBSD: driver.c,v 1.8 2002/09/20 20:54:16 mycroft Exp $");
11 #endif /* not lint */
12 
13 # include	<sys/ioctl.h>
14 # include	<sys/stat.h>
15 # include	<sys/time.h>
16 # include	<err.h>
17 # include	<errno.h>
18 # include	<signal.h>
19 # include	<stdlib.h>
20 # include	<unistd.h>
21 # include	"hunt.h"
22 
23 # ifndef pdp11
24 # define	RN	(((Seed = Seed * 11109 + 13849) >> 16) & 0xffff)
25 # else
26 # define	RN	((Seed = Seed * 11109 + 13849) & 0x7fff)
27 # endif
28 
29 int	Seed = 0;
30 
31 
32 SOCKET	Daemon;
33 char	*First_arg;		/* pointer to argv[0] */
34 char	*Last_arg;		/* pointer to end of argv/environ */
35 # ifdef	INTERNET
36 int	Test_socket;		/* test socket to answer datagrams */
37 FLAG	inetd_spawned;		/* invoked via inetd */
38 FLAG	standard_port = TRUE;	/* true if listening on standard port */
39 u_short	sock_port;		/* port # of tcp listen socket */
40 u_short	stat_port;		/* port # of statistics tcp socket */
41 # define	DAEMON_SIZE	(sizeof Daemon)
42 # else
43 # define	DAEMON_SIZE	(sizeof Daemon - 1)
44 # endif
45 
46 static	void	clear_scores __P((void));
47 static	int	havechar __P((PLAYER *, int));
48 static	void	init __P((void));
49 	int	main __P((int, char *[], char *[]));
50 static	void	makeboots __P((void));
51 static	void	send_stats __P((void));
52 static	void	zap __P((PLAYER *, FLAG, int));
53 
54 
55 /*
56  * main:
57  *	The main program.
58  */
59 int
60 main(ac, av, ep)
61 	int	ac;
62 	char	**av, **ep;
63 {
64 	PLAYER	*pp;
65 # ifdef INTERNET
66 	u_short	msg;
67 	short	port_num, reply;
68 	int	namelen;
69 	SOCKET	test;
70 # endif
71 	static FLAG	first = TRUE;
72 	static FLAG	server = FALSE;
73 	int		c, i;
74 	const int	linger = 90 * 1000;
75 
76 	First_arg = av[0];
77 	if (ep == NULL || *ep == NULL)
78 		ep = av + ac;
79 	while (*ep)
80 		ep++;
81 	Last_arg = ep[-1] + strlen(ep[-1]);
82 
83 	while ((c = getopt(ac, av, "sp:")) != -1) {
84 		switch (c) {
85 		  case 's':
86 			server = TRUE;
87 			break;
88 # ifdef INTERNET
89 		  case 'p':
90 			standard_port = FALSE;
91 			Test_port = atoi(optarg);
92 			break;
93 # endif
94 		  default:
95 erred:
96 			fprintf(stderr, "Usage: %s [-s] [-p port]\n", av[0]);
97 			exit(1);
98 		}
99 	}
100 	if (optind < ac)
101 		goto erred;
102 
103 	init();
104 
105 
106 again:
107 	do {
108 		errno = 0;
109 		while (poll(fdset, 3+MAXPL+MAXMON, INFTIM) < 0)
110 		{
111 			if (errno != EINTR)
112 # ifdef LOG
113 				syslog(LOG_WARNING, "select: %m");
114 # else
115 				warn("select");
116 # endif
117 			errno = 0;
118 		}
119 # ifdef INTERNET
120 		if (fdset[2].revents & POLLIN) {
121 			namelen = DAEMON_SIZE;
122 			port_num = htons(sock_port);
123 			(void) recvfrom(Test_socket, (char *) &msg, sizeof msg,
124 				0, (struct sockaddr *) &test, &namelen);
125 			switch (ntohs(msg)) {
126 			  case C_MESSAGE:
127 				if (Nplayer <= 0)
128 					break;
129 				reply = htons((u_short) Nplayer);
130 				(void) sendto(Test_socket, (char *) &reply,
131 					sizeof reply, 0,
132 					(struct sockaddr *) &test, DAEMON_SIZE);
133 				break;
134 			  case C_SCORES:
135 				reply = htons(stat_port);
136 				(void) sendto(Test_socket, (char *) &reply,
137 					sizeof reply, 0,
138 					(struct sockaddr *) &test, DAEMON_SIZE);
139 				break;
140 			  case C_PLAYER:
141 			  case C_MONITOR:
142 				if (msg == C_MONITOR && Nplayer <= 0)
143 					break;
144 				reply = htons(sock_port);
145 				(void) sendto(Test_socket, (char *) &reply,
146 					sizeof reply, 0,
147 					(struct sockaddr *) &test, DAEMON_SIZE);
148 				break;
149 			}
150 		}
151 # endif
152 		{
153 			for (pp = Player, i = 0; pp < End_player; pp++, i++)
154 				if (havechar(pp, i + 3)) {
155 					execute(pp);
156 					pp->p_nexec++;
157 				}
158 # ifdef MONITOR
159 			for (pp = Monitor, i = 0; pp < End_monitor; pp++, i++)
160 				if (havechar(pp, i + MAXPL + 3)) {
161 					mon_execute(pp);
162 					pp->p_nexec++;
163 				}
164 # endif
165 			moveshots();
166 			for (pp = Player, i = 0; pp < End_player; )
167 				if (pp->p_death[0] != '\0')
168 					zap(pp, TRUE, i + 3);
169 				else
170 					pp++, i++;
171 # ifdef MONITOR
172 			for (pp = Monitor, i = 0; pp < End_monitor; )
173 				if (pp->p_death[0] != '\0')
174 					zap(pp, FALSE, i + MAXPL + 3);
175 				else
176 					pp++, i++;
177 # endif
178 		}
179 		if (fdset[0].revents & POLLIN)
180 			if (answer()) {
181 # ifdef INTERNET
182 				if (first && standard_port)
183 					faketalk();
184 # endif
185 				first = FALSE;
186 			}
187 		if (fdset[1].revents & POLLIN)
188 			send_stats();
189 		for (pp = Player, i = 0; pp < End_player; pp++, i++) {
190 			if (fdset[i + 3].revents & POLLIN)
191 				sendcom(pp, READY, pp->p_nexec);
192 			pp->p_nexec = 0;
193 			(void) fflush(pp->p_output);
194 		}
195 # ifdef MONITOR
196 		for (pp = Monitor, i = 0; pp < End_monitor; pp++, i++) {
197 			if (fdset[i + MAXPL + 3].revents & POLLIN)
198 				sendcom(pp, READY, pp->p_nexec);
199 			pp->p_nexec = 0;
200 			(void) fflush(pp->p_output);
201 		}
202 # endif
203 	} while (Nplayer > 0);
204 
205 	if (poll(fdset, 3+MAXPL+MAXMON, linger) > 0) {
206 		goto again;
207 	}
208 	if (server) {
209 		clear_scores();
210 		makemaze();
211 		clearwalls();
212 # ifdef BOOTS
213 		makeboots();
214 # endif
215 		first = TRUE;
216 		goto again;
217 	}
218 
219 # ifdef MONITOR
220 	for (pp = Monitor, i = 0; pp < End_monitor; i++)
221 		zap(pp, FALSE, i + MAXPL + 3);
222 # endif
223 	cleanup(0);
224 	/* NOTREACHED */
225 	return(0);
226 }
227 
228 /*
229  * init:
230  *	Initialize the global parameters.
231  */
232 static void
233 init()
234 {
235 	int	i;
236 # ifdef	INTERNET
237 	SOCKET	test_port;
238 	int	msg;
239 	int	len;
240 # endif
241 
242 # ifndef DEBUG
243 # ifdef TIOCNOTTY
244 	(void) ioctl(fileno(stdout), TIOCNOTTY, NULL);
245 # endif
246 	(void) setpgrp(getpid(), getpid());
247 	(void) signal(SIGHUP, SIG_IGN);
248 	(void) signal(SIGINT, SIG_IGN);
249 	(void) signal(SIGQUIT, SIG_IGN);
250 	(void) signal(SIGTERM, cleanup);
251 # endif
252 
253 	(void) chdir("/var/tmp");	/* just in case it core dumps */
254 	(void) umask(0);		/* No privacy at all! */
255 	(void) signal(SIGPIPE, SIG_IGN);
256 
257 # ifdef LOG
258 # ifdef	SYSLOG_43
259 	openlog("huntd", LOG_PID, LOG_DAEMON);
260 # endif
261 # ifdef	SYSLOG_42
262 	openlog("huntd", LOG_PID);
263 # endif
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 select 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 	Seed = getpid() + time((time_t *) 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()
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(ouch, gotcha, credit, amt, shot_type)
443 	PLAYER	*ouch, *gotcha;
444 	IDENT	*credit;
445 	int	amt;
446 	char	shot_type;
447 {
448 	char	*cp;
449 
450 	if (ouch->p_death[0] != '\0')
451 		return;
452 # ifdef BOOTS
453 	if (shot_type == SLIME)
454 		switch (ouch->p_nboots) {
455 		  default:
456 			break;
457 		  case 1:
458 			amt = (amt + 1) / 2;
459 			break;
460 		  case 2:
461 			if (gotcha != NULL)
462 				message(gotcha, "He has boots on!");
463 			return;
464 		}
465 # endif
466 	ouch->p_damage += amt;
467 	if (ouch->p_damage <= ouch->p_damcap) {
468 		(void) sprintf(Buf, "%2d", ouch->p_damage);
469 		cgoto(ouch, STAT_DAM_ROW, STAT_VALUE_COL);
470 		outstr(ouch, Buf, 2);
471 		return;
472 	}
473 
474 	/* Someone DIED */
475 	switch (shot_type) {
476 	  default:
477 		cp = "Killed";
478 		break;
479 # ifdef FLY
480 	  case FALL:
481 		cp = "Killed on impact";
482 		break;
483 # endif
484 	  case KNIFE:
485 		cp = "Stabbed to death";
486 		ouch->p_ammo = 0;		/* No exploding */
487 		break;
488 	  case SHOT:
489 		cp = "Shot to death";
490 		break;
491 	  case GRENADE:
492 	  case SATCHEL:
493 	  case BOMB:
494 		cp = "Bombed";
495 		break;
496 	  case MINE:
497 	  case GMINE:
498 		cp = "Blown apart";
499 		break;
500 # ifdef	OOZE
501 	  case SLIME:
502 		cp = "Slimed";
503 		if (credit != NULL)
504 			credit->i_slime++;
505 		break;
506 # endif
507 # ifdef	VOLCANO
508 	  case LAVA:
509 		cp = "Baked";
510 		break;
511 # endif
512 # ifdef DRONE
513 	  case DSHOT:
514 		cp = "Eliminated";
515 		break;
516 # endif
517 	}
518 	if (credit == NULL) {
519 		(void) sprintf(ouch->p_death, "| %s by %s |", cp,
520 			(shot_type == MINE || shot_type == GMINE) ?
521 			"a mine" : "act of God");
522 		return;
523 	}
524 
525 	(void) sprintf(ouch->p_death, "| %s by %s |", cp, credit->i_name);
526 
527 	if (ouch == gotcha) {		/* No use killing yourself */
528 		credit->i_kills--;
529 		credit->i_bkills++;
530 	}
531 	else if (ouch->p_ident->i_team == ' '
532 	|| ouch->p_ident->i_team != credit->i_team) {
533 		credit->i_kills++;
534 		credit->i_gkills++;
535 	}
536 	else {
537 		credit->i_kills--;
538 		credit->i_bkills++;
539 	}
540 	credit->i_score = credit->i_kills / (double) credit->i_entries;
541 	ouch->p_ident->i_deaths++;
542 	if (ouch->p_nchar == 0)
543 		ouch->p_ident->i_stillb++;
544 	if (gotcha == NULL)
545 		return;
546 	gotcha->p_damcap += STABDAM;
547 	gotcha->p_damage -= STABDAM;
548 	if (gotcha->p_damage < 0)
549 		gotcha->p_damage = 0;
550 	(void) sprintf(Buf, "%2d/%2d", gotcha->p_damage, gotcha->p_damcap);
551 	cgoto(gotcha, STAT_DAM_ROW, STAT_VALUE_COL);
552 	outstr(gotcha, Buf, 5);
553 	(void) sprintf(Buf, "%3d", (gotcha->p_damcap - MAXDAM) / 2);
554 	cgoto(gotcha, STAT_KILL_ROW, STAT_VALUE_COL);
555 	outstr(gotcha, Buf, 3);
556 	(void) sprintf(Buf, "%5.2f", gotcha->p_ident->i_score);
557 	for (ouch = Player; ouch < End_player; ouch++) {
558 		cgoto(ouch, STAT_PLAY_ROW + 1 + (gotcha - Player),
559 			STAT_NAME_COL);
560 		outstr(ouch, Buf, 5);
561 	}
562 # ifdef MONITOR
563 	for (ouch = Monitor; ouch < End_monitor; ouch++) {
564 		cgoto(ouch, STAT_PLAY_ROW + 1 + (gotcha - Player),
565 			STAT_NAME_COL);
566 		outstr(ouch, Buf, 5);
567 	}
568 # endif
569 }
570 
571 /*
572  * zap:
573  *	Kill off a player and take him out of the game.
574  */
575 static void
576 zap(pp, was_player, i)
577 	PLAYER	*pp;
578 	FLAG	was_player;
579 	int	i;
580 {
581 	int	n, len;
582 	BULLET	*bp;
583 	PLAYER	*np;
584 	int	x, y;
585 	int	savefd;
586 
587 	if (was_player) {
588 		if (pp->p_undershot)
589 			fixshots(pp->p_y, pp->p_x, pp->p_over);
590 		drawplayer(pp, FALSE);
591 		Nplayer--;
592 	}
593 
594 	len = strlen(pp->p_death);	/* Display the cause of death */
595 	x = (WIDTH - len) / 2;
596 	cgoto(pp, HEIGHT / 2, x);
597 	outstr(pp, pp->p_death, len);
598 	for (n = 1; n < len; n++)
599 		pp->p_death[n] = '-';
600 	pp->p_death[0] = '+';
601 	pp->p_death[len - 1] = '+';
602 	cgoto(pp, HEIGHT / 2 - 1, x);
603 	outstr(pp, pp->p_death, len);
604 	cgoto(pp, HEIGHT / 2 + 1, x);
605 	outstr(pp, pp->p_death, len);
606 	cgoto(pp, HEIGHT, 0);
607 
608 	savefd = pp->p_fd;
609 
610 # ifdef MONITOR
611 	if (was_player) {
612 # endif
613 		for (bp = Bullets; bp != NULL; bp = bp->b_next) {
614 			if (bp->b_owner == pp)
615 				bp->b_owner = NULL;
616 			if (bp->b_x == pp->p_x && bp->b_y == pp->p_y)
617 				bp->b_over = SPACE;
618 		}
619 
620 		n = rand_num(pp->p_ammo);
621 		x = rand_num(pp->p_ammo);
622 		if (x > n)
623 			n = x;
624 		if (pp->p_ammo == 0)
625 			x = 0;
626 		else if (n == pp->p_ammo - 1) {
627 			x = pp->p_ammo;
628 			len = SLIME;
629 		}
630 		else {
631 			for (x = MAXBOMB - 1; x > 0; x--)
632 				if (n >= shot_req[x])
633 					break;
634 			for (y = MAXSLIME - 1; y > 0; y--)
635 				if (n >= slime_req[y])
636 					break;
637 			if (y >= 0 && slime_req[y] > shot_req[x]) {
638 				x = slime_req[y];
639 				len = SLIME;
640 			}
641 			else if (x != 0) {
642 				len = shot_type[x];
643 				x = shot_req[x];
644 			}
645 		}
646 		if (x > 0) {
647 			(void) add_shot(len, pp->p_y, pp->p_x, pp->p_face, x,
648 				(PLAYER *) NULL, TRUE, SPACE);
649 			(void) sprintf(Buf, "%s detonated.",
650 				pp->p_ident->i_name);
651 			for (np = Player; np < End_player; np++)
652 				message(np, Buf);
653 # ifdef MONITOR
654 			for (np = Monitor; np < End_monitor; np++)
655 				message(np, Buf);
656 # endif
657 # ifdef BOOTS
658 			while (pp->p_nboots-- > 0) {
659 				for (np = Boot; np < &Boot[NBOOTS]; np++)
660 					if (np->p_flying < 0)
661 						break;
662 				if (np >= &Boot[NBOOTS])
663 					err(1, "Too many boots");
664 				np->p_undershot = FALSE;
665 				np->p_x = pp->p_x;
666 				np->p_y = pp->p_y;
667 				np->p_flying = rand_num(20);
668 				np->p_flyx = 2 * rand_num(6) - 5;
669 				np->p_flyy = 2 * rand_num(6) - 5;
670 				np->p_over = SPACE;
671 				np->p_face = BOOT;
672 				showexpl(np->p_y, np->p_x, BOOT);
673 			}
674 # endif
675 		}
676 # ifdef BOOTS
677 		else if (pp->p_nboots > 0) {
678 			if (pp->p_nboots == 2)
679 				Maze[pp->p_y][pp->p_x] = BOOT_PAIR;
680 			else
681 				Maze[pp->p_y][pp->p_x] = BOOT;
682 			if (pp->p_undershot)
683 				fixshots(pp->p_y, pp->p_x,
684 					Maze[pp->p_y][pp->p_x]);
685 		}
686 # endif
687 
688 # ifdef VOLCANO
689 		volcano += pp->p_ammo - x;
690 		if (rand_num(100) < volcano / 50) {
691 			do {
692 				x = rand_num(WIDTH / 2) + WIDTH / 4;
693 				y = rand_num(HEIGHT / 2) + HEIGHT / 4;
694 			} while (Maze[y][x] != SPACE);
695 			(void) add_shot(LAVA, y, x, LEFTS, volcano,
696 				(PLAYER *) NULL, TRUE, SPACE);
697 			for (np = Player; np < End_player; np++)
698 				message(np, "Volcano eruption.");
699 			volcano = 0;
700 		}
701 # endif
702 
703 # ifdef	DRONE
704 		if (rand_num(100) < 2) {
705 			do {
706 				x = rand_num(WIDTH / 2) + WIDTH / 4;
707 				y = rand_num(HEIGHT / 2) + HEIGHT / 4;
708 			} while (Maze[y][x] != SPACE);
709 			add_shot(DSHOT, y, x, rand_dir(),
710 				shot_req[MINDSHOT +
711 				rand_num(MAXBOMB - MINDSHOT)],
712 				(PLAYER *) NULL, FALSE, SPACE);
713 		}
714 # endif
715 
716 		sendcom(pp, ENDWIN);
717 		(void) putc(' ', pp->p_output);
718 		(void) fclose(pp->p_output);
719 
720 		End_player--;
721 		if (pp != End_player) {
722 			memcpy(pp, End_player, sizeof (PLAYER));
723 			fdset[i] = fdset[End_player - Player + 3];
724 			fdset[End_player - Player + 3].fd = -1;
725 			(void) sprintf(Buf, "%5.2f%c%-10.10s %c",
726 				pp->p_ident->i_score, stat_char(pp),
727 				pp->p_ident->i_name, pp->p_ident->i_team);
728 			n = STAT_PLAY_ROW + 1 + (pp - Player);
729 			for (np = Player; np < End_player; np++) {
730 				cgoto(np, n, STAT_NAME_COL);
731 				outstr(np, Buf, STAT_NAME_LEN);
732 			}
733 # ifdef MONITOR
734 			for (np = Monitor; np < End_monitor; np++) {
735 				cgoto(np, n, STAT_NAME_COL);
736 				outstr(np, Buf, STAT_NAME_LEN);
737 			}
738 # endif
739 		} else
740 			fdset[i].fd = -1;
741 
742 		/* Erase the last player */
743 		n = STAT_PLAY_ROW + 1 + Nplayer;
744 		for (np = Player; np < End_player; np++) {
745 			cgoto(np, n, STAT_NAME_COL);
746 			ce(np);
747 		}
748 # ifdef MONITOR
749 		for (np = Monitor; np < End_monitor; np++) {
750 			cgoto(np, n, STAT_NAME_COL);
751 			ce(np);
752 		}
753 	}
754 	else {
755 		sendcom(pp, ENDWIN);
756 		(void) putc(LAST_PLAYER, pp->p_output);
757 		(void) fclose(pp->p_output);
758 
759 		End_monitor--;
760 		if (pp != End_monitor) {
761 			memcpy(pp, End_monitor, sizeof (PLAYER));
762 			fdset[i] = fdset[End_monitor - Monitor + MAXPL + 3];
763 			fdset[End_monitor - Monitor + MAXPL + 3].fd = -1;
764 			(void) sprintf(Buf, "%5.5s %-10.10s %c", " ",
765 				pp->p_ident->i_name, pp->p_ident->i_team);
766 			n = STAT_MON_ROW + 1 + (pp - Player);
767 			for (np = Player; np < End_player; np++) {
768 				cgoto(np, n, STAT_NAME_COL);
769 				outstr(np, Buf, STAT_NAME_LEN);
770 			}
771 			for (np = Monitor; np < End_monitor; np++) {
772 				cgoto(np, n, STAT_NAME_COL);
773 				outstr(np, Buf, STAT_NAME_LEN);
774 			}
775 		} else
776 			fdset[i].fd = -1;
777 
778 		/* Erase the last monitor */
779 		n = STAT_MON_ROW + 1 + (End_monitor - Monitor);
780 		for (np = Player; np < End_player; np++) {
781 			cgoto(np, n, STAT_NAME_COL);
782 			ce(np);
783 		}
784 		for (np = Monitor; np < End_monitor; np++) {
785 			cgoto(np, n, STAT_NAME_COL);
786 			ce(np);
787 		}
788 	}
789 # endif
790 }
791 
792 /*
793  * rand_num:
794  *	Return a random number in a given range.
795  */
796 int
797 rand_num(range)
798 	int	range;
799 {
800 	return (range == 0 ? 0 : RN % range);
801 }
802 
803 /*
804  * havechar:
805  *	Check to see if we have any characters in the input queue; if
806  *	we do, read them, stash them away, and return TRUE; else return
807  *	FALSE.
808  */
809 static int
810 havechar(pp, i)
811 	PLAYER	*pp;
812 	int	i;
813 {
814 
815 	if (pp->p_ncount < pp->p_nchar)
816 		return TRUE;
817 	if (!(fdset[i].revents & POLLIN))
818 		return FALSE;
819 check_again:
820 	errno = 0;
821 	if ((pp->p_nchar = read(pp->p_fd, pp->p_cbuf, sizeof pp->p_cbuf)) <= 0)
822 	{
823 		if (errno == EINTR)
824 			goto check_again;
825 		pp->p_cbuf[0] = 'q';
826 	}
827 	pp->p_ncount = 0;
828 	return TRUE;
829 }
830 
831 /*
832  * cleanup:
833  *	Exit with the given value, cleaning up any droppings lying around
834  */
835 SIGNAL_TYPE
836 cleanup(eval)
837 	int	eval;
838 {
839 	PLAYER	*pp;
840 
841 	for (pp = Player; pp < End_player; pp++) {
842 		cgoto(pp, HEIGHT, 0);
843 		sendcom(pp, ENDWIN);
844 		(void) putc(LAST_PLAYER, pp->p_output);
845 		(void) fclose(pp->p_output);
846 	}
847 # ifdef MONITOR
848 	for (pp = Monitor; pp < End_monitor; pp++) {
849 		cgoto(pp, HEIGHT, 0);
850 		sendcom(pp, ENDWIN);
851 		(void) putc(LAST_PLAYER, pp->p_output);
852 		(void) fclose(pp->p_output);
853 	}
854 # endif
855 	(void) close(Socket);
856 # ifdef AF_UNIX_HACK
857 	(void) unlink(Sock_name);
858 # endif
859 
860 	exit(eval);
861 }
862 
863 /*
864  * send_stats:
865  *	Print stats to requestor
866  */
867 static void
868 send_stats()
869 {
870 	IDENT	*ip;
871 	FILE	*fp;
872 	int	s;
873 	SOCKET	sockstruct;
874 	int	socklen;
875 
876 	/*
877 	 * Get the output stream ready
878 	 */
879 # ifdef INTERNET
880 	socklen = sizeof sockstruct;
881 # else
882 	socklen = sizeof sockstruct - 1;
883 # endif
884 	s = accept(Status, (struct sockaddr *) &sockstruct, &socklen);
885 	if (s < 0) {
886 		if (errno == EINTR)
887 			return;
888 # ifdef LOG
889 		syslog(LOG_WARNING, "accept: %m");
890 # else
891 		warn("accept");
892 # endif
893 		return;
894 	}
895 	fp = fdopen(s, "w");
896 	if (fp == NULL) {
897 # ifdef LOG
898 		syslog(LOG_WARNING, "fdopen: %m");
899 # else
900 		warn("fdopen");
901 # endif
902 		(void) close(s);
903 		return;
904 	}
905 
906 	/*
907 	 * Send output to requestor
908 	 */
909 	fputs("Name\t\tScore\tDucked\tAbsorb\tFaced\tShot\tRobbed\tMissed\tSlimeK\n", fp);
910 	for (ip = Scores; ip != NULL; ip = ip->i_next) {
911 		fprintf(fp, "%s\t", ip->i_name);
912 		if (strlen(ip->i_name) < 8)
913 			putc('\t', fp);
914 		fprintf(fp, "%.2f\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
915 			ip->i_score, ip->i_ducked, ip->i_absorbed,
916 			ip->i_faced, ip->i_shot, ip->i_robbed,
917 			ip->i_missed, ip->i_slime);
918 	}
919 	fputs("\n\nName\t\tEnemy\tFriend\tDeaths\tStill\tSaved\n", fp);
920 	for (ip = Scores; ip != NULL; ip = ip->i_next) {
921 		if (ip->i_team == ' ') {
922 			fprintf(fp, "%s\t", ip->i_name);
923 			if (strlen(ip->i_name) < 8)
924 				putc('\t', fp);
925 		}
926 		else {
927 			fprintf(fp, "%s[%c]\t", ip->i_name, ip->i_team);
928 			if (strlen(ip->i_name) + 3 < 8)
929 				putc('\t', fp);
930 		}
931 		fprintf(fp, "%d\t%d\t%d\t%d\t%d\n",
932 			ip->i_gkills, ip->i_bkills, ip->i_deaths,
933 			ip->i_stillb, ip->i_saved);
934 	}
935 
936 	(void) fclose(fp);
937 }
938 
939 /*
940  * clear_scores:
941  *	Clear out the scores so the next session start clean
942  */
943 static void
944 clear_scores()
945 {
946 	IDENT	*ip, *nextip;
947 
948 	for (ip = Scores; ip != NULL; ip = nextip) {
949 		nextip = ip->i_next;
950 		(void) free((char *) ip);
951 	}
952 	Scores = NULL;
953 }
954