* Copyright (c) 1985 Conrad C. Huang, Gregory S. Couch, Kenneth C.R.C. Arnold
* San Francisco, California
# define RN (((Seed = Seed * 11109 + 13849) >> 16) & 0xffff)
# define RN ((Seed = Seed * 11109 + 13849) & 0x7fff)
char *First_arg
; /* pointer to argv[0] */
char *Last_arg
; /* pointer to end of argv/environ */
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)
# define DAEMON_SIZE (sizeof Daemon - 1)
extern SIGNAL_TYPE
cleanup();
static FLAG first
= TRUE
;
static FLAG server
= FALSE
;
static struct timeval linger
= { 90, 0 };
if (ep
== NULL
|| *ep
== NULL
)
Last_arg
= ep
[-1] + strlen(ep
[-1]);
while ((c
= getopt(ac
, av
, "sp:")) != EOF
) {
Test_port
= atoi(optarg
);
fprintf(stderr
, "Usage: %s [-s] [-p port]\n", av
[0]);
Sock_mask
= (1 << Socket
);
Stat_mask
= (1 << Status
);
test_mask
= (1 << Test_socket
);
while (select(Num_fds
, &read_fds
, (int *) NULL
,
(int *) NULL
, (struct timeval
*) NULL
) < 0)
syslog(LOG_WARNING
, "select: %m");
if (read_fds
& test_mask
) {
port_num
= htons(sock_port
);
(void) recvfrom(Test_socket
, (char *) &msg
, sizeof msg
,
0, (struct sockaddr
*) &test
, &namelen
);
reply
= htons((u_short
) Nplayer
);
(void) sendto(Test_socket
, (char *) &reply
,
(struct sockaddr
*) &test
, DAEMON_SIZE
);
reply
= htons(stat_port
);
(void) sendto(Test_socket
, (char *) &reply
,
(struct sockaddr
*) &test
, DAEMON_SIZE
);
if (msg
== C_MONITOR
&& Nplayer
<= 0)
reply
= htons(sock_port
);
(void) sendto(Test_socket
, (char *) &reply
,
(struct sockaddr
*) &test
, DAEMON_SIZE
);
for (pp
= Player
; pp
< End_player
; pp
++)
for (pp
= Monitor
; pp
< End_monitor
; pp
++)
for (pp
= Player
; pp
< End_player
; )
if (pp
->p_death
[0] != '\0')
for (pp
= Monitor
; pp
< End_monitor
; )
if (pp
->p_death
[0] != '\0')
if (read_fds
& Sock_mask
)
if (first
&& standard_port
)
if (read_fds
& Stat_mask
)
for (pp
= Player
; pp
< End_player
; pp
++) {
if (read_fds
& pp
->p_mask
)
sendcom(pp
, READY
, pp
->p_nexec
);
(void) fflush(pp
->p_output
);
for (pp
= Monitor
; pp
< End_monitor
; pp
++) {
if (read_fds
& pp
->p_mask
)
sendcom(pp
, READY
, pp
->p_nexec
);
(void) fflush(pp
->p_output
);
if (select(Num_fds
, &read_fds
, (int *) NULL
, (int *) NULL
,
for (pp
= Monitor
; pp
< End_monitor
; )
* Initialize the global parameters.
(void) ioctl(fileno(stdout
), TIOCNOTTY
, NULL
);
(void) setpgrp(getpid(), getpid());
(void) signal(SIGHUP
, SIG_IGN
);
(void) signal(SIGINT
, SIG_IGN
);
(void) signal(SIGQUIT
, SIG_IGN
);
(void) signal(SIGTERM
, cleanup
);
(void) chdir("/usr/tmp"); /* just in case it core dumps */
(void) umask(0); /* No privacy at all! */
(void) signal(SIGPIPE
, SIG_IGN
);
openlog("HUNT", LOG_PID
, LOG_DAEMON
);
openlog("HUNT", LOG_PID
);
* Initialize statistics socket
Daemon
.sin_family
= SOCK_FAMILY
;
Daemon
.sin_addr
.s_addr
= INADDR_ANY
;
Daemon
.sun_family
= SOCK_FAMILY
;
(void) strcpy(Daemon
.sun_path
, Stat_name
);
Status
= socket(SOCK_FAMILY
, SOCK_STREAM
, 0);
if (bind(Status
, (struct sockaddr
*) &Daemon
, DAEMON_SIZE
) < 0) {
syslog(LOG_ERR
, "bind: %m");
(void) listen(Status
, 5);
if (getsockname(Status
, (struct sockaddr
*) &Daemon
, &len
) < 0) {
syslog(LOG_ERR
, "getsockname: %m");
stat_port
= ntohs(Daemon
.sin_port
);
Daemon
.sin_family
= SOCK_FAMILY
;
Daemon
.sin_addr
.s_addr
= INADDR_ANY
;
Daemon
.sun_family
= SOCK_FAMILY
;
(void) strcpy(Daemon
.sun_path
, Sock_name
);
Socket
= socket(SOCK_FAMILY
, SOCK_STREAM
, 0);
if (setsockopt(Socket
, SOL_SOCKET
, SO_USELOOPBACK
, &msg
, sizeof msg
)<0)
syslog(LOG_WARNING
, "setsockopt loopback %m");
perror("setsockopt loopback");
if (bind(Socket
, (struct sockaddr
*) &Daemon
, DAEMON_SIZE
) < 0) {
syslog(LOG_ERR
, "bind: %m");
(void) listen(Socket
, 5);
if (getsockname(Socket
, (struct sockaddr
*) &Daemon
, &len
) < 0) {
syslog(LOG_ERR
, "getsockname: %m");
sock_port
= ntohs(Daemon
.sin_port
);
* Initialize minimal select mask
Fds_mask
= (1 << Socket
) | (1 << Status
);
Num_fds
= ((Socket
> Status
) ? Socket
: Status
) + 1;
if (getsockname(0, (struct sockaddr
*) &test_port
, &len
) >= 0
&& test_port
.sin_family
== AF_INET
) {
if (test_port
.sin_port
!= htons((u_short
) Test_port
)) {
Test_port
= ntohs(test_port
.sin_port
);
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
,
syslog(LOG_ERR
, "bind: %m");
(void) listen(Test_socket
, 5);
Fds_mask
|= (1 << Test_socket
);
if (Test_socket
+ 1 > Num_fds
)
Num_fds
= Test_socket
+ 1;
Seed
= getpid() + time((time_t *) NULL
);
for (i
= 0; i
< NASCII
; i
++)
* Put the boots in the maze
x
= rand_num(WIDTH
- 1) + 1;
y
= rand_num(HEIGHT
- 1) + 1;
} while (Maze
[y
][x
] != SPACE
);
for (pp
= Boot
; pp
< &Boot
[NBOOTS
]; pp
++)
* Check the damage to the given player, and see if s/he is killed
checkdam(ouch
, gotcha
, credit
, amt
, shot_type
)
register PLAYER
*ouch
, *gotcha
;
if (ouch
->p_death
[0] != '\0')
switch (ouch
->p_nboots
) {
message(gotcha
, "He has boots on!");
if (ouch
->p_damage
<= ouch
->p_damcap
) {
(void) sprintf(Buf
, "%2d", ouch
->p_damage
);
cgoto(ouch
, STAT_DAM_ROW
, STAT_VALUE_COL
);
ouch
->p_ammo
= 0; /* No exploding */
(void) sprintf(ouch
->p_death
, "| %s by %s |", cp
,
(shot_type
== MINE
|| shot_type
== GMINE
) ?
"a mine" : "act of God");
(void) sprintf(ouch
->p_death
, "| %s by %s |", cp
, credit
->i_name
);
if (ouch
== gotcha
) { /* No use killing yourself */
else if (ouch
->p_ident
->i_team
== ' '
|| ouch
->p_ident
->i_team
!= credit
->i_team
) {
credit
->i_score
= credit
->i_kills
/ (double) credit
->i_entries
;
ouch
->p_ident
->i_deaths
++;
ouch
->p_ident
->i_stillb
++;
gotcha
->p_damcap
+= STABDAM
;
gotcha
->p_damage
-= STABDAM
;
if (gotcha
->p_damage
< 0)
(void) sprintf(Buf
, "%2d/%2d", gotcha
->p_damage
, gotcha
->p_damcap
);
cgoto(gotcha
, STAT_DAM_ROW
, STAT_VALUE_COL
);
(void) sprintf(Buf
, "%3d", (gotcha
->p_damcap
- MAXDAM
) / 2);
cgoto(gotcha
, STAT_KILL_ROW
, STAT_VALUE_COL
);
(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
),
for (ouch
= Monitor
; ouch
< End_monitor
; ouch
++) {
cgoto(ouch
, STAT_PLAY_ROW
+ 1 + (gotcha
- Player
),
* Kill off a player and take him out of the game.
fixshots(pp
->p_y
, pp
->p_x
, pp
->p_over
);
len
= strlen(pp
->p_death
); /* Display the cause of death */
cgoto(pp
, HEIGHT
/ 2, x
);
outstr(pp
, pp
->p_death
, len
);
for (i
= 1; i
< len
; i
++)
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
);
for (bp
= Bullets
; bp
!= NULL
; bp
= bp
->b_next
) {
if (bp
->b_x
== pp
->p_x
&& bp
->b_y
== pp
->p_y
)
i
= rand_num(pp
->p_ammo
);
x
= rand_num(pp
->p_ammo
);
else if (i
== pp
->p_ammo
- 1) {
for (x
= MAXBOMB
- 1; x
> 0; x
--)
for (y
= MAXSLIME
- 1; y
> 0; y
--)
if (y
>= 0 && slime_req
[y
] > shot_req
[x
]) {
(void) add_shot(len
, pp
->p_y
, pp
->p_x
, pp
->p_face
, x
,
(PLAYER
*) NULL
, TRUE
, SPACE
);
(void) sprintf(Buf
, "%s detonated.",
for (np
= Player
; np
< End_player
; np
++)
for (np
= Monitor
; np
< End_monitor
; np
++)
while (pp
->p_nboots
-- > 0) {
for (np
= Boot
; np
< &Boot
[NBOOTS
]; np
++)
abort(1, "Too many boots");
np
->p_flying
= rand_num(20);
np
->p_flyx
= 2 * rand_num(6) - 5;
np
->p_flyy
= 2 * rand_num(6) - 5;
showexpl(np
->p_y
, np
->p_x
, BOOT
);
else if (pp
->p_nboots
> 0) {
Maze
[pp
->p_y
][pp
->p_x
] = BOOT_PAIR
;
Maze
[pp
->p_y
][pp
->p_x
] = BOOT
;
fixshots(pp
->p_y
, pp
->p_x
,
volcano
+= pp
->p_ammo
- x
;
if (rand_num(100) < volcano
/ 50) {
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.");
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(),
rand_num(MAXBOMB
- MINDSHOT
)],
(PLAYER
*) NULL
, FALSE
, SPACE
);
(void) putc(' ', pp
->p_output
);
(void) fclose(pp
->p_output
);
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
);
for (np
= Monitor
; np
< End_monitor
; np
++) {
cgoto(np
, i
, STAT_NAME_COL
);
outstr(np
, Buf
, STAT_NAME_LEN
);
/* Erase the last player */
i
= STAT_PLAY_ROW
+ 1 + Nplayer
;
for (np
= Player
; np
< End_player
; np
++) {
cgoto(np
, i
, STAT_NAME_COL
);
for (np
= Monitor
; np
< End_monitor
; np
++) {
cgoto(np
, i
, STAT_NAME_COL
);
(void) putc(LAST_PLAYER
, pp
->p_output
);
(void) fclose(pp
->p_output
);
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
);
for (np
= Monitor
; np
< End_monitor
; np
++) {
cgoto(np
, i
, STAT_NAME_COL
);
if (Num_fds
== savefd
+ 1) {
if (Test_socket
> Socket
)
for (np
= Player
; np
< End_player
; np
++)
for (np
= Monitor
; np
< End_monitor
; np
++)
* Return a random number in a given range.
return (range
== 0 ? 0 : RN
% range
);
* 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
if (pp
->p_ncount
< pp
->p_nchar
)
if (!(Have_inp
& pp
->p_mask
))
if ((pp
->p_nchar
= read(pp
->p_fd
, pp
->p_cbuf
, sizeof pp
->p_cbuf
)) <= 0)
* Exit with the given value, cleaning up any droppings lying around
for (pp
= Player
; pp
< End_player
; pp
++) {
(void) putc(LAST_PLAYER
, pp
->p_output
);
(void) fclose(pp
->p_output
);
for (pp
= Monitor
; pp
< End_monitor
; pp
++) {
(void) putc(LAST_PLAYER
, pp
->p_output
);
(void) fclose(pp
->p_output
);
(void) unlink(Sock_name
);
* Print stats to requestor
* Get the output stream ready
socklen
= sizeof sockstruct
;
socklen
= sizeof sockstruct
- 1;
s
= accept(Status
, (struct sockaddr
*) &sockstruct
, &socklen
);
syslog(LOG_ERR
, "accept: %m");
syslog(LOG_ERR
, "fdopen: %m");
* 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)
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
) {
fprintf(fp
, "%s\t", ip
->i_name
);
if (strlen(ip
->i_name
) < 8)
fprintf(fp
, "%s[%c]\t", ip
->i_name
, ip
->i_team
);
if (strlen(ip
->i_name
) + 3 < 8)
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
);
* Clear out the scores so the next session start clean
register IDENT
*ip
, *nextip
;
for (ip
= Scores
; ip
!= NULL
; ip
= nextip
) {
(void) free((char *) ip
);