/* $NetBSD: driver.c,v 1.5 1997/10/20 00:37:16 lukem Exp $ */ /* * Hunt * Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold * San Francisco, California */ #include #ifndef lint __RCSID("$NetBSD: driver.c,v 1.5 1997/10/20 00:37:16 lukem Exp $"); #endif /* not lint */ # include # include # include # include # include # include # include # include # include "hunt.h" # ifndef pdp11 # define RN (((Seed = Seed * 11109 + 13849) >> 16) & 0xffff) # else # define RN ((Seed = Seed * 11109 + 13849) & 0x7fff) # endif int Seed = 0; SOCKET Daemon; char *First_arg; /* pointer to argv[0] */ char *Last_arg; /* pointer to end of argv/environ */ # ifdef INTERNET int Test_socket; /* test socket to answer datagrams */ FLAG inetd_spawned; /* invoked via inetd */ FLAG standard_port = TRUE; /* true if listening on standard port */ u_short sock_port; /* port # of tcp listen socket */ u_short stat_port; /* port # of statistics tcp socket */ # define DAEMON_SIZE (sizeof Daemon) # else # define DAEMON_SIZE (sizeof Daemon - 1) # endif static void clear_scores __P((void)); static int havechar __P((PLAYER *)); static void init __P((void)); int main __P((int, char *[], char *[])); static void makeboots __P((void)); static void send_stats __P((void)); static void zap __P((PLAYER *, FLAG)); /* * main: * The main program. */ int main(ac, av, ep) int ac; char **av, **ep; { PLAYER *pp; int had_char; # ifdef INTERNET u_short msg; short port_num, reply; int namelen; SOCKET test; # endif static fd_set read_fds; static FLAG first = TRUE; static FLAG server = FALSE; extern int optind; extern char *optarg; int c; static struct timeval linger = { 90, 0 }; First_arg = av[0]; if (ep == NULL || *ep == NULL) ep = av + ac; while (*ep) ep++; Last_arg = ep[-1] + strlen(ep[-1]); while ((c = getopt(ac, av, "sp:")) != -1) { switch (c) { case 's': server = TRUE; break; # ifdef INTERNET case 'p': standard_port = FALSE; Test_port = atoi(optarg); break; # endif default: erred: fprintf(stderr, "Usage: %s [-s] [-p port]\n", av[0]); exit(1); } } if (optind < ac) goto erred; init(); again: do { read_fds = Fds_mask; errno = 0; while (select(Num_fds, &read_fds, NULL, NULL, NULL) < 0) { if (errno != EINTR) # ifdef LOG syslog(LOG_WARNING, "select: %m"); # else warn("select"); # endif errno = 0; } Have_inp = read_fds; # ifdef INTERNET if (FD_ISSET(Test_socket, &read_fds)) { namelen = DAEMON_SIZE; port_num = htons(sock_port); (void) recvfrom(Test_socket, (char *) &msg, sizeof msg, 0, (struct sockaddr *) &test, &namelen); switch (ntohs(msg)) { case C_MESSAGE: if (Nplayer <= 0) break; reply = htons((u_short) Nplayer); (void) sendto(Test_socket, (char *) &reply, sizeof reply, 0, (struct sockaddr *) &test, DAEMON_SIZE); break; case C_SCORES: reply = htons(stat_port); (void) sendto(Test_socket, (char *) &reply, sizeof reply, 0, (struct sockaddr *) &test, DAEMON_SIZE); break; case C_PLAYER: case C_MONITOR: if (msg == C_MONITOR && Nplayer <= 0) break; reply = htons(sock_port); (void) sendto(Test_socket, (char *) &reply, sizeof reply, 0, (struct sockaddr *) &test, DAEMON_SIZE); break; } } # endif for (;;) { had_char = FALSE; for (pp = Player; pp < End_player; pp++) if (havechar(pp)) { execute(pp); pp->p_nexec++; had_char++; } # ifdef MONITOR for (pp = Monitor; pp < End_monitor; pp++) if (havechar(pp)) { mon_execute(pp); pp->p_nexec++; had_char++; } # endif if (!had_char) break; moveshots(); for (pp = Player; pp < End_player; ) if (pp->p_death[0] != '\0') zap(pp, TRUE); else pp++; # ifdef MONITOR for (pp = Monitor; pp < End_monitor; ) if (pp->p_death[0] != '\0') zap(pp, FALSE); else pp++; # endif } if (FD_ISSET(Socket, &read_fds)) if (answer()) { # ifdef INTERNET if (first && standard_port) faketalk(); # endif first = FALSE; } if (FD_ISSET(Status, &read_fds)) send_stats(); for (pp = Player; pp < End_player; pp++) { if (FD_ISSET(pp->p_fd, &read_fds)) sendcom(pp, READY, pp->p_nexec); pp->p_nexec = 0; (void) fflush(pp->p_output); } # ifdef MONITOR for (pp = Monitor; pp < End_monitor; pp++) { if (FD_ISSET(pp->p_fd, &read_fds)) sendcom(pp, READY, pp->p_nexec); pp->p_nexec = 0; (void) fflush(pp->p_output); } # endif } while (Nplayer > 0); read_fds = Fds_mask; if (select(Num_fds, &read_fds, NULL, NULL, &linger) > 0) { goto again; } if (server) { clear_scores(); makemaze(); clearwalls(); # ifdef BOOTS makeboots(); # endif first = TRUE; goto again; } # ifdef MONITOR for (pp = Monitor; pp < End_monitor; ) zap(pp, FALSE); # endif cleanup(0); /* NOTREACHED */ return(0); } /* * init: * Initialize the global parameters. */ static void init() { int i; # ifdef INTERNET SOCKET test_port; int msg; int len; # endif # ifndef DEBUG # ifdef TIOCNOTTY (void) ioctl(fileno(stdout), TIOCNOTTY, NULL); # endif (void) setpgrp(getpid(), getpid()); (void) signal(SIGHUP, SIG_IGN); (void) signal(SIGINT, SIG_IGN); (void) signal(SIGQUIT, SIG_IGN); (void) signal(SIGTERM, cleanup); # endif (void) chdir("/var/tmp"); /* just in case it core dumps */ (void) umask(0); /* No privacy at all! */ (void) signal(SIGPIPE, SIG_IGN); # ifdef LOG # ifdef SYSLOG_43 openlog("HUNT", LOG_PID, LOG_DAEMON); # endif # ifdef SYSLOG_42 openlog("HUNT", LOG_PID); # endif # endif /* * Initialize statistics socket */ # ifdef INTERNET Daemon.sin_family = SOCK_FAMILY; Daemon.sin_addr.s_addr = INADDR_ANY; Daemon.sin_port = 0; # else Daemon.sun_family = SOCK_FAMILY; (void) strcpy(Daemon.sun_path, Stat_name); # endif Status = socket(SOCK_FAMILY, SOCK_STREAM, 0); if (bind(Status, (struct sockaddr *) &Daemon, DAEMON_SIZE) < 0) { if (errno == EADDRINUSE) exit(0); else { # ifdef LOG syslog(LOG_ERR, "bind: %m"); # else warn("bind"); # endif cleanup(1); } } (void) listen(Status, 5); # ifdef INTERNET len = sizeof (SOCKET); if (getsockname(Status, (struct sockaddr *) &Daemon, &len) < 0) { # ifdef LOG syslog(LOG_ERR, "getsockname: %m"); # else warn("getsockname"); # endif exit(1); } stat_port = ntohs(Daemon.sin_port); # endif /* * Initialize main socket */ # ifdef INTERNET Daemon.sin_family = SOCK_FAMILY; Daemon.sin_addr.s_addr = INADDR_ANY; Daemon.sin_port = 0; # else Daemon.sun_family = SOCK_FAMILY; (void) strcpy(Daemon.sun_path, Sock_name); # endif Socket = socket(SOCK_FAMILY, SOCK_STREAM, 0); # if defined(INTERNET) msg = 1; if (setsockopt(Socket, SOL_SOCKET, SO_USELOOPBACK, &msg, sizeof msg)<0) # ifdef LOG syslog(LOG_WARNING, "setsockopt loopback %m"); # else warn("setsockopt loopback"); # endif # endif if (bind(Socket, (struct sockaddr *) &Daemon, DAEMON_SIZE) < 0) { if (errno == EADDRINUSE) exit(0); else { # ifdef LOG syslog(LOG_ERR, "bind: %m"); # else warn("bind"); # endif cleanup(1); } } (void) listen(Socket, 5); # ifdef INTERNET len = sizeof (SOCKET); if (getsockname(Socket, (struct sockaddr *) &Daemon, &len) < 0) { # ifdef LOG syslog(LOG_ERR, "getsockname: %m"); # else warn("getsockname"); # endif exit(1); } sock_port = ntohs(Daemon.sin_port); # endif /* * Initialize minimal select mask */ FD_ZERO(&Fds_mask); FD_SET(Socket, &Fds_mask); FD_SET(Status, &Fds_mask); Num_fds = ((Socket > Status) ? Socket : Status) + 1; # ifdef INTERNET len = sizeof (SOCKET); if (getsockname(0, (struct sockaddr *) &test_port, &len) >= 0 && test_port.sin_family == AF_INET) { inetd_spawned = TRUE; Test_socket = 0; if (test_port.sin_port != htons((u_short) Test_port)) { standard_port = FALSE; Test_port = ntohs(test_port.sin_port); } } else { test_port = Daemon; test_port.sin_port = htons((u_short) Test_port); Test_socket = socket(SOCK_FAMILY, SOCK_DGRAM, 0); if (bind(Test_socket, (struct sockaddr *) &test_port, DAEMON_SIZE) < 0) { # ifdef LOG syslog(LOG_ERR, "bind: %m"); # else warn("bind"); # endif exit(1); } (void) listen(Test_socket, 5); } FD_SET(Test_socket, &Fds_mask); if (Test_socket + 1 > Num_fds) Num_fds = Test_socket + 1; # endif Seed = getpid() + time((time_t *) NULL); makemaze(); # ifdef BOOTS makeboots(); # endif for (i = 0; i < NASCII; i++) See_over[i] = TRUE; See_over[DOOR] = FALSE; See_over[WALL1] = FALSE; See_over[WALL2] = FALSE; See_over[WALL3] = FALSE; # ifdef REFLECT See_over[WALL4] = FALSE; See_over[WALL5] = FALSE; # endif } # ifdef BOOTS /* * makeboots: * Put the boots in the maze */ static void makeboots() { int x, y; PLAYER *pp; do { x = rand_num(WIDTH - 1) + 1; y = rand_num(HEIGHT - 1) + 1; } while (Maze[y][x] != SPACE); Maze[y][x] = BOOT_PAIR; for (pp = Boot; pp < &Boot[NBOOTS]; pp++) pp->p_flying = -1; } # endif /* * checkdam: * Check the damage to the given player, and see if s/he is killed */ void checkdam(ouch, gotcha, credit, amt, shot_type) PLAYER *ouch, *gotcha; IDENT *credit; int amt; char shot_type; { char *cp; if (ouch->p_death[0] != '\0') return; # ifdef BOOTS if (shot_type == SLIME) switch (ouch->p_nboots) { default: break; case 1: amt = (amt + 1) / 2; break; case 2: if (gotcha != NULL) message(gotcha, "He has boots on!"); return; } # endif ouch->p_damage += amt; if (ouch->p_damage <= ouch->p_damcap) { (void) sprintf(Buf, "%2d", ouch->p_damage); cgoto(ouch, STAT_DAM_ROW, STAT_VALUE_COL); outstr(ouch, Buf, 2); return; } /* Someone DIED */ switch (shot_type) { default: cp = "Killed"; break; # ifdef FLY case FALL: cp = "Killed on impact"; break; # endif case KNIFE: cp = "Stabbed to death"; ouch->p_ammo = 0; /* No exploding */ break; case SHOT: cp = "Shot to death"; break; case GRENADE: case SATCHEL: case BOMB: cp = "Bombed"; break; case MINE: case GMINE: cp = "Blown apart"; break; # ifdef OOZE case SLIME: cp = "Slimed"; if (credit != NULL) credit->i_slime++; break; # endif # ifdef VOLCANO case LAVA: cp = "Baked"; break; # endif # ifdef DRONE case DSHOT: cp = "Eliminated"; break; # endif } if (credit == NULL) { (void) sprintf(ouch->p_death, "| %s by %s |", cp, (shot_type == MINE || shot_type == GMINE) ? "a mine" : "act of God"); return; } (void) sprintf(ouch->p_death, "| %s by %s |", cp, credit->i_name); if (ouch == gotcha) { /* No use killing yourself */ credit->i_kills--; credit->i_bkills++; } else if (ouch->p_ident->i_team == ' ' || ouch->p_ident->i_team != credit->i_team) { credit->i_kills++; credit->i_gkills++; } else { credit->i_kills--; credit->i_bkills++; } credit->i_score = credit->i_kills / (double) credit->i_entries; ouch->p_ident->i_deaths++; if (ouch->p_nchar == 0) ouch->p_ident->i_stillb++; if (gotcha == NULL) return; gotcha->p_damcap += STABDAM; gotcha->p_damage -= STABDAM; if (gotcha->p_damage < 0) gotcha->p_damage = 0; (void) sprintf(Buf, "%2d/%2d", gotcha->p_damage, gotcha->p_damcap); cgoto(gotcha, STAT_DAM_ROW, STAT_VALUE_COL); outstr(gotcha, Buf, 5); (void) sprintf(Buf, "%3d", (gotcha->p_damcap - MAXDAM) / 2); cgoto(gotcha, STAT_KILL_ROW, STAT_VALUE_COL); outstr(gotcha, Buf, 3); (void) sprintf(Buf, "%5.2f", gotcha->p_ident->i_score); for (ouch = Player; ouch < End_player; ouch++) { cgoto(ouch, STAT_PLAY_ROW + 1 + (gotcha - Player), STAT_NAME_COL); outstr(ouch, Buf, 5); } # ifdef MONITOR for (ouch = Monitor; ouch < End_monitor; ouch++) { cgoto(ouch, STAT_PLAY_ROW + 1 + (gotcha - Player), STAT_NAME_COL); outstr(ouch, Buf, 5); } # endif } /* * zap: * Kill off a player and take him out of the game. */ static void zap(pp, was_player) PLAYER *pp; FLAG was_player; { int i, len; BULLET *bp; PLAYER *np; int x, y; int savefd; if (was_player) { if (pp->p_undershot) fixshots(pp->p_y, pp->p_x, pp->p_over); drawplayer(pp, FALSE); Nplayer--; } len = strlen(pp->p_death); /* Display the cause of death */ x = (WIDTH - len) / 2; cgoto(pp, HEIGHT / 2, x); outstr(pp, pp->p_death, len); for (i = 1; i < len; i++) pp->p_death[i] = '-'; pp->p_death[0] = '+'; pp->p_death[len - 1] = '+'; cgoto(pp, HEIGHT / 2 - 1, x); outstr(pp, pp->p_death, len); cgoto(pp, HEIGHT / 2 + 1, x); outstr(pp, pp->p_death, len); cgoto(pp, HEIGHT, 0); savefd = pp->p_fd; # ifdef MONITOR if (was_player) { # endif for (bp = Bullets; bp != NULL; bp = bp->b_next) { if (bp->b_owner == pp) bp->b_owner = NULL; if (bp->b_x == pp->p_x && bp->b_y == pp->p_y) bp->b_over = SPACE; } i = rand_num(pp->p_ammo); x = rand_num(pp->p_ammo); if (x > i) i = x; if (pp->p_ammo == 0) x = 0; else if (i == pp->p_ammo - 1) { x = pp->p_ammo; len = SLIME; } else { for (x = MAXBOMB - 1; x > 0; x--) if (i >= shot_req[x]) break; for (y = MAXSLIME - 1; y > 0; y--) if (i >= slime_req[y]) break; if (y >= 0 && slime_req[y] > shot_req[x]) { x = slime_req[y]; len = SLIME; } else if (x != 0) { len = shot_type[x]; x = shot_req[x]; } } if (x > 0) { (void) add_shot(len, pp->p_y, pp->p_x, pp->p_face, x, (PLAYER *) NULL, TRUE, SPACE); (void) sprintf(Buf, "%s detonated.", pp->p_ident->i_name); for (np = Player; np < End_player; np++) message(np, Buf); # ifdef MONITOR for (np = Monitor; np < End_monitor; np++) message(np, Buf); # endif # ifdef BOOTS while (pp->p_nboots-- > 0) { for (np = Boot; np < &Boot[NBOOTS]; np++) if (np->p_flying < 0) break; if (np >= &Boot[NBOOTS]) err(1, "Too many boots"); np->p_undershot = FALSE; np->p_x = pp->p_x; np->p_y = pp->p_y; np->p_flying = rand_num(20); np->p_flyx = 2 * rand_num(6) - 5; np->p_flyy = 2 * rand_num(6) - 5; np->p_over = SPACE; np->p_face = BOOT; showexpl(np->p_y, np->p_x, BOOT); } # endif } # ifdef BOOTS else if (pp->p_nboots > 0) { if (pp->p_nboots == 2) Maze[pp->p_y][pp->p_x] = BOOT_PAIR; else Maze[pp->p_y][pp->p_x] = BOOT; if (pp->p_undershot) fixshots(pp->p_y, pp->p_x, Maze[pp->p_y][pp->p_x]); } # endif # ifdef VOLCANO volcano += pp->p_ammo - x; if (rand_num(100) < volcano / 50) { do { x = rand_num(WIDTH / 2) + WIDTH / 4; y = rand_num(HEIGHT / 2) + HEIGHT / 4; } while (Maze[y][x] != SPACE); (void) add_shot(LAVA, y, x, LEFTS, volcano, (PLAYER *) NULL, TRUE, SPACE); for (np = Player; np < End_player; np++) message(np, "Volcano eruption."); volcano = 0; } # endif # ifdef DRONE if (rand_num(100) < 2) { do { x = rand_num(WIDTH / 2) + WIDTH / 4; y = rand_num(HEIGHT / 2) + HEIGHT / 4; } while (Maze[y][x] != SPACE); add_shot(DSHOT, y, x, rand_dir(), shot_req[MINDSHOT + rand_num(MAXBOMB - MINDSHOT)], (PLAYER *) NULL, FALSE, SPACE); } # endif sendcom(pp, ENDWIN); (void) putc(' ', pp->p_output); (void) fclose(pp->p_output); End_player--; if (pp != End_player) { memcpy(pp, End_player, sizeof (PLAYER)); (void) sprintf(Buf, "%5.2f%c%-10.10s %c", pp->p_ident->i_score, stat_char(pp), pp->p_ident->i_name, pp->p_ident->i_team); i = STAT_PLAY_ROW + 1 + (pp - Player); for (np = Player; np < End_player; np++) { cgoto(np, i, STAT_NAME_COL); outstr(np, Buf, STAT_NAME_LEN); } # ifdef MONITOR for (np = Monitor; np < End_monitor; np++) { cgoto(np, i, STAT_NAME_COL); outstr(np, Buf, STAT_NAME_LEN); } # endif } /* Erase the last player */ i = STAT_PLAY_ROW + 1 + Nplayer; for (np = Player; np < End_player; np++) { cgoto(np, i, STAT_NAME_COL); ce(np); } # ifdef MONITOR for (np = Monitor; np < End_monitor; np++) { cgoto(np, i, STAT_NAME_COL); ce(np); } } else { sendcom(pp, ENDWIN); (void) putc(LAST_PLAYER, pp->p_output); (void) fclose(pp->p_output); End_monitor--; if (pp != End_monitor) { memcpy(pp, End_monitor, sizeof (PLAYER)); (void) sprintf(Buf, "%5.5s %-10.10s %c", " ", pp->p_ident->i_name, pp->p_ident->i_team); i = STAT_MON_ROW + 1 + (pp - Player); for (np = Player; np < End_player; np++) { cgoto(np, i, STAT_NAME_COL); outstr(np, Buf, STAT_NAME_LEN); } for (np = Monitor; np < End_monitor; np++) { cgoto(np, i, STAT_NAME_COL); outstr(np, Buf, STAT_NAME_LEN); } } /* Erase the last monitor */ i = STAT_MON_ROW + 1 + (End_monitor - Monitor); for (np = Player; np < End_player; np++) { cgoto(np, i, STAT_NAME_COL); ce(np); } for (np = Monitor; np < End_monitor; np++) { cgoto(np, i, STAT_NAME_COL); ce(np); } } # endif FD_CLR(savefd, &Fds_mask); if (Num_fds == savefd + 1) { Num_fds = Socket; # ifdef INTERNET if (Test_socket > Socket) Num_fds = Test_socket; # endif for (np = Player; np < End_player; np++) if (np->p_fd > Num_fds) Num_fds = np->p_fd; # ifdef MONITOR for (np = Monitor; np < End_monitor; np++) if (np->p_fd > Num_fds) Num_fds = np->p_fd; # endif Num_fds++; } } /* * rand_num: * Return a random number in a given range. */ int rand_num(range) int range; { return (range == 0 ? 0 : RN % range); } /* * havechar: * Check to see if we have any characters in the input queue; if * we do, read them, stash them away, and return TRUE; else return * FALSE. */ static int havechar(pp) PLAYER *pp; { if (pp->p_ncount < pp->p_nchar) return TRUE; if (!FD_ISSET(pp->p_fd, &Have_inp)) return FALSE; FD_CLR(pp->p_fd, &Have_inp); check_again: errno = 0; if ((pp->p_nchar = read(pp->p_fd, pp->p_cbuf, sizeof pp->p_cbuf)) <= 0) { if (errno == EINTR) goto check_again; pp->p_cbuf[0] = 'q'; } pp->p_ncount = 0; return TRUE; } /* * cleanup: * Exit with the given value, cleaning up any droppings lying around */ SIGNAL_TYPE cleanup(eval) int eval; { PLAYER *pp; for (pp = Player; pp < End_player; pp++) { cgoto(pp, HEIGHT, 0); sendcom(pp, ENDWIN); (void) putc(LAST_PLAYER, pp->p_output); (void) fclose(pp->p_output); } # ifdef MONITOR for (pp = Monitor; pp < End_monitor; pp++) { cgoto(pp, HEIGHT, 0); sendcom(pp, ENDWIN); (void) putc(LAST_PLAYER, pp->p_output); (void) fclose(pp->p_output); } # endif (void) close(Socket); # ifdef AF_UNIX_HACK (void) unlink(Sock_name); # endif exit(eval); } /* * send_stats: * Print stats to requestor */ static void send_stats() { IDENT *ip; FILE *fp; int s; SOCKET sockstruct; int socklen; /* * Get the output stream ready */ # ifdef INTERNET socklen = sizeof sockstruct; # else socklen = sizeof sockstruct - 1; # endif s = accept(Status, (struct sockaddr *) &sockstruct, &socklen); if (s < 0) { if (errno == EINTR) return; # ifdef LOG syslog(LOG_ERR, "accept: %m"); # else warn("accept"); # endif return; } fp = fdopen(s, "w"); if (fp == NULL) { # ifdef LOG syslog(LOG_ERR, "fdopen: %m"); # else warn("fdopen"); # endif (void) close(s); return; } /* * Send output to requestor */ fputs("Name\t\tScore\tDucked\tAbsorb\tFaced\tShot\tRobbed\tMissed\tSlimeK\n", fp); for (ip = Scores; ip != NULL; ip = ip->i_next) { fprintf(fp, "%s\t", ip->i_name); if (strlen(ip->i_name) < 8) putc('\t', fp); fprintf(fp, "%.2f\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", ip->i_score, ip->i_ducked, ip->i_absorbed, ip->i_faced, ip->i_shot, ip->i_robbed, ip->i_missed, ip->i_slime); } fputs("\n\nName\t\tEnemy\tFriend\tDeaths\tStill\tSaved\n", fp); for (ip = Scores; ip != NULL; ip = ip->i_next) { if (ip->i_team == ' ') { fprintf(fp, "%s\t", ip->i_name); if (strlen(ip->i_name) < 8) putc('\t', fp); } else { fprintf(fp, "%s[%c]\t", ip->i_name, ip->i_team); if (strlen(ip->i_name) + 3 < 8) putc('\t', fp); } fprintf(fp, "%d\t%d\t%d\t%d\t%d\n", ip->i_gkills, ip->i_bkills, ip->i_deaths, ip->i_stillb, ip->i_saved); } (void) fclose(fp); } /* * clear_scores: * Clear out the scores so the next session start clean */ static void clear_scores() { IDENT *ip, *nextip; for (ip = Scores; ip != NULL; ip = nextip) { nextip = ip->i_next; (void) free((char *) ip); } Scores = NULL; }