xref: /netbsd-src/games/hunt/huntd/answer.c (revision 23c8222edbfb0f0932d88a8351d3a0cf817dfb9e)
1 /*	$NetBSD: answer.c,v 1.7 2004/11/05 21:30:32 dsl Exp $	*/
2 /*
3  * Copyright (c) 1983-2003, Regents of the University of California.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
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
18  *   permission.
19  *
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.
31  */
32 
33 #include <sys/cdefs.h>
34 #ifndef lint
35 __RCSID("$NetBSD: answer.c,v 1.7 2004/11/05 21:30:32 dsl Exp $");
36 #endif /* not lint */
37 
38 # include	<ctype.h>
39 # include	<errno.h>
40 # include	<fcntl.h>
41 # include	<stdlib.h>
42 # include	<unistd.h>
43 # include	"hunt.h"
44 
45 # define	SCOREDECAY	15
46 
47 static char	Ttyname[NAMELEN];
48 
49 int
50 answer()
51 {
52 	PLAYER			*pp;
53 	int			newsock;
54 	static u_long		mode;
55 	static char		name[NAMELEN];
56 	static char		team;
57 	static int		enter_status;
58 	static int		socklen;
59 	static u_long		machine;
60 	static u_int32_t	uid;
61 	static SOCKET		sockstruct;
62 	char			*cp1, *cp2;
63 	int			flags;
64 	u_int32_t		version;
65 	int			i;
66 
67 # ifdef INTERNET
68 	socklen = sizeof sockstruct;
69 # else
70 	socklen = sizeof sockstruct - 1;
71 # endif
72 	errno = 0;
73 	newsock = accept(Socket, (struct sockaddr *) &sockstruct, &socklen);
74 	if (newsock < 0)
75 	{
76 		if (errno == EINTR)
77 			return FALSE;
78 # ifdef LOG
79 		syslog(LOG_ERR, "accept: %m");
80 # else
81 		perror("accept");
82 # endif
83 		cleanup(1);
84 	}
85 
86 # ifdef INTERNET
87 	machine = ntohl(((struct sockaddr_in *) &sockstruct)->sin_addr.s_addr);
88 # else
89 	if (machine == 0)
90 		machine = gethostid();
91 # endif
92 	version = htonl((u_int32_t) HUNT_VERSION);
93 	(void) write(newsock, (char *) &version, LONGLEN);
94 	(void) read(newsock, (char *) &uid, LONGLEN);
95 	uid = ntohl((unsigned long) uid);
96 	(void) read(newsock, name, NAMELEN);
97 	(void) read(newsock, &team, 1);
98 	(void) read(newsock, (char *) &enter_status, LONGLEN);
99 	enter_status = ntohl((unsigned long) enter_status);
100 	(void) read(newsock, Ttyname, NAMELEN);
101 	(void) read(newsock, (char *) &mode, sizeof mode);
102 	mode = ntohl(mode);
103 
104 	/*
105 	 * Turn off blocking I/O, so a slow or dead terminal won't stop
106 	 * the game.  All subsequent reads check how many bytes they read.
107 	 */
108 	flags = fcntl(newsock, F_GETFL, 0);
109 	flags |= O_NDELAY;
110 	(void) fcntl(newsock, F_SETFL, flags);
111 
112 	/*
113 	 * Make sure the name contains only printable characters
114 	 * since we use control characters for cursor control
115 	 * between driver and player processes
116 	 */
117 	for (cp1 = cp2 = name; *cp1 != '\0'; cp1++)
118 		if (isprint((unsigned char)*cp1) || *cp1 == ' ')
119 			*cp2++ = *cp1;
120 	*cp2 = '\0';
121 
122 # ifdef INTERNET
123 	if (mode == C_MESSAGE) {
124 		char	buf[BUFSIZ + 1];
125 		int	n;
126 
127 		if (team == ' ')
128 			(void) sprintf(buf, "%s: ", name);
129 		else
130 			(void) sprintf(buf, "%s[%c]: ", name, team);
131 		n = strlen(buf);
132 		for (pp = Player; pp < End_player; pp++) {
133 			cgoto(pp, HEIGHT, 0);
134 			outstr(pp, buf, n);
135 		}
136 		while ((n = read(newsock, buf, BUFSIZ)) > 0)
137 			for (pp = Player; pp < End_player; pp++)
138 				outstr(pp, buf, n);
139 		for (pp = Player; pp < End_player; pp++) {
140 			ce(pp);
141 			sendcom(pp, REFRESH);
142 			sendcom(pp, READY, 0);
143 			(void) fflush(pp->p_output);
144 		}
145 		(void) close(newsock);
146 		return FALSE;
147 	}
148 	else
149 # endif
150 # ifdef MONITOR
151 	if (mode == C_MONITOR)
152 		if (End_monitor < &Monitor[MAXMON]) {
153 			pp = End_monitor++;
154 			i = pp - Monitor + MAXPL + 3;
155 		} else {
156 			socklen = 0;
157 			(void) write(newsock, (char *) &socklen,
158 				sizeof socklen);
159 			(void) close(newsock);
160 			return FALSE;
161 		}
162 	else
163 # endif
164 		if (End_player < &Player[MAXPL]) {
165 			pp = End_player++;
166 			i = pp - Player + 3;
167 		} else {
168 			socklen = 0;
169 			(void) write(newsock, (char *) &socklen,
170 				sizeof socklen);
171 			(void) close(newsock);
172 			return FALSE;
173 		}
174 
175 #ifdef MONITOR
176 	if (mode == C_MONITOR && team == ' ')
177 		team = '*';
178 #endif
179 	pp->p_ident = get_ident(machine, uid, name, team);
180 	pp->p_output = fdopen(newsock, "w");
181 	pp->p_death[0] = '\0';
182 	pp->p_fd = newsock;
183 	fdset[i].fd = newsock;
184 	fdset[i].events = POLLIN;
185 
186 	pp->p_y = 0;
187 	pp->p_x = 0;
188 
189 # ifdef MONITOR
190 	if (mode == C_MONITOR)
191 		stmonitor(pp);
192 	else
193 # endif
194 		stplayer(pp, enter_status);
195 	return TRUE;
196 }
197 
198 # ifdef MONITOR
199 void
200 stmonitor(pp)
201 	PLAYER	*pp;
202 {
203 	int	line;
204 	PLAYER	*npp;
205 
206 	memcpy(pp->p_maze, Maze, sizeof Maze);
207 
208 	drawmaze(pp);
209 
210 	(void) sprintf(Buf, "%5.5s%c%-10.10s %c", " ", stat_char(pp),
211 		pp->p_ident->i_name, pp->p_ident->i_team);
212 	line = STAT_MON_ROW + 1 + (pp - Monitor);
213 	for (npp = Player; npp < End_player; npp++) {
214 		cgoto(npp, line, STAT_NAME_COL);
215 		outstr(npp, Buf, STAT_NAME_LEN);
216 	}
217 	for (npp = Monitor; npp < End_monitor; npp++) {
218 		cgoto(npp, line, STAT_NAME_COL);
219 		outstr(npp, Buf, STAT_NAME_LEN);
220 	}
221 
222 	sendcom(pp, REFRESH);
223 	sendcom(pp, READY, 0);
224 	(void) fflush(pp->p_output);
225 }
226 # endif
227 
228 void
229 stplayer(newpp, enter_status)
230 	PLAYER	*newpp;
231 	int	enter_status;
232 {
233 	int	x, y;
234 	PLAYER	*pp;
235 
236 	Nplayer++;
237 
238 	for (y = 0; y < UBOUND; y++)
239 		for (x = 0; x < WIDTH; x++)
240 			newpp->p_maze[y][x] = Maze[y][x];
241 	for (     ; y < DBOUND; y++) {
242 		for (x = 0; x < LBOUND; x++)
243 			newpp->p_maze[y][x] = Maze[y][x];
244 		for (     ; x < RBOUND; x++)
245 			newpp->p_maze[y][x] = SPACE;
246 		for (     ; x < WIDTH;  x++)
247 			newpp->p_maze[y][x] = Maze[y][x];
248 	}
249 	for (     ; y < HEIGHT; y++)
250 		for (x = 0; x < WIDTH; x++)
251 			newpp->p_maze[y][x] = Maze[y][x];
252 
253 	do {
254 		x = rand_num(WIDTH - 1) + 1;
255 		y = rand_num(HEIGHT - 1) + 1;
256 	} while (Maze[y][x] != SPACE);
257 	newpp->p_over = SPACE;
258 	newpp->p_x = x;
259 	newpp->p_y = y;
260 	newpp->p_undershot = FALSE;
261 
262 # ifdef FLY
263 	if (enter_status == Q_FLY) {
264 		newpp->p_flying = rand_num(20);
265 		newpp->p_flyx = 2 * rand_num(6) - 5;
266 		newpp->p_flyy = 2 * rand_num(6) - 5;
267 		newpp->p_face = FLYER;
268 	}
269 	else
270 # endif
271 	{
272 		newpp->p_flying = -1;
273 		newpp->p_face = rand_dir();
274 	}
275 	newpp->p_damage = 0;
276 	newpp->p_damcap = MAXDAM;
277 	newpp->p_nchar = 0;
278 	newpp->p_ncount = 0;
279 	newpp->p_nexec = 0;
280 	newpp->p_ammo = ISHOTS;
281 # ifdef BOOTS
282 	newpp->p_nboots = 0;
283 # endif
284 	if (enter_status == Q_SCAN) {
285 		newpp->p_scan = SCANLEN;
286 		newpp->p_cloak = 0;
287 	}
288 	else {
289 		newpp->p_scan = 0;
290 		newpp->p_cloak = CLOAKLEN;
291 	}
292 	newpp->p_ncshot = 0;
293 
294 	do {
295 		x = rand_num(WIDTH - 1) + 1;
296 		y = rand_num(HEIGHT - 1) + 1;
297 	} while (Maze[y][x] != SPACE);
298 	Maze[y][x] = GMINE;
299 # ifdef MONITOR
300 	for (pp = Monitor; pp < End_monitor; pp++)
301 		check(pp, y, x);
302 # endif
303 
304 	do {
305 		x = rand_num(WIDTH - 1) + 1;
306 		y = rand_num(HEIGHT - 1) + 1;
307 	} while (Maze[y][x] != SPACE);
308 	Maze[y][x] = MINE;
309 # ifdef MONITOR
310 	for (pp = Monitor; pp < End_monitor; pp++)
311 		check(pp, y, x);
312 # endif
313 
314 	(void) sprintf(Buf, "%5.2f%c%-10.10s %c", newpp->p_ident->i_score,
315 		stat_char(newpp), newpp->p_ident->i_name,
316 		newpp->p_ident->i_team);
317 	y = STAT_PLAY_ROW + 1 + (newpp - Player);
318 	for (pp = Player; pp < End_player; pp++) {
319 		if (pp != newpp) {
320 			char	smallbuf[10];
321 
322 			pp->p_ammo += NSHOTS;
323 			newpp->p_ammo += NSHOTS;
324 			cgoto(pp, y, STAT_NAME_COL);
325 			outstr(pp, Buf, STAT_NAME_LEN);
326 			(void) sprintf(smallbuf, "%3d", pp->p_ammo);
327 			cgoto(pp, STAT_AMMO_ROW, STAT_VALUE_COL);
328 			outstr(pp, smallbuf, 3);
329 		}
330 	}
331 # ifdef MONITOR
332 	for (pp = Monitor; pp < End_monitor; pp++) {
333 		cgoto(pp, y, STAT_NAME_COL);
334 		outstr(pp, Buf, STAT_NAME_LEN);
335 	}
336 # endif
337 
338 	drawmaze(newpp);
339 	drawplayer(newpp, TRUE);
340 	look(newpp);
341 # ifdef	FLY
342 	if (enter_status == Q_FLY)
343 		/* Make sure that the position you enter in will be erased */
344 		showexpl(newpp->p_y, newpp->p_x, FLYER);
345 # endif
346 	sendcom(newpp, REFRESH);
347 	sendcom(newpp, READY, 0);
348 	(void) fflush(newpp->p_output);
349 }
350 
351 /*
352  * rand_dir:
353  *	Return a random direction
354  */
355 int
356 rand_dir()
357 {
358 	switch (rand_num(4)) {
359 	  case 0:
360 		return LEFTS;
361 	  case 1:
362 		return RIGHT;
363 	  case 2:
364 		return BELOW;
365 	  case 3:
366 		return ABOVE;
367 	}
368 	/* NOTREACHED */
369 	return(-1);
370 }
371 
372 /*
373  * get_ident:
374  *	Get the score structure of a player
375  */
376 IDENT *
377 get_ident(machine, uid, name, team)
378 	u_long	machine;
379 	u_long	uid;
380 	char	*name;
381 	char	team;
382 {
383 	IDENT		*ip;
384 	static IDENT	punt;
385 
386 	for (ip = Scores; ip != NULL; ip = ip->i_next)
387 		if (ip->i_machine == machine
388 		&&  ip->i_uid == uid
389 		&&  ip->i_team == team
390 		&&  strncmp(ip->i_name, name, NAMELEN) == 0)
391 			break;
392 
393 	if (ip != NULL) {
394 		if (ip->i_entries < SCOREDECAY)
395 			ip->i_entries++;
396 		else
397 			ip->i_kills = (ip->i_kills * (SCOREDECAY - 1))
398 				/ SCOREDECAY;
399 		ip->i_score = ip->i_kills / (double) ip->i_entries;
400 	}
401 	else {
402 		ip = (IDENT *) malloc(sizeof (IDENT));
403 		if (ip == NULL) {
404 			/* Fourth down, time to punt */
405 			ip = &punt;
406 		}
407 		ip->i_machine = machine;
408 		ip->i_team = team;
409 		ip->i_uid = uid;
410 		strncpy(ip->i_name, name, NAMELEN);
411 		ip->i_kills = 0;
412 		ip->i_entries = 1;
413 		ip->i_score = 0;
414 		ip->i_absorbed = 0;
415 		ip->i_faced = 0;
416 		ip->i_shot = 0;
417 		ip->i_robbed = 0;
418 		ip->i_slime = 0;
419 		ip->i_missed = 0;
420 		ip->i_ducked = 0;
421 		ip->i_gkills = ip->i_bkills = ip->i_deaths = 0;
422 		ip->i_stillb = ip->i_saved = 0;
423 		ip->i_next = Scores;
424 		Scores = ip;
425 	}
426 
427 	return ip;
428 }
429