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