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