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