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: hunt.c,v 1.13 2008/03/17 09:17:56 sobrado Exp $
32 * $NetBSD: hunt.c,v 1.8 1998/09/13 15:27:28 hubertf Exp $
33 * $DragonFly: src/games/hunt/hunt/hunt.c,v 1.2 2008/09/04 16:12:51 swildner Exp $
48 #include <sys/types.h>
49 #include <sys/socket.h>
50 #include <sys/ioctl.h>
51 #include <sys/sockio.h>
53 #include <netinet/in.h>
56 #include <arpa/inet.h>
64 #define __attribute__(x)
67 FLAG Am_monitor
= FALSE
;
69 char map_key
[256]; /* what to map keys to */
71 char *Send_message
= NULL
;
73 static char *Sock_host
;
74 static char *use_port
;
75 static FLAG Query_driver
= FALSE
;
76 static FLAG Show_scores
= FALSE
;
77 static struct sockaddr Daemon
;
80 static char name
[NAMELEN
];
81 static char team
= '-';
85 static void dump_scores(void);
86 static long env_init(long);
87 static void fill_in_blanks(void);
88 static void leave(int, const char *) __attribute__((__noreturn__
));
89 static void sigterm(int);
90 static int find_driver(void);
94 * Main program for local process
97 main(int ac
, char **av
)
104 enter_status
= env_init((long) Q_CLOAK
);
105 while ((c
= getopt(ac
, av
, "Sbcfh:l:mn:op:qst:w:")) != -1) {
107 case 'l': /* rsh compatibility */
109 (void) strlcpy(name
, optarg
, sizeof name
);
113 if (!isdigit(team
) && team
!= ' ') {
114 warnx("Team names must be numeric or space");
127 case 'q': /* query whether hunt is running */
131 Send_message
= optarg
;
138 Server_port
= atoi(use_port
);
141 enter_status
= Q_CLOAK
;
144 enter_status
= Q_FLY
;
147 enter_status
= Q_SCAN
;
154 fputs("usage: hunt [-bcfmqSs] [-n name] [-p port] "
155 "[-t team] [-w message] [[-h] host]\n",
162 else if (optind
+ 1 == ac
)
163 Sock_host
= av
[ac
- 1];
165 if (Server_port
== 0) {
166 se
= getservbyname("hunt", "udp");
168 Server_port
= ntohs(se
->s_port
);
170 Server_port
= HUNT_PORT
;
179 struct driver
*driver
;
181 probe_drivers(C_MESSAGE
, Sock_host
);
182 while ((driver
= next_driver()) != NULL
) {
183 printf("%d player%s hunting on %s!\n",
185 (driver
->response
== 1) ? "" : "s",
186 driver_name(driver
));
194 errx(1, "otto mode incompatible with monitor mode");
195 (void) strlcpy(name
, "otto", sizeof name
);
200 (void) fflush(stdout
);
203 if (LINES
< SCREEN_HEIGHT
|| COLS
< SCREEN_WIDTH
) {
205 leave(1, "Need a larger window");
207 display_clear_the_screen();
208 (void) signal(SIGINT
, intr
);
209 (void) signal(SIGTERM
, sigterm
);
210 /* (void) signal(SIGPIPE, SIG_IGN); */
214 while (!find_driver()) {
217 leave(1, "No one playing");
220 if (Sock_host
== NULL
) {
222 leave(1, "huntd not running");
233 Socket
= socket(Daemon
.sa_family
, SOCK_STREAM
, 0);
238 if (setsockopt(Socket
, SOL_SOCKET
, SO_USELOOPBACK
,
239 &option
, sizeof option
) < 0)
240 warn("setsockopt loopback");
243 if (connect(Socket
, &Daemon
, Daemon
.sa_len
) == -1) {
244 if (errno
== ECONNREFUSED
)
249 do_connect(name
, team
, enter_status
);
250 if (Send_message
!= NULL
) {
252 if (enter_status
== Q_MESSAGE
)
258 if ((enter_status
= quit(enter_status
)) == Q_QUIT
)
267 * Set Daemon to be the address of a hunt driver, or return 0 on failure.
269 * We start quietly probing for drivers. As soon as one driver is found
270 * we show it in the list. If we run out of drivers and we only have one
271 * then we choose it. Otherwise we present a list of the found drivers.
276 int last_driver
, numdrivers
, waiting
, is_current
;
277 struct driver
*driver
;
282 probe_drivers(Am_monitor
? C_MONITOR
: C_PLAYER
, Sock_host
);
288 if (numdrivers
== 0) {
289 /* Silently wait for at least one driver */
290 driver
= next_driver();
291 } else if (!waiting
|| (driver
=
292 next_driver_fd(STDIN_FILENO
)) == (struct driver
*)-1) {
293 /* We have a key waiting, or no drivers left */
295 if (c
== '\r' || c
== '\n' || c
== ' ') {
298 else if (last_driver
!= -1)
299 c
= 'a' + last_driver
;
301 if (c
< 'a' || c
>= numdrivers
+ 'a') {
305 driver
= &drivers
[c
- 'a'];
309 if (driver
== NULL
) {
311 if (numdrivers
== 0) {
313 return 0; /* Failure */
315 if (numdrivers
== 1) {
316 driver
= &drivers
[0];
322 /* Use the preferred host straight away. */
326 if (numdrivers
== 0) {
327 display_clear_the_screen();
329 display_put_str("Pick one:");
332 /* Mark the last driver we used with an asterisk */
333 is_current
= (last_driver
== -1 && Daemon
.sa_len
!= 0 &&
334 memcmp(&Daemon
, &driver
->addr
, Daemon
.sa_len
) == 0);
336 last_driver
= numdrivers
;
338 /* Display it in the list if there is room */
339 if (numdrivers
< HEIGHT
- 3) {
340 xname
= driver_name(driver
);
341 display_move(3 + numdrivers
, 0);
342 snprintf(buf
, sizeof buf
, "%6c %c %s",
343 is_current
? '*' : ' ', 'a' + numdrivers
, xname
);
344 display_put_str(buf
);
347 /* Clear the last 'Enter letter' line if any */
348 display_move(4 + numdrivers
, 0);
351 if (last_driver
!= -1)
352 snprintf(buf
, sizeof buf
, "Enter letter [%c]: ",
355 snprintf(buf
, sizeof buf
, "Enter letter: ");
357 display_move(5 + numdrivers
, 0);
358 display_put_str(buf
);
364 display_clear_the_screen();
365 Daemon
= driver
->addr
;
368 return 1; /* Success */
374 struct driver
*driver
;
378 probe_drivers(C_SCORES
, Sock_host
);
379 while ((driver
= next_driver()) != NULL
) {
380 printf("\n%s:\n", driver_name(driver
));
383 if ((s
= socket(driver
->addr
.sa_family
, SOCK_STREAM
, 0)) < 0) {
387 if (connect(s
, &driver
->addr
, driver
->addr
.sa_len
) < 0) {
392 while ((cnt
= read(s
, buf
, sizeof buf
)) > 0) {
393 /* Whittle out bad characters */
394 for (i
= 0; i
< cnt
; i
++)
395 if ((buf
[i
] < ' ' || buf
[i
] > '~') &&
396 buf
[i
] != '\n' && buf
[i
] != '\t')
398 fwrite(buf
, cnt
, 1, stdout
);
412 * We had a bad connection. For the moment we assume that this
413 * means the game is full.
418 leave(1, "lost connection to huntd");
423 * version number mismatch.
429 leave(1, "Version number mismatch. No go.");
434 * Handle a terminate signal
437 sigterm(int signo __unused
)
444 * Remove a '\n' at the end of a string if there is one
451 cp
= strrchr(s
, '\n');
458 * Handle a interrupt signal
461 intr(int dummy __unused
)
467 (void) signal(SIGINT
, SIG_IGN
);
468 display_getyx(&y
, &x
);
469 display_move(HEIGHT
, 0);
470 display_put_str("Really quit? ");
480 (void) write(Socket
, "q", 1);
481 (void) close(Socket
);
485 else if (ch
== 'n') {
486 (void) signal(SIGINT
, intr
);
492 display_put_str("(Yes or No) ");
503 * Leave the game somewhat gracefully, restoring all current
507 leave(int eval
, const char *mesg
)
513 display_move(HEIGHT
, 0);
519 if (errno
== 0 && mesg
!= NULL
)
521 else if (mesg
!= NULL
)
528 * initialise game parameters from the HUNT envvar
531 env_init(long enter_status
)
534 char *envp
, *envname
, *s
;
536 /* Map all keys to themselves: */
537 for (i
= 0; i
< 256; i
++)
538 map_key
[i
] = (char) i
;
541 if ((envp
= getenv("HUNT")) != NULL
) {
542 while ((s
= strpbrk(envp
, "=,")) != NULL
) {
543 if (strncmp(envp
, "cloak,", s
- envp
+ 1) == 0) {
544 enter_status
= Q_CLOAK
;
547 else if (strncmp(envp
, "scan,", s
- envp
+ 1) == 0) {
548 enter_status
= Q_SCAN
;
551 else if (strncmp(envp
, "fly,", s
- envp
+ 1) == 0) {
552 enter_status
= Q_FLY
;
555 else if (strncmp(envp
, "nobeep,", s
- envp
+ 1) == 0) {
559 else if (strncmp(envp
, "name=", s
- envp
+ 1) == 0) {
561 if ((s
= strchr(envp
, ',')) == NULL
) {
563 strlcpy(name
, envname
, sizeof name
);
567 strlcpy(name
, envname
, sizeof name
);
570 else if (strncmp(envp
, "port=", s
- envp
+ 1) == 0) {
572 Server_port
= atoi(use_port
);
573 if ((s
= strchr(envp
, ',')) == NULL
) {
580 else if (strncmp(envp
, "host=", s
- envp
+ 1) == 0) {
582 if ((s
= strchr(envp
, ',')) == NULL
) {
589 else if (strncmp(envp
, "message=", s
- envp
+ 1) == 0) {
590 Send_message
= s
+ 1;
591 if ((s
= strchr(envp
, ',')) == NULL
) {
598 else if (strncmp(envp
, "team=", s
- envp
+ 1) == 0) {
602 if ((s
= strchr(envp
, ',')) == NULL
) {
608 } /* must be last option */
609 else if (strncmp(envp
, "mapkey=", s
- envp
+ 1) == 0) {
610 for (s
= s
+ 1; *s
!= '\0'; s
+= 2) {
611 map_key
[(unsigned int) *s
] = *(s
+ 1);
612 if (*(s
+ 1) == '\0') {
620 printf("unknown option %s\n", envp
);
621 if ((s
= strchr(envp
, ',')) == NULL
) {
630 strlcpy(name
, envp
, sizeof name
);
632 printf("unknown option %s\n", envp
);
640 * quiz the user for the information they didn't provide earlier
649 if (name
[0] != '\0') {
650 printf("Entering as '%s'", name
);
651 if (team
!= ' ' && team
!= '-')
652 printf(" on team %c.\n", team
);
656 printf("Enter your code name: ");
657 if (fgets(name
, sizeof name
, stdin
) == NULL
)
661 if (name
[0] == '\0') {
662 printf("You have to have a code name!\n");
665 for (cp
= name
; *cp
!= '\0'; cp
++)
668 printf("Illegal character in your code name.\n");
672 printf("Enter your team (0-9 or nothing): ");
676 else if (i
== '\n' || i
== EOF
|| i
== ' ')
678 /* ignore trailing chars */
679 while (i
!= '\n' && i
!= EOF
)
682 printf("Teams must be numeric.\n");