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