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