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