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