driver.c revision 1.4
190075Sobrien/*	$OpenBSD: driver.c,v 1.4 1999/02/01 06:53:56 d Exp $	*/
290075Sobrien/*	$NetBSD: driver.c,v 1.5 1997/10/20 00:37:16 lukem Exp $	*/
3117395Skan/*
490075Sobrien *  Hunt
590075Sobrien *  Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
690075Sobrien *  San Francisco, California
790075Sobrien */
890075Sobrien
990075Sobrien#include <sys/ioctl.h>
1090075Sobrien#include <sys/stat.h>
1190075Sobrien#include <sys/time.h>
1290075Sobrien#include <err.h>
1390075Sobrien#include <errno.h>
1490075Sobrien#include <signal.h>
1590075Sobrien#include <stdlib.h>
1690075Sobrien#include <unistd.h>
1790075Sobrien#include <stdio.h>
1890075Sobrien#include <tcpd.h>
1990075Sobrien#include <syslog.h>
2090075Sobrien#include <sys/socket.h>
2190075Sobrien#include <netinet/in.h>
2290075Sobrien#include <arpa/inet.h>
2390075Sobrien#include <paths.h>
2490075Sobrien#include <fcntl.h>
2590075Sobrien#include "hunt.h"
26117395Skan#include "conf.h"
2790075Sobrien#include "server.h"
2890075Sobrien
2990075Sobrienint	Seed = 0;
3090075Sobrien
3190075Sobrienchar	*First_arg;		/* pointer to argv[0] */
3290075Sobrienchar	*Last_arg;		/* pointer to end of argv/environ */
3390075Sobrienu_int16_t Server_port = HUNT_PORT;
3490075Sobrienint	Server_socket;		/* test socket to answer datagrams */
3590075SobrienFLAG	inetd_spawned;		/* invoked via inetd */
3690075SobrienFLAG	should_announce = TRUE;	/* true if listening on standard port */
3790075Sobrienu_short	sock_port;		/* port # of tcp listen socket */
3890075Sobrienu_short	stat_port;		/* port # of statistics tcp socket */
3990075Sobrienin_addr_t Server_addr = INADDR_ANY;	/* address to bind to */
4090075Sobrien
4190075Sobrienstatic	void	clear_scores __P((void));
4290075Sobrienstatic	int	havechar __P((PLAYER *));
4390075Sobrienstatic	void	init __P((void));
4490075Sobrien	int	main __P((int, char *[], char *[]));
4590075Sobrienstatic	void	makeboots __P((void));
46117395Skanstatic	void	send_stats __P((void));
4790075Sobrienstatic	void	zap __P((PLAYER *, FLAG));
4890075Sobrienstatic  void	announce_game __P((void));
49117395Skanstatic	void	siginfo __P((int));
5090075Sobrienstatic	void	print_stats __P((FILE *));
51117395Skanstatic	void	handle_wkport __P((int));
5290075Sobrien
5390075Sobrien/*
5490075Sobrien * main:
5590075Sobrien *	The main program.
5690075Sobrien */
5790075Sobrienint
5890075Sobrienmain(ac, av, ep)
5990075Sobrien	int	ac;
6090075Sobrien	char	**av, **ep;
6190075Sobrien{
6290075Sobrien	PLAYER	*pp;
6390075Sobrien	int	had_char;
6490075Sobrien	static fd_set	read_fds;
6590075Sobrien	static FLAG	first = TRUE;
6690075Sobrien	static FLAG	server = FALSE;
6790075Sobrien	extern int	optind;
6890075Sobrien	extern char	*optarg;
6990075Sobrien	int		c;
7090075Sobrien	static struct timeval	linger = { 0, 0 };
7190075Sobrien	static struct timeval	timeout = { 0, 0 }, *to;
7290075Sobrien	struct spawn	*sp;
7390075Sobrien	int		ret;
7490075Sobrien	int		nready;
7590075Sobrien
7690075Sobrien	First_arg = av[0];
7790075Sobrien	if (ep == NULL || *ep == NULL)
7890075Sobrien		ep = av + ac;
7990075Sobrien	while (*ep)
8090075Sobrien		ep++;
8190075Sobrien	Last_arg = ep[-1] + strlen(ep[-1]);
8290075Sobrien
8390075Sobrien	config();
8490075Sobrien
8590075Sobrien	while ((c = getopt(ac, av, "sp:a:")) != -1) {
8690075Sobrien		switch (c) {
8790075Sobrien		  case 's':
8896263Sobrien			server = TRUE;
8996263Sobrien			break;
9090075Sobrien		  case 'p':
9190075Sobrien			should_announce = FALSE;
9290075Sobrien			Server_port = atoi(optarg);
9390075Sobrien			break;
9490075Sobrien		  case 'a':
9590075Sobrien			Server_addr = inet_addr(optarg);
9690075Sobrien			if (Server_addr == INADDR_NONE)
9790075Sobrien				err(1, "bad interface address: %s", optarg);
98117395Skan			break;
9990075Sobrien		  default:
10090075Sobrienerred:
10190075Sobrien			fprintf(stderr, "Usage: %s [-s] [-p port] [-a addr]\n",
102117395Skan			    av[0]);
10390075Sobrien			exit(2);
10490075Sobrien		}
10590075Sobrien	}
10690075Sobrien	if (optind < ac)
10790075Sobrien		goto erred;
10890075Sobrien
10990075Sobrien	/* Open syslog: */
11090075Sobrien	openlog("huntd", LOG_PID | (conf_logerr && !server? LOG_PERROR : 0),
11190075Sobrien		LOG_DAEMON);
11290075Sobrien
11390075Sobrien	/* Initialise game parameters: */
11490075Sobrien	init();
11590075Sobrien
11690075Sobrienagain:
11790075Sobrien	do {
11890075Sobrien		/* First, poll to see if we can get input */
11990075Sobrien		timerclear(&timeout);
12090075Sobrien		do {
12190075Sobrien			read_fds = Fds_mask;
12290075Sobrien			errno = 0;
12396263Sobrien			nready = select(Num_fds, &read_fds, NULL, NULL,
12496263Sobrien			    &timeout);
12590075Sobrien			if (nready < 0 && errno != EINTR) {
12696263Sobrien				log(LOG_ERR, "select");
12790075Sobrien				cleanup(1);
12896263Sobrien			}
12996263Sobrien		} while (nready < 0);
13096263Sobrien
13190075Sobrien		if (nready == 0) {
13296263Sobrien			/*
13396263Sobrien			 * Nothing was ready. We do some work now
13490075Sobrien			 * to see if the simulation has any pending work
13590075Sobrien			 * to do, and decide if we need to to block
13690075Sobrien			 * indefinitely or just timeout.
13790075Sobrien			 */
13890075Sobrien			if (conf_simstep && can_moveshots()) {
13990075Sobrien				/*
14090075Sobrien				 * block for a short time before continuing
14190075Sobrien				 * with explosions, bullets and whatnot
14290075Sobrien				 */
14390075Sobrien				to = &timeout;
14490075Sobrien				to->tv_sec =  conf_simstep / 1000000;
14590075Sobrien				to->tv_usec = conf_simstep % 1000000;
14690075Sobrien			} else
14790075Sobrien				/*
14890075Sobrien				 * since there's nothing going on,
149117395Skan				 * just block waiting for external activity
150117395Skan				 */
151117395Skan				to = NULL;
15290075Sobrien
15390075Sobrien			do {
15490075Sobrien				read_fds = Fds_mask;
15590075Sobrien				errno = 0;
15690075Sobrien				nready = select(Num_fds, &read_fds, NULL, NULL,
15790075Sobrien				    to);
15890075Sobrien				if (nready < 0 && errno != EINTR) {
15990075Sobrien					log(LOG_ERR, "select");
16090075Sobrien					cleanup(1);
16190075Sobrien				}
16290075Sobrien			} while (nready < 0);
16390075Sobrien		}
16490075Sobrien
16590075Sobrien		/* Remember which descriptors are active: */
16690075Sobrien		Have_inp = read_fds;
16790075Sobrien
16890075Sobrien		/* Answer new player connections: */
16990075Sobrien		if (FD_ISSET(Socket, &read_fds))
17090075Sobrien			answer_first();
17190075Sobrien
17290075Sobrien		/* Continue answering new player connections: */
17390075Sobrien		for (sp = Spawn; sp; sp = sp->next)
17490075Sobrien			if (FD_ISSET(sp->fd, &read_fds) && answer_next(sp)) {
17590075Sobrien				if (first && should_announce)
17690075Sobrien					announce_game();
17790075Sobrien				first = FALSE;
17890075Sobrien			}
17990075Sobrien
18090075Sobrien		/* Process input and move bullets until we've exhausted input */
18190075Sobrien		had_char = TRUE;
18290075Sobrien		while (had_char) {
18390075Sobrien
18490075Sobrien			moveshots();
18590075Sobrien			for (pp = Player; pp < End_player; )
18690075Sobrien				if (pp->p_death[0] != '\0')
18790075Sobrien					zap(pp, TRUE);
18890075Sobrien				else
18990075Sobrien					pp++;
19090075Sobrien			for (pp = Monitor; pp < End_monitor; )
19190075Sobrien				if (pp->p_death[0] != '\0')
19290075Sobrien					zap(pp, FALSE);
19390075Sobrien				else
19490075Sobrien					pp++;
19590075Sobrien
19690075Sobrien			had_char = FALSE;
19790075Sobrien			for (pp = Player; pp < End_player; pp++)
19890075Sobrien				if (havechar(pp)) {
19990075Sobrien					execute(pp);
20090075Sobrien					pp->p_nexec++;
20190075Sobrien					had_char = TRUE;
20290075Sobrien				}
20390075Sobrien			for (pp = Monitor; pp < End_monitor; pp++)
20490075Sobrien				if (havechar(pp)) {
20590075Sobrien					mon_execute(pp);
20690075Sobrien					pp->p_nexec++;
20790075Sobrien					had_char = TRUE;
20890075Sobrien				}
20990075Sobrien		}
21090075Sobrien
21190075Sobrien		/* Handle a datagram sent to the server socket: */
21290075Sobrien		if (FD_ISSET(Server_socket, &read_fds))
21390075Sobrien			handle_wkport(Server_socket);
21490075Sobrien
21590075Sobrien		/* Answer statistics connections: */
21690075Sobrien		if (FD_ISSET(Status, &read_fds))
21790075Sobrien			send_stats();
21890075Sobrien
21990075Sobrien		/* Flush/synchronize all the displays: */
22090075Sobrien		for (pp = Player; pp < End_player; pp++) {
22190075Sobrien			if (FD_ISSET(pp->p_fd, &read_fds))
22290075Sobrien				sendcom(pp, READY, pp->p_nexec);
22390075Sobrien			pp->p_nexec = 0;
224117395Skan			flush(pp);
225117395Skan		}
226117395Skan		for (pp = Monitor; pp < End_monitor; pp++) {
227117395Skan			if (FD_ISSET(pp->p_fd, &read_fds))
228117395Skan				sendcom(pp, READY, pp->p_nexec);
229117395Skan			pp->p_nexec = 0;
230117395Skan			flush(pp);
231117395Skan		}
232117395Skan	} while (Nplayer > 0);
233117395Skan
234117395Skan	/* No more players! */
235117395Skan
236117395Skan	/* Continuous game? */
237117395Skan	if (conf_linger < 0)
238117395Skan		goto again;
239117395Skan
240117395Skan	/* Wait a short while for one to come back: */
241117395Skan	read_fds = Fds_mask;
242117395Skan	linger.tv_sec = conf_linger;
243117395Skan	while ((ret = select(Num_fds, &read_fds, NULL, NULL, &linger)) < 0) {
244117395Skan		if (errno != EINTR) {
245117395Skan			log(LOG_WARNING, "select");
246117395Skan			break;
247117395Skan		}
248117395Skan	}
24990075Sobrien	if (ret > 0)
25090075Sobrien		/* Someone returned! Resume the game: */
25190075Sobrien		goto again;
25290075Sobrien	/* else, it timed out, and the game is really over. */
25390075Sobrien
25490075Sobrien	/* If we are an inetd server, we should re-init the map and restart: */
25590075Sobrien	if (server) {
25690075Sobrien		clear_scores();
25790075Sobrien		makemaze();
25890075Sobrien		clearwalls();
25990075Sobrien		makeboots();
26090075Sobrien		first = TRUE;
26190075Sobrien		goto again;
26290075Sobrien	}
26390075Sobrien
26490075Sobrien	/* Get rid of any attached monitors: */
26590075Sobrien	for (pp = Monitor; pp < End_monitor; )
26690075Sobrien		zap(pp, FALSE);
26790075Sobrien
26890075Sobrien	/* Fin: */
26990075Sobrien	cleanup(0);
27090075Sobrien	exit(0);
27190075Sobrien}
27290075Sobrien
27390075Sobrien/*
27490075Sobrien * init:
27590075Sobrien *	Initialize the global parameters.
27690075Sobrien */
27790075Sobrienstatic void
27890075Sobrieninit()
27990075Sobrien{
28090075Sobrien	int	i;
28190075Sobrien	struct sockaddr_in	test_port;
28290075Sobrien	int	msg;
28390075Sobrien	int	len;
28490075Sobrien	struct sockaddr_in	addr;
28590075Sobrien	struct sigaction	sact;
28690075Sobrien
28790075Sobrien	(void) setsid();
28890075Sobrien	if (setpgid(getpid(), getpid()) == -1)
28990075Sobrien		err(1, "setpgid");
29090075Sobrien
29190075Sobrien	sact.sa_flags = SA_RESTART;
29290075Sobrien	sigemptyset(&sact.sa_mask);
29390075Sobrien
29490075Sobrien	/* Ignore HUP, QUIT and PIPE: */
29590075Sobrien	sact.sa_handler = SIG_IGN;
29690075Sobrien	if (sigaction(SIGHUP, &sact, NULL) == -1)
29790075Sobrien		err(1, "sigaction SIGHUP");
29890075Sobrien	if (sigaction(SIGQUIT, &sact, NULL) == -1)
29990075Sobrien		err(1, "sigaction SIGQUIT");
30090075Sobrien	if (sigaction(SIGPIPE, &sact, NULL) == -1)
30190075Sobrien		err(1, "sigaction SIGPIPE");
30290075Sobrien
30390075Sobrien	/* Clean up gracefully on INT and TERM: */
30490075Sobrien	sact.sa_handler = cleanup;
30590075Sobrien	if (sigaction(SIGINT, &sact, NULL) == -1)
30690075Sobrien		err(1, "sigaction SIGINT");
30790075Sobrien	if (sigaction(SIGTERM, &sact, NULL) == -1)
30890075Sobrien		err(1, "sigaction SIGTERM");
30990075Sobrien
31090075Sobrien	/* Handle INFO: */
31190075Sobrien	sact.sa_handler = siginfo;
31290075Sobrien	if (sigaction(SIGINFO, &sact, NULL) == -1)
31390075Sobrien		err(1, "sigaction SIGINFO");
31490075Sobrien
31590075Sobrien	if (chdir("/") == -1)
31690075Sobrien		warn("chdir");
317117395Skan	(void) umask(0777);
31890075Sobrien
31990075Sobrien	/* Initialize statistics socket: */
32090075Sobrien	addr.sin_family = AF_INET;
32190075Sobrien	addr.sin_addr.s_addr = Server_addr;
32290075Sobrien	addr.sin_port = 0;
32390075Sobrien
32490075Sobrien	Status = socket(AF_INET, SOCK_STREAM, 0);
32590075Sobrien	if (bind(Status, (struct sockaddr *) &addr, sizeof addr) < 0) {
32690075Sobrien		log(LOG_ERR, "bind");
32790075Sobrien		cleanup(1);
32890075Sobrien	}
32990075Sobrien	if (listen(Status, 5) == -1) {
33090075Sobrien		log(LOG_ERR, "listen");
33190075Sobrien		cleanup(1);
33290075Sobrien	}
33390075Sobrien
33490075Sobrien	len = sizeof (struct sockaddr_in);
33590075Sobrien	if (getsockname(Status, (struct sockaddr *) &addr, &len) < 0)  {
33690075Sobrien		log(LOG_ERR, "getsockname");
33790075Sobrien		cleanup(1);
33890075Sobrien	}
33990075Sobrien	stat_port = ntohs(addr.sin_port);
34090075Sobrien
341117395Skan	/* Initialize main socket: */
342117395Skan	addr.sin_family = AF_INET;
343117395Skan	addr.sin_addr.s_addr = Server_addr;
344117395Skan	addr.sin_port = 0;
345117395Skan
346117395Skan	Socket = socket(AF_INET, SOCK_STREAM, 0);
347117395Skan	msg = 1;
348117395Skan	if (setsockopt(Socket, SOL_SOCKET, SO_USELOOPBACK, &msg, sizeof msg)<0)
349117395Skan		log(LOG_ERR, "setsockopt loopback");
35090075Sobrien	if (bind(Socket, (struct sockaddr *) &addr, sizeof addr) < 0) {
351117395Skan		log(LOG_ERR, "bind");
352117395Skan		cleanup(1);
353117395Skan	}
35490075Sobrien	if (listen(Socket, 5) == -1) {
355117395Skan		log(LOG_ERR, "listen");
356117395Skan		cleanup(1);
357117395Skan	}
35890075Sobrien
359117395Skan	len = sizeof (struct sockaddr_in);
360117395Skan	if (getsockname(Socket, (struct sockaddr *) &addr, &len) < 0)  {
36190075Sobrien		log(LOG_ERR, "getsockname");
36290075Sobrien		cleanup(1);
363117395Skan	}
364117395Skan	sock_port = ntohs(addr.sin_port);
365117395Skan
366117395Skan	/* Initialize minimal select mask */
36790075Sobrien	FD_ZERO(&Fds_mask);
368117395Skan	FD_SET(Socket, &Fds_mask);
36990075Sobrien	FD_SET(Status, &Fds_mask);
370117395Skan	Num_fds = ((Socket > Status) ? Socket : Status) + 1;
37190075Sobrien
37290075Sobrien	/* Check if stdin is a socket: */
37390075Sobrien	len = sizeof (struct sockaddr_in);
37490075Sobrien	if (getsockname(STDIN_FILENO, (struct sockaddr *) &test_port, &len) >= 0
37590075Sobrien	    && test_port.sin_family == AF_INET) {
37690075Sobrien		/* We are probably running from inetd: */
37790075Sobrien		inetd_spawned = TRUE;
37890075Sobrien		Server_socket = STDIN_FILENO;
37990075Sobrien		if (test_port.sin_port != htons((u_short) Server_port)) {
38090075Sobrien			should_announce = FALSE;
38190075Sobrien			Server_port = ntohs(test_port.sin_port);
38290075Sobrien		}
38390075Sobrien	} else {
384117395Skan		/* We need to listen on a socket: */
385117395Skan		test_port = addr;
386117395Skan		test_port.sin_port = htons((u_short) Server_port);
387117395Skan
388117395Skan		Server_socket = socket(AF_INET, SOCK_DGRAM, 0);
389117395Skan		if (bind(Server_socket, (struct sockaddr *) &test_port,
390117395Skan		    sizeof test_port) < 0) {
391117395Skan			log(LOG_ERR, "bind port %d", Server_port);
392117395Skan			cleanup(1);
393117395Skan		}
394117395Skan
395117395Skan		/* Datagram sockets do not need a listen() call. */
396117395Skan	}
397117395Skan
398117395Skan	/* We'll handle the broadcast listener in the main loop: */
399117395Skan	FD_SET(Server_socket, &Fds_mask);
400117395Skan	if (Server_socket + 1 > Num_fds)
401117395Skan		Num_fds = Server_socket + 1;
402117395Skan
403117395Skan	/* Initialise the random seed: */
404117395Skan	Seed = getpid() + time((time_t *) NULL);
405117395Skan
406117395Skan	/* Dig the maze: */
407117395Skan	makemaze();
408117395Skan
409117395Skan	/* Create some boots, if needed: */
410117395Skan	makeboots();
411117395Skan
412117395Skan	/* Construct a table of what objects a player can see over: */
413117395Skan	for (i = 0; i < NASCII; i++)
414117395Skan		See_over[i] = TRUE;
415117395Skan	See_over[DOOR] = FALSE;
416117395Skan	See_over[WALL1] = FALSE;
417117395Skan	See_over[WALL2] = FALSE;
418117395Skan	See_over[WALL3] = FALSE;
419117395Skan	See_over[WALL4] = FALSE;
420117395Skan	See_over[WALL5] = FALSE;
421117395Skan
42290075Sobrien	logx(LOG_INFO, "game started");
42390075Sobrien}
42490075Sobrien
42590075Sobrien/*
42690075Sobrien * makeboots:
42790075Sobrien *	Put the boots in the maze
42890075Sobrien */
42990075Sobrienstatic void
43090075Sobrienmakeboots()
43190075Sobrien{
43290075Sobrien	int	x, y;
43390075Sobrien	PLAYER	*pp;
43490075Sobrien
43590075Sobrien	if (conf_boots) {
43690075Sobrien		do {
43790075Sobrien			x = rand_num(WIDTH - 1) + 1;
43890075Sobrien			y = rand_num(HEIGHT - 1) + 1;
43990075Sobrien		} while (Maze[y][x] != SPACE);
44090075Sobrien		Maze[y][x] = BOOT_PAIR;
44190075Sobrien	}
44290075Sobrien
44390075Sobrien	for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
44490075Sobrien		pp->p_flying = -1;
44590075Sobrien}
44690075Sobrien
44790075Sobrien
44890075Sobrien/*
44990075Sobrien * checkdam:
45090075Sobrien *	Apply damage to the victim from an attacker.
45190075Sobrien *	If the victim dies as a result, give points to 'credit',
45290075Sobrien */
45390075Sobrienvoid
45490075Sobriencheckdam(victim, attacker, credit, damage, shot_type)
45590075Sobrien	PLAYER	*victim, *attacker;
45690075Sobrien	IDENT	*credit;
45790075Sobrien	int	damage;
45890075Sobrien	char	shot_type;
45990075Sobrien{
46090075Sobrien	char	*cp;
46190075Sobrien	int	y;
46290075Sobrien
46390075Sobrien	/* Don't do anything if the victim is already in the throes of death */
46490075Sobrien	if (victim->p_death[0] != '\0')
46590075Sobrien		return;
46690075Sobrien
46790075Sobrien	/* Weaken slime attacks by 0.5 * number of boots the victim has on: */
46890075Sobrien	if (shot_type == SLIME)
46990075Sobrien		switch (victim->p_nboots) {
47090075Sobrien		  default:
47190075Sobrien			break;
47290075Sobrien		  case 1:
47390075Sobrien			damage = (damage + 1) / 2;
47490075Sobrien			break;
47590075Sobrien		  case 2:
47690075Sobrien			if (attacker != NULL)
47790075Sobrien				message(attacker, "He has boots on!");
47890075Sobrien			return;
47990075Sobrien		}
48090075Sobrien
48190075Sobrien	/* The victim sustains some damage: */
48290075Sobrien	victim->p_damage += damage;
48390075Sobrien
48490075Sobrien	/* Check if the victim survives the hit: */
48590075Sobrien	if (victim->p_damage <= victim->p_damcap) {
48690075Sobrien		/* They survive. */
48790075Sobrien		outyx(victim, STAT_DAM_ROW, STAT_VALUE_COL, "%2d",
48890075Sobrien			victim->p_damage);
48990075Sobrien		return;
49090075Sobrien	}
49190075Sobrien
49290075Sobrien	/* Describe how the victim died: */
49390075Sobrien	switch (shot_type) {
49490075Sobrien	  default:
49590075Sobrien		cp = "Killed";
49690075Sobrien		break;
49790075Sobrien	  case FALL:
49890075Sobrien		cp = "Killed on impact";
49990075Sobrien		break;
50090075Sobrien	  case KNIFE:
50190075Sobrien		cp = "Stabbed to death";
50296263Sobrien		victim->p_ammo = 0;		/* No exploding */
50396263Sobrien		break;
50490075Sobrien	  case SHOT:
50596263Sobrien		cp = "Shot to death";
50690075Sobrien		break;
50790075Sobrien	  case GRENADE:
50890075Sobrien	  case SATCHEL:
50990075Sobrien	  case BOMB:
51090075Sobrien		cp = "Bombed";
51190075Sobrien		break;
51290075Sobrien	  case MINE:
51390075Sobrien	  case GMINE:
51490075Sobrien		cp = "Blown apart";
51590075Sobrien		break;
51690075Sobrien	  case SLIME:
51790075Sobrien		cp = "Slimed";
51890075Sobrien		if (credit != NULL)
51990075Sobrien			credit->i_slime++;
52090075Sobrien		break;
52190075Sobrien	  case LAVA:
52290075Sobrien		cp = "Baked";
52390075Sobrien		break;
52490075Sobrien	  case DSHOT:
52590075Sobrien		cp = "Eliminated";
52690075Sobrien		break;
52790075Sobrien	}
52890075Sobrien
52990075Sobrien	if (credit == NULL) {
53090075Sobrien		char *blame;
53190075Sobrien
53290075Sobrien		/*
53390075Sobrien		 * Nobody is taking the credit for the kill.
53490075Sobrien		 * Attribute it to either a mine or 'act of God'.
53590075Sobrien		 */
53690075Sobrien		switch (shot_type) {
53790075Sobrien		case MINE:
53890075Sobrien		case GMINE:
53990075Sobrien			blame = "a mine";
54090075Sobrien			break;
54190075Sobrien		default:
54290075Sobrien			blame = "act of God";
54390075Sobrien			break;
54490075Sobrien		}
54590075Sobrien
54690075Sobrien		/* Set the death message: */
54790075Sobrien		(void) snprintf(victim->p_death, sizeof victim->p_death,
54890075Sobrien			"| %s by %s |", cp, blame);
54990075Sobrien
55090075Sobrien		/* No further score crediting needed. */
55190075Sobrien		return;
55290075Sobrien	}
55390075Sobrien
55490075Sobrien	/* Set the death message: */
55590075Sobrien	(void) snprintf(victim->p_death, sizeof victim->p_death,
55690075Sobrien		"| %s by %s |", cp, credit->i_name);
55790075Sobrien
55890075Sobrien	if (victim == attacker) {
55990075Sobrien		/* No use killing yourself. */
56090075Sobrien		credit->i_kills--;
56190075Sobrien		credit->i_bkills++;
56290075Sobrien	}
56396263Sobrien	else if (victim->p_ident->i_team == ' '
56496263Sobrien	    || victim->p_ident->i_team != credit->i_team) {
56590075Sobrien		/* A cross-team kill: */
56690075Sobrien		credit->i_kills++;
56790075Sobrien		credit->i_gkills++;
56890075Sobrien	}
56990075Sobrien	else {
57090075Sobrien		/* They killed someone on the same team: */
57190075Sobrien		credit->i_kills--;
57290075Sobrien		credit->i_bkills++;
57390075Sobrien	}
57490075Sobrien
57590075Sobrien	/* Compute the new credited score: */
57690075Sobrien	credit->i_score = credit->i_kills / (double) credit->i_entries;
57790075Sobrien
57890075Sobrien	/* The victim accrues one death: */
57990075Sobrien	victim->p_ident->i_deaths++;
58090075Sobrien
58190075Sobrien	/* Account for 'Stillborn' deaths */
58290075Sobrien	if (victim->p_nchar == 0)
58390075Sobrien		victim->p_ident->i_stillb++;
58490075Sobrien
58590075Sobrien	if (attacker) {
58690075Sobrien		/* Give the attacker player a bit more strength */
58790075Sobrien		attacker->p_damcap += conf_killgain;
58890075Sobrien		attacker->p_damage -= conf_killgain;
58990075Sobrien		if (attacker->p_damage < 0)
59090075Sobrien			attacker->p_damage = 0;
59190075Sobrien
59290075Sobrien		/* Tell the attacker's his new strength: */
59390075Sobrien		outyx(attacker, STAT_DAM_ROW, STAT_VALUE_COL, "%2d/%2d",
59490075Sobrien			attacker->p_damage, attacker->p_damcap);
59590075Sobrien
59690075Sobrien		/* Tell the attacker's his new 'kill count': */
59790075Sobrien		outyx(attacker, STAT_KILL_ROW, STAT_VALUE_COL, "%3d",
59890075Sobrien			(attacker->p_damcap - conf_maxdam) / 2);
59990075Sobrien
60090075Sobrien		/* Update the attacker's score for everyone else */
60190075Sobrien		y = STAT_PLAY_ROW + 1 + (attacker - Player);
60290075Sobrien		outyx(ALL_PLAYERS, y, STAT_NAME_COL,
60390075Sobrien			"%5.2f", attacker->p_ident->i_score);
60490075Sobrien	}
60590075Sobrien}
60690075Sobrien
60790075Sobrien/*
60896263Sobrien * zap:
60996263Sobrien *	Kill off a player and take them out of the game.
61096263Sobrien *	The 'was_player' flag indicates that the player was not
61196263Sobrien *	a monitor and needs extra cleaning up.
61290075Sobrien */
61390075Sobrienstatic void
61490075Sobrienzap(pp, was_player)
61590075Sobrien	PLAYER	*pp;
61690075Sobrien	FLAG	was_player;
61790075Sobrien{
61890075Sobrien	int	len;
61990075Sobrien	BULLET	*bp;
62090075Sobrien	PLAYER	*np;
62190075Sobrien	int	x, y;
62290075Sobrien	int	savefd;
62390075Sobrien
62490075Sobrien	if (was_player) {
62590075Sobrien		/* If they died from a shot, clean up shrapnel */
62690075Sobrien		if (pp->p_undershot)
62790075Sobrien			fixshots(pp->p_y, pp->p_x, pp->p_over);
62890075Sobrien		/* Let the player see their last position: */
62990075Sobrien		drawplayer(pp, FALSE);
63090075Sobrien		/* Remove from game: */
63190075Sobrien		Nplayer--;
63290075Sobrien	}
63390075Sobrien
63490075Sobrien	/* Display the cause of death in the centre of the screen: */
63590075Sobrien	len = strlen(pp->p_death);
63690075Sobrien	x = (WIDTH - len) / 2;
63790075Sobrien	outyx(pp, HEIGHT / 2, x, "%s", pp->p_death);
63890075Sobrien
63990075Sobrien	/* Put some horizontal lines around and below the death message: */
64090075Sobrien	memset(pp->p_death + 1, '-', len - 2);
64190075Sobrien	pp->p_death[0] = '+';
64290075Sobrien	pp->p_death[len - 1] = '+';
64390075Sobrien	outyx(pp, HEIGHT / 2 - 1, x, "%s", pp->p_death);
64490075Sobrien	outyx(pp, HEIGHT / 2 + 1, x, "%s", pp->p_death);
64590075Sobrien
64690075Sobrien	/* Move to bottom left */
647117395Skan	cgoto(pp, HEIGHT, 0);
648117395Skan
64990075Sobrien	savefd = pp->p_fd;
650117395Skan
651117395Skan	if (was_player) {
652117395Skan		int	expl_charge;
653117395Skan		int	expl_type;
654117395Skan		int	ammo_exploding;
65590075Sobrien
656117395Skan		/* Check all the bullets: */
657117395Skan		for (bp = Bullets; bp != NULL; bp = bp->b_next) {
658117395Skan			if (bp->b_owner == pp)
659117395Skan				/* Zapped players can't own bullets: */
660117395Skan				bp->b_owner = NULL;
661117395Skan			if (bp->b_x == pp->p_x && bp->b_y == pp->p_y)
662117395Skan				/* Bullets over the player are now over air: */
663117395Skan				bp->b_over = SPACE;
664117395Skan		}
665117395Skan
666117395Skan		/* Explode a random fraction of the player's ammo: */
667117395Skan		ammo_exploding = rand_num(pp->p_ammo);
668117395Skan
669117395Skan		/* Determine the type and amount of detonation: */
670117395Skan		expl_charge = rand_num(ammo_exploding + 1);
671117395Skan		if (pp->p_ammo == 0)
672117395Skan			/* Ignore the no-ammo case: */
673117395Skan			expl_charge = 0;
674117395Skan		else if (ammo_exploding >= pp->p_ammo - 1) {
675117395Skan			/* Maximal explosions always appear as slime: */
67690075Sobrien			expl_charge = pp->p_ammo;
67790075Sobrien			expl_type = SLIME;
67890075Sobrien		} else {
67990075Sobrien			/*
68090075Sobrien			 * Figure out the best effective explosion
68190075Sobrien			 * type to use, given the amount of charge
68290075Sobrien			 */
683117395Skan			int btype, stype;
684117395Skan			for (btype = MAXBOMB - 1; btype > 0; btype--)
685117395Skan				if (expl_charge >= shot_req[btype])
686117395Skan					break;
687117395Skan			for (stype = MAXSLIME - 1; stype > 0; stype--)
688117395Skan				if (expl_charge >= slime_req[stype])
689117395Skan					break;
690117395Skan			/* Pick the larger of the bomb or slime: */
691117395Skan			if (btype >= 0 && stype >= 0) {
692117395Skan				if (shot_req[btype] > slime_req[btype])
693117395Skan					btype = -1;
694117395Skan			}
69590075Sobrien			if (btype >= 0)  {
69690075Sobrien				expl_type = shot_type[btype];
69790075Sobrien				expl_charge = shot_req[btype];
69890075Sobrien			} else
69990075Sobrien				expl_type = SLIME;
70090075Sobrien		}
70190075Sobrien
70290075Sobrien		if (expl_charge > 0) {
70390075Sobrien			char buf[BUFSIZ];
70490075Sobrien
70590075Sobrien			/* Detonate: */
70690075Sobrien			(void) add_shot(expl_type, pp->p_y, pp->p_x,
70790075Sobrien			    pp->p_face, expl_charge, (PLAYER *) NULL,
70890075Sobrien			    TRUE, SPACE);
70990075Sobrien
71090075Sobrien			/* Explain what the explosion is about. */
71190075Sobrien			snprintf(buf, sizeof buf, "%s detonated.",
71290075Sobrien				pp->p_ident->i_name);
71390075Sobrien			message(ALL_PLAYERS, buf);
71490075Sobrien
71590075Sobrien			while (pp->p_nboots-- > 0) {
71690075Sobrien				/* Throw one of the boots away: */
71790075Sobrien				for (np = Boot; np < &Boot[NBOOTS]; np++)
71890075Sobrien					if (np->p_flying < 0)
71990075Sobrien						break;
72090075Sobrien#ifdef DIAGNOSTIC
72190075Sobrien				if (np >= &Boot[NBOOTS])
72290075Sobrien					err(1, "Too many boots");
72390075Sobrien#endif
72490075Sobrien				/* Start the boots from where the player is */
72590075Sobrien				np->p_undershot = FALSE;
72690075Sobrien				np->p_x = pp->p_x;
72790075Sobrien				np->p_y = pp->p_y;
72890075Sobrien				/* Throw for up to 20 steps */
72990075Sobrien				np->p_flying = rand_num(20);
73090075Sobrien				np->p_flyx = 2 * rand_num(6) - 5;
73190075Sobrien				np->p_flyy = 2 * rand_num(6) - 5;
73290075Sobrien				np->p_over = SPACE;
73390075Sobrien				np->p_face = BOOT;
73490075Sobrien				showexpl(np->p_y, np->p_x, BOOT);
73590075Sobrien			}
73690075Sobrien		}
73790075Sobrien		/* No explosion. Leave the player's boots behind. */
73890075Sobrien		else if (pp->p_nboots > 0) {
73990075Sobrien			if (pp->p_nboots == 2)
74090075Sobrien				Maze[pp->p_y][pp->p_x] = BOOT_PAIR;
74190075Sobrien			else
74290075Sobrien				Maze[pp->p_y][pp->p_x] = BOOT;
74390075Sobrien			if (pp->p_undershot)
74490075Sobrien				fixshots(pp->p_y, pp->p_x,
74590075Sobrien					Maze[pp->p_y][pp->p_x]);
74690075Sobrien		}
74790075Sobrien
74890075Sobrien		/* Any unexploded ammo builds up in the volcano: */
74990075Sobrien		volcano += pp->p_ammo - expl_charge;
75090075Sobrien
75190075Sobrien		/* Volcano eruption: */
75290075Sobrien		if (conf_volcano && rand_num(100) < volcano /
75390075Sobrien		    conf_volcano_max) {
75490075Sobrien			/* Erupt near the middle of the map */
75590075Sobrien			do {
75690075Sobrien				x = rand_num(WIDTH / 2) + WIDTH / 4;
75790075Sobrien				y = rand_num(HEIGHT / 2) + HEIGHT / 4;
75890075Sobrien			} while (Maze[y][x] != SPACE);
75990075Sobrien
760117395Skan			/* Convert volcano charge into lava: */
76190075Sobrien			(void) add_shot(LAVA, y, x, LEFTS, volcano,
76290075Sobrien				(PLAYER *) NULL, TRUE, SPACE);
76390075Sobrien			volcano = 0;
76490075Sobrien
76590075Sobrien			/* Tell eveyone what's happening */
76690075Sobrien			message(ALL_PLAYERS, "Volcano eruption.");
76790075Sobrien		}
76890075Sobrien
76990075Sobrien		/* Drone: */
77090075Sobrien		if (conf_drone && rand_num(100) < 2) {
77190075Sobrien			/* Find a starting place near the middle of the map: */
77290075Sobrien			do {
77390075Sobrien				x = rand_num(WIDTH / 2) + WIDTH / 4;
77490075Sobrien				y = rand_num(HEIGHT / 2) + HEIGHT / 4;
77590075Sobrien			} while (Maze[y][x] != SPACE);
77690075Sobrien
77790075Sobrien			/* Start the drone going: */
77890075Sobrien			add_shot(DSHOT, y, x, rand_dir(),
77990075Sobrien				shot_req[conf_mindshot +
78090075Sobrien				rand_num(MAXBOMB - conf_mindshot)],
78190075Sobrien				(PLAYER *) NULL, FALSE, SPACE);
78290075Sobrien		}
78390075Sobrien
78490075Sobrien		/* Tell the zapped player's client to shut down. */
78590075Sobrien		sendcom(pp, ENDWIN, ' ');
78690075Sobrien		(void) fclose(pp->p_output);
78790075Sobrien
78890075Sobrien		/* Close up the gap in the Player array: */
78990075Sobrien		End_player--;
79090075Sobrien		if (pp != End_player) {
79190075Sobrien			/* Move the last player into the gap: */
79290075Sobrien			memcpy(pp, End_player, sizeof *pp);
79390075Sobrien			outyx(ALL_PLAYERS,
79490075Sobrien				STAT_PLAY_ROW + 1 + (pp - Player),
79590075Sobrien				STAT_NAME_COL,
79690075Sobrien				"%5.2f%c%-10.10s %c",
79790075Sobrien				pp->p_ident->i_score, stat_char(pp),
79890075Sobrien				pp->p_ident->i_name, pp->p_ident->i_team);
79990075Sobrien		}
80090075Sobrien
80190075Sobrien		/* Erase the last player from the display: */
80290075Sobrien		cgoto(ALL_PLAYERS, STAT_PLAY_ROW + 1 + Nplayer, STAT_NAME_COL);
80390075Sobrien		ce(ALL_PLAYERS);
80490075Sobrien	}
80590075Sobrien	else {
80690075Sobrien		/* Zap a monitor */
80790075Sobrien
808117395Skan		/* Close the session: */
80990075Sobrien		sendcom(pp, ENDWIN, LAST_PLAYER);
810117395Skan		(void) fclose(pp->p_output);
811117395Skan
812117395Skan		/* shuffle the monitor table */
813117395Skan		End_monitor--;
814117395Skan		if (pp != End_monitor) {
815117395Skan			memcpy(pp, End_monitor, sizeof *pp);
816117395Skan			outyx(ALL_PLAYERS,
817117395Skan				STAT_MON_ROW + 1 + (pp - Player), STAT_NAME_COL,
818117395Skan				"%5.5s %-10.10s %c", " ",
819117395Skan				pp->p_ident->i_name, pp->p_ident->i_team);
820117395Skan		}
821117395Skan
822117395Skan		/* Erase the last monitor in the list */
823117395Skan		cgoto(ALL_PLAYERS,
82490075Sobrien			STAT_MON_ROW + 1 + (End_monitor - Monitor),
82590075Sobrien			STAT_NAME_COL);
82690075Sobrien		ce(ALL_PLAYERS);
82790075Sobrien	}
82890075Sobrien
82990075Sobrien	/* Update the file descriptor sets used by select: */
83090075Sobrien	FD_CLR(savefd, &Fds_mask);
83190075Sobrien	if (Num_fds == savefd + 1) {
83290075Sobrien		Num_fds = Socket;
83390075Sobrien		if (Server_socket > Socket)
83490075Sobrien			Num_fds = Server_socket;
83590075Sobrien		for (np = Player; np < End_player; np++)
83690075Sobrien			if (np->p_fd > Num_fds)
83790075Sobrien				Num_fds = np->p_fd;
83890075Sobrien		for (np = Monitor; np < End_monitor; np++)
83990075Sobrien			if (np->p_fd > Num_fds)
84090075Sobrien				Num_fds = np->p_fd;
84190075Sobrien		Num_fds++;
84290075Sobrien	}
84390075Sobrien}
84490075Sobrien
84590075Sobrien/*
84690075Sobrien * rand_num:
84790075Sobrien *	Return a random number in a given range.
84890075Sobrien */
84990075Sobrienint
85090075Sobrienrand_num(range)
85190075Sobrien	int	range;
85290075Sobrien{
85390075Sobrien	if (range == 0)
85490075Sobrien		return 0;
85590075Sobrien	Seed = Seed * 11109 + 13849;
85690075Sobrien	return (((Seed >> 16) & 0xffff) % range);
85790075Sobrien}
85890075Sobrien
85990075Sobrien/*
86090075Sobrien * havechar:
86190075Sobrien *	Check to see if we have any characters in the input queue; if
86290075Sobrien *	we do, read them, stash them away, and return TRUE; else return
86390075Sobrien *	FALSE.
86490075Sobrien */
86590075Sobrienstatic int
86690075Sobrienhavechar(pp)
86790075Sobrien	PLAYER	*pp;
86890075Sobrien{
86990075Sobrien
87090075Sobrien	/* Do we already have characters? */
87190075Sobrien	if (pp->p_ncount < pp->p_nchar)
87290075Sobrien		return TRUE;
87390075Sobrien	/* Is the player being quiet? */
87490075Sobrien	if (!FD_ISSET(pp->p_fd, &Have_inp))
87590075Sobrien		return FALSE;
87690075Sobrien	/* Remove the player from the read set until we have drained them: */
87790075Sobrien	FD_CLR(pp->p_fd, &Have_inp);
87890075Sobrien
87990075Sobrien	/* Suck their keypresses into a buffer: */
88090075Sobriencheck_again:
88190075Sobrien	if ((pp->p_nchar = read(pp->p_fd, pp->p_cbuf, sizeof pp->p_cbuf)) <= 0)
88290075Sobrien	{
88390075Sobrien		if (errno == EINTR)
88490075Sobrien			goto check_again;
88590075Sobrien		if (errno != EAGAIN) {
88690075Sobrien			log(LOG_INFO, "read");
88790075Sobrien			/* Assume their connection was lost/closed: */
88890075Sobrien			pp->p_cbuf[0] = 'q';
88990075Sobrien			pp->p_nchar = 1;
89090075Sobrien		}
89190075Sobrien	}
89290075Sobrien	/* Reset pointer into read buffer */
89390075Sobrien	pp->p_ncount = 0;
89490075Sobrien	return TRUE;
89590075Sobrien}
89690075Sobrien
89790075Sobrien/*
89890075Sobrien * cleanup:
89990075Sobrien *	Exit with the given value, cleaning up any droppings lying around
90090075Sobrien */
90190075Sobrienvoid
90290075Sobriencleanup(eval)
90390075Sobrien	int	eval;
90490075Sobrien{
90590075Sobrien	PLAYER	*pp;
90690075Sobrien
90790075Sobrien	/* Place their cursor in a friendly position: */
90890075Sobrien	cgoto(ALL_PLAYERS, HEIGHT, 0);
90990075Sobrien
91090075Sobrien	/* Send them all the ENDWIN command: */
91190075Sobrien	sendcom(ALL_PLAYERS, ENDWIN, LAST_PLAYER);
91290075Sobrien
91390075Sobrien	/* And close their connections: */
91490075Sobrien	for (pp = Player; pp < End_player; pp++)
91590075Sobrien		(void) fclose(pp->p_output);
91690075Sobrien	for (pp = Monitor; pp < End_monitor; pp++)
91790075Sobrien		(void) fclose(pp->p_output);
91890075Sobrien
91990075Sobrien	/* Close the server socket: */
92090075Sobrien	(void) close(Socket);
92190075Sobrien
92290075Sobrien	/* The end: */
92390075Sobrien	logx(LOG_INFO, "game over");
92490075Sobrien	exit(eval);
92590075Sobrien}
92690075Sobrien
92790075Sobrien/*
92890075Sobrien * send_stats:
92990075Sobrien *	Accept a connection to the statistics port, and emit
93090075Sobrien *	the stats.
93190075Sobrien */
93290075Sobrienstatic void
93390075Sobriensend_stats()
93490075Sobrien{
93590075Sobrien	FILE	*fp;
93690075Sobrien	int	s;
93790075Sobrien	struct sockaddr_in	sockstruct;
93890075Sobrien	int	socklen;
93990075Sobrien	struct request_info ri;
94090075Sobrien
94190075Sobrien	/* Accept a connection to the statistics socket: */
94290075Sobrien	socklen = sizeof sockstruct;
94390075Sobrien	s = accept(Status, (struct sockaddr *) &sockstruct, &socklen);
94490075Sobrien	if (s < 0) {
94590075Sobrien		if (errno == EINTR)
94690075Sobrien			return;
94790075Sobrien		logx(LOG_ERR, "accept");
94890075Sobrien		return;
94990075Sobrien	}
95090075Sobrien
95190075Sobrien        /* Check for access permissions: */
95290075Sobrien        request_init(&ri, RQ_DAEMON, "huntd", RQ_FILE, s, 0);
95390075Sobrien        if (hosts_access(&ri) == 0) {
95490075Sobrien                close(s);
95590075Sobrien                return;
95690075Sobrien        }
95790075Sobrien
95890075Sobrien	fp = fdopen(s, "w");
959117395Skan	if (fp == NULL) {
960117395Skan		log(LOG_ERR, "fdopen");
961117395Skan		(void) close(s);
962117395Skan		return;
963117395Skan	}
96490075Sobrien
96590075Sobrien	print_stats(fp);
966117395Skan
967117395Skan	(void) fclose(fp);
96890075Sobrien}
96990075Sobrien
97090075Sobrien/*
97190075Sobrien * print_stats:
97290075Sobrien * 	emit the game statistics
97390075Sobrien */
97490075Sobrienvoid
97590075Sobrienprint_stats(fp)
97690075Sobrien	FILE *fp;
97790075Sobrien{
97890075Sobrien	IDENT	*ip;
97990075Sobrien	PLAYER  *pp;
98090075Sobrien
98190075Sobrien	/* Send the statistics as raw text down the socket: */
98290075Sobrien	fputs("Name\t\tScore\tDucked\tAbsorb\tFaced\tShot\tRobbed\tMissed\tSlimeK\n", fp);
98390075Sobrien	for (ip = Scores; ip != NULL; ip = ip->i_next) {
98490075Sobrien		fprintf(fp, "%s%c%c%c\t", ip->i_name,
98590075Sobrien			ip->i_team == ' ' ? ' ' : '[',
98690075Sobrien			ip->i_team,
98790075Sobrien			ip->i_team == ' ' ? ' ' : ']'
98890075Sobrien		);
98990075Sobrien		if (strlen(ip->i_name) + 3 < 8)
99090075Sobrien			putc('\t', fp);
99190075Sobrien		fprintf(fp, "%.2f\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
99290075Sobrien			ip->i_score, ip->i_ducked, ip->i_absorbed,
99390075Sobrien			ip->i_faced, ip->i_shot, ip->i_robbed,
99490075Sobrien			ip->i_missed, ip->i_slime);
99590075Sobrien	}
99690075Sobrien	fputs("\n\nName\t\tEnemy\tFriend\tDeaths\tStill\tSaved\tConnect\n", fp);
99790075Sobrien	for (ip = Scores; ip != NULL; ip = ip->i_next) {
99890075Sobrien		fprintf(fp, "%s%c%c%c\t", ip->i_name,
99990075Sobrien			ip->i_team == ' ' ? ' ' : '[',
100090075Sobrien			ip->i_team,
100190075Sobrien			ip->i_team == ' ' ? ' ' : ']'
100290075Sobrien		);
100390075Sobrien		if (strlen(ip->i_name) + 3 < 8)
100490075Sobrien			putc('\t', fp);
100590075Sobrien		fprintf(fp, "%d\t%d\t%d\t%d\t%d\t",
100690075Sobrien			ip->i_gkills, ip->i_bkills, ip->i_deaths,
100790075Sobrien			ip->i_stillb, ip->i_saved);
100890075Sobrien		for (pp = Player; pp < End_player; pp++)
100990075Sobrien			if (pp->p_ident == ip)
101090075Sobrien				putc('p', fp);
101190075Sobrien		for (pp = Monitor; pp < End_monitor; pp++)
101290075Sobrien			if (pp->p_ident == ip)
101390075Sobrien				putc('m', fp);
101490075Sobrien		putc('\n', fp);
101590075Sobrien	}
101690075Sobrien}
101790075Sobrien
101890075Sobrien
101990075Sobrien/*
102090075Sobrien * Send the game statistics to the controlling tty
102190075Sobrien */
102290075Sobrienstatic void
102390075Sobriensiginfo(sig)
102490075Sobrien	int sig;
102590075Sobrien{
102690075Sobrien	int tty;
102790075Sobrien	FILE *fp;
102890075Sobrien
102990075Sobrien	if ((tty = open(_PATH_TTY, O_WRONLY)) >= 0) {
103090075Sobrien		fp = fdopen(tty, "w");
103190075Sobrien		print_stats(fp);
103290075Sobrien		answer_info(fp);
103390075Sobrien		fclose(fp);
103490075Sobrien	}
103590075Sobrien}
103690075Sobrien
103790075Sobrien/*
103890075Sobrien * clear_scores:
103990075Sobrien *	Clear the Scores list.
104090075Sobrien */
104190075Sobrienstatic void
104290075Sobrienclear_scores()
104390075Sobrien{
104490075Sobrien	IDENT	*ip, *nextip;
104590075Sobrien
104690075Sobrien	/* Release the list of scores: */
1047117395Skan	for (ip = Scores; ip != NULL; ip = nextip) {
104890075Sobrien		nextip = ip->i_next;
104990075Sobrien		free((char *) ip);
105090075Sobrien	}
105190075Sobrien	Scores = NULL;
105290075Sobrien}
105390075Sobrien
105490075Sobrien/*
105590075Sobrien * announce_game:
105690075Sobrien *	Publically announce the game
105790075Sobrien */
105890075Sobrienstatic void
105996263Sobrienannounce_game()
106090075Sobrien{
106190075Sobrien
106290075Sobrien	/* Stub */
106390075Sobrien}
106490075Sobrien
106590075Sobrien/*
106690075Sobrien * Handle a UDP packet sent to the well known port.
106790075Sobrien */
106890075Sobrienstatic void
106990075Sobrienhandle_wkport(fd)
107090075Sobrien	int fd;
107190075Sobrien{
107290075Sobrien	struct sockaddr		fromaddr;
107390075Sobrien	int 			fromlen;
107490075Sobrien	u_int16_t		query;
107590075Sobrien	u_int16_t		response;
107690075Sobrien
107790075Sobrien	fromlen = sizeof fromaddr;
107890075Sobrien	if (recvfrom(fd, &query, sizeof query, 0, &fromaddr, &fromlen) == -1)
107990075Sobrien	{
108090075Sobrien		log(LOG_WARNING, "recvfrom");
108190075Sobrien		return;
108290075Sobrien	}
108390075Sobrien	query = ntohs(query);
108490075Sobrien
108590075Sobrien	switch (query) {
108690075Sobrien	  case C_MESSAGE:
108790075Sobrien		if (Nplayer <= 0)
108890075Sobrien			/* Don't bother replying if nobody to talk to: */
108990075Sobrien			return;
109090075Sobrien		/* Return the number of people playing: */
109190075Sobrien		response = Nplayer;
109290075Sobrien		break;
109390075Sobrien	  case C_SCORES:
109490075Sobrien		/* Someone wants the statistics port: */
109590075Sobrien		response = stat_port;
1096117395Skan		break;
109790075Sobrien	  case C_PLAYER:
109890075Sobrien	  case C_MONITOR:
109990075Sobrien		/* Someone wants to play or watch: */
110090075Sobrien		if (query == C_MONITOR && Nplayer <= 0)
110190075Sobrien			/* Don't bother replying if there's nothing to watch: */
110290075Sobrien			return;
110390075Sobrien		/* Otherwise, tell them how to get to the game: */
110490075Sobrien		response = sock_port;
110590075Sobrien		break;
110690075Sobrien	  default:
1107117395Skan		log(LOG_INFO, "unknown udp query %d", query);
1108117395Skan		return;
1109117395Skan	}
1110117395Skan
1111117395Skan	response = ntohs(response);
111290075Sobrien	if (sendto(fd, &response, sizeof response, 0,
111390075Sobrien	    &fromaddr, sizeof fromaddr) == -1)
111490075Sobrien		log(LOG_WARNING, "sendto");
111590075Sobrien}
111690075Sobrien