2 * Copyright (c) 1983-2003, Regents of the University of California.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the University of California, San Francisco nor
15 * the names of its contributors may be used to endorse or promote
16 * products derived from this software without specific prior written
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 * $OpenBSD: answer.c,v 1.11 2007/11/06 10:22:29 chl Exp $
32 * $NetBSD: answer.c,v 1.3 1997/10/10 16:32:50 lukem Exp $
33 * $DragonFly: src/games/hunt/huntd/answer.c,v 1.2 2008/09/04 16:12:51 swildner Exp $
36 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
54 /* Exported symbols for hosts_access(): */
55 int allow_severity
= LOG_INFO
;
56 int deny_severity
= LOG_WARNING
;
59 /* List of spawning connections: */
60 struct spawn
*Spawn
= NULL
;
62 static void stplayer(PLAYER
*, int);
63 static void stmonitor(PLAYER
*);
64 static IDENT
* get_ident(struct sockaddr
*, int, uid_t
, char *, char);
69 struct sockaddr sockstruct
;
73 struct request_info ri
;
76 /* Answer the call to hunt: */
77 socklen
= sizeof sockstruct
;
78 newsock
= accept(Socket
, (struct sockaddr
*) &sockstruct
, &socklen
);
80 logit(LOG_ERR
, "accept");
84 /* Check for access permissions: */
85 request_init(&ri
, RQ_DAEMON
, "huntd", RQ_FILE
, newsock
, 0);
87 if (hosts_access(&ri
) == 0) {
88 logx(LOG_INFO
, "rejected connection from %s", eval_client(&ri
));
93 /* Remember this spawning connection: */
94 sp
= (struct spawn
*)malloc(sizeof *sp
);
96 logit(LOG_ERR
, "malloc");
100 memset(sp
, '\0', sizeof *sp
);
102 /* Keep the calling machine's source addr for ident purposes: */
103 memcpy(&sp
->source
, &sockstruct
, sizeof sp
->source
);
104 sp
->sourcelen
= socklen
;
106 /* Warn if we lose connection info: */
107 if (socklen
> sizeof Spawn
->source
)
109 "struct sockaddr is not big enough! (%d > %zu)",
110 socklen
, sizeof Spawn
->source
);
113 * Turn off blocking I/O, so a slow or dead terminal won't stop
114 * the game. All subsequent reads check how many bytes they read.
116 flags
= fcntl(newsock
, F_GETFL
, 0);
118 (void) fcntl(newsock
, F_SETFL
, flags
);
120 /* Start listening to the spawning connection */
122 FD_SET(sp
->fd
, &Fds_mask
);
123 if (sp
->fd
>= Num_fds
)
124 Num_fds
= sp
->fd
+ 1;
129 /* Add to the spawning list */
130 if ((sp
->next
= Spawn
) != NULL
)
131 Spawn
->prevnext
= &sp
->next
;
132 sp
->prevnext
= &Spawn
;
137 answer_next(struct spawn
*sp
)
144 char teamstr
[] = "[x]";
146 if (sp
->reading_msg
) {
147 /* Receive a message from a player */
148 len
= read(sp
->fd
, sp
->msg
+ sp
->msglen
,
149 sizeof sp
->msg
- sp
->msglen
);
153 if (len
&& sp
->msglen
< (int)(sizeof sp
->msg
))
156 teamstr
[1] = sp
->team
;
157 outyx(ALL_PLAYERS
, HEIGHT
, 0, "%s%s: %.*s",
159 sp
->team
== ' ' ? "": teamstr
,
163 sendcom(ALL_PLAYERS
, REFRESH
);
164 sendcom(ALL_PLAYERS
, READY
, 0);
169 /* Fill the buffer */
170 len
= read(sp
->fd
, sp
->inbuf
+ sp
->inlen
,
171 sizeof sp
->inbuf
- sp
->inlen
);
175 if (sp
->inlen
< (int)(sizeof sp
->inbuf
))
178 /* Extract values from the buffer */
180 memcpy(&sp
->uid
, cp1
, sizeof (u_int32_t
));
181 cp1
+= sizeof(u_int32_t
);
182 memcpy(sp
->name
, cp1
, NAMELEN
);
184 memcpy(&sp
->team
, cp1
, sizeof (u_int8_t
));
185 cp1
+= sizeof(u_int8_t
);
186 memcpy(&sp
->enter_status
, cp1
, sizeof (u_int32_t
));
187 cp1
+= sizeof(u_int32_t
);
188 memcpy(sp
->ttyname
, cp1
, NAMELEN
);
190 memcpy(&sp
->mode
, cp1
, sizeof (u_int32_t
));
191 cp1
+= sizeof(u_int32_t
);
193 /* Convert data from network byte order: */
194 sp
->uid
= ntohl(sp
->uid
);
195 sp
->enter_status
= ntohl(sp
->enter_status
);
196 sp
->mode
= ntohl(sp
->mode
);
199 * Make sure the name contains only printable characters
200 * since we use control characters for cursor control
201 * between driver and player processes
203 sp
->name
[NAMELEN
] = '\0';
204 for (cp1
= cp2
= sp
->name
; *cp1
!= '\0'; cp1
++)
205 if (isprint(*cp1
) || *cp1
== ' ')
209 /* Make sure team name is valid */
210 if (sp
->team
< '1' || sp
->team
> '9')
213 /* Tell the other end this server's hunt driver version: */
214 version
= htonl((u_int32_t
) HUNT_VERSION
);
215 (void) write(sp
->fd
, &version
, sizeof version
);
217 if (sp
->mode
== C_MESSAGE
) {
218 /* The clients only wants to send a message: */
224 /* Use a stdio file descriptor from now on: */
225 conn
= fdopen(sp
->fd
, "w");
227 /* The player is a monitor: */
228 if (sp
->mode
== C_MONITOR
) {
229 if (conf_monitor
&& End_monitor
< &Monitor
[MAXMON
]) {
234 /* Too many monitors */
235 fprintf(conn
, "Too many monitors\n");
237 logx(LOG_NOTICE
, "too many monitors");
241 /* The player is a normal hunter: */
243 if (End_player
< &Player
[MAXPL
])
246 fprintf(conn
, "Too many players\n");
248 /* Too many players */
249 logx(LOG_NOTICE
, "too many players");
254 /* Find the player's running scorecard */
255 pp
->p_ident
= get_ident(&sp
->source
, sp
->sourcelen
, sp
->uid
,
258 pp
->p_death
[0] = '\0';
261 /* No idea where the player starts: */
265 /* Mode-specific initialisation: */
266 if (sp
->mode
== C_MONITOR
)
269 stplayer(pp
, sp
->enter_status
);
271 /* And, they're off! Caller should remove and free sp. */
276 logit(LOG_WARNING
, "read");
278 logx(LOG_WARNING
, "lost connection to new client");
281 /* Destroy the spawn */
282 *sp
->prevnext
= sp
->next
;
283 if (sp
->next
) sp
->next
->prevnext
= sp
->prevnext
;
284 FD_CLR(sp
->fd
, &Fds_mask
);
290 /* Start a monitor: */
292 stmonitor(PLAYER
*pp
)
295 /* Monitors get to see the entire maze: */
296 memcpy(pp
->p_maze
, Maze
, sizeof pp
->p_maze
);
299 /* Put the monitor's name near the bottom right on all screens: */
301 STAT_MON_ROW
+ 1 + (pp
- Monitor
), STAT_NAME_COL
,
302 "%5.5s%c%-10.10s %c", " ",
303 stat_char(pp
), pp
->p_ident
->i_name
, pp
->p_ident
->i_team
);
305 /* Ready the monitor: */
306 sendcom(pp
, REFRESH
);
307 sendcom(pp
, READY
, 0);
311 /* Start a player: */
313 stplayer(PLAYER
*newpp
, int enter_status
)
321 for (y
= 0; y
< UBOUND
; y
++)
322 for (x
= 0; x
< WIDTH
; x
++)
323 newpp
->p_maze
[y
][x
] = Maze
[y
][x
];
324 for ( ; y
< DBOUND
; y
++) {
325 for (x
= 0; x
< LBOUND
; x
++)
326 newpp
->p_maze
[y
][x
] = Maze
[y
][x
];
327 for ( ; x
< RBOUND
; x
++)
328 newpp
->p_maze
[y
][x
] = SPACE
;
329 for ( ; x
< WIDTH
; x
++)
330 newpp
->p_maze
[y
][x
] = Maze
[y
][x
];
332 for ( ; y
< HEIGHT
; y
++)
333 for (x
= 0; x
< WIDTH
; x
++)
334 newpp
->p_maze
[y
][x
] = Maze
[y
][x
];
336 /* Drop the new player somewhere in the maze: */
338 x
= rand_num(WIDTH
- 1) + 1;
339 y
= rand_num(HEIGHT
- 1) + 1;
340 } while (Maze
[y
][x
] != SPACE
);
341 newpp
->p_over
= SPACE
;
344 newpp
->p_undershot
= FALSE
;
346 /* Send them flying if needed */
347 if (enter_status
== Q_FLY
&& conf_fly
) {
348 newpp
->p_flying
= rand_num(conf_flytime
);
349 newpp
->p_flyx
= 2 * rand_num(conf_flystep
+ 1) - conf_flystep
;
350 newpp
->p_flyy
= 2 * rand_num(conf_flystep
+ 1) - conf_flystep
;
351 newpp
->p_face
= FLYER
;
353 newpp
->p_flying
= -1;
354 newpp
->p_face
= rand_dir();
357 /* Initialize the new player's attributes: */
359 newpp
->p_damcap
= conf_maxdam
;
363 newpp
->p_ammo
= conf_ishots
;
366 /* Decide on what cloak/scan status to enter with */
367 if (enter_status
== Q_SCAN
&& conf_scan
) {
368 newpp
->p_scan
= conf_scanlen
* Nplayer
;
370 } else if (conf_cloak
) {
372 newpp
->p_cloak
= conf_cloaklen
;
380 * For each new player, place a large mine and
381 * a small mine somewhere in the maze:
384 x
= rand_num(WIDTH
- 1) + 1;
385 y
= rand_num(HEIGHT
- 1) + 1;
386 } while (Maze
[y
][x
] != SPACE
);
388 for (pp
= Monitor
; pp
< End_monitor
; pp
++)
392 x
= rand_num(WIDTH
- 1) + 1;
393 y
= rand_num(HEIGHT
- 1) + 1;
394 } while (Maze
[y
][x
] != SPACE
);
396 for (pp
= Monitor
; pp
< End_monitor
; pp
++)
399 /* Create a score line for the new player: */
400 (void) snprintf(Buf
, sizeof Buf
, "%5.2f%c%-10.10s %c",
401 newpp
->p_ident
->i_score
, stat_char(newpp
),
402 newpp
->p_ident
->i_name
, newpp
->p_ident
->i_team
);
404 y
= STAT_PLAY_ROW
+ 1 + (newpp
- Player
);
405 for (pp
= Player
; pp
< End_player
; pp
++) {
407 /* Give everyone a few more shots: */
408 pp
->p_ammo
+= conf_nshots
;
409 newpp
->p_ammo
+= conf_nshots
;
410 outyx(pp
, y
, STAT_NAME_COL
, Buf
, len
);
414 for (pp
= Monitor
; pp
< End_monitor
; pp
++)
415 outyx(pp
, y
, STAT_NAME_COL
, Buf
, len
);
417 /* Show the new player what they can see and where they are: */
419 drawplayer(newpp
, TRUE
);
422 /* Make sure that the position they enter in will be erased: */
423 if (enter_status
== Q_FLY
&& conf_fly
)
424 showexpl(newpp
->p_y
, newpp
->p_x
, FLYER
);
426 /* Ready the new player: */
427 sendcom(newpp
, REFRESH
);
428 sendcom(newpp
, READY
, 0);
434 * Return a random direction
439 switch (rand_num(4)) {
455 * Get the score structure of a player
458 get_ident(struct sockaddr
*sa
, int salen __unused
, uid_t uid
, char *name
,
465 if (sa
->sa_family
== AF_INET
)
466 machine
= ntohl((u_long
)((struct sockaddr_in
*)sa
)->sin_addr
.s_addr
);
470 for (ip
= Scores
; ip
!= NULL
; ip
= ip
->i_next
)
471 if (ip
->i_machine
== machine
473 /* && ip->i_team == team */
474 && strncmp(ip
->i_name
, name
, NAMELEN
) == 0)
478 if (ip
->i_team
!= team
) {
479 logx(LOG_INFO
, "player %s %s team %c",
481 team
== ' ' ? "left" : ip
->i_team
== ' ' ?
482 "joined" : "changed to",
483 team
== ' ' ? ip
->i_team
: team
);
486 if (ip
->i_entries
< conf_scoredecay
)
489 ip
->i_kills
= (ip
->i_kills
* (conf_scoredecay
- 1))
491 ip
->i_score
= ip
->i_kills
/ (double) ip
->i_entries
;
494 /* Alloc new entry -- it is released in clear_scores() */
495 ip
= (IDENT
*) malloc(sizeof (IDENT
));
497 logit(LOG_ERR
, "malloc");
498 /* Fourth down, time to punt */
501 ip
->i_machine
= machine
;
504 strlcpy(ip
->i_name
, name
, sizeof ip
->i_name
);
515 ip
->i_gkills
= ip
->i_bkills
= ip
->i_deaths
= 0;
516 ip
->i_stillb
= ip
->i_saved
= 0;
520 logx(LOG_INFO
, "new player: %s%s%c%s",
522 team
== ' ' ? "" : " (team ",
524 team
== ' ' ? "" : ")");
531 answer_info(FILE *fp
)
536 struct sockaddr_in
*sa
;
540 fprintf(fp
, "\nSpawning connections:\n");
541 for (sp
= Spawn
; sp
; sp
= sp
->next
) {
542 sa
= (struct sockaddr_in
*)&sp
->source
;
543 bf
= inet_ntop(AF_INET
, &sa
->sin_addr
, buf
, sizeof buf
);
545 logit(LOG_WARNING
, "inet_ntop");
548 fprintf(fp
, "fd %d: state %d, from %s:%d\n",
549 sp
->fd
, sp
->inlen
+ (sp
->reading_msg
? sp
->msglen
: 0),