xref: /dragonfly/games/hunt/huntd/driver.c (revision d9f85b33)
16693db17SSascha Wildner /*-
282a5c12eSMatthew Dillon  * Copyright (c) 1983-2003, Regents of the University of California.
382a5c12eSMatthew Dillon  * All rights reserved.
482a5c12eSMatthew Dillon  *
582a5c12eSMatthew Dillon  * Redistribution and use in source and binary forms, with or without
682a5c12eSMatthew Dillon  * modification, are permitted provided that the following conditions are
782a5c12eSMatthew Dillon  * met:
882a5c12eSMatthew Dillon  *
96693db17SSascha Wildner  * 1. Redistributions of source code must retain the above copyright
1082a5c12eSMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
116693db17SSascha Wildner  * 2. Redistributions in binary form must reproduce the above copyright
1282a5c12eSMatthew Dillon  *    notice, this list of conditions and the following disclaimer in the
1382a5c12eSMatthew Dillon  *    documentation and/or other materials provided with the distribution.
146693db17SSascha Wildner  * 3. Neither the name of the University of California, San Francisco nor
1582a5c12eSMatthew Dillon  *    the names of its contributors may be used to endorse or promote
1682a5c12eSMatthew Dillon  *    products derived from this software without specific prior written
1782a5c12eSMatthew Dillon  *    permission.
1882a5c12eSMatthew Dillon  *
1982a5c12eSMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
2082a5c12eSMatthew Dillon  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2182a5c12eSMatthew Dillon  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
2282a5c12eSMatthew Dillon  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2382a5c12eSMatthew Dillon  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2482a5c12eSMatthew Dillon  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2582a5c12eSMatthew Dillon  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2682a5c12eSMatthew Dillon  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2782a5c12eSMatthew Dillon  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2882a5c12eSMatthew Dillon  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2982a5c12eSMatthew Dillon  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3082a5c12eSMatthew Dillon  *
3182a5c12eSMatthew Dillon  * $OpenBSD: driver.c,v 1.17 2007/04/02 14:55:16 jmc Exp $
3282a5c12eSMatthew Dillon  * $NetBSD: driver.c,v 1.5 1997/10/20 00:37:16 lukem Exp $
3382a5c12eSMatthew Dillon  */
3482a5c12eSMatthew Dillon 
3582a5c12eSMatthew Dillon #include <sys/ioctl.h>
3682a5c12eSMatthew Dillon #include <sys/stat.h>
3782a5c12eSMatthew Dillon #include <sys/time.h>
3882a5c12eSMatthew Dillon #include <sys/socket.h>
3982a5c12eSMatthew Dillon 
4082a5c12eSMatthew Dillon #include <netinet/in.h>
4182a5c12eSMatthew Dillon #include <arpa/inet.h>
4282a5c12eSMatthew Dillon 
4382a5c12eSMatthew Dillon #include <err.h>
4482a5c12eSMatthew Dillon #include <errno.h>
4582a5c12eSMatthew Dillon #include <signal.h>
4682a5c12eSMatthew Dillon #include <stdlib.h>
4782a5c12eSMatthew Dillon #include <string.h>
4882a5c12eSMatthew Dillon #include <unistd.h>
4982a5c12eSMatthew Dillon #include <stdio.h>
5082a5c12eSMatthew Dillon #include <tcpd.h>
5182a5c12eSMatthew Dillon #include <syslog.h>
5282a5c12eSMatthew Dillon #include <netdb.h>
5382a5c12eSMatthew Dillon #include <paths.h>
5482a5c12eSMatthew Dillon #include <fcntl.h>
5582a5c12eSMatthew Dillon #include "hunt.h"
5682a5c12eSMatthew Dillon #include "conf.h"
5782a5c12eSMatthew Dillon #include "server.h"
5882a5c12eSMatthew Dillon 
5982a5c12eSMatthew Dillon char	*First_arg;		/* pointer to argv[0] */
6082a5c12eSMatthew Dillon u_int16_t Server_port;
6182a5c12eSMatthew Dillon int	Server_socket;		/* test socket to answer datagrams */
6282a5c12eSMatthew Dillon FLAG	should_announce = TRUE;	/* true if listening on standard port */
6382a5c12eSMatthew Dillon u_short	sock_port;		/* port # of tcp listen socket */
6482a5c12eSMatthew Dillon u_short	stat_port;		/* port # of statistics tcp socket */
6582a5c12eSMatthew Dillon in_addr_t Server_addr = INADDR_ANY;	/* address to bind to */
6682a5c12eSMatthew Dillon 
6782a5c12eSMatthew Dillon static	void	clear_scores(void);
6882a5c12eSMatthew Dillon static	int	havechar(PLAYER *);
6982a5c12eSMatthew Dillon static	void	init(void);
7082a5c12eSMatthew Dillon static	void	makeboots(void);
7182a5c12eSMatthew Dillon static	void	send_stats(void);
7282a5c12eSMatthew Dillon static	void	zap(PLAYER *, FLAG);
7382a5c12eSMatthew Dillon static  void	announce_game(void);
7482a5c12eSMatthew Dillon static	void	siginfo(int);
7582a5c12eSMatthew Dillon static	void	print_stats(FILE *);
7682a5c12eSMatthew Dillon static	void	handle_wkport(int);
7782a5c12eSMatthew Dillon 
7882a5c12eSMatthew Dillon /*
7982a5c12eSMatthew Dillon  * main:
8082a5c12eSMatthew Dillon  *	The main program.
8182a5c12eSMatthew Dillon  */
8282a5c12eSMatthew Dillon int
main(int ac,char ** av)836beb426bSSascha Wildner main(int ac, char **av)
8482a5c12eSMatthew Dillon {
8582a5c12eSMatthew Dillon 	PLAYER		*pp;
8682a5c12eSMatthew Dillon 	int		had_char;
8782a5c12eSMatthew Dillon 	static fd_set	read_fds;
8882a5c12eSMatthew Dillon 	static FLAG	first = TRUE;
8982a5c12eSMatthew Dillon 	static FLAG	server = FALSE;
9082a5c12eSMatthew Dillon 	int		c;
9182a5c12eSMatthew Dillon 	static struct timeval	linger = { 0, 0 };
9282a5c12eSMatthew Dillon 	static struct timeval	timeout = { 0, 0 }, *to;
9382a5c12eSMatthew Dillon 	struct spawn	*sp, *spnext;
9482a5c12eSMatthew Dillon 	int		ret;
9582a5c12eSMatthew Dillon 	int		nready;
9682a5c12eSMatthew Dillon 	int		fd;
9782a5c12eSMatthew Dillon 
9882a5c12eSMatthew Dillon 	First_arg = av[0];
9982a5c12eSMatthew Dillon 
10082a5c12eSMatthew Dillon 	config();
10182a5c12eSMatthew Dillon 
10282a5c12eSMatthew Dillon 	while ((c = getopt(ac, av, "sp:a:D:")) != -1) {
10382a5c12eSMatthew Dillon 		switch (c) {
10482a5c12eSMatthew Dillon 		  case 's':
10582a5c12eSMatthew Dillon 			server = TRUE;
10682a5c12eSMatthew Dillon 			break;
10782a5c12eSMatthew Dillon 		  case 'p':
10882a5c12eSMatthew Dillon 			should_announce = FALSE;
10982a5c12eSMatthew Dillon 			Server_port = atoi(optarg);
11082a5c12eSMatthew Dillon 			break;
11182a5c12eSMatthew Dillon 		  case 'a':
11282a5c12eSMatthew Dillon 			if (!inet_aton(optarg, (struct in_addr *)&Server_addr))
11382a5c12eSMatthew Dillon 				err(1, "bad interface address: %s", optarg);
11482a5c12eSMatthew Dillon 			break;
11582a5c12eSMatthew Dillon 		  case 'D':
11682a5c12eSMatthew Dillon 			config_arg(optarg);
11782a5c12eSMatthew Dillon 			break;
11882a5c12eSMatthew Dillon 		  default:
11982a5c12eSMatthew Dillon erred:
12082a5c12eSMatthew Dillon 			fprintf(stderr,
12182a5c12eSMatthew Dillon 			    "usage: %s [-s] [-a addr] [-Dvar=value ...] "
12282a5c12eSMatthew Dillon 			    "[-p port]\n",
12382a5c12eSMatthew Dillon 			    av[0]);
12482a5c12eSMatthew Dillon 			exit(2);
12582a5c12eSMatthew Dillon 		}
12682a5c12eSMatthew Dillon 	}
12782a5c12eSMatthew Dillon 	if (optind < ac)
12882a5c12eSMatthew Dillon 		goto erred;
12982a5c12eSMatthew Dillon 
13082a5c12eSMatthew Dillon 	/* Open syslog: */
13182a5c12eSMatthew Dillon 	openlog("huntd", LOG_PID | (conf_logerr && !server? LOG_PERROR : 0),
13282a5c12eSMatthew Dillon 		LOG_DAEMON);
13382a5c12eSMatthew Dillon 
13482a5c12eSMatthew Dillon 	/* Initialise game parameters: */
13582a5c12eSMatthew Dillon 	init();
13682a5c12eSMatthew Dillon 
13782a5c12eSMatthew Dillon again:
13882a5c12eSMatthew Dillon 	do {
13982a5c12eSMatthew Dillon 		/* First, poll to see if we can get input */
14082a5c12eSMatthew Dillon 		do {
14182a5c12eSMatthew Dillon 			read_fds = Fds_mask;
14282a5c12eSMatthew Dillon 			errno = 0;
14382a5c12eSMatthew Dillon 			timerclear(&timeout);
14482a5c12eSMatthew Dillon 			nready = select(Num_fds, &read_fds, NULL, NULL,
14582a5c12eSMatthew Dillon 			    &timeout);
14682a5c12eSMatthew Dillon 			if (nready < 0 && errno != EINTR) {
14782a5c12eSMatthew Dillon 				logit(LOG_ERR, "select");
14882a5c12eSMatthew Dillon 				cleanup(1);
14982a5c12eSMatthew Dillon 			}
15082a5c12eSMatthew Dillon 		} while (nready < 0);
15182a5c12eSMatthew Dillon 
15282a5c12eSMatthew Dillon 		if (nready == 0) {
15382a5c12eSMatthew Dillon 			/*
15482a5c12eSMatthew Dillon 			 * Nothing was ready. We do some work now
15582a5c12eSMatthew Dillon 			 * to see if the simulation has any pending work
15682a5c12eSMatthew Dillon 			 * to do, and decide if we need to block
15782a5c12eSMatthew Dillon 			 * indefinitely or just timeout.
15882a5c12eSMatthew Dillon 			 */
15982a5c12eSMatthew Dillon 			do {
16082a5c12eSMatthew Dillon 				if (conf_simstep && can_moveshots()) {
16182a5c12eSMatthew Dillon 				/*
16282a5c12eSMatthew Dillon 				 * block for a short time before continuing
16382a5c12eSMatthew Dillon 				 * with explosions, bullets and whatnot
16482a5c12eSMatthew Dillon 				 */
16582a5c12eSMatthew Dillon 					to = &timeout;
16682a5c12eSMatthew Dillon 					to->tv_sec =  conf_simstep / 1000000;
16782a5c12eSMatthew Dillon 					to->tv_usec = conf_simstep % 1000000;
16882a5c12eSMatthew Dillon 				} else
16982a5c12eSMatthew Dillon 				/*
17082a5c12eSMatthew Dillon 				 * since there's nothing going on,
17182a5c12eSMatthew Dillon 				 * just block waiting for external activity
17282a5c12eSMatthew Dillon 				 */
17382a5c12eSMatthew Dillon 					to = NULL;
17482a5c12eSMatthew Dillon 
17582a5c12eSMatthew Dillon 				read_fds = Fds_mask;
17682a5c12eSMatthew Dillon 				errno = 0;
17782a5c12eSMatthew Dillon 				nready = select(Num_fds, &read_fds, NULL, NULL,
17882a5c12eSMatthew Dillon 				    to);
17982a5c12eSMatthew Dillon 				if (nready < 0 && errno != EINTR) {
18082a5c12eSMatthew Dillon 					logit(LOG_ERR, "select");
18182a5c12eSMatthew Dillon 					cleanup(1);
18282a5c12eSMatthew Dillon 				}
18382a5c12eSMatthew Dillon 			} while (nready < 0);
18482a5c12eSMatthew Dillon 		}
18582a5c12eSMatthew Dillon 
18682a5c12eSMatthew Dillon 		/* Remember which descriptors are active: */
18782a5c12eSMatthew Dillon 		Have_inp = read_fds;
18882a5c12eSMatthew Dillon 
18982a5c12eSMatthew Dillon 		/* Answer new player connections: */
19082a5c12eSMatthew Dillon 		if (FD_ISSET(Socket, &Have_inp))
19182a5c12eSMatthew Dillon 			answer_first();
19282a5c12eSMatthew Dillon 
19382a5c12eSMatthew Dillon 		/* Continue answering new player connections: */
19482a5c12eSMatthew Dillon 		for (sp = Spawn; sp; ) {
19582a5c12eSMatthew Dillon 			spnext = sp->next;
19682a5c12eSMatthew Dillon 			fd = sp->fd;
19782a5c12eSMatthew Dillon 			if (FD_ISSET(fd, &Have_inp) && answer_next(sp)) {
19882a5c12eSMatthew Dillon 				/*
19982a5c12eSMatthew Dillon 				 * Remove from the spawn list. (fd remains in
20082a5c12eSMatthew Dillon 				 * read set).
20182a5c12eSMatthew Dillon 				 */
20282a5c12eSMatthew Dillon 				*sp->prevnext = sp->next;
20382a5c12eSMatthew Dillon 				if (sp->next)
20482a5c12eSMatthew Dillon 					sp->next->prevnext = sp->prevnext;
20582a5c12eSMatthew Dillon 				free(sp);
20682a5c12eSMatthew Dillon 
20782a5c12eSMatthew Dillon 				/* We probably consumed all data. */
20882a5c12eSMatthew Dillon 				FD_CLR(fd, &Have_inp);
20982a5c12eSMatthew Dillon 
21082a5c12eSMatthew Dillon 				/* Announce game if this is the first spawn. */
21182a5c12eSMatthew Dillon 				if (first && should_announce)
21282a5c12eSMatthew Dillon 					announce_game();
21382a5c12eSMatthew Dillon 				first = FALSE;
21482a5c12eSMatthew Dillon 			}
21582a5c12eSMatthew Dillon 			sp = spnext;
21682a5c12eSMatthew Dillon 		}
21782a5c12eSMatthew Dillon 
21882a5c12eSMatthew Dillon 		/* Process input and move bullets until we've exhausted input */
21982a5c12eSMatthew Dillon 		had_char = TRUE;
22082a5c12eSMatthew Dillon 		while (had_char) {
22182a5c12eSMatthew Dillon 
22282a5c12eSMatthew Dillon 			moveshots();
22382a5c12eSMatthew Dillon 			for (pp = Player; pp < End_player; )
22482a5c12eSMatthew Dillon 				if (pp->p_death[0] != '\0')
22582a5c12eSMatthew Dillon 					zap(pp, TRUE);
22682a5c12eSMatthew Dillon 				else
22782a5c12eSMatthew Dillon 					pp++;
22882a5c12eSMatthew Dillon 			for (pp = Monitor; pp < End_monitor; )
22982a5c12eSMatthew Dillon 				if (pp->p_death[0] != '\0')
23082a5c12eSMatthew Dillon 					zap(pp, FALSE);
23182a5c12eSMatthew Dillon 				else
23282a5c12eSMatthew Dillon 					pp++;
23382a5c12eSMatthew Dillon 
23482a5c12eSMatthew Dillon 			had_char = FALSE;
23582a5c12eSMatthew Dillon 			for (pp = Player; pp < End_player; pp++)
23682a5c12eSMatthew Dillon 				if (havechar(pp)) {
23782a5c12eSMatthew Dillon 					execute(pp);
23882a5c12eSMatthew Dillon 					pp->p_nexec++;
23982a5c12eSMatthew Dillon 					had_char = TRUE;
24082a5c12eSMatthew Dillon 				}
24182a5c12eSMatthew Dillon 			for (pp = Monitor; pp < End_monitor; pp++)
24282a5c12eSMatthew Dillon 				if (havechar(pp)) {
24382a5c12eSMatthew Dillon 					mon_execute(pp);
24482a5c12eSMatthew Dillon 					pp->p_nexec++;
24582a5c12eSMatthew Dillon 					had_char = TRUE;
24682a5c12eSMatthew Dillon 				}
24782a5c12eSMatthew Dillon 		}
24882a5c12eSMatthew Dillon 
24982a5c12eSMatthew Dillon 		/* Handle a datagram sent to the server socket: */
25082a5c12eSMatthew Dillon 		if (FD_ISSET(Server_socket, &Have_inp))
25182a5c12eSMatthew Dillon 			handle_wkport(Server_socket);
25282a5c12eSMatthew Dillon 
25382a5c12eSMatthew Dillon 		/* Answer statistics connections: */
25482a5c12eSMatthew Dillon 		if (FD_ISSET(Status, &Have_inp))
25582a5c12eSMatthew Dillon 			send_stats();
25682a5c12eSMatthew Dillon 
25782a5c12eSMatthew Dillon 		/* Flush/synchronize all the displays: */
25882a5c12eSMatthew Dillon 		for (pp = Player; pp < End_player; pp++) {
25982a5c12eSMatthew Dillon 			if (FD_ISSET(pp->p_fd, &read_fds)) {
26082a5c12eSMatthew Dillon 				sendcom(pp, READY, pp->p_nexec);
26182a5c12eSMatthew Dillon 				pp->p_nexec = 0;
26282a5c12eSMatthew Dillon 			}
26382a5c12eSMatthew Dillon 			flush(pp);
26482a5c12eSMatthew Dillon 		}
26582a5c12eSMatthew Dillon 		for (pp = Monitor; pp < End_monitor; pp++) {
26682a5c12eSMatthew Dillon 			if (FD_ISSET(pp->p_fd, &read_fds)) {
26782a5c12eSMatthew Dillon 				sendcom(pp, READY, pp->p_nexec);
26882a5c12eSMatthew Dillon 				pp->p_nexec = 0;
26982a5c12eSMatthew Dillon 			}
27082a5c12eSMatthew Dillon 			flush(pp);
27182a5c12eSMatthew Dillon 		}
27282a5c12eSMatthew Dillon 	} while (Nplayer > 0);
27382a5c12eSMatthew Dillon 
27482a5c12eSMatthew Dillon 	/* No more players! */
27582a5c12eSMatthew Dillon 
27682a5c12eSMatthew Dillon 	/* No players yet or a continuous game? */
27782a5c12eSMatthew Dillon 	if (first || conf_linger < 0)
27882a5c12eSMatthew Dillon 		goto again;
27982a5c12eSMatthew Dillon 
28082a5c12eSMatthew Dillon 	/* Wait a short while for one to come back: */
28182a5c12eSMatthew Dillon 	read_fds = Fds_mask;
28282a5c12eSMatthew Dillon 	linger.tv_sec = conf_linger;
28382a5c12eSMatthew Dillon 	while ((ret = select(Num_fds, &read_fds, NULL, NULL, &linger)) < 0) {
28482a5c12eSMatthew Dillon 		if (errno != EINTR) {
28582a5c12eSMatthew Dillon 			logit(LOG_WARNING, "select");
28682a5c12eSMatthew Dillon 			break;
28782a5c12eSMatthew Dillon 		}
28882a5c12eSMatthew Dillon 		read_fds = Fds_mask;
28982a5c12eSMatthew Dillon 		linger.tv_sec = conf_linger;
29082a5c12eSMatthew Dillon 		linger.tv_usec = 0;
29182a5c12eSMatthew Dillon 	}
29282a5c12eSMatthew Dillon 	if (ret > 0)
29382a5c12eSMatthew Dillon 		/* Someone returned! Resume the game: */
29482a5c12eSMatthew Dillon 		goto again;
29582a5c12eSMatthew Dillon 	/* else, it timed out, and the game is really over. */
29682a5c12eSMatthew Dillon 
29782a5c12eSMatthew Dillon 	/* If we are an inetd server, we should re-init the map and restart: */
29882a5c12eSMatthew Dillon 	if (server) {
29982a5c12eSMatthew Dillon 		clear_scores();
30082a5c12eSMatthew Dillon 		makemaze();
30182a5c12eSMatthew Dillon 		clearwalls();
30282a5c12eSMatthew Dillon 		makeboots();
30382a5c12eSMatthew Dillon 		first = TRUE;
30482a5c12eSMatthew Dillon 		goto again;
30582a5c12eSMatthew Dillon 	}
30682a5c12eSMatthew Dillon 
30782a5c12eSMatthew Dillon 	/* Get rid of any attached monitors: */
30882a5c12eSMatthew Dillon 	for (pp = Monitor; pp < End_monitor; )
30982a5c12eSMatthew Dillon 		zap(pp, FALSE);
31082a5c12eSMatthew Dillon 
31182a5c12eSMatthew Dillon 	/* Fin: */
31282a5c12eSMatthew Dillon 	cleanup(0);
31382a5c12eSMatthew Dillon 	exit(0);
31482a5c12eSMatthew Dillon }
31582a5c12eSMatthew Dillon 
31682a5c12eSMatthew Dillon /*
31782a5c12eSMatthew Dillon  * init:
31882a5c12eSMatthew Dillon  *	Initialize the global parameters.
31982a5c12eSMatthew Dillon  */
32082a5c12eSMatthew Dillon static void
init(void)3216beb426bSSascha Wildner init(void)
32282a5c12eSMatthew Dillon {
32382a5c12eSMatthew Dillon 	int	i;
32482a5c12eSMatthew Dillon 	struct sockaddr_in	test_port;
32582a5c12eSMatthew Dillon 	int	true = 1;
32682a5c12eSMatthew Dillon 	socklen_t	len;
32782a5c12eSMatthew Dillon 	struct sockaddr_in	addr;
32882a5c12eSMatthew Dillon 	struct sigaction	sact;
32982a5c12eSMatthew Dillon 	struct servent *se;
33082a5c12eSMatthew Dillon 
331*d9f85b33Szrj 	setsid();
33282a5c12eSMatthew Dillon 	if (setpgid(getpid(), getpid()) == -1)
33382a5c12eSMatthew Dillon 		err(1, "setpgid");
33482a5c12eSMatthew Dillon 
33582a5c12eSMatthew Dillon 	sact.sa_flags = SA_RESTART;
33682a5c12eSMatthew Dillon 	sigemptyset(&sact.sa_mask);
33782a5c12eSMatthew Dillon 
33882a5c12eSMatthew Dillon 	/* Ignore HUP, QUIT and PIPE: */
33982a5c12eSMatthew Dillon 	sact.sa_handler = SIG_IGN;
34082a5c12eSMatthew Dillon 	if (sigaction(SIGHUP, &sact, NULL) == -1)
34182a5c12eSMatthew Dillon 		err(1, "sigaction SIGHUP");
34282a5c12eSMatthew Dillon 	if (sigaction(SIGQUIT, &sact, NULL) == -1)
34382a5c12eSMatthew Dillon 		err(1, "sigaction SIGQUIT");
34482a5c12eSMatthew Dillon 	if (sigaction(SIGPIPE, &sact, NULL) == -1)
34582a5c12eSMatthew Dillon 		err(1, "sigaction SIGPIPE");
34682a5c12eSMatthew Dillon 
34782a5c12eSMatthew Dillon 	/* Clean up gracefully on INT and TERM: */
34882a5c12eSMatthew Dillon 	sact.sa_handler = cleanup;
34982a5c12eSMatthew Dillon 	if (sigaction(SIGINT, &sact, NULL) == -1)
35082a5c12eSMatthew Dillon 		err(1, "sigaction SIGINT");
35182a5c12eSMatthew Dillon 	if (sigaction(SIGTERM, &sact, NULL) == -1)
35282a5c12eSMatthew Dillon 		err(1, "sigaction SIGTERM");
35382a5c12eSMatthew Dillon 
35482a5c12eSMatthew Dillon 	/* Handle INFO: */
35582a5c12eSMatthew Dillon 	sact.sa_handler = siginfo;
35682a5c12eSMatthew Dillon 	if (sigaction(SIGINFO, &sact, NULL) == -1)
35782a5c12eSMatthew Dillon 		err(1, "sigaction SIGINFO");
35882a5c12eSMatthew Dillon 
35982a5c12eSMatthew Dillon 	if (chdir("/") == -1)
36082a5c12eSMatthew Dillon 		warn("chdir");
361*d9f85b33Szrj 	umask(0777);
36282a5c12eSMatthew Dillon 
36382a5c12eSMatthew Dillon 	/* Initialize statistics socket: */
36482a5c12eSMatthew Dillon 	addr.sin_family = AF_INET;
36582a5c12eSMatthew Dillon 	addr.sin_addr.s_addr = Server_addr;
36682a5c12eSMatthew Dillon 	addr.sin_port = 0;
36782a5c12eSMatthew Dillon 
36882a5c12eSMatthew Dillon 	Status = socket(AF_INET, SOCK_STREAM, 0);
36982a5c12eSMatthew Dillon 	if (bind(Status, (struct sockaddr *) &addr, sizeof addr) < 0) {
37082a5c12eSMatthew Dillon 		logit(LOG_ERR, "bind");
37182a5c12eSMatthew Dillon 		cleanup(1);
37282a5c12eSMatthew Dillon 	}
37382a5c12eSMatthew Dillon 	if (listen(Status, 5) == -1) {
37482a5c12eSMatthew Dillon 		logit(LOG_ERR, "listen");
37582a5c12eSMatthew Dillon 		cleanup(1);
37682a5c12eSMatthew Dillon 	}
37782a5c12eSMatthew Dillon 
37882a5c12eSMatthew Dillon 	len = sizeof (struct sockaddr_in);
37982a5c12eSMatthew Dillon 	if (getsockname(Status, (struct sockaddr *) &addr, &len) < 0)  {
38082a5c12eSMatthew Dillon 		logit(LOG_ERR, "getsockname");
38182a5c12eSMatthew Dillon 		cleanup(1);
38282a5c12eSMatthew Dillon 	}
38382a5c12eSMatthew Dillon 	stat_port = ntohs(addr.sin_port);
38482a5c12eSMatthew Dillon 
38582a5c12eSMatthew Dillon 	/* Initialize main socket: */
38682a5c12eSMatthew Dillon 	addr.sin_family = AF_INET;
38782a5c12eSMatthew Dillon 	addr.sin_addr.s_addr = Server_addr;
38882a5c12eSMatthew Dillon 	addr.sin_port = 0;
38982a5c12eSMatthew Dillon 
39082a5c12eSMatthew Dillon 	Socket = socket(AF_INET, SOCK_STREAM, 0);
39182a5c12eSMatthew Dillon 
39282a5c12eSMatthew Dillon 	if (bind(Socket, (struct sockaddr *) &addr, sizeof addr) < 0) {
39382a5c12eSMatthew Dillon 		logit(LOG_ERR, "bind");
39482a5c12eSMatthew Dillon 		cleanup(1);
39582a5c12eSMatthew Dillon 	}
39682a5c12eSMatthew Dillon 	if (listen(Socket, 5) == -1) {
39782a5c12eSMatthew Dillon 		logit(LOG_ERR, "listen");
39882a5c12eSMatthew Dillon 		cleanup(1);
39982a5c12eSMatthew Dillon 	}
40082a5c12eSMatthew Dillon 
40182a5c12eSMatthew Dillon 	len = sizeof (struct sockaddr_in);
40282a5c12eSMatthew Dillon 	if (getsockname(Socket, (struct sockaddr *) &addr, &len) < 0)  {
40382a5c12eSMatthew Dillon 		logit(LOG_ERR, "getsockname");
40482a5c12eSMatthew Dillon 		cleanup(1);
40582a5c12eSMatthew Dillon 	}
40682a5c12eSMatthew Dillon 	sock_port = ntohs(addr.sin_port);
40782a5c12eSMatthew Dillon 
40882a5c12eSMatthew Dillon 	/* Initialize minimal select mask */
40982a5c12eSMatthew Dillon 	FD_ZERO(&Fds_mask);
41082a5c12eSMatthew Dillon 	FD_SET(Socket, &Fds_mask);
41182a5c12eSMatthew Dillon 	FD_SET(Status, &Fds_mask);
41282a5c12eSMatthew Dillon 	Num_fds = ((Socket > Status) ? Socket : Status) + 1;
41382a5c12eSMatthew Dillon 
41482a5c12eSMatthew Dillon 	/* Find the port that huntd should run on */
41582a5c12eSMatthew Dillon 	if (Server_port == 0) {
41682a5c12eSMatthew Dillon 		se = getservbyname("hunt", "udp");
41782a5c12eSMatthew Dillon 		if (se != NULL)
41882a5c12eSMatthew Dillon 			Server_port = ntohs(se->s_port);
41982a5c12eSMatthew Dillon 		else
42082a5c12eSMatthew Dillon 			Server_port = HUNT_PORT;
42182a5c12eSMatthew Dillon 	}
42282a5c12eSMatthew Dillon 
42382a5c12eSMatthew Dillon 	/* Check if stdin is a socket: */
42482a5c12eSMatthew Dillon 	len = sizeof (struct sockaddr_in);
42582a5c12eSMatthew Dillon 	if (getsockname(STDIN_FILENO, (struct sockaddr *) &test_port, &len) >= 0
42682a5c12eSMatthew Dillon 	    && test_port.sin_family == AF_INET) {
42782a5c12eSMatthew Dillon 		/* We are probably running from inetd:  don't log to stderr */
42882a5c12eSMatthew Dillon 		Server_socket = STDIN_FILENO;
42982a5c12eSMatthew Dillon 		conf_logerr = 0;
43082a5c12eSMatthew Dillon 		if (test_port.sin_port != htons((u_short) Server_port)) {
43182a5c12eSMatthew Dillon 			/* Private game */
43282a5c12eSMatthew Dillon 			should_announce = FALSE;
43382a5c12eSMatthew Dillon 			Server_port = ntohs(test_port.sin_port);
43482a5c12eSMatthew Dillon 		}
43582a5c12eSMatthew Dillon 	} else {
43682a5c12eSMatthew Dillon 		/* We need to listen on a socket: */
43782a5c12eSMatthew Dillon 		test_port = addr;
43882a5c12eSMatthew Dillon 		test_port.sin_port = htons((u_short) Server_port);
43982a5c12eSMatthew Dillon 
44082a5c12eSMatthew Dillon 		Server_socket = socket(AF_INET, SOCK_DGRAM, 0);
44182a5c12eSMatthew Dillon 
44282a5c12eSMatthew Dillon 		/* Permit multiple huntd's on the same port. */
44382a5c12eSMatthew Dillon 		if (setsockopt(Server_socket, SOL_SOCKET, SO_REUSEPORT, &true,
44482a5c12eSMatthew Dillon 		    sizeof true) < 0)
44582a5c12eSMatthew Dillon 			logit(LOG_ERR, "setsockopt SO_REUSEADDR");
44682a5c12eSMatthew Dillon 
44782a5c12eSMatthew Dillon 		if (bind(Server_socket, (struct sockaddr *) &test_port,
44882a5c12eSMatthew Dillon 		    sizeof test_port) < 0) {
44982a5c12eSMatthew Dillon 			logit(LOG_ERR, "bind port %d", Server_port);
45082a5c12eSMatthew Dillon 			cleanup(1);
45182a5c12eSMatthew Dillon 		}
45282a5c12eSMatthew Dillon 
45382a5c12eSMatthew Dillon 		/* Datagram sockets do not need a listen() call. */
45482a5c12eSMatthew Dillon 	}
45582a5c12eSMatthew Dillon 
45682a5c12eSMatthew Dillon 	/* We'll handle the broadcast listener in the main loop: */
45782a5c12eSMatthew Dillon 	FD_SET(Server_socket, &Fds_mask);
45882a5c12eSMatthew Dillon 	if (Server_socket + 1 > Num_fds)
45982a5c12eSMatthew Dillon 		Num_fds = Server_socket + 1;
46082a5c12eSMatthew Dillon 
46182a5c12eSMatthew Dillon 	/* Initialise the random seed: */
46282a5c12eSMatthew Dillon 	srandomdev();
46382a5c12eSMatthew Dillon 
46482a5c12eSMatthew Dillon 	/* Dig the maze: */
46582a5c12eSMatthew Dillon 	makemaze();
46682a5c12eSMatthew Dillon 
46782a5c12eSMatthew Dillon 	/* Create some boots, if needed: */
46882a5c12eSMatthew Dillon 	makeboots();
46982a5c12eSMatthew Dillon 
47082a5c12eSMatthew Dillon 	/* Construct a table of what objects a player can see over: */
47182a5c12eSMatthew Dillon 	for (i = 0; i < NASCII; i++)
47282a5c12eSMatthew Dillon 		See_over[i] = TRUE;
47382a5c12eSMatthew Dillon 	See_over[DOOR] = FALSE;
47482a5c12eSMatthew Dillon 	See_over[WALL1] = FALSE;
47582a5c12eSMatthew Dillon 	See_over[WALL2] = FALSE;
47682a5c12eSMatthew Dillon 	See_over[WALL3] = FALSE;
47782a5c12eSMatthew Dillon 	See_over[WALL4] = FALSE;
47882a5c12eSMatthew Dillon 	See_over[WALL5] = FALSE;
47982a5c12eSMatthew Dillon 
48082a5c12eSMatthew Dillon 	logx(LOG_INFO, "game started");
48182a5c12eSMatthew Dillon }
48282a5c12eSMatthew Dillon 
48382a5c12eSMatthew Dillon /*
48482a5c12eSMatthew Dillon  * makeboots:
48582a5c12eSMatthew Dillon  *	Put the boots in the maze
48682a5c12eSMatthew Dillon  */
48782a5c12eSMatthew Dillon static void
makeboots(void)4886beb426bSSascha Wildner makeboots(void)
48982a5c12eSMatthew Dillon {
49082a5c12eSMatthew Dillon 	int	x, y;
49182a5c12eSMatthew Dillon 	PLAYER	*pp;
49282a5c12eSMatthew Dillon 
49382a5c12eSMatthew Dillon 	if (conf_boots) {
49482a5c12eSMatthew Dillon 		do {
49582a5c12eSMatthew Dillon 			x = rand_num(WIDTH - 1) + 1;
49682a5c12eSMatthew Dillon 			y = rand_num(HEIGHT - 1) + 1;
49782a5c12eSMatthew Dillon 		} while (Maze[y][x] != SPACE);
49882a5c12eSMatthew Dillon 		Maze[y][x] = BOOT_PAIR;
49982a5c12eSMatthew Dillon 	}
50082a5c12eSMatthew Dillon 
50182a5c12eSMatthew Dillon 	for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
50282a5c12eSMatthew Dillon 		pp->p_flying = -1;
50382a5c12eSMatthew Dillon }
50482a5c12eSMatthew Dillon 
50582a5c12eSMatthew Dillon 
50682a5c12eSMatthew Dillon /*
50782a5c12eSMatthew Dillon  * checkdam:
50882a5c12eSMatthew Dillon  *	Apply damage to the victim from an attacker.
50982a5c12eSMatthew Dillon  *	If the victim dies as a result, give points to 'credit',
51082a5c12eSMatthew Dillon  */
51182a5c12eSMatthew Dillon void
checkdam(PLAYER * victim,PLAYER * attacker,IDENT * credit,int damage,char stype)5126beb426bSSascha Wildner checkdam(PLAYER *victim, PLAYER *attacker, IDENT *credit, int damage,
5136beb426bSSascha Wildner     char stype)
51482a5c12eSMatthew Dillon {
51582a5c12eSMatthew Dillon 	const char	*cp;
51682a5c12eSMatthew Dillon 	int	y;
51782a5c12eSMatthew Dillon 
51882a5c12eSMatthew Dillon 	/* Don't do anything if the victim is already in the throes of death */
51982a5c12eSMatthew Dillon 	if (victim->p_death[0] != '\0')
52082a5c12eSMatthew Dillon 		return;
52182a5c12eSMatthew Dillon 
52282a5c12eSMatthew Dillon 	/* Weaken slime attacks by 0.5 * number of boots the victim has on: */
52382a5c12eSMatthew Dillon 	if (stype == SLIME)
52482a5c12eSMatthew Dillon 		switch (victim->p_nboots) {
52582a5c12eSMatthew Dillon 		  default:
52682a5c12eSMatthew Dillon 			break;
52782a5c12eSMatthew Dillon 		  case 1:
52882a5c12eSMatthew Dillon 			damage = (damage + 1) / 2;
52982a5c12eSMatthew Dillon 			break;
53082a5c12eSMatthew Dillon 		  case 2:
53182a5c12eSMatthew Dillon 			if (attacker != NULL)
53282a5c12eSMatthew Dillon 				message(attacker, "He has boots on!");
53382a5c12eSMatthew Dillon 			return;
53482a5c12eSMatthew Dillon 		}
53582a5c12eSMatthew Dillon 
53682a5c12eSMatthew Dillon 	/* The victim sustains some damage: */
53782a5c12eSMatthew Dillon 	victim->p_damage += damage;
53882a5c12eSMatthew Dillon 
53982a5c12eSMatthew Dillon 	/* Check if the victim survives the hit: */
54082a5c12eSMatthew Dillon 	if (victim->p_damage <= victim->p_damcap) {
54182a5c12eSMatthew Dillon 		/* They survive. */
54282a5c12eSMatthew Dillon 		outyx(victim, STAT_DAM_ROW, STAT_VALUE_COL, "%2d",
54382a5c12eSMatthew Dillon 			victim->p_damage);
54482a5c12eSMatthew Dillon 		return;
54582a5c12eSMatthew Dillon 	}
54682a5c12eSMatthew Dillon 
54782a5c12eSMatthew Dillon 	/* Describe how the victim died: */
54882a5c12eSMatthew Dillon 	switch (stype) {
54982a5c12eSMatthew Dillon 	  default:
55082a5c12eSMatthew Dillon 		cp = "Killed";
55182a5c12eSMatthew Dillon 		break;
55282a5c12eSMatthew Dillon 	  case FALL:
55382a5c12eSMatthew Dillon 		cp = "Killed on impact";
55482a5c12eSMatthew Dillon 		break;
55582a5c12eSMatthew Dillon 	  case KNIFE:
55682a5c12eSMatthew Dillon 		cp = "Stabbed to death";
55782a5c12eSMatthew Dillon 		victim->p_ammo = 0;		/* No exploding */
55882a5c12eSMatthew Dillon 		break;
55982a5c12eSMatthew Dillon 	  case SHOT:
56082a5c12eSMatthew Dillon 		cp = "Shot to death";
56182a5c12eSMatthew Dillon 		break;
56282a5c12eSMatthew Dillon 	  case GRENADE:
56382a5c12eSMatthew Dillon 	  case SATCHEL:
56482a5c12eSMatthew Dillon 	  case BOMB:
56582a5c12eSMatthew Dillon 		cp = "Bombed";
56682a5c12eSMatthew Dillon 		break;
56782a5c12eSMatthew Dillon 	  case MINE:
56882a5c12eSMatthew Dillon 	  case GMINE:
56982a5c12eSMatthew Dillon 		cp = "Blown apart";
57082a5c12eSMatthew Dillon 		break;
57182a5c12eSMatthew Dillon 	  case SLIME:
57282a5c12eSMatthew Dillon 		cp = "Slimed";
57382a5c12eSMatthew Dillon 		if (credit != NULL)
57482a5c12eSMatthew Dillon 			credit->i_slime++;
57582a5c12eSMatthew Dillon 		break;
57682a5c12eSMatthew Dillon 	  case LAVA:
57782a5c12eSMatthew Dillon 		cp = "Baked";
57882a5c12eSMatthew Dillon 		break;
57982a5c12eSMatthew Dillon 	  case DSHOT:
58082a5c12eSMatthew Dillon 		cp = "Eliminated";
58182a5c12eSMatthew Dillon 		break;
58282a5c12eSMatthew Dillon 	}
58382a5c12eSMatthew Dillon 
58482a5c12eSMatthew Dillon 	if (credit == NULL) {
58582a5c12eSMatthew Dillon 		const char *blame;
58682a5c12eSMatthew Dillon 
58782a5c12eSMatthew Dillon 		/*
58882a5c12eSMatthew Dillon 		 * Nobody is taking the credit for the kill.
58982a5c12eSMatthew Dillon 		 * Attribute it to either a mine or 'act of God'.
59082a5c12eSMatthew Dillon 		 */
59182a5c12eSMatthew Dillon 		switch (stype) {
59282a5c12eSMatthew Dillon 		case MINE:
59382a5c12eSMatthew Dillon 		case GMINE:
59482a5c12eSMatthew Dillon 			blame = "a mine";
59582a5c12eSMatthew Dillon 			break;
59682a5c12eSMatthew Dillon 		default:
59782a5c12eSMatthew Dillon 			blame = "act of God";
59882a5c12eSMatthew Dillon 			break;
59982a5c12eSMatthew Dillon 		}
60082a5c12eSMatthew Dillon 
60182a5c12eSMatthew Dillon 		/* Set the death message: */
602*d9f85b33Szrj 		snprintf(victim->p_death, sizeof victim->p_death,
60382a5c12eSMatthew Dillon 			"| %s by %s |", cp, blame);
60482a5c12eSMatthew Dillon 
60582a5c12eSMatthew Dillon 		/* No further score crediting needed. */
60682a5c12eSMatthew Dillon 		return;
60782a5c12eSMatthew Dillon 	}
60882a5c12eSMatthew Dillon 
60982a5c12eSMatthew Dillon 	/* Set the death message: */
610*d9f85b33Szrj 	snprintf(victim->p_death, sizeof victim->p_death,
61182a5c12eSMatthew Dillon 		"| %s by %s |", cp, credit->i_name);
61282a5c12eSMatthew Dillon 
61382a5c12eSMatthew Dillon 	if (victim == attacker) {
61482a5c12eSMatthew Dillon 		/* No use killing yourself. */
61582a5c12eSMatthew Dillon 		credit->i_kills--;
61682a5c12eSMatthew Dillon 		credit->i_bkills++;
61782a5c12eSMatthew Dillon 	}
61882a5c12eSMatthew Dillon 	else if (victim->p_ident->i_team == ' '
61982a5c12eSMatthew Dillon 	    || victim->p_ident->i_team != credit->i_team) {
62082a5c12eSMatthew Dillon 		/* A cross-team kill: */
62182a5c12eSMatthew Dillon 		credit->i_kills++;
62282a5c12eSMatthew Dillon 		credit->i_gkills++;
62382a5c12eSMatthew Dillon 	}
62482a5c12eSMatthew Dillon 	else {
62582a5c12eSMatthew Dillon 		/* They killed someone on the same team: */
62682a5c12eSMatthew Dillon 		credit->i_kills--;
62782a5c12eSMatthew Dillon 		credit->i_bkills++;
62882a5c12eSMatthew Dillon 	}
62982a5c12eSMatthew Dillon 
63082a5c12eSMatthew Dillon 	/* Compute the new credited score: */
63182a5c12eSMatthew Dillon 	credit->i_score = credit->i_kills / (double) credit->i_entries;
63282a5c12eSMatthew Dillon 
63382a5c12eSMatthew Dillon 	/* The victim accrues one death: */
63482a5c12eSMatthew Dillon 	victim->p_ident->i_deaths++;
63582a5c12eSMatthew Dillon 
63682a5c12eSMatthew Dillon 	/* Account for 'Stillborn' deaths */
63782a5c12eSMatthew Dillon 	if (victim->p_nchar == 0)
63882a5c12eSMatthew Dillon 		victim->p_ident->i_stillb++;
63982a5c12eSMatthew Dillon 
64082a5c12eSMatthew Dillon 	if (attacker) {
64182a5c12eSMatthew Dillon 		/* Give the attacker player a bit more strength */
64282a5c12eSMatthew Dillon 		attacker->p_damcap += conf_killgain;
64382a5c12eSMatthew Dillon 		attacker->p_damage -= conf_killgain;
64482a5c12eSMatthew Dillon 		if (attacker->p_damage < 0)
64582a5c12eSMatthew Dillon 			attacker->p_damage = 0;
64682a5c12eSMatthew Dillon 
64782a5c12eSMatthew Dillon 		/* Tell the attacker his new strength: */
64882a5c12eSMatthew Dillon 		outyx(attacker, STAT_DAM_ROW, STAT_VALUE_COL, "%2d/%2d",
64982a5c12eSMatthew Dillon 			attacker->p_damage, attacker->p_damcap);
65082a5c12eSMatthew Dillon 
65182a5c12eSMatthew Dillon 		/* Tell the attacker his new 'kill count': */
65282a5c12eSMatthew Dillon 		outyx(attacker, STAT_KILL_ROW, STAT_VALUE_COL, "%3d",
65382a5c12eSMatthew Dillon 			(attacker->p_damcap - conf_maxdam) / 2);
65482a5c12eSMatthew Dillon 
65582a5c12eSMatthew Dillon 		/* Update the attacker's score for everyone else */
65682a5c12eSMatthew Dillon 		y = STAT_PLAY_ROW + 1 + (attacker - Player);
65782a5c12eSMatthew Dillon 		outyx(ALL_PLAYERS, y, STAT_NAME_COL,
65882a5c12eSMatthew Dillon 			"%5.2f", attacker->p_ident->i_score);
65982a5c12eSMatthew Dillon 	}
66082a5c12eSMatthew Dillon }
66182a5c12eSMatthew Dillon 
66282a5c12eSMatthew Dillon /*
66382a5c12eSMatthew Dillon  * zap:
66482a5c12eSMatthew Dillon  *	Kill off a player and take them out of the game.
66582a5c12eSMatthew Dillon  *	The 'was_player' flag indicates that the player was not
66682a5c12eSMatthew Dillon  *	a monitor and needs extra cleaning up.
66782a5c12eSMatthew Dillon  */
66882a5c12eSMatthew Dillon static void
zap(PLAYER * pp,FLAG was_player)6696beb426bSSascha Wildner zap(PLAYER *pp, FLAG was_player)
67082a5c12eSMatthew Dillon {
67182a5c12eSMatthew Dillon 	int	len;
67282a5c12eSMatthew Dillon 	BULLET	*bp;
67382a5c12eSMatthew Dillon 	PLAYER	*np;
67482a5c12eSMatthew Dillon 	int	x, y;
67582a5c12eSMatthew Dillon 	int	savefd;
67682a5c12eSMatthew Dillon 
67782a5c12eSMatthew Dillon 	if (was_player) {
67882a5c12eSMatthew Dillon 		/* If they died from a shot, clean up shrapnel */
67982a5c12eSMatthew Dillon 		if (pp->p_undershot)
68082a5c12eSMatthew Dillon 			fixshots(pp->p_y, pp->p_x, pp->p_over);
68182a5c12eSMatthew Dillon 		/* Let the player see their last position: */
68282a5c12eSMatthew Dillon 		drawplayer(pp, FALSE);
68382a5c12eSMatthew Dillon 		/* Remove from game: */
68482a5c12eSMatthew Dillon 		Nplayer--;
68582a5c12eSMatthew Dillon 	}
68682a5c12eSMatthew Dillon 
68782a5c12eSMatthew Dillon 	/* Display the cause of death in the centre of the screen: */
68882a5c12eSMatthew Dillon 	len = strlen(pp->p_death);
68982a5c12eSMatthew Dillon 	x = (WIDTH - len) / 2;
69082a5c12eSMatthew Dillon 	outyx(pp, HEIGHT / 2, x, "%s", pp->p_death);
69182a5c12eSMatthew Dillon 
69282a5c12eSMatthew Dillon 	/* Put some horizontal lines around and below the death message: */
69382a5c12eSMatthew Dillon 	memset(pp->p_death + 1, '-', len - 2);
69482a5c12eSMatthew Dillon 	pp->p_death[0] = '+';
69582a5c12eSMatthew Dillon 	pp->p_death[len - 1] = '+';
69682a5c12eSMatthew Dillon 	outyx(pp, HEIGHT / 2 - 1, x, "%s", pp->p_death);
69782a5c12eSMatthew Dillon 	outyx(pp, HEIGHT / 2 + 1, x, "%s", pp->p_death);
69882a5c12eSMatthew Dillon 
69982a5c12eSMatthew Dillon 	/* Move to bottom left */
70082a5c12eSMatthew Dillon 	cgoto(pp, HEIGHT, 0);
70182a5c12eSMatthew Dillon 
70282a5c12eSMatthew Dillon 	savefd = pp->p_fd;
70382a5c12eSMatthew Dillon 
70482a5c12eSMatthew Dillon 	if (was_player) {
70582a5c12eSMatthew Dillon 		int	expl_charge;
70682a5c12eSMatthew Dillon 		int	expl_type;
70782a5c12eSMatthew Dillon 		int	ammo_exploding;
70882a5c12eSMatthew Dillon 
70982a5c12eSMatthew Dillon 		/* Check all the bullets: */
71082a5c12eSMatthew Dillon 		for (bp = Bullets; bp != NULL; bp = bp->b_next) {
71182a5c12eSMatthew Dillon 			if (bp->b_owner == pp)
71282a5c12eSMatthew Dillon 				/* Zapped players can't own bullets: */
71382a5c12eSMatthew Dillon 				bp->b_owner = NULL;
71482a5c12eSMatthew Dillon 			if (bp->b_x == pp->p_x && bp->b_y == pp->p_y)
71582a5c12eSMatthew Dillon 				/* Bullets over the player are now over air: */
71682a5c12eSMatthew Dillon 				bp->b_over = SPACE;
71782a5c12eSMatthew Dillon 		}
71882a5c12eSMatthew Dillon 
71982a5c12eSMatthew Dillon 		/* Explode a random fraction of the player's ammo: */
72082a5c12eSMatthew Dillon 		ammo_exploding = rand_num(pp->p_ammo);
72182a5c12eSMatthew Dillon 
72282a5c12eSMatthew Dillon 		/* Determine the type and amount of detonation: */
72382a5c12eSMatthew Dillon 		expl_charge = rand_num(ammo_exploding + 1);
72482a5c12eSMatthew Dillon 		if (pp->p_ammo == 0)
72582a5c12eSMatthew Dillon 			/* Ignore the no-ammo case: */
7267383c38bSSascha Wildner 			expl_charge = expl_type = 0;
72782a5c12eSMatthew Dillon 		else if (ammo_exploding >= pp->p_ammo - 1) {
72882a5c12eSMatthew Dillon 			/* Maximal explosions always appear as slime: */
72982a5c12eSMatthew Dillon 			expl_charge = pp->p_ammo;
73082a5c12eSMatthew Dillon 			expl_type = SLIME;
73182a5c12eSMatthew Dillon 		} else {
73282a5c12eSMatthew Dillon 			/*
73382a5c12eSMatthew Dillon 			 * Figure out the best effective explosion
73482a5c12eSMatthew Dillon 			 * type to use, given the amount of charge
73582a5c12eSMatthew Dillon 			 */
73682a5c12eSMatthew Dillon 			int btype, stype;
73782a5c12eSMatthew Dillon 			for (btype = MAXBOMB - 1; btype > 0; btype--)
73882a5c12eSMatthew Dillon 				if (expl_charge >= shot_req[btype])
73982a5c12eSMatthew Dillon 					break;
74082a5c12eSMatthew Dillon 			for (stype = MAXSLIME - 1; stype > 0; stype--)
74182a5c12eSMatthew Dillon 				if (expl_charge >= slime_req[stype])
74282a5c12eSMatthew Dillon 					break;
74382a5c12eSMatthew Dillon 			/* Pick the larger of the bomb or slime: */
74482a5c12eSMatthew Dillon 			if (btype >= 0 && stype >= 0) {
74582a5c12eSMatthew Dillon 				if (shot_req[btype] > slime_req[btype])
74682a5c12eSMatthew Dillon 					btype = -1;
74782a5c12eSMatthew Dillon 			}
74882a5c12eSMatthew Dillon 			if (btype >= 0)  {
74982a5c12eSMatthew Dillon 				expl_type = shot_type[btype];
75082a5c12eSMatthew Dillon 				expl_charge = shot_req[btype];
75182a5c12eSMatthew Dillon 			} else
75282a5c12eSMatthew Dillon 				expl_type = SLIME;
75382a5c12eSMatthew Dillon 		}
75482a5c12eSMatthew Dillon 
75582a5c12eSMatthew Dillon 		if (expl_charge > 0) {
75682a5c12eSMatthew Dillon 			char buf[BUFSIZ];
75782a5c12eSMatthew Dillon 
75882a5c12eSMatthew Dillon 			/* Detonate: */
759*d9f85b33Szrj 			add_shot(expl_type, pp->p_y, pp->p_x,
7602038fb68SSascha Wildner 			    pp->p_face, expl_charge, NULL,
76182a5c12eSMatthew Dillon 			    TRUE, SPACE);
76282a5c12eSMatthew Dillon 
76382a5c12eSMatthew Dillon 			/* Explain what the explosion is about. */
76482a5c12eSMatthew Dillon 			snprintf(buf, sizeof buf, "%s detonated.",
76582a5c12eSMatthew Dillon 				pp->p_ident->i_name);
76682a5c12eSMatthew Dillon 			message(ALL_PLAYERS, buf);
76782a5c12eSMatthew Dillon 
76882a5c12eSMatthew Dillon 			while (pp->p_nboots-- > 0) {
76982a5c12eSMatthew Dillon 				/* Throw one of the boots away: */
77082a5c12eSMatthew Dillon 				for (np = Boot; np < &Boot[NBOOTS]; np++)
77182a5c12eSMatthew Dillon 					if (np->p_flying < 0)
77282a5c12eSMatthew Dillon 						break;
77382a5c12eSMatthew Dillon #ifdef DIAGNOSTIC
77482a5c12eSMatthew Dillon 				if (np >= &Boot[NBOOTS])
77582a5c12eSMatthew Dillon 					err(1, "Too many boots");
77682a5c12eSMatthew Dillon #endif
77782a5c12eSMatthew Dillon 				/* Start the boots from where the player is */
77882a5c12eSMatthew Dillon 				np->p_undershot = FALSE;
77982a5c12eSMatthew Dillon 				np->p_x = pp->p_x;
78082a5c12eSMatthew Dillon 				np->p_y = pp->p_y;
78182a5c12eSMatthew Dillon 				/* Throw for up to 20 steps */
78282a5c12eSMatthew Dillon 				np->p_flying = rand_num(20);
78382a5c12eSMatthew Dillon 				np->p_flyx = 2 * rand_num(6) - 5;
78482a5c12eSMatthew Dillon 				np->p_flyy = 2 * rand_num(6) - 5;
78582a5c12eSMatthew Dillon 				np->p_over = SPACE;
78682a5c12eSMatthew Dillon 				np->p_face = BOOT;
78782a5c12eSMatthew Dillon 				showexpl(np->p_y, np->p_x, BOOT);
78882a5c12eSMatthew Dillon 			}
78982a5c12eSMatthew Dillon 		}
79082a5c12eSMatthew Dillon 		/* No explosion. Leave the player's boots behind. */
79182a5c12eSMatthew Dillon 		else if (pp->p_nboots > 0) {
79282a5c12eSMatthew Dillon 			if (pp->p_nboots == 2)
79382a5c12eSMatthew Dillon 				Maze[pp->p_y][pp->p_x] = BOOT_PAIR;
79482a5c12eSMatthew Dillon 			else
79582a5c12eSMatthew Dillon 				Maze[pp->p_y][pp->p_x] = BOOT;
79682a5c12eSMatthew Dillon 			if (pp->p_undershot)
79782a5c12eSMatthew Dillon 				fixshots(pp->p_y, pp->p_x,
79882a5c12eSMatthew Dillon 					Maze[pp->p_y][pp->p_x]);
79982a5c12eSMatthew Dillon 		}
80082a5c12eSMatthew Dillon 
80182a5c12eSMatthew Dillon 		/* Any unexploded ammo builds up in the volcano: */
80282a5c12eSMatthew Dillon 		volcano += pp->p_ammo - expl_charge;
80382a5c12eSMatthew Dillon 
80482a5c12eSMatthew Dillon 		/* Volcano eruption: */
80582a5c12eSMatthew Dillon 		if (conf_volcano && rand_num(100) < volcano /
80682a5c12eSMatthew Dillon 		    conf_volcano_max) {
80782a5c12eSMatthew Dillon 			/* Erupt near the middle of the map */
80882a5c12eSMatthew Dillon 			do {
80982a5c12eSMatthew Dillon 				x = rand_num(WIDTH / 2) + WIDTH / 4;
81082a5c12eSMatthew Dillon 				y = rand_num(HEIGHT / 2) + HEIGHT / 4;
81182a5c12eSMatthew Dillon 			} while (Maze[y][x] != SPACE);
81282a5c12eSMatthew Dillon 
81382a5c12eSMatthew Dillon 			/* Convert volcano charge into lava: */
814*d9f85b33Szrj 			add_shot(LAVA, y, x, LEFTS, volcano,
8152038fb68SSascha Wildner 				NULL, TRUE, SPACE);
81682a5c12eSMatthew Dillon 			volcano = 0;
81782a5c12eSMatthew Dillon 
81882a5c12eSMatthew Dillon 			/* Tell eveyone what's happening */
81982a5c12eSMatthew Dillon 			message(ALL_PLAYERS, "Volcano eruption.");
82082a5c12eSMatthew Dillon 		}
82182a5c12eSMatthew Dillon 
82282a5c12eSMatthew Dillon 		/* Drone: */
82382a5c12eSMatthew Dillon 		if (conf_drone && rand_num(100) < 2) {
82482a5c12eSMatthew Dillon 			/* Find a starting place near the middle of the map: */
82582a5c12eSMatthew Dillon 			do {
82682a5c12eSMatthew Dillon 				x = rand_num(WIDTH / 2) + WIDTH / 4;
82782a5c12eSMatthew Dillon 				y = rand_num(HEIGHT / 2) + HEIGHT / 4;
82882a5c12eSMatthew Dillon 			} while (Maze[y][x] != SPACE);
82982a5c12eSMatthew Dillon 
83082a5c12eSMatthew Dillon 			/* Start the drone going: */
83182a5c12eSMatthew Dillon 			add_shot(DSHOT, y, x, rand_dir(),
83282a5c12eSMatthew Dillon 				shot_req[conf_mindshot +
83382a5c12eSMatthew Dillon 				rand_num(MAXBOMB - conf_mindshot)],
8342038fb68SSascha Wildner 				NULL, FALSE, SPACE);
83582a5c12eSMatthew Dillon 		}
83682a5c12eSMatthew Dillon 
83782a5c12eSMatthew Dillon 		/* Tell the zapped player's client to shut down. */
83882a5c12eSMatthew Dillon 		sendcom(pp, ENDWIN, ' ');
839*d9f85b33Szrj 		fclose(pp->p_output);
84082a5c12eSMatthew Dillon 
84182a5c12eSMatthew Dillon 		/* Close up the gap in the Player array: */
84282a5c12eSMatthew Dillon 		End_player--;
84382a5c12eSMatthew Dillon 		if (pp != End_player) {
84482a5c12eSMatthew Dillon 			/* Move the last player into the gap: */
84582a5c12eSMatthew Dillon 			memcpy(pp, End_player, sizeof *pp);
84682a5c12eSMatthew Dillon 			outyx(ALL_PLAYERS,
84782a5c12eSMatthew Dillon 				STAT_PLAY_ROW + 1 + (pp - Player),
84882a5c12eSMatthew Dillon 				STAT_NAME_COL,
84982a5c12eSMatthew Dillon 				"%5.2f%c%-10.10s %c",
85082a5c12eSMatthew Dillon 				pp->p_ident->i_score, stat_char(pp),
85182a5c12eSMatthew Dillon 				pp->p_ident->i_name, pp->p_ident->i_team);
85282a5c12eSMatthew Dillon 		}
85382a5c12eSMatthew Dillon 
85482a5c12eSMatthew Dillon 		/* Erase the last player from the display: */
85582a5c12eSMatthew Dillon 		cgoto(ALL_PLAYERS, STAT_PLAY_ROW + 1 + Nplayer, STAT_NAME_COL);
85682a5c12eSMatthew Dillon 		ce(ALL_PLAYERS);
85782a5c12eSMatthew Dillon 	}
85882a5c12eSMatthew Dillon 	else {
85982a5c12eSMatthew Dillon 		/* Zap a monitor */
86082a5c12eSMatthew Dillon 
86182a5c12eSMatthew Dillon 		/* Close the session: */
86282a5c12eSMatthew Dillon 		sendcom(pp, ENDWIN, LAST_PLAYER);
863*d9f85b33Szrj 		fclose(pp->p_output);
86482a5c12eSMatthew Dillon 
86582a5c12eSMatthew Dillon 		/* shuffle the monitor table */
86682a5c12eSMatthew Dillon 		End_monitor--;
86782a5c12eSMatthew Dillon 		if (pp != End_monitor) {
86882a5c12eSMatthew Dillon 			memcpy(pp, End_monitor, sizeof *pp);
86982a5c12eSMatthew Dillon 			outyx(ALL_PLAYERS,
87082a5c12eSMatthew Dillon 				STAT_MON_ROW + 1 + (pp - Player), STAT_NAME_COL,
87182a5c12eSMatthew Dillon 				"%5.5s %-10.10s %c", " ",
87282a5c12eSMatthew Dillon 				pp->p_ident->i_name, pp->p_ident->i_team);
87382a5c12eSMatthew Dillon 		}
87482a5c12eSMatthew Dillon 
87582a5c12eSMatthew Dillon 		/* Erase the last monitor in the list */
87682a5c12eSMatthew Dillon 		cgoto(ALL_PLAYERS,
87782a5c12eSMatthew Dillon 			STAT_MON_ROW + 1 + (End_monitor - Monitor),
87882a5c12eSMatthew Dillon 			STAT_NAME_COL);
87982a5c12eSMatthew Dillon 		ce(ALL_PLAYERS);
88082a5c12eSMatthew Dillon 	}
88182a5c12eSMatthew Dillon 
88282a5c12eSMatthew Dillon 	/* Update the file descriptor sets used by select: */
88382a5c12eSMatthew Dillon 	FD_CLR(savefd, &Fds_mask);
88482a5c12eSMatthew Dillon 	if (Num_fds == savefd + 1) {
88582a5c12eSMatthew Dillon 		Num_fds = Socket;
88682a5c12eSMatthew Dillon 		if (Server_socket > Socket)
88782a5c12eSMatthew Dillon 			Num_fds = Server_socket;
88882a5c12eSMatthew Dillon 		for (np = Player; np < End_player; np++)
88982a5c12eSMatthew Dillon 			if (np->p_fd > Num_fds)
89082a5c12eSMatthew Dillon 				Num_fds = np->p_fd;
89182a5c12eSMatthew Dillon 		for (np = Monitor; np < End_monitor; np++)
89282a5c12eSMatthew Dillon 			if (np->p_fd > Num_fds)
89382a5c12eSMatthew Dillon 				Num_fds = np->p_fd;
89482a5c12eSMatthew Dillon 		Num_fds++;
89582a5c12eSMatthew Dillon 	}
89682a5c12eSMatthew Dillon }
89782a5c12eSMatthew Dillon 
89882a5c12eSMatthew Dillon /*
89982a5c12eSMatthew Dillon  * rand_num:
90082a5c12eSMatthew Dillon  *	Return a random number in a given range.
90182a5c12eSMatthew Dillon  */
90282a5c12eSMatthew Dillon int
rand_num(int range)9036beb426bSSascha Wildner rand_num(int range)
90482a5c12eSMatthew Dillon {
90582a5c12eSMatthew Dillon 	if (range == 0)
90682a5c12eSMatthew Dillon 		return 0;
90782a5c12eSMatthew Dillon 	return (random() % range);
90882a5c12eSMatthew Dillon }
90982a5c12eSMatthew Dillon 
91082a5c12eSMatthew Dillon /*
91182a5c12eSMatthew Dillon  * havechar:
91282a5c12eSMatthew Dillon  *	Check to see if we have any characters in the input queue; if
91382a5c12eSMatthew Dillon  *	we do, read them, stash them away, and return TRUE; else return
91482a5c12eSMatthew Dillon  *	FALSE.
91582a5c12eSMatthew Dillon  */
91682a5c12eSMatthew Dillon static int
havechar(PLAYER * pp)9176beb426bSSascha Wildner havechar(PLAYER *pp)
91882a5c12eSMatthew Dillon {
91982a5c12eSMatthew Dillon 	int ret;
92082a5c12eSMatthew Dillon 
92182a5c12eSMatthew Dillon 	/* Do we already have characters? */
92282a5c12eSMatthew Dillon 	if (pp->p_ncount < pp->p_nchar)
92382a5c12eSMatthew Dillon 		return TRUE;
92482a5c12eSMatthew Dillon 	/* Ignore if nothing to read. */
92582a5c12eSMatthew Dillon 	if (!FD_ISSET(pp->p_fd, &Have_inp))
92682a5c12eSMatthew Dillon 		return FALSE;
92782a5c12eSMatthew Dillon 	/* Remove the player from the read set until we have drained them: */
92882a5c12eSMatthew Dillon 	FD_CLR(pp->p_fd, &Have_inp);
92982a5c12eSMatthew Dillon 
93082a5c12eSMatthew Dillon 	/* Suck their keypresses into a buffer: */
93182a5c12eSMatthew Dillon check_again:
93282a5c12eSMatthew Dillon 	errno = 0;
93382a5c12eSMatthew Dillon 	ret = read(pp->p_fd, pp->p_cbuf, sizeof pp->p_cbuf);
93482a5c12eSMatthew Dillon 	if (ret == -1) {
93582a5c12eSMatthew Dillon 		if (errno == EINTR)
93682a5c12eSMatthew Dillon 			goto check_again;
93782a5c12eSMatthew Dillon 		if (errno == EAGAIN) {
93882a5c12eSMatthew Dillon #ifdef DEBUG
93982a5c12eSMatthew Dillon 			warn("Have_inp is wrong for %d", pp->p_fd);
94082a5c12eSMatthew Dillon #endif
94182a5c12eSMatthew Dillon 			return FALSE;
94282a5c12eSMatthew Dillon 		}
94382a5c12eSMatthew Dillon 		logit(LOG_INFO, "read");
94482a5c12eSMatthew Dillon 	}
94582a5c12eSMatthew Dillon 	if (ret > 0) {
94682a5c12eSMatthew Dillon 		/* Got some data */
94782a5c12eSMatthew Dillon 		pp->p_nchar = ret;
94882a5c12eSMatthew Dillon 	} else {
94982a5c12eSMatthew Dillon 		/* Connection was lost/closed: */
95082a5c12eSMatthew Dillon 		pp->p_cbuf[0] = 'q';
95182a5c12eSMatthew Dillon 		pp->p_nchar = 1;
95282a5c12eSMatthew Dillon 	}
95382a5c12eSMatthew Dillon 	/* Reset pointer into read buffer */
95482a5c12eSMatthew Dillon 	pp->p_ncount = 0;
95582a5c12eSMatthew Dillon 	return TRUE;
95682a5c12eSMatthew Dillon }
95782a5c12eSMatthew Dillon 
95882a5c12eSMatthew Dillon /*
95982a5c12eSMatthew Dillon  * cleanup:
96082a5c12eSMatthew Dillon  *	Exit with the given value, cleaning up any droppings lying around
96182a5c12eSMatthew Dillon  */
96282a5c12eSMatthew Dillon void
cleanup(int eval)9636beb426bSSascha Wildner cleanup(int eval)
96482a5c12eSMatthew Dillon {
96582a5c12eSMatthew Dillon 	PLAYER	*pp;
96682a5c12eSMatthew Dillon 
96782a5c12eSMatthew Dillon 	/* Place their cursor in a friendly position: */
96882a5c12eSMatthew Dillon 	cgoto(ALL_PLAYERS, HEIGHT, 0);
96982a5c12eSMatthew Dillon 
97082a5c12eSMatthew Dillon 	/* Send them all the ENDWIN command: */
97182a5c12eSMatthew Dillon 	sendcom(ALL_PLAYERS, ENDWIN, LAST_PLAYER);
97282a5c12eSMatthew Dillon 
97382a5c12eSMatthew Dillon 	/* And close their connections: */
97482a5c12eSMatthew Dillon 	for (pp = Player; pp < End_player; pp++)
975*d9f85b33Szrj 		fclose(pp->p_output);
97682a5c12eSMatthew Dillon 	for (pp = Monitor; pp < End_monitor; pp++)
977*d9f85b33Szrj 		fclose(pp->p_output);
97882a5c12eSMatthew Dillon 
97982a5c12eSMatthew Dillon 	/* Close the server socket: */
980*d9f85b33Szrj 	close(Socket);
98182a5c12eSMatthew Dillon 
98282a5c12eSMatthew Dillon 	/* The end: */
98382a5c12eSMatthew Dillon 	logx(LOG_INFO, "game over");
98482a5c12eSMatthew Dillon 	exit(eval);
98582a5c12eSMatthew Dillon }
98682a5c12eSMatthew Dillon 
98782a5c12eSMatthew Dillon /*
98882a5c12eSMatthew Dillon  * send_stats:
98982a5c12eSMatthew Dillon  *	Accept a connection to the statistics port, and emit
99082a5c12eSMatthew Dillon  *	the stats.
99182a5c12eSMatthew Dillon  */
99282a5c12eSMatthew Dillon static void
send_stats(void)9936beb426bSSascha Wildner send_stats(void)
99482a5c12eSMatthew Dillon {
99582a5c12eSMatthew Dillon 	FILE	*fp;
99682a5c12eSMatthew Dillon 	int	s;
99782a5c12eSMatthew Dillon 	struct sockaddr_in	sockstruct;
99882a5c12eSMatthew Dillon 	socklen_t	socklen;
99982a5c12eSMatthew Dillon 	struct request_info ri;
100082a5c12eSMatthew Dillon 	int	flags;
100182a5c12eSMatthew Dillon 
100282a5c12eSMatthew Dillon 	/* Accept a connection to the statistics socket: */
100382a5c12eSMatthew Dillon 	socklen = sizeof sockstruct;
100482a5c12eSMatthew Dillon 	s = accept(Status, (struct sockaddr *) &sockstruct, &socklen);
100582a5c12eSMatthew Dillon 	if (s < 0) {
100682a5c12eSMatthew Dillon 		if (errno == EINTR)
100782a5c12eSMatthew Dillon 			return;
100882a5c12eSMatthew Dillon 		logx(LOG_ERR, "accept");
100982a5c12eSMatthew Dillon 		return;
101082a5c12eSMatthew Dillon 	}
101182a5c12eSMatthew Dillon 
101282a5c12eSMatthew Dillon 	/* Check for access permissions: */
101382a5c12eSMatthew Dillon 	request_init(&ri, RQ_DAEMON, "huntd", RQ_FILE, s, 0);
101482a5c12eSMatthew Dillon 	fromhost(&ri);
101582a5c12eSMatthew Dillon 	if (hosts_access(&ri) == 0) {
101682a5c12eSMatthew Dillon 		logx(LOG_INFO, "rejected connection from %s", eval_client(&ri));
101782a5c12eSMatthew Dillon 		close(s);
101882a5c12eSMatthew Dillon 		return;
101982a5c12eSMatthew Dillon 	}
102082a5c12eSMatthew Dillon 
102182a5c12eSMatthew Dillon 	/* Don't allow the writes to block: */
102282a5c12eSMatthew Dillon 	flags = fcntl(s, F_GETFL, 0);
102382a5c12eSMatthew Dillon 	flags |= O_NDELAY;
1024*d9f85b33Szrj 	fcntl(s, F_SETFL, flags);
102582a5c12eSMatthew Dillon 
102682a5c12eSMatthew Dillon 	fp = fdopen(s, "w");
102782a5c12eSMatthew Dillon 	if (fp == NULL) {
102882a5c12eSMatthew Dillon 		logit(LOG_ERR, "fdopen");
1029*d9f85b33Szrj 		close(s);
103082a5c12eSMatthew Dillon 		return;
103182a5c12eSMatthew Dillon 	}
103282a5c12eSMatthew Dillon 
103382a5c12eSMatthew Dillon 	print_stats(fp);
103482a5c12eSMatthew Dillon 
1035*d9f85b33Szrj 	fclose(fp);
103682a5c12eSMatthew Dillon }
103782a5c12eSMatthew Dillon 
103882a5c12eSMatthew Dillon /*
103982a5c12eSMatthew Dillon  * print_stats:
104082a5c12eSMatthew Dillon  * 	emit the game statistics
104182a5c12eSMatthew Dillon  */
104282a5c12eSMatthew Dillon void
print_stats(FILE * fp)10436beb426bSSascha Wildner print_stats(FILE *fp)
104482a5c12eSMatthew Dillon {
104582a5c12eSMatthew Dillon 	IDENT	*ip;
104682a5c12eSMatthew Dillon 	PLAYER  *pp;
104782a5c12eSMatthew Dillon 
104882a5c12eSMatthew Dillon 	/* Send the statistics as raw text down the socket: */
104982a5c12eSMatthew Dillon 	fputs("Name\t\tScore\tDucked\tAbsorb\tFaced\tShot\tRobbed\tMissed\tSlimeK\n", fp);
105082a5c12eSMatthew Dillon 	for (ip = Scores; ip != NULL; ip = ip->i_next) {
105182a5c12eSMatthew Dillon 		fprintf(fp, "%s%c%c%c\t", ip->i_name,
105282a5c12eSMatthew Dillon 			ip->i_team == ' ' ? ' ' : '[',
105382a5c12eSMatthew Dillon 			ip->i_team,
105482a5c12eSMatthew Dillon 			ip->i_team == ' ' ? ' ' : ']'
105582a5c12eSMatthew Dillon 		);
105682a5c12eSMatthew Dillon 		if (strlen(ip->i_name) + 3 < 8)
105782a5c12eSMatthew Dillon 			putc('\t', fp);
105882a5c12eSMatthew Dillon 		fprintf(fp, "%.2f\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
105982a5c12eSMatthew Dillon 			ip->i_score, ip->i_ducked, ip->i_absorbed,
106082a5c12eSMatthew Dillon 			ip->i_faced, ip->i_shot, ip->i_robbed,
106182a5c12eSMatthew Dillon 			ip->i_missed, ip->i_slime);
106282a5c12eSMatthew Dillon 	}
106382a5c12eSMatthew Dillon 	fputs("\n\nName\t\tEnemy\tFriend\tDeaths\tStill\tSaved\tConnect\n", fp);
106482a5c12eSMatthew Dillon 	for (ip = Scores; ip != NULL; ip = ip->i_next) {
106582a5c12eSMatthew Dillon 		fprintf(fp, "%s%c%c%c\t", ip->i_name,
106682a5c12eSMatthew Dillon 			ip->i_team == ' ' ? ' ' : '[',
106782a5c12eSMatthew Dillon 			ip->i_team,
106882a5c12eSMatthew Dillon 			ip->i_team == ' ' ? ' ' : ']'
106982a5c12eSMatthew Dillon 		);
107082a5c12eSMatthew Dillon 		if (strlen(ip->i_name) + 3 < 8)
107182a5c12eSMatthew Dillon 			putc('\t', fp);
107282a5c12eSMatthew Dillon 		fprintf(fp, "%d\t%d\t%d\t%d\t%d\t",
107382a5c12eSMatthew Dillon 			ip->i_gkills, ip->i_bkills, ip->i_deaths,
107482a5c12eSMatthew Dillon 			ip->i_stillb, ip->i_saved);
107582a5c12eSMatthew Dillon 		for (pp = Player; pp < End_player; pp++)
107682a5c12eSMatthew Dillon 			if (pp->p_ident == ip)
107782a5c12eSMatthew Dillon 				putc('p', fp);
107882a5c12eSMatthew Dillon 		for (pp = Monitor; pp < End_monitor; pp++)
107982a5c12eSMatthew Dillon 			if (pp->p_ident == ip)
108082a5c12eSMatthew Dillon 				putc('m', fp);
108182a5c12eSMatthew Dillon 		putc('\n', fp);
108282a5c12eSMatthew Dillon 	}
108382a5c12eSMatthew Dillon }
108482a5c12eSMatthew Dillon 
108582a5c12eSMatthew Dillon 
108682a5c12eSMatthew Dillon /*
108782a5c12eSMatthew Dillon  * Send the game statistics to the controlling tty
108882a5c12eSMatthew Dillon  */
108982a5c12eSMatthew Dillon static void
siginfo(int sig __unused)109082a5c12eSMatthew Dillon siginfo(int sig __unused)
109182a5c12eSMatthew Dillon {
109282a5c12eSMatthew Dillon 	int tty;
109382a5c12eSMatthew Dillon 	FILE *fp;
109482a5c12eSMatthew Dillon 
109582a5c12eSMatthew Dillon 	if ((tty = open(_PATH_TTY, O_WRONLY)) >= 0) {
109682a5c12eSMatthew Dillon 		fp = fdopen(tty, "w");
109782a5c12eSMatthew Dillon 		print_stats(fp);
109882a5c12eSMatthew Dillon 		answer_info(fp);
109982a5c12eSMatthew Dillon 		fclose(fp);
110082a5c12eSMatthew Dillon 	}
110182a5c12eSMatthew Dillon }
110282a5c12eSMatthew Dillon 
110382a5c12eSMatthew Dillon /*
110482a5c12eSMatthew Dillon  * clear_scores:
110582a5c12eSMatthew Dillon  *	Clear the Scores list.
110682a5c12eSMatthew Dillon  */
110782a5c12eSMatthew Dillon static void
clear_scores(void)11086beb426bSSascha Wildner clear_scores(void)
110982a5c12eSMatthew Dillon {
111082a5c12eSMatthew Dillon 	IDENT	*ip, *nextip;
111182a5c12eSMatthew Dillon 
111282a5c12eSMatthew Dillon 	/* Release the list of scores: */
111382a5c12eSMatthew Dillon 	for (ip = Scores; ip != NULL; ip = nextip) {
111482a5c12eSMatthew Dillon 		nextip = ip->i_next;
111582a5c12eSMatthew Dillon 		free((char *) ip);
111682a5c12eSMatthew Dillon 	}
111782a5c12eSMatthew Dillon 	Scores = NULL;
111882a5c12eSMatthew Dillon }
111982a5c12eSMatthew Dillon 
112082a5c12eSMatthew Dillon /*
112182a5c12eSMatthew Dillon  * announce_game:
112282a5c12eSMatthew Dillon  *	Publically announce the game
112382a5c12eSMatthew Dillon  */
112482a5c12eSMatthew Dillon static void
announce_game(void)11256beb426bSSascha Wildner announce_game(void)
112682a5c12eSMatthew Dillon {
112782a5c12eSMatthew Dillon 
112882a5c12eSMatthew Dillon 	/* TODO: could use system() to do something user-configurable */
112982a5c12eSMatthew Dillon }
113082a5c12eSMatthew Dillon 
113182a5c12eSMatthew Dillon /*
113282a5c12eSMatthew Dillon  * Handle a UDP packet sent to the well known port.
113382a5c12eSMatthew Dillon  */
113482a5c12eSMatthew Dillon static void
handle_wkport(int fd)11356beb426bSSascha Wildner handle_wkport(int fd)
113682a5c12eSMatthew Dillon {
113782a5c12eSMatthew Dillon 	struct sockaddr		fromaddr;
113882a5c12eSMatthew Dillon 	socklen_t		fromlen;
113982a5c12eSMatthew Dillon 	u_int16_t		query;
114082a5c12eSMatthew Dillon 	u_int16_t		response;
114182a5c12eSMatthew Dillon 	struct request_info	ri;
114282a5c12eSMatthew Dillon 
114382a5c12eSMatthew Dillon 	request_init(&ri, RQ_DAEMON, "huntd", RQ_FILE, fd, 0);
114482a5c12eSMatthew Dillon 	fromhost(&ri);
114582a5c12eSMatthew Dillon 	fromlen = sizeof fromaddr;
114682a5c12eSMatthew Dillon 	if (recvfrom(fd, &query, sizeof query, 0, &fromaddr, &fromlen) == -1)
114782a5c12eSMatthew Dillon 	{
114882a5c12eSMatthew Dillon 		logit(LOG_WARNING, "recvfrom");
114982a5c12eSMatthew Dillon 		return;
115082a5c12eSMatthew Dillon 	}
115182a5c12eSMatthew Dillon 
115282a5c12eSMatthew Dillon #ifdef DEBUG
115382a5c12eSMatthew Dillon 	fprintf(stderr, "query %d (%s) from %s:%d\n", query,
115482a5c12eSMatthew Dillon 		query == C_MESSAGE ? "C_MESSAGE" :
115582a5c12eSMatthew Dillon 		query == C_SCORES ? "C_SCORES" :
115682a5c12eSMatthew Dillon 		query == C_PLAYER ? "C_PLAYER" :
115782a5c12eSMatthew Dillon 		query == C_MONITOR ? "C_MONITOR" : "?",
115882a5c12eSMatthew Dillon 		inet_ntoa(((struct sockaddr_in *)&fromaddr)->sin_addr),
115982a5c12eSMatthew Dillon 		ntohs(((struct sockaddr_in *)&fromaddr)->sin_port));
116082a5c12eSMatthew Dillon #endif
116182a5c12eSMatthew Dillon 
116282a5c12eSMatthew Dillon 	/* Do we allow access? */
116382a5c12eSMatthew Dillon 	if (hosts_access(&ri) == 0) {
116482a5c12eSMatthew Dillon 		logx(LOG_INFO, "rejected connection from %s", eval_client(&ri));
116582a5c12eSMatthew Dillon 		return;
116682a5c12eSMatthew Dillon 	}
116782a5c12eSMatthew Dillon 
116882a5c12eSMatthew Dillon 	query = ntohs(query);
116982a5c12eSMatthew Dillon 
117082a5c12eSMatthew Dillon 	switch (query) {
117182a5c12eSMatthew Dillon 	  case C_MESSAGE:
117282a5c12eSMatthew Dillon 		if (Nplayer <= 0)
117382a5c12eSMatthew Dillon 			/* Don't bother replying if nobody to talk to: */
117482a5c12eSMatthew Dillon 			return;
117582a5c12eSMatthew Dillon 		/* Return the number of people playing: */
117682a5c12eSMatthew Dillon 		response = Nplayer;
117782a5c12eSMatthew Dillon 		break;
117882a5c12eSMatthew Dillon 	  case C_SCORES:
117982a5c12eSMatthew Dillon 		/* Someone wants the statistics port: */
118082a5c12eSMatthew Dillon 		response = stat_port;
118182a5c12eSMatthew Dillon 		break;
118282a5c12eSMatthew Dillon 	  case C_PLAYER:
118382a5c12eSMatthew Dillon 	  case C_MONITOR:
118482a5c12eSMatthew Dillon 		/* Someone wants to play or watch: */
118582a5c12eSMatthew Dillon 		if (query == C_MONITOR && Nplayer <= 0)
118682a5c12eSMatthew Dillon 			/* Don't bother replying if there's nothing to watch: */
118782a5c12eSMatthew Dillon 			return;
118882a5c12eSMatthew Dillon 		/* Otherwise, tell them how to get to the game: */
118982a5c12eSMatthew Dillon 		response = sock_port;
119082a5c12eSMatthew Dillon 		break;
119182a5c12eSMatthew Dillon 	  default:
119282a5c12eSMatthew Dillon 		logit(LOG_INFO, "unknown udp query %d", query);
119382a5c12eSMatthew Dillon 		return;
119482a5c12eSMatthew Dillon 	}
119582a5c12eSMatthew Dillon 
119682a5c12eSMatthew Dillon 	response = ntohs(response);
119782a5c12eSMatthew Dillon 	if (sendto(fd, &response, sizeof response, 0,
119882a5c12eSMatthew Dillon 	    &fromaddr, sizeof fromaddr) == -1)
119982a5c12eSMatthew Dillon 		logit(LOG_WARNING, "sendto");
120082a5c12eSMatthew Dillon }
1201