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 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 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 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 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 * 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