1 /* 2 * Copyright (c) 1983-2003, Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * + Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * + Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * + Neither the name of the University of California, San Francisco nor 15 * the names of its contributors may be used to endorse or promote 16 * products derived from this software without specific prior written 17 * permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 * 31 * $OpenBSD: playit.c,v 1.8 2003/06/11 08:45:25 pjanzen Exp $ 32 * $NetBSD: playit.c,v 1.4 1997/10/20 00:37:15 lukem Exp $ 33 * $DragonFly: src/games/hunt/hunt/playit.c,v 1.2 2008/09/04 16:12:51 swildner Exp $ 34 */ 35 36 #include <sys/types.h> 37 #include <sys/file.h> 38 #include <arpa/inet.h> 39 40 #include <err.h> 41 #include <errno.h> 42 #include <ctype.h> 43 #include <termios.h> 44 #include <signal.h> 45 #include <string.h> 46 #include <unistd.h> 47 #include <stdio.h> 48 #include "hunt.h" 49 #include "display.h" 50 #include "client.h" 51 52 static int nchar_send; 53 static FLAG Last_player; 54 static int Otto_expect; 55 56 # define MAX_SEND 5 57 58 /* 59 * ibuf is the input buffer used for the stream from the driver. 60 * It is small because we do not check for user input when there 61 * are characters in the input buffer. 62 */ 63 static int icnt = 0; 64 static unsigned char ibuf[256], *iptr = ibuf; 65 66 #define GETCHR() (--icnt < 0 ? getchr() : *iptr++) 67 68 static unsigned char getchr(void); 69 static void send_stuff(void); 70 71 /* 72 * playit: 73 * Play a given game, handling all the curses commands from 74 * the driver. 75 */ 76 void 77 playit(void) 78 { 79 int ch; 80 int y, x; 81 u_int32_t version; 82 int otto_y, otto_x; 83 char otto_face = ' '; 84 int chars_processed; 85 86 if (read(Socket, &version, sizeof version) != sizeof version) { 87 bad_con(); 88 /* NOTREACHED */ 89 } 90 if (ntohl(version) != (unsigned int)HUNT_VERSION) { 91 bad_ver(); 92 /* NOTREACHED */ 93 } 94 errno = 0; 95 nchar_send = MAX_SEND; 96 Otto_expect = 0; 97 while ((ch = GETCHR()) != EOF) { 98 switch (ch & 0377) { 99 case MOVE: 100 y = GETCHR(); 101 x = GETCHR(); 102 display_move(y, x); 103 break; 104 105 case CLRTOEOL: 106 display_clear_eol(); 107 break; 108 case CLEAR: 109 display_clear_the_screen(); 110 break; 111 case REFRESH: 112 display_refresh(); 113 break; 114 case REDRAW: 115 display_redraw_screen(); 116 display_refresh(); 117 break; 118 case ENDWIN: 119 display_refresh(); 120 if ((ch = GETCHR()) == LAST_PLAYER) 121 Last_player = TRUE; 122 ch = EOF; 123 goto out; 124 case BELL: 125 display_beep(); 126 break; 127 case READY: 128 chars_processed = GETCHR(); 129 display_refresh(); 130 if (nchar_send < 0) 131 tcflush(STDIN_FILENO, TCIFLUSH); 132 nchar_send = MAX_SEND; 133 if (Otto_mode) { 134 /* 135 * The driver returns the number of keypresses 136 * that it has processed. Use this to figure 137 * out if otto's commands have completed. 138 */ 139 Otto_expect -= chars_processed; 140 if (Otto_expect == 0) { 141 /* not very fair! */ 142 static char buf[MAX_SEND * 2]; 143 int len; 144 145 /* Ask otto what it wants to do: */ 146 len = otto(otto_y, otto_x, otto_face, 147 buf, sizeof buf); 148 if (len) { 149 /* Pass it on to the driver: */ 150 write(Socket, buf, len); 151 /* Update expectations: */ 152 Otto_expect += len; 153 } 154 } 155 } 156 break; 157 case ADDCH: 158 ch = GETCHR(); 159 /* FALLTHROUGH */ 160 default: 161 if (!isprint(ch)) 162 ch = ' '; 163 display_put_ch(ch); 164 if (Otto_mode) 165 switch (ch) { 166 case '<': 167 case '>': 168 case '^': 169 case 'v': 170 otto_face = ch; 171 display_getyx(&otto_y, &otto_x); 172 otto_x--; 173 break; 174 } 175 break; 176 } 177 } 178 out: 179 (void) close(Socket); 180 } 181 182 /* 183 * getchr: 184 * Grab input and pass it along to the driver 185 * Return any characters from the driver 186 * When this routine is called by GETCHR, we already know there are 187 * no characters in the input buffer. 188 */ 189 static unsigned char 190 getchr(void) 191 { 192 fd_set readfds, s_readfds; 193 int nfds, s_nfds; 194 195 FD_ZERO(&s_readfds); 196 FD_SET(Socket, &s_readfds); 197 FD_SET(STDIN_FILENO, &s_readfds); 198 s_nfds = (Socket > STDIN_FILENO) ? Socket : STDIN_FILENO; 199 s_nfds++; 200 201 one_more_time: 202 do { 203 errno = 0; 204 readfds = s_readfds; 205 nfds = s_nfds; 206 nfds = select(nfds, &readfds, NULL, NULL, NULL); 207 } while (nfds <= 0 && errno == EINTR); 208 209 if (FD_ISSET(STDIN_FILENO, &readfds)) 210 send_stuff(); 211 if (!FD_ISSET(Socket, &readfds)) 212 goto one_more_time; 213 icnt = read(Socket, ibuf, sizeof ibuf); 214 if (icnt <= 0) { 215 bad_con(); 216 /* NOTREACHED */ 217 } 218 iptr = ibuf; 219 icnt--; 220 return *iptr++; 221 } 222 223 /* 224 * send_stuff: 225 * Send standard input characters to the driver 226 */ 227 static void 228 send_stuff(void) 229 { 230 int count; 231 char *sp, *nsp; 232 static char inp[BUFSIZ]; 233 static char Buf[BUFSIZ]; 234 235 /* Drain the user's keystrokes: */ 236 count = read(STDIN_FILENO, Buf, sizeof Buf); 237 if (count < 0) 238 err(1, "read"); 239 if (count == 0) 240 return; 241 242 if (nchar_send <= 0 && !no_beep) { 243 display_beep(); 244 return; 245 } 246 247 /* 248 * look for 'q'uit commands; if we find one, 249 * confirm it. If it is not confirmed, strip 250 * it out of the input 251 */ 252 Buf[count] = '\0'; 253 for (sp = Buf, nsp = inp; *sp != '\0'; sp++, nsp++) { 254 *nsp = map_key[(int)*sp]; 255 if (*nsp == 'q') 256 intr(0); 257 } 258 count = nsp - inp; 259 if (count) { 260 nchar_send -= count; 261 if (nchar_send < 0) 262 count += nchar_send; 263 (void) write(Socket, inp, count); 264 if (Otto_mode) { 265 /* 266 * The user can insert commands over otto. 267 * So, otto shouldn't be alarmed when the 268 * server processes more than otto asks for. 269 */ 270 Otto_expect += count; 271 } 272 } 273 } 274 275 /* 276 * quit: 277 * Handle the end of the game when the player dies 278 */ 279 int 280 quit(int old_status) 281 { 282 int explain, ch; 283 284 if (Last_player) 285 return Q_QUIT; 286 if (Otto_mode) 287 return otto_quit(old_status); 288 display_move(HEIGHT, 0); 289 display_put_str("Re-enter game [ynwo]? "); 290 display_clear_eol(); 291 explain = FALSE; 292 for (;;) { 293 display_refresh(); 294 if (isupper(ch = getchar())) 295 ch = tolower(ch); 296 if (ch == 'y') 297 return old_status; 298 else if (ch == 'o') 299 break; 300 else if (ch == 'n') { 301 display_move(HEIGHT, 0); 302 display_put_str("Write a parting message [yn]? "); 303 display_clear_eol(); 304 display_refresh(); 305 for (;;) { 306 if (isupper(ch = getchar())) 307 ch = tolower(ch); 308 if (ch == 'y') 309 goto get_message; 310 if (ch == 'n') 311 return Q_QUIT; 312 } 313 } 314 else if (ch == 'w') { 315 static char buf[WIDTH + WIDTH % 2]; 316 char *cp, c; 317 318 get_message: 319 c = ch; /* save how we got here */ 320 display_move(HEIGHT, 0); 321 display_put_str("Message: "); 322 display_clear_eol(); 323 display_refresh(); 324 cp = buf; 325 for (;;) { 326 display_refresh(); 327 if ((ch = getchar()) == '\n' || ch == '\r') 328 break; 329 if (display_iserasechar(ch)) 330 { 331 if (cp > buf) { 332 int y, x; 333 334 display_getyx(&y, &x); 335 display_move(y, x - 1); 336 cp -= 1; 337 display_clear_eol(); 338 } 339 continue; 340 } 341 else if (display_iskillchar(ch)) 342 { 343 int y, x; 344 345 display_getyx(&y, &x); 346 display_move(y, x - (cp - buf)); 347 cp = buf; 348 display_clear_eol(); 349 continue; 350 } else if (!isprint(ch)) { 351 display_beep(); 352 continue; 353 } 354 display_put_ch(ch); 355 *cp++ = ch; 356 if (cp + 1 >= buf + sizeof buf) 357 break; 358 } 359 *cp = '\0'; 360 Send_message = buf; 361 return (c == 'w') ? old_status : Q_MESSAGE; 362 } 363 display_beep(); 364 if (!explain) { 365 display_put_str("(Yes, No, Write message, or Options) "); 366 explain = TRUE; 367 } 368 } 369 370 display_move(HEIGHT, 0); 371 display_put_str("Scan, Cloak, Flying, or Quit? "); 372 display_clear_eol(); 373 display_refresh(); 374 explain = FALSE; 375 for (;;) { 376 if (isupper(ch = getchar())) 377 ch = tolower(ch); 378 if (ch == 's') 379 return Q_SCAN; 380 else if (ch == 'c') 381 return Q_CLOAK; 382 else if (ch == 'f') 383 return Q_FLY; 384 else if (ch == 'q') 385 return Q_QUIT; 386 display_beep(); 387 if (!explain) { 388 display_put_str("[SCFQ] "); 389 explain = TRUE; 390 } 391 display_refresh(); 392 } 393 } 394 395 /* 396 * do_message: 397 * Send a message to the driver and return 398 */ 399 void 400 do_message(void) 401 { 402 u_int32_t version; 403 404 if (read(Socket, &version, sizeof version) != sizeof version) { 405 bad_con(); 406 /* NOTREACHED */ 407 } 408 if (ntohl(version) != (unsigned int)HUNT_VERSION) { 409 bad_ver(); 410 /* NOTREACHED */ 411 } 412 if (write(Socket, Send_message, strlen(Send_message)) < 0) { 413 bad_con(); 414 /* NOTREACHED */ 415 } 416 (void) close(Socket); 417 } 418