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