1 /* $NetBSD: driver.c,v 1.35 2014/03/30 01:44:37 dholland Exp $ */
3 * Copyright (c) 1983-2003, Regents of the University of California.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
10 * + Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * + Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * + Neither the name of the University of California, San Francisco nor
16 * the names of its contributors may be used to endorse or promote
17 * products derived from this software without specific prior written
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
21 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #include <sys/cdefs.h>
35 __RCSID("$NetBSD: driver.c,v 1.35 2014/03/30 01:44:37 dholland Exp $");
38 #include <sys/types.h>
39 #include <sys/socket.h>
44 #include <netinet/in.h>
46 #include <arpa/inet.h>
56 #include "pathnames.h"
60 * There are three listening sockets in this daemon:
61 * - a datagram socket that listens on a well known port
62 * - a stream socket for general player connections
63 * - a second stream socket that prints the stats/scores
65 * These are (now) named as follows:
66 * - contact (contactsock, contactaddr, etc.)
67 * - hunt (huntsock, huntaddr, etc.)
68 * - stat (statsock, stataddr, etc.)
70 * Until being cleaned up in 2014 the code used an assortment of
71 * inconsistent and confusing names to refer to the pieces of these:
73 * test_port -> contactaddr
74 * Test_port -> contactport
75 * Test_socket -> contactsock
77 * sock_port -> huntport
80 * Daemon -> both stataddr and huntaddr
81 * stat_port -> statport
84 * It isn't clear to me what purpose contactsocket is supposed to
85 * serve; maybe it was supposed to avoid asking inetd to support
86 * tcp/wait sockets? Anyway, we can't really change the protocol at
87 * this point. (To complicate matters, contactsocket doesn't exist if
88 * using AF_UNIX sockets; you just connect to the game socket.)
90 * When using internet sockets:
91 * - the contact socket listens on INADDR_ANY using the game port
92 * (either specified or the default: CONTACT_PORT, currently
94 * - the hunt socket listens on INADDR_ANY using whatever port
95 * - the stat socket listens on INADDR_ANY using whatever port
97 * When using AF_UNIX sockets:
98 * - the contact socket isn't used
99 * - the hunt socket listens on the game socket (either a specified path
101 * - the stat socket listens on its own socket (huntsocket's path +
105 static bool localmode
; /* true -> AF_UNIX; false -> AF_INET */
106 static bool inetd_spawned
; /* invoked via inetd? */
107 static bool standard_port
= true; /* listening on standard port? */
109 static struct sockaddr_storage huntaddr
;
110 static struct sockaddr_storage stataddr
;
111 static socklen_t huntaddrlen
;
112 static socklen_t stataddrlen
;
114 static uint16_t contactport
= TEST_PORT
;
115 static uint16_t huntport
; /* port # of tcp listen socket */
116 static uint16_t statport
; /* port # of statistics tcp socket */
118 static const char *huntsockpath
= PATH_HUNTSOCKET
;
119 static char *statsockpath
;
121 static int contactsock
; /* socket to answer datagrams */
122 int huntsock
; /* main socket */
123 static int statsock
; /* stat socket */
126 static int volcano
= 0; /* Explosion size */
129 static void clear_scores(void);
130 static bool havechar(PLAYER
*, int);
131 static void init(void);
132 static void makeboots(void);
133 static void send_stats(void);
134 static void zap(PLAYER
*, bool, int);
137 getnum(const char *s
, unsigned long *ret
)
142 *ret
= strtoul(s
, &t
, 0);
143 if (errno
|| *t
!= '\0') {
150 usage(const char *av0
)
152 fprintf(stderr
, "Usage: %s [-s] [-p portnumber|socketpath]\n", av0
);
157 makeaddr(const char *path
, uint16_t port
,
158 struct sockaddr_storage
*ss
, socklen_t
*len
)
160 struct sockaddr_in
*sin
;
161 struct sockaddr_un
*sun
;
164 sin
= (struct sockaddr_in
*)ss
;
165 sin
->sin_family
= AF_INET
;
166 sin
->sin_addr
.s_addr
= INADDR_ANY
;
167 sin
->sin_port
= htons(port
);
170 sun
= (struct sockaddr_un
*)ss
;
171 sun
->sun_family
= AF_UNIX
;
172 strlcpy(sun
->sun_path
, path
, sizeof(sun
->sun_path
));
178 getsockport(int sock
)
180 struct sockaddr_storage addr
;
184 if (getsockname(sock
, (struct sockaddr
*)&addr
, &len
) < 0) {
185 complain(LOG_ERR
, "getsockname");
188 switch (addr
.ss_family
) {
190 return ntohs(((struct sockaddr_in
*)&addr
)->sin_port
);
192 return ntohs(((struct sockaddr_in6
*)&addr
)->sin6_port
);
204 main(int ac
, char **av
)
207 unsigned long optargnum
;
209 struct sockaddr_storage msgaddr
;
210 socklen_t msgaddrlen
;
211 static bool first
= true;
212 static bool server
= false;
214 const int linger
= 90 * 1000;
216 while ((c
= getopt(ac
, av
, "sp:")) != -1) {
222 standard_port
= false;
223 if (getnum(optarg
, &optargnum
) < 0) {
225 huntsockpath
= optarg
;
226 } else if (optargnum
< 0xffff) {
228 contactport
= optargnum
;
240 asprintf(&statsockpath
, "%s.stats", huntsockpath
);
247 while (poll(fdset
, 3+MAXPL
+MAXMON
, INFTIM
) < 0)
250 complain(LOG_WARNING
, "poll");
253 if (!localmode
&& fdset
[2].revents
& POLLIN
) {
254 msgaddrlen
= sizeof(msgaddr
);
255 (void) recvfrom(contactsock
, &msg
, sizeof msg
,
256 0, (struct sockaddr
*)&msgaddr
, &msgaddrlen
);
257 switch (ntohs(msg
)) {
261 reply
= htons((u_short
) Nplayer
);
262 (void) sendto(contactsock
, &reply
,
264 (struct sockaddr
*)&msgaddr
,
268 reply
= htons(statport
);
269 (void) sendto(contactsock
, &reply
,
271 (struct sockaddr
*)&msgaddr
,
276 if (msg
== C_MONITOR
&& Nplayer
<= 0)
278 reply
= htons(huntport
);
279 (void) sendto(contactsock
, &reply
,
281 (struct sockaddr
*)&msgaddr
,
288 for (pp
= Player
, i
= 0; pp
< End_player
; pp
++, i
++)
289 if (havechar(pp
, i
+ 3)) {
294 for (pp
= Monitor
, i
= 0; pp
< End_monitor
; pp
++, i
++)
295 if (havechar(pp
, i
+ MAXPL
+ 3)) {
301 for (pp
= Player
, i
= 0; pp
< End_player
; )
302 if (pp
->p_death
[0] != '\0')
303 zap(pp
, true, i
+ 3);
307 for (pp
= Monitor
, i
= 0; pp
< End_monitor
; )
308 if (pp
->p_death
[0] != '\0')
309 zap(pp
, false, i
+ MAXPL
+ 3);
314 if (fdset
[0].revents
& POLLIN
)
317 /* announce start of game? */
321 if (fdset
[1].revents
& POLLIN
)
323 for (pp
= Player
, i
= 0; pp
< End_player
; pp
++, i
++) {
324 if (fdset
[i
+ 3].revents
& POLLIN
)
325 sendcom(pp
, READY
, pp
->p_nexec
);
327 (void) fflush(pp
->p_output
);
330 for (pp
= Monitor
, i
= 0; pp
< End_monitor
; pp
++, i
++) {
331 if (fdset
[i
+ MAXPL
+ 3].revents
& POLLIN
)
332 sendcom(pp
, READY
, pp
->p_nexec
);
334 (void) fflush(pp
->p_output
);
337 } while (Nplayer
> 0);
339 if (poll(fdset
, 3+MAXPL
+MAXMON
, linger
) > 0) {
354 for (pp
= Monitor
, i
= 0; pp
< End_monitor
; i
++)
355 zap(pp
, false, i
+ MAXPL
+ 3);
364 * Initialize the global parameters.
370 struct sockaddr_storage stdinaddr
;
371 struct sockaddr_storage contactaddr
;
372 socklen_t contactaddrlen
;
377 (void) ioctl(fileno(stdout
), TIOCNOTTY
, NULL
);
379 (void) setpgid(getpid(), getpid());
380 (void) signal(SIGHUP
, SIG_IGN
);
381 (void) signal(SIGINT
, SIG_IGN
);
382 (void) signal(SIGQUIT
, SIG_IGN
);
383 (void) signal(SIGTERM
, cleanup
);
386 (void) chdir("/var/tmp"); /* just in case it core dumps */
387 (void) umask(0); /* No privacy at all! */
388 (void) signal(SIGPIPE
, SIG_IGN
);
391 openlog("huntd", LOG_PID
, LOG_DAEMON
);
397 len
= sizeof(stdinaddr
);
398 if (getsockname(STDIN_FILENO
, (struct sockaddr
*)&stdinaddr
,
400 inetd_spawned
= true;
401 /* force localmode, assimilate stdin as appropriate */
402 if (stdinaddr
.ss_family
== AF_UNIX
) {
405 huntsock
= STDIN_FILENO
;
409 contactsock
= STDIN_FILENO
;
413 /* keep value of localmode; no sockets yet */
419 * initialize contact socket
421 if (!localmode
&& contactsock
< 0) {
422 makeaddr(NULL
, contactport
, &contactaddr
, &contactaddrlen
);
423 contactsock
= socket(AF_INET
, SOCK_DGRAM
, 0);
424 if (bind(contactsock
, (struct sockaddr
*) &contactaddr
,
425 contactaddrlen
) < 0) {
426 complain(LOG_ERR
, "bind");
429 (void) listen(contactsock
, 5);
433 * Initialize main socket
436 makeaddr(localmode
? huntsockpath
: NULL
, 0, &huntaddr
,
438 huntsock
= socket(huntaddr
.ss_family
, SOCK_STREAM
, 0);
439 if (bind(huntsock
, (struct sockaddr
*)&huntaddr
,
441 if (errno
== EADDRINUSE
)
444 complain(LOG_ERR
, "bind");
448 (void) listen(huntsock
, 5);
452 * Initialize statistics socket
454 makeaddr(localmode
? statsockpath
: NULL
, 0, &stataddr
, &stataddrlen
);
455 statsock
= socket(stataddr
.ss_family
, SOCK_STREAM
, 0);
456 if (bind(statsock
, (struct sockaddr
*)&stataddr
, stataddrlen
) < 0) {
457 if (errno
== EADDRINUSE
)
460 complain(LOG_ERR
, "bind");
464 (void) listen(statsock
, 5);
467 contactport
= getsockport(contactsock
);
468 statport
= getsockport(statsock
);
469 huntport
= getsockport(huntsock
);
470 if (contactport
!= TEST_PORT
) {
471 standard_port
= false;
476 * Initialize minimal poll mask
478 fdset
[0].fd
= huntsock
;
479 fdset
[0].events
= POLLIN
;
480 fdset
[1].fd
= statsock
;
481 fdset
[1].events
= POLLIN
;
486 fdset
[2].fd
= contactsock
;
487 fdset
[2].events
= POLLIN
;
496 for (i
= 0; i
< NASCII
; i
++)
498 See_over
[DOOR
] = false;
499 See_over
[WALL1
] = false;
500 See_over
[WALL2
] = false;
501 See_over
[WALL3
] = false;
503 See_over
[WALL4
] = false;
504 See_over
[WALL5
] = false;
512 * Put the boots in the maze
521 x
= rand_num(WIDTH
- 1) + 1;
522 y
= rand_num(HEIGHT
- 1) + 1;
523 } while (Maze
[y
][x
] != SPACE
);
524 Maze
[y
][x
] = BOOT_PAIR
;
525 for (pp
= Boot
; pp
< &Boot
[NBOOTS
]; pp
++)
533 * Check the damage to the given player, and see if s/he is killed
536 checkdam(PLAYER
*ouch
, PLAYER
*gotcha
, IDENT
*credit
, int amt
,
541 if (ouch
->p_death
[0] != '\0')
544 if (this_shot_type
== SLIME
)
545 switch (ouch
->p_nboots
) {
553 message(gotcha
, "He has boots on!");
557 ouch
->p_damage
+= amt
;
558 if (ouch
->p_damage
<= ouch
->p_damcap
) {
559 (void) snprintf(Buf
, sizeof(Buf
), "%2d", ouch
->p_damage
);
560 cgoto(ouch
, STAT_DAM_ROW
, STAT_VALUE_COL
);
561 outstr(ouch
, Buf
, 2);
566 switch (this_shot_type
) {
572 cp
= "Killed on impact";
576 cp
= "Stabbed to death";
577 ouch
->p_ammo
= 0; /* No exploding */
580 cp
= "Shot to death";
609 if (credit
== NULL
) {
610 (void) snprintf(ouch
->p_death
, sizeof(ouch
->p_death
),
612 (this_shot_type
== MINE
|| this_shot_type
== GMINE
) ?
613 "a mine" : "act of God");
617 (void) snprintf(ouch
->p_death
, sizeof(ouch
->p_death
),
618 "| %s by %s |", cp
, credit
->i_name
);
620 if (ouch
== gotcha
) { /* No use killing yourself */
624 else if (ouch
->p_ident
->i_team
== ' '
625 || ouch
->p_ident
->i_team
!= credit
->i_team
) {
633 credit
->i_score
= credit
->i_kills
/ (double) credit
->i_entries
;
634 ouch
->p_ident
->i_deaths
++;
635 if (ouch
->p_nchar
== 0)
636 ouch
->p_ident
->i_stillb
++;
639 gotcha
->p_damcap
+= STABDAM
;
640 gotcha
->p_damage
-= STABDAM
;
641 if (gotcha
->p_damage
< 0)
642 gotcha
->p_damage
= 0;
643 (void) snprintf(Buf
, sizeof(Buf
), "%2d/%2d", gotcha
->p_damage
,
645 cgoto(gotcha
, STAT_DAM_ROW
, STAT_VALUE_COL
);
646 outstr(gotcha
, Buf
, 5);
647 (void) snprintf(Buf
, sizeof(Buf
), "%3d",
648 (gotcha
->p_damcap
- MAXDAM
) / 2);
649 cgoto(gotcha
, STAT_KILL_ROW
, STAT_VALUE_COL
);
650 outstr(gotcha
, Buf
, 3);
651 (void) snprintf(Buf
, sizeof(Buf
), "%5.2f", gotcha
->p_ident
->i_score
);
652 for (ouch
= Player
; ouch
< End_player
; ouch
++) {
653 cgoto(ouch
, STAT_PLAY_ROW
+ 1 + (gotcha
- Player
),
655 outstr(ouch
, Buf
, 5);
658 for (ouch
= Monitor
; ouch
< End_monitor
; ouch
++) {
659 cgoto(ouch
, STAT_PLAY_ROW
+ 1 + (gotcha
- Player
),
661 outstr(ouch
, Buf
, 5);
668 * Kill off a player and take him out of the game.
671 zap(PLAYER
*pp
, bool was_player
, int i
)
680 fixshots(pp
->p_y
, pp
->p_x
, pp
->p_over
);
681 drawplayer(pp
, false);
685 len
= strlen(pp
->p_death
); /* Display the cause of death */
686 x
= (WIDTH
- len
) / 2;
687 cgoto(pp
, HEIGHT
/ 2, x
);
688 outstr(pp
, pp
->p_death
, len
);
689 for (n
= 1; n
< len
; n
++)
690 pp
->p_death
[n
] = '-';
691 pp
->p_death
[0] = '+';
692 pp
->p_death
[len
- 1] = '+';
693 cgoto(pp
, HEIGHT
/ 2 - 1, x
);
694 outstr(pp
, pp
->p_death
, len
);
695 cgoto(pp
, HEIGHT
/ 2 + 1, x
);
696 outstr(pp
, pp
->p_death
, len
);
697 cgoto(pp
, HEIGHT
, 0);
702 for (bp
= Bullets
; bp
!= NULL
; bp
= bp
->b_next
) {
703 if (bp
->b_owner
== pp
)
705 if (bp
->b_x
== pp
->p_x
&& bp
->b_y
== pp
->p_y
)
709 n
= rand_num(pp
->p_ammo
);
710 x
= rand_num(pp
->p_ammo
);
715 else if (n
== pp
->p_ammo
- 1) {
720 for (x
= MAXBOMB
- 1; x
> 0; x
--)
721 if (n
>= shot_req
[x
])
723 for (y
= MAXSLIME
- 1; y
> 0; y
--)
724 if (n
>= slime_req
[y
])
726 if (y
>= 0 && slime_req
[y
] > shot_req
[x
]) {
736 (void) add_shot(len
, pp
->p_y
, pp
->p_x
, pp
->p_face
, x
,
738 (void) snprintf(Buf
, sizeof(Buf
), "%s detonated.",
739 pp
->p_ident
->i_name
);
740 for (np
= Player
; np
< End_player
; np
++)
743 for (np
= Monitor
; np
< End_monitor
; np
++)
747 while (pp
->p_nboots
-- > 0) {
748 for (np
= Boot
; np
< &Boot
[NBOOTS
]; np
++)
749 if (np
->p_flying
< 0)
751 if (np
>= &Boot
[NBOOTS
])
752 err(1, "Too many boots");
753 np
->p_undershot
= false;
756 np
->p_flying
= rand_num(20);
757 np
->p_flyx
= 2 * rand_num(6) - 5;
758 np
->p_flyy
= 2 * rand_num(6) - 5;
761 showexpl(np
->p_y
, np
->p_x
, BOOT
);
766 else if (pp
->p_nboots
> 0) {
767 if (pp
->p_nboots
== 2)
768 Maze
[pp
->p_y
][pp
->p_x
] = BOOT_PAIR
;
770 Maze
[pp
->p_y
][pp
->p_x
] = BOOT
;
772 fixshots(pp
->p_y
, pp
->p_x
,
773 Maze
[pp
->p_y
][pp
->p_x
]);
778 volcano
+= pp
->p_ammo
- x
;
779 if (rand_num(100) < volcano
/ 50) {
781 x
= rand_num(WIDTH
/ 2) + WIDTH
/ 4;
782 y
= rand_num(HEIGHT
/ 2) + HEIGHT
/ 4;
783 } while (Maze
[y
][x
] != SPACE
);
784 (void) add_shot(LAVA
, y
, x
, LEFTS
, volcano
,
786 for (np
= Player
; np
< End_player
; np
++)
787 message(np
, "Volcano eruption.");
793 if (rand_num(100) < 2) {
795 x
= rand_num(WIDTH
/ 2) + WIDTH
/ 4;
796 y
= rand_num(HEIGHT
/ 2) + HEIGHT
/ 4;
797 } while (Maze
[y
][x
] != SPACE
);
798 add_shot(DSHOT
, y
, x
, rand_dir(),
800 rand_num(MAXBOMB
- MINDSHOT
)],
806 (void) putc(' ', pp
->p_output
);
807 (void) fclose(pp
->p_output
);
810 if (pp
!= End_player
) {
811 memcpy(pp
, End_player
, sizeof (PLAYER
));
812 fdset
[i
] = fdset
[End_player
- Player
+ 3];
813 fdset
[End_player
- Player
+ 3].fd
= -1;
814 (void) snprintf(Buf
, sizeof(Buf
), "%5.2f%c%-10.10s %c",
815 pp
->p_ident
->i_score
, stat_char(pp
),
816 pp
->p_ident
->i_name
, pp
->p_ident
->i_team
);
817 n
= STAT_PLAY_ROW
+ 1 + (pp
- Player
);
818 for (np
= Player
; np
< End_player
; np
++) {
819 cgoto(np
, n
, STAT_NAME_COL
);
820 outstr(np
, Buf
, STAT_NAME_LEN
);
823 for (np
= Monitor
; np
< End_monitor
; np
++) {
824 cgoto(np
, n
, STAT_NAME_COL
);
825 outstr(np
, Buf
, STAT_NAME_LEN
);
831 /* Erase the last player */
832 n
= STAT_PLAY_ROW
+ 1 + Nplayer
;
833 for (np
= Player
; np
< End_player
; np
++) {
834 cgoto(np
, n
, STAT_NAME_COL
);
838 for (np
= Monitor
; np
< End_monitor
; np
++) {
839 cgoto(np
, n
, STAT_NAME_COL
);
845 (void) putc(LAST_PLAYER
, pp
->p_output
);
846 (void) fclose(pp
->p_output
);
849 if (pp
!= End_monitor
) {
850 memcpy(pp
, End_monitor
, sizeof (PLAYER
));
851 fdset
[i
] = fdset
[End_monitor
- Monitor
+ MAXPL
+ 3];
852 fdset
[End_monitor
- Monitor
+ MAXPL
+ 3].fd
= -1;
853 (void) snprintf(Buf
, sizeof(Buf
), "%5.5s %-10.10s %c",
855 pp
->p_ident
->i_name
, pp
->p_ident
->i_team
);
856 n
= STAT_MON_ROW
+ 1 + (pp
- Player
);
857 for (np
= Player
; np
< End_player
; np
++) {
858 cgoto(np
, n
, STAT_NAME_COL
);
859 outstr(np
, Buf
, STAT_NAME_LEN
);
861 for (np
= Monitor
; np
< End_monitor
; np
++) {
862 cgoto(np
, n
, STAT_NAME_COL
);
863 outstr(np
, Buf
, STAT_NAME_LEN
);
868 /* Erase the last monitor */
869 n
= STAT_MON_ROW
+ 1 + (End_monitor
- Monitor
);
870 for (np
= Player
; np
< End_player
; np
++) {
871 cgoto(np
, n
, STAT_NAME_COL
);
874 for (np
= Monitor
; np
< End_monitor
; np
++) {
875 cgoto(np
, n
, STAT_NAME_COL
);
884 * Return a random number in a given range.
889 return (range
== 0 ? 0 : random() % range
);
894 * Check to see if we have any characters in the input queue; if
895 * we do, read them, stash them away, and return true; else return
899 havechar(PLAYER
*pp
, int i
)
902 if (pp
->p_ncount
< pp
->p_nchar
)
904 if (!(fdset
[i
].revents
& POLLIN
))
907 pp
->p_nchar
= read(pp
->p_fd
, pp
->p_cbuf
, sizeof pp
->p_cbuf
);
908 if (pp
->p_nchar
< 0 && errno
== EINTR
) {
910 } else if (pp
->p_nchar
<= 0) {
920 * Exit with the given value, cleaning up any droppings lying around
927 for (pp
= Player
; pp
< End_player
; pp
++) {
928 cgoto(pp
, HEIGHT
, 0);
930 (void) putc(LAST_PLAYER
, pp
->p_output
);
931 (void) fclose(pp
->p_output
);
934 for (pp
= Monitor
; pp
< End_monitor
; pp
++) {
935 cgoto(pp
, HEIGHT
, 0);
937 (void) putc(LAST_PLAYER
, pp
->p_output
);
938 (void) fclose(pp
->p_output
);
941 (void) close(huntsock
);
943 (void) unlink(huntsockpath
);
951 * Print stats to requestor
959 struct sockaddr_storage newaddr
;
963 * Get the output stream ready
965 socklen
= sizeof(newaddr
);
966 s
= accept(statsock
, (struct sockaddr
*)&newaddr
, &socklen
);
970 complain(LOG_WARNING
, "accept");
975 complain(LOG_WARNING
, "fdopen");
981 * Send output to requestor
983 fputs("Name\t\tScore\tDucked\tAbsorb\tFaced\tShot\tRobbed\tMissed\tSlimeK\n", fp
);
984 for (ip
= Scores
; ip
!= NULL
; ip
= ip
->i_next
) {
985 fprintf(fp
, "%s\t", ip
->i_name
);
986 if (strlen(ip
->i_name
) < 8)
988 fprintf(fp
, "%.2f\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
989 ip
->i_score
, ip
->i_ducked
, ip
->i_absorbed
,
990 ip
->i_faced
, ip
->i_shot
, ip
->i_robbed
,
991 ip
->i_missed
, ip
->i_slime
);
993 fputs("\n\nName\t\tEnemy\tFriend\tDeaths\tStill\tSaved\n", fp
);
994 for (ip
= Scores
; ip
!= NULL
; ip
= ip
->i_next
) {
995 if (ip
->i_team
== ' ') {
996 fprintf(fp
, "%s\t", ip
->i_name
);
997 if (strlen(ip
->i_name
) < 8)
1001 fprintf(fp
, "%s[%c]\t", ip
->i_name
, ip
->i_team
);
1002 if (strlen(ip
->i_name
) + 3 < 8)
1005 fprintf(fp
, "%d\t%d\t%d\t%d\t%d\n",
1006 ip
->i_gkills
, ip
->i_bkills
, ip
->i_deaths
,
1007 ip
->i_stillb
, ip
->i_saved
);
1015 * Clear out the scores so the next session start clean
1022 for (ip
= Scores
; ip
!= NULL
; ip
= nextip
) {
1023 nextip
= ip
->i_next
;