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