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