driver.c revision 1.4
190075Sobrien/* $OpenBSD: driver.c,v 1.4 1999/02/01 06:53:56 d Exp $ */ 290075Sobrien/* $NetBSD: driver.c,v 1.5 1997/10/20 00:37:16 lukem Exp $ */ 3117395Skan/* 490075Sobrien * Hunt 590075Sobrien * Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold 690075Sobrien * San Francisco, California 790075Sobrien */ 890075Sobrien 990075Sobrien#include <sys/ioctl.h> 1090075Sobrien#include <sys/stat.h> 1190075Sobrien#include <sys/time.h> 1290075Sobrien#include <err.h> 1390075Sobrien#include <errno.h> 1490075Sobrien#include <signal.h> 1590075Sobrien#include <stdlib.h> 1690075Sobrien#include <unistd.h> 1790075Sobrien#include <stdio.h> 1890075Sobrien#include <tcpd.h> 1990075Sobrien#include <syslog.h> 2090075Sobrien#include <sys/socket.h> 2190075Sobrien#include <netinet/in.h> 2290075Sobrien#include <arpa/inet.h> 2390075Sobrien#include <paths.h> 2490075Sobrien#include <fcntl.h> 2590075Sobrien#include "hunt.h" 26117395Skan#include "conf.h" 2790075Sobrien#include "server.h" 2890075Sobrien 2990075Sobrienint Seed = 0; 3090075Sobrien 3190075Sobrienchar *First_arg; /* pointer to argv[0] */ 3290075Sobrienchar *Last_arg; /* pointer to end of argv/environ */ 3390075Sobrienu_int16_t Server_port = HUNT_PORT; 3490075Sobrienint Server_socket; /* test socket to answer datagrams */ 3590075SobrienFLAG inetd_spawned; /* invoked via inetd */ 3690075SobrienFLAG should_announce = TRUE; /* true if listening on standard port */ 3790075Sobrienu_short sock_port; /* port # of tcp listen socket */ 3890075Sobrienu_short stat_port; /* port # of statistics tcp socket */ 3990075Sobrienin_addr_t Server_addr = INADDR_ANY; /* address to bind to */ 4090075Sobrien 4190075Sobrienstatic void clear_scores __P((void)); 4290075Sobrienstatic int havechar __P((PLAYER *)); 4390075Sobrienstatic void init __P((void)); 4490075Sobrien int main __P((int, char *[], char *[])); 4590075Sobrienstatic void makeboots __P((void)); 46117395Skanstatic void send_stats __P((void)); 4790075Sobrienstatic void zap __P((PLAYER *, FLAG)); 4890075Sobrienstatic void announce_game __P((void)); 49117395Skanstatic void siginfo __P((int)); 5090075Sobrienstatic void print_stats __P((FILE *)); 51117395Skanstatic void handle_wkport __P((int)); 5290075Sobrien 5390075Sobrien/* 5490075Sobrien * main: 5590075Sobrien * The main program. 5690075Sobrien */ 5790075Sobrienint 5890075Sobrienmain(ac, av, ep) 5990075Sobrien int ac; 6090075Sobrien char **av, **ep; 6190075Sobrien{ 6290075Sobrien PLAYER *pp; 6390075Sobrien int had_char; 6490075Sobrien static fd_set read_fds; 6590075Sobrien static FLAG first = TRUE; 6690075Sobrien static FLAG server = FALSE; 6790075Sobrien extern int optind; 6890075Sobrien extern char *optarg; 6990075Sobrien int c; 7090075Sobrien static struct timeval linger = { 0, 0 }; 7190075Sobrien static struct timeval timeout = { 0, 0 }, *to; 7290075Sobrien struct spawn *sp; 7390075Sobrien int ret; 7490075Sobrien int nready; 7590075Sobrien 7690075Sobrien First_arg = av[0]; 7790075Sobrien if (ep == NULL || *ep == NULL) 7890075Sobrien ep = av + ac; 7990075Sobrien while (*ep) 8090075Sobrien ep++; 8190075Sobrien Last_arg = ep[-1] + strlen(ep[-1]); 8290075Sobrien 8390075Sobrien config(); 8490075Sobrien 8590075Sobrien while ((c = getopt(ac, av, "sp:a:")) != -1) { 8690075Sobrien switch (c) { 8790075Sobrien case 's': 8896263Sobrien server = TRUE; 8996263Sobrien break; 9090075Sobrien case 'p': 9190075Sobrien should_announce = FALSE; 9290075Sobrien Server_port = atoi(optarg); 9390075Sobrien break; 9490075Sobrien case 'a': 9590075Sobrien Server_addr = inet_addr(optarg); 9690075Sobrien if (Server_addr == INADDR_NONE) 9790075Sobrien err(1, "bad interface address: %s", optarg); 98117395Skan break; 9990075Sobrien default: 10090075Sobrienerred: 10190075Sobrien fprintf(stderr, "Usage: %s [-s] [-p port] [-a addr]\n", 102117395Skan av[0]); 10390075Sobrien exit(2); 10490075Sobrien } 10590075Sobrien } 10690075Sobrien if (optind < ac) 10790075Sobrien goto erred; 10890075Sobrien 10990075Sobrien /* Open syslog: */ 11090075Sobrien openlog("huntd", LOG_PID | (conf_logerr && !server? LOG_PERROR : 0), 11190075Sobrien LOG_DAEMON); 11290075Sobrien 11390075Sobrien /* Initialise game parameters: */ 11490075Sobrien init(); 11590075Sobrien 11690075Sobrienagain: 11790075Sobrien do { 11890075Sobrien /* First, poll to see if we can get input */ 11990075Sobrien timerclear(&timeout); 12090075Sobrien do { 12190075Sobrien read_fds = Fds_mask; 12290075Sobrien errno = 0; 12396263Sobrien nready = select(Num_fds, &read_fds, NULL, NULL, 12496263Sobrien &timeout); 12590075Sobrien if (nready < 0 && errno != EINTR) { 12696263Sobrien log(LOG_ERR, "select"); 12790075Sobrien cleanup(1); 12896263Sobrien } 12996263Sobrien } while (nready < 0); 13096263Sobrien 13190075Sobrien if (nready == 0) { 13296263Sobrien /* 13396263Sobrien * Nothing was ready. We do some work now 13490075Sobrien * to see if the simulation has any pending work 13590075Sobrien * to do, and decide if we need to to block 13690075Sobrien * indefinitely or just timeout. 13790075Sobrien */ 13890075Sobrien if (conf_simstep && can_moveshots()) { 13990075Sobrien /* 14090075Sobrien * block for a short time before continuing 14190075Sobrien * with explosions, bullets and whatnot 14290075Sobrien */ 14390075Sobrien to = &timeout; 14490075Sobrien to->tv_sec = conf_simstep / 1000000; 14590075Sobrien to->tv_usec = conf_simstep % 1000000; 14690075Sobrien } else 14790075Sobrien /* 14890075Sobrien * since there's nothing going on, 149117395Skan * just block waiting for external activity 150117395Skan */ 151117395Skan to = NULL; 15290075Sobrien 15390075Sobrien do { 15490075Sobrien read_fds = Fds_mask; 15590075Sobrien errno = 0; 15690075Sobrien nready = select(Num_fds, &read_fds, NULL, NULL, 15790075Sobrien to); 15890075Sobrien if (nready < 0 && errno != EINTR) { 15990075Sobrien log(LOG_ERR, "select"); 16090075Sobrien cleanup(1); 16190075Sobrien } 16290075Sobrien } while (nready < 0); 16390075Sobrien } 16490075Sobrien 16590075Sobrien /* Remember which descriptors are active: */ 16690075Sobrien Have_inp = read_fds; 16790075Sobrien 16890075Sobrien /* Answer new player connections: */ 16990075Sobrien if (FD_ISSET(Socket, &read_fds)) 17090075Sobrien answer_first(); 17190075Sobrien 17290075Sobrien /* Continue answering new player connections: */ 17390075Sobrien for (sp = Spawn; sp; sp = sp->next) 17490075Sobrien if (FD_ISSET(sp->fd, &read_fds) && answer_next(sp)) { 17590075Sobrien if (first && should_announce) 17690075Sobrien announce_game(); 17790075Sobrien first = FALSE; 17890075Sobrien } 17990075Sobrien 18090075Sobrien /* Process input and move bullets until we've exhausted input */ 18190075Sobrien had_char = TRUE; 18290075Sobrien while (had_char) { 18390075Sobrien 18490075Sobrien moveshots(); 18590075Sobrien for (pp = Player; pp < End_player; ) 18690075Sobrien if (pp->p_death[0] != '\0') 18790075Sobrien zap(pp, TRUE); 18890075Sobrien else 18990075Sobrien pp++; 19090075Sobrien for (pp = Monitor; pp < End_monitor; ) 19190075Sobrien if (pp->p_death[0] != '\0') 19290075Sobrien zap(pp, FALSE); 19390075Sobrien else 19490075Sobrien pp++; 19590075Sobrien 19690075Sobrien had_char = FALSE; 19790075Sobrien for (pp = Player; pp < End_player; pp++) 19890075Sobrien if (havechar(pp)) { 19990075Sobrien execute(pp); 20090075Sobrien pp->p_nexec++; 20190075Sobrien had_char = TRUE; 20290075Sobrien } 20390075Sobrien for (pp = Monitor; pp < End_monitor; pp++) 20490075Sobrien if (havechar(pp)) { 20590075Sobrien mon_execute(pp); 20690075Sobrien pp->p_nexec++; 20790075Sobrien had_char = TRUE; 20890075Sobrien } 20990075Sobrien } 21090075Sobrien 21190075Sobrien /* Handle a datagram sent to the server socket: */ 21290075Sobrien if (FD_ISSET(Server_socket, &read_fds)) 21390075Sobrien handle_wkport(Server_socket); 21490075Sobrien 21590075Sobrien /* Answer statistics connections: */ 21690075Sobrien if (FD_ISSET(Status, &read_fds)) 21790075Sobrien send_stats(); 21890075Sobrien 21990075Sobrien /* Flush/synchronize all the displays: */ 22090075Sobrien for (pp = Player; pp < End_player; pp++) { 22190075Sobrien if (FD_ISSET(pp->p_fd, &read_fds)) 22290075Sobrien sendcom(pp, READY, pp->p_nexec); 22390075Sobrien pp->p_nexec = 0; 224117395Skan flush(pp); 225117395Skan } 226117395Skan for (pp = Monitor; pp < End_monitor; pp++) { 227117395Skan if (FD_ISSET(pp->p_fd, &read_fds)) 228117395Skan sendcom(pp, READY, pp->p_nexec); 229117395Skan pp->p_nexec = 0; 230117395Skan flush(pp); 231117395Skan } 232117395Skan } while (Nplayer > 0); 233117395Skan 234117395Skan /* No more players! */ 235117395Skan 236117395Skan /* Continuous game? */ 237117395Skan if (conf_linger < 0) 238117395Skan goto again; 239117395Skan 240117395Skan /* Wait a short while for one to come back: */ 241117395Skan read_fds = Fds_mask; 242117395Skan linger.tv_sec = conf_linger; 243117395Skan while ((ret = select(Num_fds, &read_fds, NULL, NULL, &linger)) < 0) { 244117395Skan if (errno != EINTR) { 245117395Skan log(LOG_WARNING, "select"); 246117395Skan break; 247117395Skan } 248117395Skan } 24990075Sobrien if (ret > 0) 25090075Sobrien /* Someone returned! Resume the game: */ 25190075Sobrien goto again; 25290075Sobrien /* else, it timed out, and the game is really over. */ 25390075Sobrien 25490075Sobrien /* If we are an inetd server, we should re-init the map and restart: */ 25590075Sobrien if (server) { 25690075Sobrien clear_scores(); 25790075Sobrien makemaze(); 25890075Sobrien clearwalls(); 25990075Sobrien makeboots(); 26090075Sobrien first = TRUE; 26190075Sobrien goto again; 26290075Sobrien } 26390075Sobrien 26490075Sobrien /* Get rid of any attached monitors: */ 26590075Sobrien for (pp = Monitor; pp < End_monitor; ) 26690075Sobrien zap(pp, FALSE); 26790075Sobrien 26890075Sobrien /* Fin: */ 26990075Sobrien cleanup(0); 27090075Sobrien exit(0); 27190075Sobrien} 27290075Sobrien 27390075Sobrien/* 27490075Sobrien * init: 27590075Sobrien * Initialize the global parameters. 27690075Sobrien */ 27790075Sobrienstatic void 27890075Sobrieninit() 27990075Sobrien{ 28090075Sobrien int i; 28190075Sobrien struct sockaddr_in test_port; 28290075Sobrien int msg; 28390075Sobrien int len; 28490075Sobrien struct sockaddr_in addr; 28590075Sobrien struct sigaction sact; 28690075Sobrien 28790075Sobrien (void) setsid(); 28890075Sobrien if (setpgid(getpid(), getpid()) == -1) 28990075Sobrien err(1, "setpgid"); 29090075Sobrien 29190075Sobrien sact.sa_flags = SA_RESTART; 29290075Sobrien sigemptyset(&sact.sa_mask); 29390075Sobrien 29490075Sobrien /* Ignore HUP, QUIT and PIPE: */ 29590075Sobrien sact.sa_handler = SIG_IGN; 29690075Sobrien if (sigaction(SIGHUP, &sact, NULL) == -1) 29790075Sobrien err(1, "sigaction SIGHUP"); 29890075Sobrien if (sigaction(SIGQUIT, &sact, NULL) == -1) 29990075Sobrien err(1, "sigaction SIGQUIT"); 30090075Sobrien if (sigaction(SIGPIPE, &sact, NULL) == -1) 30190075Sobrien err(1, "sigaction SIGPIPE"); 30290075Sobrien 30390075Sobrien /* Clean up gracefully on INT and TERM: */ 30490075Sobrien sact.sa_handler = cleanup; 30590075Sobrien if (sigaction(SIGINT, &sact, NULL) == -1) 30690075Sobrien err(1, "sigaction SIGINT"); 30790075Sobrien if (sigaction(SIGTERM, &sact, NULL) == -1) 30890075Sobrien err(1, "sigaction SIGTERM"); 30990075Sobrien 31090075Sobrien /* Handle INFO: */ 31190075Sobrien sact.sa_handler = siginfo; 31290075Sobrien if (sigaction(SIGINFO, &sact, NULL) == -1) 31390075Sobrien err(1, "sigaction SIGINFO"); 31490075Sobrien 31590075Sobrien if (chdir("/") == -1) 31690075Sobrien warn("chdir"); 317117395Skan (void) umask(0777); 31890075Sobrien 31990075Sobrien /* Initialize statistics socket: */ 32090075Sobrien addr.sin_family = AF_INET; 32190075Sobrien addr.sin_addr.s_addr = Server_addr; 32290075Sobrien addr.sin_port = 0; 32390075Sobrien 32490075Sobrien Status = socket(AF_INET, SOCK_STREAM, 0); 32590075Sobrien if (bind(Status, (struct sockaddr *) &addr, sizeof addr) < 0) { 32690075Sobrien log(LOG_ERR, "bind"); 32790075Sobrien cleanup(1); 32890075Sobrien } 32990075Sobrien if (listen(Status, 5) == -1) { 33090075Sobrien log(LOG_ERR, "listen"); 33190075Sobrien cleanup(1); 33290075Sobrien } 33390075Sobrien 33490075Sobrien len = sizeof (struct sockaddr_in); 33590075Sobrien if (getsockname(Status, (struct sockaddr *) &addr, &len) < 0) { 33690075Sobrien log(LOG_ERR, "getsockname"); 33790075Sobrien cleanup(1); 33890075Sobrien } 33990075Sobrien stat_port = ntohs(addr.sin_port); 34090075Sobrien 341117395Skan /* Initialize main socket: */ 342117395Skan addr.sin_family = AF_INET; 343117395Skan addr.sin_addr.s_addr = Server_addr; 344117395Skan addr.sin_port = 0; 345117395Skan 346117395Skan Socket = socket(AF_INET, SOCK_STREAM, 0); 347117395Skan msg = 1; 348117395Skan if (setsockopt(Socket, SOL_SOCKET, SO_USELOOPBACK, &msg, sizeof msg)<0) 349117395Skan log(LOG_ERR, "setsockopt loopback"); 35090075Sobrien if (bind(Socket, (struct sockaddr *) &addr, sizeof addr) < 0) { 351117395Skan log(LOG_ERR, "bind"); 352117395Skan cleanup(1); 353117395Skan } 35490075Sobrien if (listen(Socket, 5) == -1) { 355117395Skan log(LOG_ERR, "listen"); 356117395Skan cleanup(1); 357117395Skan } 35890075Sobrien 359117395Skan len = sizeof (struct sockaddr_in); 360117395Skan if (getsockname(Socket, (struct sockaddr *) &addr, &len) < 0) { 36190075Sobrien log(LOG_ERR, "getsockname"); 36290075Sobrien cleanup(1); 363117395Skan } 364117395Skan sock_port = ntohs(addr.sin_port); 365117395Skan 366117395Skan /* Initialize minimal select mask */ 36790075Sobrien FD_ZERO(&Fds_mask); 368117395Skan FD_SET(Socket, &Fds_mask); 36990075Sobrien FD_SET(Status, &Fds_mask); 370117395Skan Num_fds = ((Socket > Status) ? Socket : Status) + 1; 37190075Sobrien 37290075Sobrien /* Check if stdin is a socket: */ 37390075Sobrien len = sizeof (struct sockaddr_in); 37490075Sobrien if (getsockname(STDIN_FILENO, (struct sockaddr *) &test_port, &len) >= 0 37590075Sobrien && test_port.sin_family == AF_INET) { 37690075Sobrien /* We are probably running from inetd: */ 37790075Sobrien inetd_spawned = TRUE; 37890075Sobrien Server_socket = STDIN_FILENO; 37990075Sobrien if (test_port.sin_port != htons((u_short) Server_port)) { 38090075Sobrien should_announce = FALSE; 38190075Sobrien Server_port = ntohs(test_port.sin_port); 38290075Sobrien } 38390075Sobrien } else { 384117395Skan /* We need to listen on a socket: */ 385117395Skan test_port = addr; 386117395Skan test_port.sin_port = htons((u_short) Server_port); 387117395Skan 388117395Skan Server_socket = socket(AF_INET, SOCK_DGRAM, 0); 389117395Skan if (bind(Server_socket, (struct sockaddr *) &test_port, 390117395Skan sizeof test_port) < 0) { 391117395Skan log(LOG_ERR, "bind port %d", Server_port); 392117395Skan cleanup(1); 393117395Skan } 394117395Skan 395117395Skan /* Datagram sockets do not need a listen() call. */ 396117395Skan } 397117395Skan 398117395Skan /* We'll handle the broadcast listener in the main loop: */ 399117395Skan FD_SET(Server_socket, &Fds_mask); 400117395Skan if (Server_socket + 1 > Num_fds) 401117395Skan Num_fds = Server_socket + 1; 402117395Skan 403117395Skan /* Initialise the random seed: */ 404117395Skan Seed = getpid() + time((time_t *) NULL); 405117395Skan 406117395Skan /* Dig the maze: */ 407117395Skan makemaze(); 408117395Skan 409117395Skan /* Create some boots, if needed: */ 410117395Skan makeboots(); 411117395Skan 412117395Skan /* Construct a table of what objects a player can see over: */ 413117395Skan for (i = 0; i < NASCII; i++) 414117395Skan See_over[i] = TRUE; 415117395Skan See_over[DOOR] = FALSE; 416117395Skan See_over[WALL1] = FALSE; 417117395Skan See_over[WALL2] = FALSE; 418117395Skan See_over[WALL3] = FALSE; 419117395Skan See_over[WALL4] = FALSE; 420117395Skan See_over[WALL5] = FALSE; 421117395Skan 42290075Sobrien logx(LOG_INFO, "game started"); 42390075Sobrien} 42490075Sobrien 42590075Sobrien/* 42690075Sobrien * makeboots: 42790075Sobrien * Put the boots in the maze 42890075Sobrien */ 42990075Sobrienstatic void 43090075Sobrienmakeboots() 43190075Sobrien{ 43290075Sobrien int x, y; 43390075Sobrien PLAYER *pp; 43490075Sobrien 43590075Sobrien if (conf_boots) { 43690075Sobrien do { 43790075Sobrien x = rand_num(WIDTH - 1) + 1; 43890075Sobrien y = rand_num(HEIGHT - 1) + 1; 43990075Sobrien } while (Maze[y][x] != SPACE); 44090075Sobrien Maze[y][x] = BOOT_PAIR; 44190075Sobrien } 44290075Sobrien 44390075Sobrien for (pp = Boot; pp < &Boot[NBOOTS]; pp++) 44490075Sobrien pp->p_flying = -1; 44590075Sobrien} 44690075Sobrien 44790075Sobrien 44890075Sobrien/* 44990075Sobrien * checkdam: 45090075Sobrien * Apply damage to the victim from an attacker. 45190075Sobrien * If the victim dies as a result, give points to 'credit', 45290075Sobrien */ 45390075Sobrienvoid 45490075Sobriencheckdam(victim, attacker, credit, damage, shot_type) 45590075Sobrien PLAYER *victim, *attacker; 45690075Sobrien IDENT *credit; 45790075Sobrien int damage; 45890075Sobrien char shot_type; 45990075Sobrien{ 46090075Sobrien char *cp; 46190075Sobrien int y; 46290075Sobrien 46390075Sobrien /* Don't do anything if the victim is already in the throes of death */ 46490075Sobrien if (victim->p_death[0] != '\0') 46590075Sobrien return; 46690075Sobrien 46790075Sobrien /* Weaken slime attacks by 0.5 * number of boots the victim has on: */ 46890075Sobrien if (shot_type == SLIME) 46990075Sobrien switch (victim->p_nboots) { 47090075Sobrien default: 47190075Sobrien break; 47290075Sobrien case 1: 47390075Sobrien damage = (damage + 1) / 2; 47490075Sobrien break; 47590075Sobrien case 2: 47690075Sobrien if (attacker != NULL) 47790075Sobrien message(attacker, "He has boots on!"); 47890075Sobrien return; 47990075Sobrien } 48090075Sobrien 48190075Sobrien /* The victim sustains some damage: */ 48290075Sobrien victim->p_damage += damage; 48390075Sobrien 48490075Sobrien /* Check if the victim survives the hit: */ 48590075Sobrien if (victim->p_damage <= victim->p_damcap) { 48690075Sobrien /* They survive. */ 48790075Sobrien outyx(victim, STAT_DAM_ROW, STAT_VALUE_COL, "%2d", 48890075Sobrien victim->p_damage); 48990075Sobrien return; 49090075Sobrien } 49190075Sobrien 49290075Sobrien /* Describe how the victim died: */ 49390075Sobrien switch (shot_type) { 49490075Sobrien default: 49590075Sobrien cp = "Killed"; 49690075Sobrien break; 49790075Sobrien case FALL: 49890075Sobrien cp = "Killed on impact"; 49990075Sobrien break; 50090075Sobrien case KNIFE: 50190075Sobrien cp = "Stabbed to death"; 50296263Sobrien victim->p_ammo = 0; /* No exploding */ 50396263Sobrien break; 50490075Sobrien case SHOT: 50596263Sobrien cp = "Shot to death"; 50690075Sobrien break; 50790075Sobrien case GRENADE: 50890075Sobrien case SATCHEL: 50990075Sobrien case BOMB: 51090075Sobrien cp = "Bombed"; 51190075Sobrien break; 51290075Sobrien case MINE: 51390075Sobrien case GMINE: 51490075Sobrien cp = "Blown apart"; 51590075Sobrien break; 51690075Sobrien case SLIME: 51790075Sobrien cp = "Slimed"; 51890075Sobrien if (credit != NULL) 51990075Sobrien credit->i_slime++; 52090075Sobrien break; 52190075Sobrien case LAVA: 52290075Sobrien cp = "Baked"; 52390075Sobrien break; 52490075Sobrien case DSHOT: 52590075Sobrien cp = "Eliminated"; 52690075Sobrien break; 52790075Sobrien } 52890075Sobrien 52990075Sobrien if (credit == NULL) { 53090075Sobrien char *blame; 53190075Sobrien 53290075Sobrien /* 53390075Sobrien * Nobody is taking the credit for the kill. 53490075Sobrien * Attribute it to either a mine or 'act of God'. 53590075Sobrien */ 53690075Sobrien switch (shot_type) { 53790075Sobrien case MINE: 53890075Sobrien case GMINE: 53990075Sobrien blame = "a mine"; 54090075Sobrien break; 54190075Sobrien default: 54290075Sobrien blame = "act of God"; 54390075Sobrien break; 54490075Sobrien } 54590075Sobrien 54690075Sobrien /* Set the death message: */ 54790075Sobrien (void) snprintf(victim->p_death, sizeof victim->p_death, 54890075Sobrien "| %s by %s |", cp, blame); 54990075Sobrien 55090075Sobrien /* No further score crediting needed. */ 55190075Sobrien return; 55290075Sobrien } 55390075Sobrien 55490075Sobrien /* Set the death message: */ 55590075Sobrien (void) snprintf(victim->p_death, sizeof victim->p_death, 55690075Sobrien "| %s by %s |", cp, credit->i_name); 55790075Sobrien 55890075Sobrien if (victim == attacker) { 55990075Sobrien /* No use killing yourself. */ 56090075Sobrien credit->i_kills--; 56190075Sobrien credit->i_bkills++; 56290075Sobrien } 56396263Sobrien else if (victim->p_ident->i_team == ' ' 56496263Sobrien || victim->p_ident->i_team != credit->i_team) { 56590075Sobrien /* A cross-team kill: */ 56690075Sobrien credit->i_kills++; 56790075Sobrien credit->i_gkills++; 56890075Sobrien } 56990075Sobrien else { 57090075Sobrien /* They killed someone on the same team: */ 57190075Sobrien credit->i_kills--; 57290075Sobrien credit->i_bkills++; 57390075Sobrien } 57490075Sobrien 57590075Sobrien /* Compute the new credited score: */ 57690075Sobrien credit->i_score = credit->i_kills / (double) credit->i_entries; 57790075Sobrien 57890075Sobrien /* The victim accrues one death: */ 57990075Sobrien victim->p_ident->i_deaths++; 58090075Sobrien 58190075Sobrien /* Account for 'Stillborn' deaths */ 58290075Sobrien if (victim->p_nchar == 0) 58390075Sobrien victim->p_ident->i_stillb++; 58490075Sobrien 58590075Sobrien if (attacker) { 58690075Sobrien /* Give the attacker player a bit more strength */ 58790075Sobrien attacker->p_damcap += conf_killgain; 58890075Sobrien attacker->p_damage -= conf_killgain; 58990075Sobrien if (attacker->p_damage < 0) 59090075Sobrien attacker->p_damage = 0; 59190075Sobrien 59290075Sobrien /* Tell the attacker's his new strength: */ 59390075Sobrien outyx(attacker, STAT_DAM_ROW, STAT_VALUE_COL, "%2d/%2d", 59490075Sobrien attacker->p_damage, attacker->p_damcap); 59590075Sobrien 59690075Sobrien /* Tell the attacker's his new 'kill count': */ 59790075Sobrien outyx(attacker, STAT_KILL_ROW, STAT_VALUE_COL, "%3d", 59890075Sobrien (attacker->p_damcap - conf_maxdam) / 2); 59990075Sobrien 60090075Sobrien /* Update the attacker's score for everyone else */ 60190075Sobrien y = STAT_PLAY_ROW + 1 + (attacker - Player); 60290075Sobrien outyx(ALL_PLAYERS, y, STAT_NAME_COL, 60390075Sobrien "%5.2f", attacker->p_ident->i_score); 60490075Sobrien } 60590075Sobrien} 60690075Sobrien 60790075Sobrien/* 60896263Sobrien * zap: 60996263Sobrien * Kill off a player and take them out of the game. 61096263Sobrien * The 'was_player' flag indicates that the player was not 61196263Sobrien * a monitor and needs extra cleaning up. 61290075Sobrien */ 61390075Sobrienstatic void 61490075Sobrienzap(pp, was_player) 61590075Sobrien PLAYER *pp; 61690075Sobrien FLAG was_player; 61790075Sobrien{ 61890075Sobrien int len; 61990075Sobrien BULLET *bp; 62090075Sobrien PLAYER *np; 62190075Sobrien int x, y; 62290075Sobrien int savefd; 62390075Sobrien 62490075Sobrien if (was_player) { 62590075Sobrien /* If they died from a shot, clean up shrapnel */ 62690075Sobrien if (pp->p_undershot) 62790075Sobrien fixshots(pp->p_y, pp->p_x, pp->p_over); 62890075Sobrien /* Let the player see their last position: */ 62990075Sobrien drawplayer(pp, FALSE); 63090075Sobrien /* Remove from game: */ 63190075Sobrien Nplayer--; 63290075Sobrien } 63390075Sobrien 63490075Sobrien /* Display the cause of death in the centre of the screen: */ 63590075Sobrien len = strlen(pp->p_death); 63690075Sobrien x = (WIDTH - len) / 2; 63790075Sobrien outyx(pp, HEIGHT / 2, x, "%s", pp->p_death); 63890075Sobrien 63990075Sobrien /* Put some horizontal lines around and below the death message: */ 64090075Sobrien memset(pp->p_death + 1, '-', len - 2); 64190075Sobrien pp->p_death[0] = '+'; 64290075Sobrien pp->p_death[len - 1] = '+'; 64390075Sobrien outyx(pp, HEIGHT / 2 - 1, x, "%s", pp->p_death); 64490075Sobrien outyx(pp, HEIGHT / 2 + 1, x, "%s", pp->p_death); 64590075Sobrien 64690075Sobrien /* Move to bottom left */ 647117395Skan cgoto(pp, HEIGHT, 0); 648117395Skan 64990075Sobrien savefd = pp->p_fd; 650117395Skan 651117395Skan if (was_player) { 652117395Skan int expl_charge; 653117395Skan int expl_type; 654117395Skan int ammo_exploding; 65590075Sobrien 656117395Skan /* Check all the bullets: */ 657117395Skan for (bp = Bullets; bp != NULL; bp = bp->b_next) { 658117395Skan if (bp->b_owner == pp) 659117395Skan /* Zapped players can't own bullets: */ 660117395Skan bp->b_owner = NULL; 661117395Skan if (bp->b_x == pp->p_x && bp->b_y == pp->p_y) 662117395Skan /* Bullets over the player are now over air: */ 663117395Skan bp->b_over = SPACE; 664117395Skan } 665117395Skan 666117395Skan /* Explode a random fraction of the player's ammo: */ 667117395Skan ammo_exploding = rand_num(pp->p_ammo); 668117395Skan 669117395Skan /* Determine the type and amount of detonation: */ 670117395Skan expl_charge = rand_num(ammo_exploding + 1); 671117395Skan if (pp->p_ammo == 0) 672117395Skan /* Ignore the no-ammo case: */ 673117395Skan expl_charge = 0; 674117395Skan else if (ammo_exploding >= pp->p_ammo - 1) { 675117395Skan /* Maximal explosions always appear as slime: */ 67690075Sobrien expl_charge = pp->p_ammo; 67790075Sobrien expl_type = SLIME; 67890075Sobrien } else { 67990075Sobrien /* 68090075Sobrien * Figure out the best effective explosion 68190075Sobrien * type to use, given the amount of charge 68290075Sobrien */ 683117395Skan int btype, stype; 684117395Skan for (btype = MAXBOMB - 1; btype > 0; btype--) 685117395Skan if (expl_charge >= shot_req[btype]) 686117395Skan break; 687117395Skan for (stype = MAXSLIME - 1; stype > 0; stype--) 688117395Skan if (expl_charge >= slime_req[stype]) 689117395Skan break; 690117395Skan /* Pick the larger of the bomb or slime: */ 691117395Skan if (btype >= 0 && stype >= 0) { 692117395Skan if (shot_req[btype] > slime_req[btype]) 693117395Skan btype = -1; 694117395Skan } 69590075Sobrien if (btype >= 0) { 69690075Sobrien expl_type = shot_type[btype]; 69790075Sobrien expl_charge = shot_req[btype]; 69890075Sobrien } else 69990075Sobrien expl_type = SLIME; 70090075Sobrien } 70190075Sobrien 70290075Sobrien if (expl_charge > 0) { 70390075Sobrien char buf[BUFSIZ]; 70490075Sobrien 70590075Sobrien /* Detonate: */ 70690075Sobrien (void) add_shot(expl_type, pp->p_y, pp->p_x, 70790075Sobrien pp->p_face, expl_charge, (PLAYER *) NULL, 70890075Sobrien TRUE, SPACE); 70990075Sobrien 71090075Sobrien /* Explain what the explosion is about. */ 71190075Sobrien snprintf(buf, sizeof buf, "%s detonated.", 71290075Sobrien pp->p_ident->i_name); 71390075Sobrien message(ALL_PLAYERS, buf); 71490075Sobrien 71590075Sobrien while (pp->p_nboots-- > 0) { 71690075Sobrien /* Throw one of the boots away: */ 71790075Sobrien for (np = Boot; np < &Boot[NBOOTS]; np++) 71890075Sobrien if (np->p_flying < 0) 71990075Sobrien break; 72090075Sobrien#ifdef DIAGNOSTIC 72190075Sobrien if (np >= &Boot[NBOOTS]) 72290075Sobrien err(1, "Too many boots"); 72390075Sobrien#endif 72490075Sobrien /* Start the boots from where the player is */ 72590075Sobrien np->p_undershot = FALSE; 72690075Sobrien np->p_x = pp->p_x; 72790075Sobrien np->p_y = pp->p_y; 72890075Sobrien /* Throw for up to 20 steps */ 72990075Sobrien np->p_flying = rand_num(20); 73090075Sobrien np->p_flyx = 2 * rand_num(6) - 5; 73190075Sobrien np->p_flyy = 2 * rand_num(6) - 5; 73290075Sobrien np->p_over = SPACE; 73390075Sobrien np->p_face = BOOT; 73490075Sobrien showexpl(np->p_y, np->p_x, BOOT); 73590075Sobrien } 73690075Sobrien } 73790075Sobrien /* No explosion. Leave the player's boots behind. */ 73890075Sobrien else if (pp->p_nboots > 0) { 73990075Sobrien if (pp->p_nboots == 2) 74090075Sobrien Maze[pp->p_y][pp->p_x] = BOOT_PAIR; 74190075Sobrien else 74290075Sobrien Maze[pp->p_y][pp->p_x] = BOOT; 74390075Sobrien if (pp->p_undershot) 74490075Sobrien fixshots(pp->p_y, pp->p_x, 74590075Sobrien Maze[pp->p_y][pp->p_x]); 74690075Sobrien } 74790075Sobrien 74890075Sobrien /* Any unexploded ammo builds up in the volcano: */ 74990075Sobrien volcano += pp->p_ammo - expl_charge; 75090075Sobrien 75190075Sobrien /* Volcano eruption: */ 75290075Sobrien if (conf_volcano && rand_num(100) < volcano / 75390075Sobrien conf_volcano_max) { 75490075Sobrien /* Erupt near the middle of the map */ 75590075Sobrien do { 75690075Sobrien x = rand_num(WIDTH / 2) + WIDTH / 4; 75790075Sobrien y = rand_num(HEIGHT / 2) + HEIGHT / 4; 75890075Sobrien } while (Maze[y][x] != SPACE); 75990075Sobrien 760117395Skan /* Convert volcano charge into lava: */ 76190075Sobrien (void) add_shot(LAVA, y, x, LEFTS, volcano, 76290075Sobrien (PLAYER *) NULL, TRUE, SPACE); 76390075Sobrien volcano = 0; 76490075Sobrien 76590075Sobrien /* Tell eveyone what's happening */ 76690075Sobrien message(ALL_PLAYERS, "Volcano eruption."); 76790075Sobrien } 76890075Sobrien 76990075Sobrien /* Drone: */ 77090075Sobrien if (conf_drone && rand_num(100) < 2) { 77190075Sobrien /* Find a starting place near the middle of the map: */ 77290075Sobrien do { 77390075Sobrien x = rand_num(WIDTH / 2) + WIDTH / 4; 77490075Sobrien y = rand_num(HEIGHT / 2) + HEIGHT / 4; 77590075Sobrien } while (Maze[y][x] != SPACE); 77690075Sobrien 77790075Sobrien /* Start the drone going: */ 77890075Sobrien add_shot(DSHOT, y, x, rand_dir(), 77990075Sobrien shot_req[conf_mindshot + 78090075Sobrien rand_num(MAXBOMB - conf_mindshot)], 78190075Sobrien (PLAYER *) NULL, FALSE, SPACE); 78290075Sobrien } 78390075Sobrien 78490075Sobrien /* Tell the zapped player's client to shut down. */ 78590075Sobrien sendcom(pp, ENDWIN, ' '); 78690075Sobrien (void) fclose(pp->p_output); 78790075Sobrien 78890075Sobrien /* Close up the gap in the Player array: */ 78990075Sobrien End_player--; 79090075Sobrien if (pp != End_player) { 79190075Sobrien /* Move the last player into the gap: */ 79290075Sobrien memcpy(pp, End_player, sizeof *pp); 79390075Sobrien outyx(ALL_PLAYERS, 79490075Sobrien STAT_PLAY_ROW + 1 + (pp - Player), 79590075Sobrien STAT_NAME_COL, 79690075Sobrien "%5.2f%c%-10.10s %c", 79790075Sobrien pp->p_ident->i_score, stat_char(pp), 79890075Sobrien pp->p_ident->i_name, pp->p_ident->i_team); 79990075Sobrien } 80090075Sobrien 80190075Sobrien /* Erase the last player from the display: */ 80290075Sobrien cgoto(ALL_PLAYERS, STAT_PLAY_ROW + 1 + Nplayer, STAT_NAME_COL); 80390075Sobrien ce(ALL_PLAYERS); 80490075Sobrien } 80590075Sobrien else { 80690075Sobrien /* Zap a monitor */ 80790075Sobrien 808117395Skan /* Close the session: */ 80990075Sobrien sendcom(pp, ENDWIN, LAST_PLAYER); 810117395Skan (void) fclose(pp->p_output); 811117395Skan 812117395Skan /* shuffle the monitor table */ 813117395Skan End_monitor--; 814117395Skan if (pp != End_monitor) { 815117395Skan memcpy(pp, End_monitor, sizeof *pp); 816117395Skan outyx(ALL_PLAYERS, 817117395Skan STAT_MON_ROW + 1 + (pp - Player), STAT_NAME_COL, 818117395Skan "%5.5s %-10.10s %c", " ", 819117395Skan pp->p_ident->i_name, pp->p_ident->i_team); 820117395Skan } 821117395Skan 822117395Skan /* Erase the last monitor in the list */ 823117395Skan cgoto(ALL_PLAYERS, 82490075Sobrien STAT_MON_ROW + 1 + (End_monitor - Monitor), 82590075Sobrien STAT_NAME_COL); 82690075Sobrien ce(ALL_PLAYERS); 82790075Sobrien } 82890075Sobrien 82990075Sobrien /* Update the file descriptor sets used by select: */ 83090075Sobrien FD_CLR(savefd, &Fds_mask); 83190075Sobrien if (Num_fds == savefd + 1) { 83290075Sobrien Num_fds = Socket; 83390075Sobrien if (Server_socket > Socket) 83490075Sobrien Num_fds = Server_socket; 83590075Sobrien for (np = Player; np < End_player; np++) 83690075Sobrien if (np->p_fd > Num_fds) 83790075Sobrien Num_fds = np->p_fd; 83890075Sobrien for (np = Monitor; np < End_monitor; np++) 83990075Sobrien if (np->p_fd > Num_fds) 84090075Sobrien Num_fds = np->p_fd; 84190075Sobrien Num_fds++; 84290075Sobrien } 84390075Sobrien} 84490075Sobrien 84590075Sobrien/* 84690075Sobrien * rand_num: 84790075Sobrien * Return a random number in a given range. 84890075Sobrien */ 84990075Sobrienint 85090075Sobrienrand_num(range) 85190075Sobrien int range; 85290075Sobrien{ 85390075Sobrien if (range == 0) 85490075Sobrien return 0; 85590075Sobrien Seed = Seed * 11109 + 13849; 85690075Sobrien return (((Seed >> 16) & 0xffff) % range); 85790075Sobrien} 85890075Sobrien 85990075Sobrien/* 86090075Sobrien * havechar: 86190075Sobrien * Check to see if we have any characters in the input queue; if 86290075Sobrien * we do, read them, stash them away, and return TRUE; else return 86390075Sobrien * FALSE. 86490075Sobrien */ 86590075Sobrienstatic int 86690075Sobrienhavechar(pp) 86790075Sobrien PLAYER *pp; 86890075Sobrien{ 86990075Sobrien 87090075Sobrien /* Do we already have characters? */ 87190075Sobrien if (pp->p_ncount < pp->p_nchar) 87290075Sobrien return TRUE; 87390075Sobrien /* Is the player being quiet? */ 87490075Sobrien if (!FD_ISSET(pp->p_fd, &Have_inp)) 87590075Sobrien return FALSE; 87690075Sobrien /* Remove the player from the read set until we have drained them: */ 87790075Sobrien FD_CLR(pp->p_fd, &Have_inp); 87890075Sobrien 87990075Sobrien /* Suck their keypresses into a buffer: */ 88090075Sobriencheck_again: 88190075Sobrien if ((pp->p_nchar = read(pp->p_fd, pp->p_cbuf, sizeof pp->p_cbuf)) <= 0) 88290075Sobrien { 88390075Sobrien if (errno == EINTR) 88490075Sobrien goto check_again; 88590075Sobrien if (errno != EAGAIN) { 88690075Sobrien log(LOG_INFO, "read"); 88790075Sobrien /* Assume their connection was lost/closed: */ 88890075Sobrien pp->p_cbuf[0] = 'q'; 88990075Sobrien pp->p_nchar = 1; 89090075Sobrien } 89190075Sobrien } 89290075Sobrien /* Reset pointer into read buffer */ 89390075Sobrien pp->p_ncount = 0; 89490075Sobrien return TRUE; 89590075Sobrien} 89690075Sobrien 89790075Sobrien/* 89890075Sobrien * cleanup: 89990075Sobrien * Exit with the given value, cleaning up any droppings lying around 90090075Sobrien */ 90190075Sobrienvoid 90290075Sobriencleanup(eval) 90390075Sobrien int eval; 90490075Sobrien{ 90590075Sobrien PLAYER *pp; 90690075Sobrien 90790075Sobrien /* Place their cursor in a friendly position: */ 90890075Sobrien cgoto(ALL_PLAYERS, HEIGHT, 0); 90990075Sobrien 91090075Sobrien /* Send them all the ENDWIN command: */ 91190075Sobrien sendcom(ALL_PLAYERS, ENDWIN, LAST_PLAYER); 91290075Sobrien 91390075Sobrien /* And close their connections: */ 91490075Sobrien for (pp = Player; pp < End_player; pp++) 91590075Sobrien (void) fclose(pp->p_output); 91690075Sobrien for (pp = Monitor; pp < End_monitor; pp++) 91790075Sobrien (void) fclose(pp->p_output); 91890075Sobrien 91990075Sobrien /* Close the server socket: */ 92090075Sobrien (void) close(Socket); 92190075Sobrien 92290075Sobrien /* The end: */ 92390075Sobrien logx(LOG_INFO, "game over"); 92490075Sobrien exit(eval); 92590075Sobrien} 92690075Sobrien 92790075Sobrien/* 92890075Sobrien * send_stats: 92990075Sobrien * Accept a connection to the statistics port, and emit 93090075Sobrien * the stats. 93190075Sobrien */ 93290075Sobrienstatic void 93390075Sobriensend_stats() 93490075Sobrien{ 93590075Sobrien FILE *fp; 93690075Sobrien int s; 93790075Sobrien struct sockaddr_in sockstruct; 93890075Sobrien int socklen; 93990075Sobrien struct request_info ri; 94090075Sobrien 94190075Sobrien /* Accept a connection to the statistics socket: */ 94290075Sobrien socklen = sizeof sockstruct; 94390075Sobrien s = accept(Status, (struct sockaddr *) &sockstruct, &socklen); 94490075Sobrien if (s < 0) { 94590075Sobrien if (errno == EINTR) 94690075Sobrien return; 94790075Sobrien logx(LOG_ERR, "accept"); 94890075Sobrien return; 94990075Sobrien } 95090075Sobrien 95190075Sobrien /* Check for access permissions: */ 95290075Sobrien request_init(&ri, RQ_DAEMON, "huntd", RQ_FILE, s, 0); 95390075Sobrien if (hosts_access(&ri) == 0) { 95490075Sobrien close(s); 95590075Sobrien return; 95690075Sobrien } 95790075Sobrien 95890075Sobrien fp = fdopen(s, "w"); 959117395Skan if (fp == NULL) { 960117395Skan log(LOG_ERR, "fdopen"); 961117395Skan (void) close(s); 962117395Skan return; 963117395Skan } 96490075Sobrien 96590075Sobrien print_stats(fp); 966117395Skan 967117395Skan (void) fclose(fp); 96890075Sobrien} 96990075Sobrien 97090075Sobrien/* 97190075Sobrien * print_stats: 97290075Sobrien * emit the game statistics 97390075Sobrien */ 97490075Sobrienvoid 97590075Sobrienprint_stats(fp) 97690075Sobrien FILE *fp; 97790075Sobrien{ 97890075Sobrien IDENT *ip; 97990075Sobrien PLAYER *pp; 98090075Sobrien 98190075Sobrien /* Send the statistics as raw text down the socket: */ 98290075Sobrien fputs("Name\t\tScore\tDucked\tAbsorb\tFaced\tShot\tRobbed\tMissed\tSlimeK\n", fp); 98390075Sobrien for (ip = Scores; ip != NULL; ip = ip->i_next) { 98490075Sobrien fprintf(fp, "%s%c%c%c\t", ip->i_name, 98590075Sobrien ip->i_team == ' ' ? ' ' : '[', 98690075Sobrien ip->i_team, 98790075Sobrien ip->i_team == ' ' ? ' ' : ']' 98890075Sobrien ); 98990075Sobrien if (strlen(ip->i_name) + 3 < 8) 99090075Sobrien putc('\t', fp); 99190075Sobrien fprintf(fp, "%.2f\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", 99290075Sobrien ip->i_score, ip->i_ducked, ip->i_absorbed, 99390075Sobrien ip->i_faced, ip->i_shot, ip->i_robbed, 99490075Sobrien ip->i_missed, ip->i_slime); 99590075Sobrien } 99690075Sobrien fputs("\n\nName\t\tEnemy\tFriend\tDeaths\tStill\tSaved\tConnect\n", fp); 99790075Sobrien for (ip = Scores; ip != NULL; ip = ip->i_next) { 99890075Sobrien fprintf(fp, "%s%c%c%c\t", ip->i_name, 99990075Sobrien ip->i_team == ' ' ? ' ' : '[', 100090075Sobrien ip->i_team, 100190075Sobrien ip->i_team == ' ' ? ' ' : ']' 100290075Sobrien ); 100390075Sobrien if (strlen(ip->i_name) + 3 < 8) 100490075Sobrien putc('\t', fp); 100590075Sobrien fprintf(fp, "%d\t%d\t%d\t%d\t%d\t", 100690075Sobrien ip->i_gkills, ip->i_bkills, ip->i_deaths, 100790075Sobrien ip->i_stillb, ip->i_saved); 100890075Sobrien for (pp = Player; pp < End_player; pp++) 100990075Sobrien if (pp->p_ident == ip) 101090075Sobrien putc('p', fp); 101190075Sobrien for (pp = Monitor; pp < End_monitor; pp++) 101290075Sobrien if (pp->p_ident == ip) 101390075Sobrien putc('m', fp); 101490075Sobrien putc('\n', fp); 101590075Sobrien } 101690075Sobrien} 101790075Sobrien 101890075Sobrien 101990075Sobrien/* 102090075Sobrien * Send the game statistics to the controlling tty 102190075Sobrien */ 102290075Sobrienstatic void 102390075Sobriensiginfo(sig) 102490075Sobrien int sig; 102590075Sobrien{ 102690075Sobrien int tty; 102790075Sobrien FILE *fp; 102890075Sobrien 102990075Sobrien if ((tty = open(_PATH_TTY, O_WRONLY)) >= 0) { 103090075Sobrien fp = fdopen(tty, "w"); 103190075Sobrien print_stats(fp); 103290075Sobrien answer_info(fp); 103390075Sobrien fclose(fp); 103490075Sobrien } 103590075Sobrien} 103690075Sobrien 103790075Sobrien/* 103890075Sobrien * clear_scores: 103990075Sobrien * Clear the Scores list. 104090075Sobrien */ 104190075Sobrienstatic void 104290075Sobrienclear_scores() 104390075Sobrien{ 104490075Sobrien IDENT *ip, *nextip; 104590075Sobrien 104690075Sobrien /* Release the list of scores: */ 1047117395Skan for (ip = Scores; ip != NULL; ip = nextip) { 104890075Sobrien nextip = ip->i_next; 104990075Sobrien free((char *) ip); 105090075Sobrien } 105190075Sobrien Scores = NULL; 105290075Sobrien} 105390075Sobrien 105490075Sobrien/* 105590075Sobrien * announce_game: 105690075Sobrien * Publically announce the game 105790075Sobrien */ 105890075Sobrienstatic void 105996263Sobrienannounce_game() 106090075Sobrien{ 106190075Sobrien 106290075Sobrien /* Stub */ 106390075Sobrien} 106490075Sobrien 106590075Sobrien/* 106690075Sobrien * Handle a UDP packet sent to the well known port. 106790075Sobrien */ 106890075Sobrienstatic void 106990075Sobrienhandle_wkport(fd) 107090075Sobrien int fd; 107190075Sobrien{ 107290075Sobrien struct sockaddr fromaddr; 107390075Sobrien int fromlen; 107490075Sobrien u_int16_t query; 107590075Sobrien u_int16_t response; 107690075Sobrien 107790075Sobrien fromlen = sizeof fromaddr; 107890075Sobrien if (recvfrom(fd, &query, sizeof query, 0, &fromaddr, &fromlen) == -1) 107990075Sobrien { 108090075Sobrien log(LOG_WARNING, "recvfrom"); 108190075Sobrien return; 108290075Sobrien } 108390075Sobrien query = ntohs(query); 108490075Sobrien 108590075Sobrien switch (query) { 108690075Sobrien case C_MESSAGE: 108790075Sobrien if (Nplayer <= 0) 108890075Sobrien /* Don't bother replying if nobody to talk to: */ 108990075Sobrien return; 109090075Sobrien /* Return the number of people playing: */ 109190075Sobrien response = Nplayer; 109290075Sobrien break; 109390075Sobrien case C_SCORES: 109490075Sobrien /* Someone wants the statistics port: */ 109590075Sobrien response = stat_port; 1096117395Skan break; 109790075Sobrien case C_PLAYER: 109890075Sobrien case C_MONITOR: 109990075Sobrien /* Someone wants to play or watch: */ 110090075Sobrien if (query == C_MONITOR && Nplayer <= 0) 110190075Sobrien /* Don't bother replying if there's nothing to watch: */ 110290075Sobrien return; 110390075Sobrien /* Otherwise, tell them how to get to the game: */ 110490075Sobrien response = sock_port; 110590075Sobrien break; 110690075Sobrien default: 1107117395Skan log(LOG_INFO, "unknown udp query %d", query); 1108117395Skan return; 1109117395Skan } 1110117395Skan 1111117395Skan response = ntohs(response); 111290075Sobrien if (sendto(fd, &response, sizeof response, 0, 111390075Sobrien &fromaddr, sizeof fromaddr) == -1) 111490075Sobrien log(LOG_WARNING, "sendto"); 111590075Sobrien} 111690075Sobrien