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