1 /* $NetBSD: driver.c,v 1.13 2008/01/28 03:23:29 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.13 2008/01/28 03:23:29 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, (char *) &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, (char *) &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, (char *) &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, (char *) &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) sprintf(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) sprintf(ouch->p_death, "| %s by %s |", cp, 545 (this_shot_type == MINE || this_shot_type == GMINE) ? 546 "a mine" : "act of God"); 547 return; 548 } 549 550 (void) sprintf(ouch->p_death, "| %s by %s |", cp, credit->i_name); 551 552 if (ouch == gotcha) { /* No use killing yourself */ 553 credit->i_kills--; 554 credit->i_bkills++; 555 } 556 else if (ouch->p_ident->i_team == ' ' 557 || ouch->p_ident->i_team != credit->i_team) { 558 credit->i_kills++; 559 credit->i_gkills++; 560 } 561 else { 562 credit->i_kills--; 563 credit->i_bkills++; 564 } 565 credit->i_score = credit->i_kills / (double) credit->i_entries; 566 ouch->p_ident->i_deaths++; 567 if (ouch->p_nchar == 0) 568 ouch->p_ident->i_stillb++; 569 if (gotcha == NULL) 570 return; 571 gotcha->p_damcap += STABDAM; 572 gotcha->p_damage -= STABDAM; 573 if (gotcha->p_damage < 0) 574 gotcha->p_damage = 0; 575 (void) sprintf(Buf, "%2d/%2d", gotcha->p_damage, gotcha->p_damcap); 576 cgoto(gotcha, STAT_DAM_ROW, STAT_VALUE_COL); 577 outstr(gotcha, Buf, 5); 578 (void) sprintf(Buf, "%3d", (gotcha->p_damcap - MAXDAM) / 2); 579 cgoto(gotcha, STAT_KILL_ROW, STAT_VALUE_COL); 580 outstr(gotcha, Buf, 3); 581 (void) sprintf(Buf, "%5.2f", gotcha->p_ident->i_score); 582 for (ouch = Player; ouch < End_player; ouch++) { 583 cgoto(ouch, STAT_PLAY_ROW + 1 + (gotcha - Player), 584 STAT_NAME_COL); 585 outstr(ouch, Buf, 5); 586 } 587 # ifdef MONITOR 588 for (ouch = Monitor; ouch < End_monitor; ouch++) { 589 cgoto(ouch, STAT_PLAY_ROW + 1 + (gotcha - Player), 590 STAT_NAME_COL); 591 outstr(ouch, Buf, 5); 592 } 593 # endif 594 } 595 596 /* 597 * zap: 598 * Kill off a player and take him out of the game. 599 */ 600 static void 601 zap(pp, was_player, i) 602 PLAYER *pp; 603 FLAG was_player; 604 int i; 605 { 606 int n, len; 607 BULLET *bp; 608 PLAYER *np; 609 int x, y; 610 int savefd; 611 612 if (was_player) { 613 if (pp->p_undershot) 614 fixshots(pp->p_y, pp->p_x, pp->p_over); 615 drawplayer(pp, FALSE); 616 Nplayer--; 617 } 618 619 len = strlen(pp->p_death); /* Display the cause of death */ 620 x = (WIDTH - len) / 2; 621 cgoto(pp, HEIGHT / 2, x); 622 outstr(pp, pp->p_death, len); 623 for (n = 1; n < len; n++) 624 pp->p_death[n] = '-'; 625 pp->p_death[0] = '+'; 626 pp->p_death[len - 1] = '+'; 627 cgoto(pp, HEIGHT / 2 - 1, x); 628 outstr(pp, pp->p_death, len); 629 cgoto(pp, HEIGHT / 2 + 1, x); 630 outstr(pp, pp->p_death, len); 631 cgoto(pp, HEIGHT, 0); 632 633 savefd = pp->p_fd; 634 635 # ifdef MONITOR 636 if (was_player) { 637 # endif 638 for (bp = Bullets; bp != NULL; bp = bp->b_next) { 639 if (bp->b_owner == pp) 640 bp->b_owner = NULL; 641 if (bp->b_x == pp->p_x && bp->b_y == pp->p_y) 642 bp->b_over = SPACE; 643 } 644 645 n = rand_num(pp->p_ammo); 646 x = rand_num(pp->p_ammo); 647 if (x > n) 648 n = x; 649 if (pp->p_ammo == 0) 650 x = 0; 651 else if (n == pp->p_ammo - 1) { 652 x = pp->p_ammo; 653 len = SLIME; 654 } 655 else { 656 for (x = MAXBOMB - 1; x > 0; x--) 657 if (n >= shot_req[x]) 658 break; 659 for (y = MAXSLIME - 1; y > 0; y--) 660 if (n >= slime_req[y]) 661 break; 662 if (y >= 0 && slime_req[y] > shot_req[x]) { 663 x = slime_req[y]; 664 len = SLIME; 665 } 666 else if (x != 0) { 667 len = shot_type[x]; 668 x = shot_req[x]; 669 } 670 } 671 if (x > 0) { 672 (void) add_shot(len, pp->p_y, pp->p_x, pp->p_face, x, 673 (PLAYER *) NULL, TRUE, SPACE); 674 (void) sprintf(Buf, "%s detonated.", 675 pp->p_ident->i_name); 676 for (np = Player; np < End_player; np++) 677 message(np, Buf); 678 # ifdef MONITOR 679 for (np = Monitor; np < End_monitor; np++) 680 message(np, Buf); 681 # endif 682 # ifdef BOOTS 683 while (pp->p_nboots-- > 0) { 684 for (np = Boot; np < &Boot[NBOOTS]; np++) 685 if (np->p_flying < 0) 686 break; 687 if (np >= &Boot[NBOOTS]) 688 err(1, "Too many boots"); 689 np->p_undershot = FALSE; 690 np->p_x = pp->p_x; 691 np->p_y = pp->p_y; 692 np->p_flying = rand_num(20); 693 np->p_flyx = 2 * rand_num(6) - 5; 694 np->p_flyy = 2 * rand_num(6) - 5; 695 np->p_over = SPACE; 696 np->p_face = BOOT; 697 showexpl(np->p_y, np->p_x, BOOT); 698 } 699 # endif 700 } 701 # ifdef BOOTS 702 else if (pp->p_nboots > 0) { 703 if (pp->p_nboots == 2) 704 Maze[pp->p_y][pp->p_x] = BOOT_PAIR; 705 else 706 Maze[pp->p_y][pp->p_x] = BOOT; 707 if (pp->p_undershot) 708 fixshots(pp->p_y, pp->p_x, 709 Maze[pp->p_y][pp->p_x]); 710 } 711 # endif 712 713 # ifdef VOLCANO 714 volcano += pp->p_ammo - x; 715 if (rand_num(100) < volcano / 50) { 716 do { 717 x = rand_num(WIDTH / 2) + WIDTH / 4; 718 y = rand_num(HEIGHT / 2) + HEIGHT / 4; 719 } while (Maze[y][x] != SPACE); 720 (void) add_shot(LAVA, y, x, LEFTS, volcano, 721 (PLAYER *) NULL, TRUE, SPACE); 722 for (np = Player; np < End_player; np++) 723 message(np, "Volcano eruption."); 724 volcano = 0; 725 } 726 # endif 727 728 # ifdef DRONE 729 if (rand_num(100) < 2) { 730 do { 731 x = rand_num(WIDTH / 2) + WIDTH / 4; 732 y = rand_num(HEIGHT / 2) + HEIGHT / 4; 733 } while (Maze[y][x] != SPACE); 734 add_shot(DSHOT, y, x, rand_dir(), 735 shot_req[MINDSHOT + 736 rand_num(MAXBOMB - MINDSHOT)], 737 (PLAYER *) NULL, FALSE, SPACE); 738 } 739 # endif 740 741 sendcom(pp, ENDWIN); 742 (void) putc(' ', pp->p_output); 743 (void) fclose(pp->p_output); 744 745 End_player--; 746 if (pp != End_player) { 747 memcpy(pp, End_player, sizeof (PLAYER)); 748 fdset[i] = fdset[End_player - Player + 3]; 749 fdset[End_player - Player + 3].fd = -1; 750 (void) sprintf(Buf, "%5.2f%c%-10.10s %c", 751 pp->p_ident->i_score, stat_char(pp), 752 pp->p_ident->i_name, pp->p_ident->i_team); 753 n = STAT_PLAY_ROW + 1 + (pp - Player); 754 for (np = Player; np < End_player; np++) { 755 cgoto(np, n, STAT_NAME_COL); 756 outstr(np, Buf, STAT_NAME_LEN); 757 } 758 # ifdef MONITOR 759 for (np = Monitor; np < End_monitor; np++) { 760 cgoto(np, n, STAT_NAME_COL); 761 outstr(np, Buf, STAT_NAME_LEN); 762 } 763 # endif 764 } else 765 fdset[i].fd = -1; 766 767 /* Erase the last player */ 768 n = STAT_PLAY_ROW + 1 + Nplayer; 769 for (np = Player; np < End_player; np++) { 770 cgoto(np, n, STAT_NAME_COL); 771 ce(np); 772 } 773 # ifdef MONITOR 774 for (np = Monitor; np < End_monitor; np++) { 775 cgoto(np, n, STAT_NAME_COL); 776 ce(np); 777 } 778 } 779 else { 780 sendcom(pp, ENDWIN); 781 (void) putc(LAST_PLAYER, pp->p_output); 782 (void) fclose(pp->p_output); 783 784 End_monitor--; 785 if (pp != End_monitor) { 786 memcpy(pp, End_monitor, sizeof (PLAYER)); 787 fdset[i] = fdset[End_monitor - Monitor + MAXPL + 3]; 788 fdset[End_monitor - Monitor + MAXPL + 3].fd = -1; 789 (void) sprintf(Buf, "%5.5s %-10.10s %c", " ", 790 pp->p_ident->i_name, pp->p_ident->i_team); 791 n = STAT_MON_ROW + 1 + (pp - Player); 792 for (np = Player; np < End_player; np++) { 793 cgoto(np, n, STAT_NAME_COL); 794 outstr(np, Buf, STAT_NAME_LEN); 795 } 796 for (np = Monitor; np < End_monitor; np++) { 797 cgoto(np, n, STAT_NAME_COL); 798 outstr(np, Buf, STAT_NAME_LEN); 799 } 800 } else 801 fdset[i].fd = -1; 802 803 /* Erase the last monitor */ 804 n = STAT_MON_ROW + 1 + (End_monitor - Monitor); 805 for (np = Player; np < End_player; np++) { 806 cgoto(np, n, STAT_NAME_COL); 807 ce(np); 808 } 809 for (np = Monitor; np < End_monitor; np++) { 810 cgoto(np, n, STAT_NAME_COL); 811 ce(np); 812 } 813 } 814 # endif 815 } 816 817 /* 818 * rand_num: 819 * Return a random number in a given range. 820 */ 821 int 822 rand_num(range) 823 int range; 824 { 825 return (range == 0 ? 0 : RN % range); 826 } 827 828 /* 829 * havechar: 830 * Check to see if we have any characters in the input queue; if 831 * we do, read them, stash them away, and return TRUE; else return 832 * FALSE. 833 */ 834 static int 835 havechar(pp, i) 836 PLAYER *pp; 837 int i; 838 { 839 840 if (pp->p_ncount < pp->p_nchar) 841 return TRUE; 842 if (!(fdset[i].revents & POLLIN)) 843 return FALSE; 844 check_again: 845 errno = 0; 846 if ((pp->p_nchar = read(pp->p_fd, pp->p_cbuf, sizeof pp->p_cbuf)) <= 0) 847 { 848 if (errno == EINTR) 849 goto check_again; 850 pp->p_cbuf[0] = 'q'; 851 } 852 pp->p_ncount = 0; 853 return TRUE; 854 } 855 856 /* 857 * cleanup: 858 * Exit with the given value, cleaning up any droppings lying around 859 */ 860 SIGNAL_TYPE 861 cleanup(eval) 862 int eval; 863 { 864 PLAYER *pp; 865 866 for (pp = Player; pp < End_player; pp++) { 867 cgoto(pp, HEIGHT, 0); 868 sendcom(pp, ENDWIN); 869 (void) putc(LAST_PLAYER, pp->p_output); 870 (void) fclose(pp->p_output); 871 } 872 # ifdef MONITOR 873 for (pp = Monitor; pp < End_monitor; pp++) { 874 cgoto(pp, HEIGHT, 0); 875 sendcom(pp, ENDWIN); 876 (void) putc(LAST_PLAYER, pp->p_output); 877 (void) fclose(pp->p_output); 878 } 879 # endif 880 (void) close(Socket); 881 # ifdef AF_UNIX_HACK 882 (void) unlink(Sock_name); 883 # endif 884 885 exit(eval); 886 } 887 888 /* 889 * send_stats: 890 * Print stats to requestor 891 */ 892 static void 893 send_stats() 894 { 895 IDENT *ip; 896 FILE *fp; 897 int s; 898 SOCKET sockstruct; 899 socklen_t socklen; 900 901 /* 902 * Get the output stream ready 903 */ 904 # ifdef INTERNET 905 socklen = sizeof sockstruct; 906 # else 907 socklen = sizeof sockstruct - 1; 908 # endif 909 s = accept(Status, (struct sockaddr *) &sockstruct, &socklen); 910 if (s < 0) { 911 if (errno == EINTR) 912 return; 913 # ifdef LOG 914 syslog(LOG_WARNING, "accept: %m"); 915 # else 916 warn("accept"); 917 # endif 918 return; 919 } 920 fp = fdopen(s, "w"); 921 if (fp == NULL) { 922 # ifdef LOG 923 syslog(LOG_WARNING, "fdopen: %m"); 924 # else 925 warn("fdopen"); 926 # endif 927 (void) close(s); 928 return; 929 } 930 931 /* 932 * Send output to requestor 933 */ 934 fputs("Name\t\tScore\tDucked\tAbsorb\tFaced\tShot\tRobbed\tMissed\tSlimeK\n", fp); 935 for (ip = Scores; ip != NULL; ip = ip->i_next) { 936 fprintf(fp, "%s\t", ip->i_name); 937 if (strlen(ip->i_name) < 8) 938 putc('\t', fp); 939 fprintf(fp, "%.2f\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", 940 ip->i_score, ip->i_ducked, ip->i_absorbed, 941 ip->i_faced, ip->i_shot, ip->i_robbed, 942 ip->i_missed, ip->i_slime); 943 } 944 fputs("\n\nName\t\tEnemy\tFriend\tDeaths\tStill\tSaved\n", fp); 945 for (ip = Scores; ip != NULL; ip = ip->i_next) { 946 if (ip->i_team == ' ') { 947 fprintf(fp, "%s\t", ip->i_name); 948 if (strlen(ip->i_name) < 8) 949 putc('\t', fp); 950 } 951 else { 952 fprintf(fp, "%s[%c]\t", ip->i_name, ip->i_team); 953 if (strlen(ip->i_name) + 3 < 8) 954 putc('\t', fp); 955 } 956 fprintf(fp, "%d\t%d\t%d\t%d\t%d\n", 957 ip->i_gkills, ip->i_bkills, ip->i_deaths, 958 ip->i_stillb, ip->i_saved); 959 } 960 961 (void) fclose(fp); 962 } 963 964 /* 965 * clear_scores: 966 * Clear out the scores so the next session start clean 967 */ 968 static void 969 clear_scores() 970 { 971 IDENT *ip, *nextip; 972 973 for (ip = Scores; ip != NULL; ip = nextip) { 974 nextip = ip->i_next; 975 (void) free((char *) ip); 976 } 977 Scores = NULL; 978 } 979