2 * Empire - A multi-player, client/server Internet based war game.
3 * Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
4 * Ken Stevens, Steve McClure
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * See files README, COPYING and CREDITS in the root of the source
23 * tree for related information and legal notices. It is expected
24 * that future projects/authors will amend these files as needed.
28 * play.c: Playing the game
30 * Known contributors to this file:
31 * Markus Armbruster, 2007
42 #include <sys/types.h>
50 #define EOF_COOKIE "ctld\n"
51 #define INTR_COOKIE "\naborted\n"
54 static volatile sig_atomic_t send_intr; /* need to send INTR_COOKIE */
57 * Receive and process server output from SOCK.
58 * Return number of characters received on success, -1 on error.
64 * Read a chunk of server output and feed its characters into a
65 * simple state machine.
66 * Initial state is SCANNING_ID.
67 * In state SCANNING_ID, buffer the character. If it's a space,
68 * decode the id that has been buffered, and enter state BUFFERING
69 * or COPYING depending on its value.
70 * In state BUFFERING, buffer the character. If it's newline,
71 * pass id and buffered text to servercmd(), then enter state
73 * In state COPYING, pass the character to outch(). If it's
74 * newline, enter state SCANNING_ID.
77 SCANNING_ID, BUFFERING, COPYING
78 } state = SCANNING_ID;
80 static struct lbuf lbuf;
86 n = read(sock, buf, sizeof(buf));
90 for (i = 0; i < n; i++) {
95 /* FIXME gripe unexpected! */
100 lbuf_putc(&lbuf, ch);
103 line = lbuf_line(&lbuf);
104 id = strtol(line, &end, 16);
105 if (end == line || *end) {
106 /* FIXME gripe bad id */
123 /* unknown or unexpected id, treat like C_DATA */
131 len = lbuf_putc(&lbuf, ch);
133 line = lbuf_line(&lbuf);
134 servercmd(id, line, len);
151 * Receive command input from FD into INBUF.
152 * Return 1 on receipt of input, zero on EOF, -1 on error.
155 recv_input(int fd, struct ring *inbuf)
157 static struct lbuf cmdbuf;
162 n = ring_from_file(inbuf, fd);
167 if (lbuf_len(&cmdbuf)) {
168 /* incomplete line */
169 ring_putc(inbuf, '\n');
173 * Can't put EOF cookie into INBUF here, it may not fit.
174 * Leave it to caller.
179 /* copy input to AUXFP etc. */
180 for (i = -n; i < 0; i++) {
181 ch = ring_peek(inbuf, i);
183 if (lbuf_putc(&cmdbuf, ch)) {
184 line = lbuf_line(&cmdbuf);
200 signal(SIGINT, intr);
206 * The session must be in the playing phase.
207 * Return 0 when the session ended, -1 on error.
213 * Player input flows from INPUT_FD through recv_input() into ring
214 * buffer INBUF, which drains into SOCK. This must not block.
215 * Server output flows from SOCK into recv_output(). Reading SOCK
219 struct ring inbuf; /* input buffer, draining to SOCK */
220 int eof_fd0; /* read fd 0 hit EOF? */
221 int send_eof; /* need to send EOF_COOKIE */
226 sa.sa_handler = intr;
227 sigaction(SIGINT, &sa, NULL);
228 sa.sa_handler = SIG_IGN;
229 sigaction(SIGPIPE, &sa, NULL);
232 eof_fd0 = send_eof = 0;
239 * Want to read player input only when we don't need to send
240 * cookies, and we haven't hit EOF on fd 0, and INBUF can
243 if (!send_intr && !send_eof && !eof_fd0 && ring_space(&inbuf))
244 FD_SET(input_fd, &rdfd);
245 /* Want to send player input only when we have something */
246 if (send_intr || send_eof || ring_len(&inbuf))
248 /* Always want to read server output */
251 n = select(MAX(input_fd, sock) + 1, &rdfd, &wrfd, NULL, NULL);
253 if (errno != EINTR) {
260 && ring_putm(&inbuf, EOF_COOKIE, sizeof(EOF_COOKIE) - 1) >= 0)
263 && ring_putm(&inbuf, INTR_COOKIE, sizeof(INTR_COOKIE) - 1) >= 0)
269 /* read player input */
270 if (FD_ISSET(input_fd, &rdfd)) {
271 n = recv_input(input_fd, &inbuf);
273 perror("read stdin"); /* FIXME stdin misleading, could be execing */
280 /* execute done, switch back to fd 0 */
283 /* stop reading input, drain socket ring buffers */
285 sa.sa_handler = SIG_DFL;
286 sigaction(SIGINT, &sa, NULL);
291 /* send it to the server */
292 if (FD_ISSET(sock, &wrfd)) {
293 n = ring_to_file(&inbuf, sock);
295 perror("write socket");
300 /* read server output and print it */
301 if (FD_ISSET(sock, &rdfd)) {
302 n = recv_output(sock);
304 perror("read socket");