]> git.cameronkatri.com Git - bsdgames-darwin.git/blob - hunt/huntd/driver.c
Fix merge conflicts
[bsdgames-darwin.git] / hunt / huntd / driver.c
1 /* $NetBSD: driver.c,v 1.36 2021/05/02 12:50:45 rillig Exp $ */
2 /*
3 * Copyright (c) 1983-2003, Regents of the University of California.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * + Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * + Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * + Neither the name of the University of California, San Francisco nor
16 * the names of its contributors may be used to endorse or promote
17 * products derived from this software without specific prior written
18 * permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
21 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <sys/cdefs.h>
34 #ifndef lint
35 __RCSID("$NetBSD: driver.c,v 1.36 2021/05/02 12:50:45 rillig Exp $");
36 #endif /* not lint */
37
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <sys/stat.h>
41 #include <sys/time.h>
42 #include <sys/un.h>
43
44 #include <netinet/in.h>
45 #include <netdb.h>
46 #include <arpa/inet.h>
47 #include <net/if.h>
48
49 #include <stdlib.h>
50 #include <unistd.h>
51 #include <signal.h>
52 #include <errno.h>
53 #include <err.h>
54
55 #include "hunt.h"
56 #include "pathnames.h"
57 #define INFTIM (-1)
58
59 /*
60 * There are three listening sockets in this daemon:
61 * - a datagram socket that listens on a well known port
62 * - a stream socket for general player connections
63 * - a second stream socket that prints the stats/scores
64 *
65 * These are (now) named as follows:
66 * - contact (contactsock, contactaddr, etc.)
67 * - hunt (huntsock, huntaddr, etc.)
68 * - stat (statsock, stataddr, etc.)
69 *
70 * Until being cleaned up in 2014 the code used an assortment of
71 * inconsistent and confusing names to refer to the pieces of these:
72 *
73 * test_port -> contactaddr
74 * Test_port -> contactport
75 * Test_socket -> contactsock
76 *
77 * sock_port -> huntport
78 * Socket -> huntsock
79 *
80 * Daemon -> both stataddr and huntaddr
81 * stat_port -> statport
82 * status -> statsock
83 *
84 * It isn't clear to me what purpose contactsocket is supposed to
85 * serve; maybe it was supposed to avoid asking inetd to support
86 * tcp/wait sockets? Anyway, we can't really change the protocol at
87 * this point. (To complicate matters, contactsocket doesn't exist if
88 * using AF_UNIX sockets; you just connect to the game socket.)
89 *
90 * When using internet sockets:
91 * - the contact socket listens on INADDR_ANY using the game port
92 * (either specified or the default: CONTACT_PORT, currently
93 * spelled TEST_PORT)
94 * - the hunt socket listens on INADDR_ANY using whatever port
95 * - the stat socket listens on INADDR_ANY using whatever port
96 *
97 * When using AF_UNIX sockets:
98 * - the contact socket isn't used
99 * - the hunt socket listens on the game socket (either a specified path
100 * or /tmp/hunt)
101 * - the stat socket listens on its own socket (huntsocket's path +
102 * ".stats")
103 */
104
105 static bool localmode; /* true -> AF_UNIX; false -> AF_INET */
106 static bool inetd_spawned; /* invoked via inetd? */
107 static bool standard_port = true; /* listening on standard port? */
108
109 static struct sockaddr_storage huntaddr;
110 static struct sockaddr_storage stataddr;
111 static socklen_t huntaddrlen;
112 static socklen_t stataddrlen;
113
114 static uint16_t contactport = TEST_PORT;
115 static uint16_t huntport; /* port # of tcp listen socket */
116 static uint16_t statport; /* port # of statistics tcp socket */
117
118 static const char *huntsockpath = PATH_HUNTSOCKET;
119 static char *statsockpath;
120
121 static int contactsock; /* socket to answer datagrams */
122 int huntsock; /* main socket */
123 static int statsock; /* stat socket */
124
125 #ifdef VOLCANO
126 static int volcano = 0; /* Explosion size */
127 #endif
128
129 static void clear_scores(void);
130 static bool havechar(PLAYER *, int);
131 static void init(void);
132 static void makeboots(void);
133 static void send_stats(void);
134 static void zap(PLAYER *, bool, int);
135
136 static int
137 getnum(const char *s, unsigned long *ret)
138 {
139 char *t;
140
141 errno = 0;
142 *ret = strtoul(s, &t, 0);
143 if (errno || *t != '\0') {
144 return -1;
145 }
146 return 0;
147 }
148
149 static __dead void
150 usage(const char *av0)
151 {
152 fprintf(stderr, "Usage: %s [-s] [-p portnumber|socketpath]\n", av0);
153 exit(1);
154 }
155
156 static void
157 makeaddr(const char *path, uint16_t port,
158 struct sockaddr_storage *ss, socklen_t *len)
159 {
160 struct sockaddr_in *sin;
161 struct sockaddr_un *sun;
162
163 if (path == NULL) {
164 sin = (struct sockaddr_in *)ss;
165 sin->sin_family = AF_INET;
166 sin->sin_addr.s_addr = INADDR_ANY;
167 sin->sin_port = htons(port);
168 *len = sizeof(*sin);
169 } else {
170 sun = (struct sockaddr_un *)ss;
171 sun->sun_family = AF_UNIX;
172 strlcpy(sun->sun_path, path, sizeof(sun->sun_path));
173 *len = SUN_LEN(sun);
174 }
175 }
176
177 static uint16_t
178 getsockport(int sock)
179 {
180 struct sockaddr_storage addr;
181 socklen_t len;
182
183 len = sizeof(addr);
184 if (getsockname(sock, (struct sockaddr *)&addr, &len) < 0) {
185 complain(LOG_ERR, "getsockname");
186 exit(1);
187 }
188 switch (addr.ss_family) {
189 case AF_INET:
190 return ntohs(((struct sockaddr_in *)&addr)->sin_port);
191 case AF_INET6:
192 return ntohs(((struct sockaddr_in6 *)&addr)->sin6_port);
193 default:
194 break;
195 }
196 return 0;
197 }
198
199 /*
200 * main:
201 * The main program.
202 */
203 int
204 main(int ac, char **av)
205 {
206 PLAYER *pp;
207 unsigned long optargnum;
208 uint16_t msg, reply;
209 struct sockaddr_storage msgaddr;
210 socklen_t msgaddrlen;
211 static bool first = true;
212 static bool server = false;
213 int c, i;
214 const int linger = 90 * 1000;
215
216 while ((c = getopt(ac, av, "sp:")) != -1) {
217 switch (c) {
218 case 's':
219 server = true;
220 break;
221 case 'p':
222 standard_port = false;
223 if (getnum(optarg, &optargnum) < 0) {
224 localmode = true;
225 huntsockpath = optarg;
226 } else if (optargnum < 0xffff) {
227 localmode = false;
228 contactport = optargnum;
229 } else {
230 usage(av[0]);
231 }
232 break;
233 default:
234 usage(av[0]);
235 }
236 }
237 if (optind < ac)
238 usage(av[0]);
239
240 asprintf(&statsockpath, "%s.stats", huntsockpath);
241 init();
242
243
244 again:
245 do {
246 errno = 0;
247 while (poll(fdset, 3+MAXPL+MAXMON, INFTIM) < 0)
248 {
249 if (errno != EINTR)
250 complain(LOG_WARNING, "poll");
251 errno = 0;
252 }
253 if (!localmode && fdset[2].revents & POLLIN) {
254 msgaddrlen = sizeof(msgaddr);
255 (void) recvfrom(contactsock, &msg, sizeof msg,
256 0, (struct sockaddr *)&msgaddr, &msgaddrlen);
257 switch (ntohs(msg)) {
258 case C_MESSAGE:
259 if (Nplayer <= 0)
260 break;
261 reply = htons((u_short) Nplayer);
262 (void) sendto(contactsock, &reply,
263 sizeof reply, 0,
264 (struct sockaddr *)&msgaddr,
265 msgaddrlen);
266 break;
267 case C_SCORES:
268 reply = htons(statport);
269 (void) sendto(contactsock, &reply,
270 sizeof reply, 0,
271 (struct sockaddr *)&msgaddr,
272 msgaddrlen);
273 break;
274 case C_PLAYER:
275 case C_MONITOR:
276 if (msg == C_MONITOR && Nplayer <= 0)
277 break;
278 reply = htons(huntport);
279 (void) sendto(contactsock, &reply,
280 sizeof reply, 0,
281 (struct sockaddr *)&msgaddr,
282 msgaddrlen);
283 break;
284 }
285 }
286
287 {
288 for (pp = Player, i = 0; pp < End_player; pp++, i++)
289 if (havechar(pp, i + 3)) {
290 execute(pp);
291 pp->p_nexec++;
292 }
293 #ifdef MONITOR
294 for (pp = Monitor, i = 0; pp < End_monitor; pp++, i++)
295 if (havechar(pp, i + MAXPL + 3)) {
296 mon_execute(pp);
297 pp->p_nexec++;
298 }
299 #endif
300 moveshots();
301 for (pp = Player, i = 0; pp < End_player; )
302 if (pp->p_death[0] != '\0')
303 zap(pp, true, i + 3);
304 else
305 pp++, i++;
306 #ifdef MONITOR
307 for (pp = Monitor, i = 0; pp < End_monitor; )
308 if (pp->p_death[0] != '\0')
309 zap(pp, false, i + MAXPL + 3);
310 else
311 pp++, i++;
312 #endif
313 }
314 if (fdset[0].revents & POLLIN)
315 if (answer()) {
316 if (first) {
317 /* announce start of game? */
318 }
319 first = false;
320 }
321 if (fdset[1].revents & POLLIN)
322 send_stats();
323 for (pp = Player, i = 0; pp < End_player; pp++, i++) {
324 if (fdset[i + 3].revents & POLLIN)
325 sendcom(pp, READY, pp->p_nexec);
326 pp->p_nexec = 0;
327 (void) fflush(pp->p_output);
328 }
329 #ifdef MONITOR
330 for (pp = Monitor, i = 0; pp < End_monitor; pp++, i++) {
331 if (fdset[i + MAXPL + 3].revents & POLLIN)
332 sendcom(pp, READY, pp->p_nexec);
333 pp->p_nexec = 0;
334 (void) fflush(pp->p_output);
335 }
336 #endif
337 } while (Nplayer > 0);
338
339 if (poll(fdset, 3+MAXPL+MAXMON, linger) > 0) {
340 goto again;
341 }
342 if (server) {
343 clear_scores();
344 makemaze();
345 clearwalls();
346 #ifdef BOOTS
347 makeboots();
348 #endif
349 first = true;
350 goto again;
351 }
352
353 #ifdef MONITOR
354 for (pp = Monitor, i = 0; pp < End_monitor; i++)
355 zap(pp, false, i + MAXPL + 3);
356 #endif
357 cleanup(0);
358 /* NOTREACHED */
359 return(0);
360 }
361
362 /*
363 * init:
364 * Initialize the global parameters.
365 */
366 static void
367 init(void)
368 {
369 int i;
370 struct sockaddr_storage stdinaddr;
371 struct sockaddr_storage contactaddr;
372 socklen_t contactaddrlen;
373 socklen_t len;
374
375 #ifndef DEBUG
376 #ifdef TIOCNOTTY
377 (void) ioctl(fileno(stdout), TIOCNOTTY, NULL);
378 #endif
379 (void) setpgid(getpid(), getpid());
380 (void) signal(SIGHUP, SIG_IGN);
381 (void) signal(SIGINT, SIG_IGN);
382 (void) signal(SIGQUIT, SIG_IGN);
383 (void) signal(SIGTERM, cleanup);
384 #endif
385
386 (void) chdir("/var/tmp"); /* just in case it core dumps */
387 (void) umask(0); /* No privacy at all! */
388 (void) signal(SIGPIPE, SIG_IGN);
389
390 #ifdef LOG
391 openlog("huntd", LOG_PID, LOG_DAEMON);
392 #endif
393
394 /*
395 * check for inetd
396 */
397 len = sizeof(stdinaddr);
398 if (getsockname(STDIN_FILENO, (struct sockaddr *)&stdinaddr,
399 &len) >= 0) {
400 inetd_spawned = true;
401 /* force localmode, assimilate stdin as appropriate */
402 if (stdinaddr.ss_family == AF_UNIX) {
403 localmode = true;
404 contactsock = -1;
405 huntsock = STDIN_FILENO;
406 }
407 else {
408 localmode = false;
409 contactsock = STDIN_FILENO;
410 huntsock = -1;
411 }
412 } else {
413 /* keep value of localmode; no sockets yet */
414 contactsock = -1;
415 huntsock = -1;
416 }
417
418 /*
419 * initialize contact socket
420 */
421 if (!localmode && contactsock < 0) {
422 makeaddr(NULL, contactport, &contactaddr, &contactaddrlen);
423 contactsock = socket(AF_INET, SOCK_DGRAM, 0);
424 if (bind(contactsock, (struct sockaddr *) &contactaddr,
425 contactaddrlen) < 0) {
426 complain(LOG_ERR, "bind");
427 exit(1);
428 }
429 (void) listen(contactsock, 5);
430 }
431
432 /*
433 * Initialize main socket
434 */
435 if (huntsock < 0) {
436 makeaddr(localmode ? huntsockpath : NULL, 0, &huntaddr,
437 &huntaddrlen);
438 huntsock = socket(huntaddr.ss_family, SOCK_STREAM, 0);
439 if (bind(huntsock, (struct sockaddr *)&huntaddr,
440 huntaddrlen) < 0) {
441 if (errno == EADDRINUSE)
442 exit(0);
443 else {
444 complain(LOG_ERR, "bind");
445 cleanup(1);
446 }
447 }
448 (void) listen(huntsock, 5);
449 }
450
451 /*
452 * Initialize statistics socket
453 */
454 makeaddr(localmode ? statsockpath : NULL, 0, &stataddr, &stataddrlen);
455 statsock = socket(stataddr.ss_family, SOCK_STREAM, 0);
456 if (bind(statsock, (struct sockaddr *)&stataddr, stataddrlen) < 0) {
457 if (errno == EADDRINUSE)
458 exit(0);
459 else {
460 complain(LOG_ERR, "bind");
461 cleanup(1);
462 }
463 }
464 (void) listen(statsock, 5);
465
466 if (!localmode) {
467 contactport = getsockport(contactsock);
468 statport = getsockport(statsock);
469 huntport = getsockport(huntsock);
470 if (contactport != TEST_PORT) {
471 standard_port = false;
472 }
473 }
474
475 /*
476 * Initialize minimal poll mask
477 */
478 fdset[0].fd = huntsock;
479 fdset[0].events = POLLIN;
480 fdset[1].fd = statsock;
481 fdset[1].events = POLLIN;
482 if (localmode) {
483 fdset[2].fd = -1;
484 fdset[2].events = 0;
485 } else {
486 fdset[2].fd = contactsock;
487 fdset[2].events = POLLIN;
488 }
489
490 srandom(time(NULL));
491 makemaze();
492 #ifdef BOOTS
493 makeboots();
494 #endif
495
496 for (i = 0; i < NASCII; i++)
497 See_over[i] = true;
498 See_over[DOOR] = false;
499 See_over[WALL1] = false;
500 See_over[WALL2] = false;
501 See_over[WALL3] = false;
502 #ifdef REFLECT
503 See_over[WALL4] = false;
504 See_over[WALL5] = false;
505 #endif
506
507 }
508
509 #ifdef BOOTS
510 /*
511 * makeboots:
512 * Put the boots in the maze
513 */
514 static void
515 makeboots(void)
516 {
517 int x, y;
518 PLAYER *pp;
519
520 do {
521 x = rand_num(WIDTH - 1) + 1;
522 y = rand_num(HEIGHT - 1) + 1;
523 } while (Maze[y][x] != SPACE);
524 Maze[y][x] = BOOT_PAIR;
525 for (pp = Boot; pp < &Boot[NBOOTS]; pp++)
526 pp->p_flying = -1;
527 }
528 #endif
529
530
531 /*
532 * checkdam:
533 * Check the damage to the given player, and see if s/he is killed
534 */
535 void
536 checkdam(PLAYER *ouch, PLAYER *gotcha, IDENT *credit, int amt,
537 char this_shot_type)
538 {
539 const char *cp;
540
541 if (ouch->p_death[0] != '\0')
542 return;
543 #ifdef BOOTS
544 if (this_shot_type == SLIME)
545 switch (ouch->p_nboots) {
546 default:
547 break;
548 case 1:
549 amt = (amt + 1) / 2;
550 break;
551 case 2:
552 if (gotcha != NULL)
553 message(gotcha, "He has boots on!");
554 return;
555 }
556 #endif
557 ouch->p_damage += amt;
558 if (ouch->p_damage <= ouch->p_damcap) {
559 (void) snprintf(Buf, sizeof(Buf), "%2d", ouch->p_damage);
560 cgoto(ouch, STAT_DAM_ROW, STAT_VALUE_COL);
561 outstr(ouch, Buf, 2);
562 return;
563 }
564
565 /* Someone DIED */
566 switch (this_shot_type) {
567 default:
568 cp = "Killed";
569 break;
570 #ifdef FLY
571 case FALL:
572 cp = "Killed on impact";
573 break;
574 #endif
575 case KNIFE:
576 cp = "Stabbed to death";
577 ouch->p_ammo = 0; /* No exploding */
578 break;
579 case SHOT:
580 cp = "Shot to death";
581 break;
582 case GRENADE:
583 case SATCHEL:
584 case BOMB:
585 cp = "Bombed";
586 break;
587 case MINE:
588 case GMINE:
589 cp = "Blown apart";
590 break;
591 #ifdef OOZE
592 case SLIME:
593 cp = "Slimed";
594 if (credit != NULL)
595 credit->i_slime++;
596 break;
597 #endif
598 #ifdef VOLCANO
599 case LAVA:
600 cp = "Baked";
601 break;
602 #endif
603 #ifdef DRONE
604 case DSHOT:
605 cp = "Eliminated";
606 break;
607 #endif
608 }
609 if (credit == NULL) {
610 (void) snprintf(ouch->p_death, sizeof(ouch->p_death),
611 "| %s by %s |", cp,
612 (this_shot_type == MINE || this_shot_type == GMINE) ?
613 "a mine" : "act of God");
614 return;
615 }
616
617 (void) snprintf(ouch->p_death, sizeof(ouch->p_death),
618 "| %s by %s |", cp, credit->i_name);
619
620 if (ouch == gotcha) { /* No use killing yourself */
621 credit->i_kills--;
622 credit->i_bkills++;
623 }
624 else if (ouch->p_ident->i_team == ' '
625 || ouch->p_ident->i_team != credit->i_team) {
626 credit->i_kills++;
627 credit->i_gkills++;
628 }
629 else {
630 credit->i_kills--;
631 credit->i_bkills++;
632 }
633 credit->i_score = credit->i_kills / (double) credit->i_entries;
634 ouch->p_ident->i_deaths++;
635 if (ouch->p_nchar == 0)
636 ouch->p_ident->i_stillb++;
637 if (gotcha == NULL)
638 return;
639 gotcha->p_damcap += STABDAM;
640 gotcha->p_damage -= STABDAM;
641 if (gotcha->p_damage < 0)
642 gotcha->p_damage = 0;
643 (void) snprintf(Buf, sizeof(Buf), "%2d/%2d", gotcha->p_damage,
644 gotcha->p_damcap);
645 cgoto(gotcha, STAT_DAM_ROW, STAT_VALUE_COL);
646 outstr(gotcha, Buf, 5);
647 (void) snprintf(Buf, sizeof(Buf), "%3d",
648 (gotcha->p_damcap - MAXDAM) / 2);
649 cgoto(gotcha, STAT_KILL_ROW, STAT_VALUE_COL);
650 outstr(gotcha, Buf, 3);
651 (void) snprintf(Buf, sizeof(Buf), "%5.2f", gotcha->p_ident->i_score);
652 for (ouch = Player; ouch < End_player; ouch++) {
653 cgoto(ouch, STAT_PLAY_ROW + 1 + (gotcha - Player),
654 STAT_NAME_COL);
655 outstr(ouch, Buf, 5);
656 }
657 #ifdef MONITOR
658 for (ouch = Monitor; ouch < End_monitor; ouch++) {
659 cgoto(ouch, STAT_PLAY_ROW + 1 + (gotcha - Player),
660 STAT_NAME_COL);
661 outstr(ouch, Buf, 5);
662 }
663 #endif
664 }
665
666 /*
667 * zap:
668 * Kill off a player and take him out of the game.
669 */
670 static void
671 zap(PLAYER *pp, bool was_player, int i)
672 {
673 int n, len;
674 BULLET *bp;
675 PLAYER *np;
676 int x, y;
677
678 if (was_player) {
679 if (pp->p_undershot)
680 fixshots(pp->p_y, pp->p_x, pp->p_over);
681 drawplayer(pp, false);
682 Nplayer--;
683 }
684
685 len = strlen(pp->p_death); /* Display the cause of death */
686 x = (WIDTH - len) / 2;
687 cgoto(pp, HEIGHT / 2, x);
688 outstr(pp, pp->p_death, len);
689 for (n = 1; n < len; n++)
690 pp->p_death[n] = '-';
691 pp->p_death[0] = '+';
692 pp->p_death[len - 1] = '+';
693 cgoto(pp, HEIGHT / 2 - 1, x);
694 outstr(pp, pp->p_death, len);
695 cgoto(pp, HEIGHT / 2 + 1, x);
696 outstr(pp, pp->p_death, len);
697 cgoto(pp, HEIGHT, 0);
698
699 #ifdef MONITOR
700 if (was_player) {
701 #endif
702 for (bp = Bullets; bp != NULL; bp = bp->b_next) {
703 if (bp->b_owner == pp)
704 bp->b_owner = NULL;
705 if (bp->b_x == pp->p_x && bp->b_y == pp->p_y)
706 bp->b_over = SPACE;
707 }
708
709 n = rand_num(pp->p_ammo);
710 x = rand_num(pp->p_ammo);
711 if (x > n)
712 n = x;
713 if (pp->p_ammo == 0)
714 x = 0;
715 else if (n == pp->p_ammo - 1) {
716 x = pp->p_ammo;
717 len = SLIME;
718 }
719 else {
720 for (x = MAXBOMB - 1; x > 0; x--)
721 if (n >= shot_req[x])
722 break;
723 for (y = MAXSLIME - 1; y > 0; y--)
724 if (n >= slime_req[y])
725 break;
726 if (y >= 0 && slime_req[y] > shot_req[x]) {
727 x = slime_req[y];
728 len = SLIME;
729 }
730 else if (x != 0) {
731 len = shot_type[x];
732 x = shot_req[x];
733 }
734 }
735 if (x > 0) {
736 (void) add_shot(len, pp->p_y, pp->p_x, pp->p_face, x,
737 NULL, true, SPACE);
738 (void) snprintf(Buf, sizeof(Buf), "%s detonated.",
739 pp->p_ident->i_name);
740 for (np = Player; np < End_player; np++)
741 message(np, Buf);
742 #ifdef MONITOR
743 for (np = Monitor; np < End_monitor; np++)
744 message(np, Buf);
745 #endif
746 #ifdef BOOTS
747 while (pp->p_nboots-- > 0) {
748 for (np = Boot; np < &Boot[NBOOTS]; np++)
749 if (np->p_flying < 0)
750 break;
751 if (np >= &Boot[NBOOTS])
752 err(1, "Too many boots");
753 np->p_undershot = false;
754 np->p_x = pp->p_x;
755 np->p_y = pp->p_y;
756 np->p_flying = rand_num(20);
757 np->p_flyx = 2 * rand_num(6) - 5;
758 np->p_flyy = 2 * rand_num(6) - 5;
759 np->p_over = SPACE;
760 np->p_face = BOOT;
761 showexpl(np->p_y, np->p_x, BOOT);
762 }
763 #endif
764 }
765 #ifdef BOOTS
766 else if (pp->p_nboots > 0) {
767 if (pp->p_nboots == 2)
768 Maze[pp->p_y][pp->p_x] = BOOT_PAIR;
769 else
770 Maze[pp->p_y][pp->p_x] = BOOT;
771 if (pp->p_undershot)
772 fixshots(pp->p_y, pp->p_x,
773 Maze[pp->p_y][pp->p_x]);
774 }
775 #endif
776
777 #ifdef VOLCANO
778 volcano += pp->p_ammo - x;
779 if (rand_num(100) < volcano / 50) {
780 do {
781 x = rand_num(WIDTH / 2) + WIDTH / 4;
782 y = rand_num(HEIGHT / 2) + HEIGHT / 4;
783 } while (Maze[y][x] != SPACE);
784 (void) add_shot(LAVA, y, x, LEFTS, volcano,
785 NULL, true, SPACE);
786 for (np = Player; np < End_player; np++)
787 message(np, "Volcano eruption.");
788 volcano = 0;
789 }
790 #endif
791
792 #ifdef DRONE
793 if (rand_num(100) < 2) {
794 do {
795 x = rand_num(WIDTH / 2) + WIDTH / 4;
796 y = rand_num(HEIGHT / 2) + HEIGHT / 4;
797 } while (Maze[y][x] != SPACE);
798 add_shot(DSHOT, y, x, rand_dir(),
799 shot_req[MINDSHOT +
800 rand_num(MAXBOMB - MINDSHOT)],
801 NULL, false, SPACE);
802 }
803 #endif
804
805 sendcom(pp, ENDWIN);
806 (void) putc(' ', pp->p_output);
807 (void) fclose(pp->p_output);
808
809 End_player--;
810 if (pp != End_player) {
811 memcpy(pp, End_player, sizeof (PLAYER));
812 fdset[i] = fdset[End_player - Player + 3];
813 fdset[End_player - Player + 3].fd = -1;
814 (void) snprintf(Buf, sizeof(Buf), "%5.2f%c%-10.10s %c",
815 pp->p_ident->i_score, stat_char(pp),
816 pp->p_ident->i_name, pp->p_ident->i_team);
817 n = STAT_PLAY_ROW + 1 + (pp - Player);
818 for (np = Player; np < End_player; np++) {
819 cgoto(np, n, STAT_NAME_COL);
820 outstr(np, Buf, STAT_NAME_LEN);
821 }
822 #ifdef MONITOR
823 for (np = Monitor; np < End_monitor; np++) {
824 cgoto(np, n, STAT_NAME_COL);
825 outstr(np, Buf, STAT_NAME_LEN);
826 }
827 #endif
828 } else
829 fdset[i].fd = -1;
830
831 /* Erase the last player */
832 n = STAT_PLAY_ROW + 1 + Nplayer;
833 for (np = Player; np < End_player; np++) {
834 cgoto(np, n, STAT_NAME_COL);
835 ce(np);
836 }
837 #ifdef MONITOR
838 for (np = Monitor; np < End_monitor; np++) {
839 cgoto(np, n, STAT_NAME_COL);
840 ce(np);
841 }
842 }
843 else {
844 sendcom(pp, ENDWIN);
845 (void) putc(LAST_PLAYER, pp->p_output);
846 (void) fclose(pp->p_output);
847
848 End_monitor--;
849 if (pp != End_monitor) {
850 memcpy(pp, End_monitor, sizeof (PLAYER));
851 fdset[i] = fdset[End_monitor - Monitor + MAXPL + 3];
852 fdset[End_monitor - Monitor + MAXPL + 3].fd = -1;
853 (void) snprintf(Buf, sizeof(Buf), "%5.5s %-10.10s %c",
854 " ",
855 pp->p_ident->i_name, pp->p_ident->i_team);
856 n = STAT_MON_ROW + 1 + (pp - Player);
857 for (np = Player; np < End_player; np++) {
858 cgoto(np, n, STAT_NAME_COL);
859 outstr(np, Buf, STAT_NAME_LEN);
860 }
861 for (np = Monitor; np < End_monitor; np++) {
862 cgoto(np, n, STAT_NAME_COL);
863 outstr(np, Buf, STAT_NAME_LEN);
864 }
865 } else
866 fdset[i].fd = -1;
867
868 /* Erase the last monitor */
869 n = STAT_MON_ROW + 1 + (End_monitor - Monitor);
870 for (np = Player; np < End_player; np++) {
871 cgoto(np, n, STAT_NAME_COL);
872 ce(np);
873 }
874 for (np = Monitor; np < End_monitor; np++) {
875 cgoto(np, n, STAT_NAME_COL);
876 ce(np);
877 }
878 }
879 #endif
880 }
881
882 /*
883 * rand_num:
884 * Return a random number in a given range.
885 */
886 int
887 rand_num(int range)
888 {
889 return (range == 0 ? 0 : random() % range);
890 }
891
892 /*
893 * havechar:
894 * Check to see if we have any characters in the input queue; if
895 * we do, read them, stash them away, and return true; else return
896 * false.
897 */
898 static bool
899 havechar(PLAYER *pp, int i)
900 {
901
902 if (pp->p_ncount < pp->p_nchar)
903 return true;
904 if (!(fdset[i].revents & POLLIN))
905 return false;
906 check_again:
907 pp->p_nchar = read(pp->p_fd, pp->p_cbuf, sizeof pp->p_cbuf);
908 if (pp->p_nchar < 0 && errno == EINTR) {
909 goto check_again;
910 } else if (pp->p_nchar <= 0) {
911 if (errno == EINTR)
912 pp->p_cbuf[0] = 'q';
913 }
914 pp->p_ncount = 0;
915 return true;
916 }
917
918 /*
919 * cleanup:
920 * Exit with the given value, cleaning up any droppings lying around
921 */
922 void
923 cleanup(int exitval)
924 {
925 PLAYER *pp;
926
927 for (pp = Player; pp < End_player; pp++) {
928 cgoto(pp, HEIGHT, 0);
929 sendcom(pp, ENDWIN);
930 (void) putc(LAST_PLAYER, pp->p_output);
931 (void) fclose(pp->p_output);
932 }
933 #ifdef MONITOR
934 for (pp = Monitor; pp < End_monitor; pp++) {
935 cgoto(pp, HEIGHT, 0);
936 sendcom(pp, ENDWIN);
937 (void) putc(LAST_PLAYER, pp->p_output);
938 (void) fclose(pp->p_output);
939 }
940 #endif
941 (void) close(huntsock);
942 #ifdef AF_UNIX_HACK
943 (void) unlink(huntsockpath);
944 #endif
945
946 exit(exitval);
947 }
948
949 /*
950 * send_stats:
951 * Print stats to requestor
952 */
953 static void
954 send_stats(void)
955 {
956 IDENT *ip;
957 FILE *fp;
958 int s;
959 struct sockaddr_storage newaddr;
960 socklen_t socklen;
961
962 /*
963 * Get the output stream ready
964 */
965 socklen = sizeof(newaddr);
966 s = accept(statsock, (struct sockaddr *)&newaddr, &socklen);
967 if (s < 0) {
968 if (errno == EINTR)
969 return;
970 complain(LOG_WARNING, "accept");
971 return;
972 }
973 fp = fdopen(s, "w");
974 if (fp == NULL) {
975 complain(LOG_WARNING, "fdopen");
976 (void) close(s);
977 return;
978 }
979
980 /*
981 * Send output to requestor
982 */
983 fputs("Name\t\tScore\tDucked\tAbsorb\tFaced\tShot\tRobbed\tMissed\tSlimeK\n", fp);
984 for (ip = Scores; ip != NULL; ip = ip->i_next) {
985 fprintf(fp, "%s\t", ip->i_name);
986 if (strlen(ip->i_name) < 8)
987 putc('\t', fp);
988 fprintf(fp, "%.2f\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
989 ip->i_score, ip->i_ducked, ip->i_absorbed,
990 ip->i_faced, ip->i_shot, ip->i_robbed,
991 ip->i_missed, ip->i_slime);
992 }
993 fputs("\n\nName\t\tEnemy\tFriend\tDeaths\tStill\tSaved\n", fp);
994 for (ip = Scores; ip != NULL; ip = ip->i_next) {
995 if (ip->i_team == ' ') {
996 fprintf(fp, "%s\t", ip->i_name);
997 if (strlen(ip->i_name) < 8)
998 putc('\t', fp);
999 }
1000 else {
1001 fprintf(fp, "%s[%c]\t", ip->i_name, ip->i_team);
1002 if (strlen(ip->i_name) + 3 < 8)
1003 putc('\t', fp);
1004 }
1005 fprintf(fp, "%d\t%d\t%d\t%d\t%d\n",
1006 ip->i_gkills, ip->i_bkills, ip->i_deaths,
1007 ip->i_stillb, ip->i_saved);
1008 }
1009
1010 (void) fclose(fp);
1011 }
1012
1013 /*
1014 * clear_scores:
1015 * Clear out the scores so the next session start clean
1016 */
1017 static void
1018 clear_scores(void)
1019 {
1020 IDENT *ip, *nextip;
1021
1022 for (ip = Scores; ip != NULL; ip = nextip) {
1023 nextip = ip->i_next;
1024 (void) free(ip);
1025 }
1026 Scores = NULL;
1027 }