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