/* * XPilot NG, a multiplayer space war game. * * Copyright (C) 1991-2001 by * * Bjørn Stabell * Ken Ronny Schouten * Bert Gijsbers * Dick Balaska * * Copyright (C) 2000-2004 Uoti Urpala * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "xpclient.h" #define TALK_RETRY 2 #define MAX_MAP_ACK_LEN 500 #define KEYBOARD_STORE 20 /* * Type definitions. */ typedef struct { long loops; sockbuf_t sbuf; } frame_buf_t; /* * Exported variables. */ setup_t *Setup = NULL; display_t server_display; int receive_window_size = 3; long last_loops; bool packetMeasurement; pointer_move_t pointer_moves[MAX_POINTER_MOVES]; int pointer_move_next; long last_keyboard_ack; bool dirPrediction; #ifdef _WINDOWS int received_self = FALSE; #endif /* * Local variables. */ static sockbuf_t rbuf, cbuf, wbuf; static frame_buf_t *Frames; static int (*receive_tbl[256])(void), (*reliable_tbl[256])(void); static int keyboard_delta; static unsigned magic; static time_t last_send_anything; static long last_keyboard_change, last_keyboard_update, reliable_offset, talk_pending, talk_sequence_num, talk_last_send; static long keyboard_change[KEYBOARD_STORE], keyboard_update[KEYBOARD_STORE], keyboard_acktime[KEYBOARD_STORE]; static char talk_str[MAX_CHARS]; /* * Initialize the function dispatch tables. * There are two tables. One for the semi-important unreliable * data like frame updates. * The other one is for the reliable data stream, which is * received as part of the unreliable data packets. */ static void Receive_init(void) { int i; for (i = 0; i < 256; i++) { receive_tbl[i] = NULL; reliable_tbl[i] = NULL; } receive_tbl[PKT_EYES] = Receive_eyes; receive_tbl[PKT_TIME_LEFT] = Receive_time_left; receive_tbl[PKT_AUDIO] = Receive_audio; receive_tbl[PKT_START] = Receive_start; receive_tbl[PKT_END] = Receive_end; receive_tbl[PKT_SELF] = Receive_self; receive_tbl[PKT_DAMAGED] = Receive_damaged; receive_tbl[PKT_CONNECTOR] = Receive_connector; receive_tbl[PKT_LASER] = Receive_laser; receive_tbl[PKT_REFUEL] = Receive_refuel; receive_tbl[PKT_SHIP] = Receive_ship; receive_tbl[PKT_ECM] = Receive_ecm; receive_tbl[PKT_TRANS] = Receive_trans; receive_tbl[PKT_PAUSED] = Receive_paused; receive_tbl[PKT_APPEARING] = Receive_appearing; receive_tbl[PKT_ITEM] = Receive_item; receive_tbl[PKT_MINE] = Receive_mine; receive_tbl[PKT_BALL] = Receive_ball; receive_tbl[PKT_MISSILE] = Receive_missile; receive_tbl[PKT_SHUTDOWN] = Receive_shutdown; receive_tbl[PKT_DESTRUCT] = Receive_destruct; receive_tbl[PKT_SELF_ITEMS] = Receive_self_items; receive_tbl[PKT_FUEL] = Receive_fuel; receive_tbl[PKT_CANNON] = Receive_cannon; receive_tbl[PKT_TARGET] = Receive_target; receive_tbl[PKT_RADAR] = Receive_radar; receive_tbl[PKT_FASTRADAR] = Receive_fastradar; receive_tbl[PKT_RELIABLE] = Receive_reliable; receive_tbl[PKT_QUIT] = Receive_quit; receive_tbl[PKT_MODIFIERS] = Receive_modifiers; receive_tbl[PKT_FASTSHOT] = Receive_fastshot; receive_tbl[PKT_THRUSTTIME] = Receive_thrusttime; receive_tbl[PKT_SHIELDTIME] = Receive_shieldtime; receive_tbl[PKT_PHASINGTIME]= Receive_phasingtime; receive_tbl[PKT_ROUNDDELAY] = Receive_rounddelay; receive_tbl[PKT_LOSEITEM] = Receive_loseitem; receive_tbl[PKT_WRECKAGE] = Receive_wreckage; receive_tbl[PKT_ASTEROID] = Receive_asteroid; receive_tbl[PKT_WORMHOLE] = Receive_wormhole; receive_tbl[PKT_POLYSTYLE] = Receive_polystyle; for (i = 0; i < DEBRIS_TYPES; i++) receive_tbl[PKT_DEBRIS + i] = Receive_debris; reliable_tbl[PKT_MOTD] = Receive_motd; reliable_tbl[PKT_MESSAGE] = Receive_message; reliable_tbl[PKT_TEAM_SCORE] = Receive_team_score; reliable_tbl[PKT_PLAYER] = Receive_player; reliable_tbl[PKT_TEAM] = Receive_team; reliable_tbl[PKT_SCORE] = Receive_score; reliable_tbl[PKT_TIMING] = Receive_timing; reliable_tbl[PKT_LEAVE] = Receive_leave; reliable_tbl[PKT_WAR] = Receive_war; reliable_tbl[PKT_SEEK] = Receive_seek; reliable_tbl[PKT_BASE] = Receive_base; reliable_tbl[PKT_QUIT] = Receive_quit; reliable_tbl[PKT_STRING] = Receive_string; reliable_tbl[PKT_SCORE_OBJECT] = Receive_score_object; reliable_tbl[PKT_TALK_ACK] = Receive_talk_ack; } /* * Uncompress the map which is compressed using a simple * Run-Length-Encoding algorithm. * The map object type is encoded in the lower seven bits * of a byte. * If the high bit of a byte is set then the next byte * means the number of contiguous map data bytes that * have the same type. Otherwise only one map byte * has this type. * Because we uncompress the map backwards to save on * memory usage there is some complexity involved. */ static int Uncompress_map(void) { u_byte *cmp, /* compressed map pointer */ *ump, /* uncompressed map pointer */ *p; /* temporary search pointer */ int i, count; if (Setup->map_order != SETUP_MAP_ORDER_XY) { warn("Unknown map ordering in setup (%d)", Setup->map_order); return -1; } /* Point to last compressed map byte */ cmp = Setup->map_data + Setup->map_data_len - 1; /* Point to last uncompressed map byte */ ump = Setup->map_data + Setup->x * Setup->y - 1; while (cmp >= Setup->map_data) { for (p = cmp; p > Setup->map_data; p--) { if ((p[-1] & SETUP_COMPRESSED) == 0) break; } if (p == cmp) { *ump-- = *cmp--; continue; } if ((cmp - p) % 2 == 0) *ump-- = *cmp--; while (p < cmp) { count = *cmp--; if (count < 2) { warn("Map compress count error %d", count); return -1; } *cmp &= ~SETUP_COMPRESSED; for (i = 0; i < count; i++) *ump-- = *cmp; cmp--; if (ump < cmp) { warn("Map uncompression error (%d,%d)", cmp - Setup->map_data, ump - Setup->map_data); return -1; } } } if (ump != cmp) { warn("map uncompress error (%d,%d)", cmp - Setup->map_data, ump - Setup->map_data); return -1; } Setup->map_order = SETUP_MAP_UNCOMPRESSED; return 0; } /* * Receive the map data and some game parameters from * the server. The map data may be in compressed form. */ int Net_setup(void) { int n, len, done = 0, retries; size_t size; long todo = sizeof(setup_t); char *ptr; if ((Setup = (setup_t *) malloc(sizeof(setup_t))) == NULL) { error("No memory for setup data"); return -1; } ptr = (char *) Setup; while (todo > 0) { if (cbuf.ptr != cbuf.buf) Sockbuf_advance(&cbuf, cbuf.ptr - cbuf.buf); len = cbuf.len; if (len > todo) len = todo; if (len > 0) { if (done == 0) { if (oldServer) { n = Packet_scanf(&cbuf, "%ld" "%ld%hd" "%hd%hd" "%hd%hd" "%s%s", &Setup->map_data_len, &Setup->mode, &Setup->lives, &Setup->x, &Setup->y, &Setup->frames_per_second, &Setup->map_order, Setup->name, Setup->author); Setup->width = Setup->x * BLOCK_SZ; Setup->height = Setup->y * BLOCK_SZ; } else { n = Packet_scanf(&cbuf, "%ld" "%ld%hd" "%hd%hd" "%hd%s" "%s%S", &Setup->map_data_len, &Setup->mode, &Setup->lives, &Setup->width, &Setup->height, &Setup->frames_per_second, Setup->name, Setup->author, Setup->data_url); } if (n <= 0) { warn("Can't read setup info from reliable data buffer"); return -1; } /* * Do some consistency checks on the server setup structure. */ if (Setup->map_data_len <= 0 || Setup->width <= 0 || Setup->height <= 0 || (oldServer && Setup->map_data_len > Setup->x * Setup->y)) { warn("Got bad map specs from server (%d,%d,%d)", Setup->map_data_len, Setup->width, Setup->height); return -1; } if (oldServer && Setup->map_order != SETUP_MAP_ORDER_XY && Setup->map_order != SETUP_MAP_UNCOMPRESSED) { warn("Unknown map order type (%d)", Setup->map_order); return -1; } size = sizeof(setup_t) + Setup->map_data_len; if (oldServer) size = sizeof(setup_t) + Setup->x * Setup->y; if ((Setup = (setup_t *) realloc(ptr, size)) == NULL) { error("No memory for setup and map"); return -1; } ptr = (char *) Setup; done = (char *) &Setup->map_data[0] - ptr; todo = Setup->map_data_len; } else { assert(len > 0); memcpy(&ptr[done], cbuf.ptr, (size_t)len); Sockbuf_advance(&cbuf, len + cbuf.ptr - cbuf.buf); done += len; todo -= len; } } if (todo > 0) { if (rbuf.ptr != rbuf.buf) Sockbuf_advance(&rbuf, rbuf.ptr - rbuf.buf); if (rbuf.len > 0) { if (rbuf.ptr[0] != PKT_RELIABLE) { if (rbuf.ptr[0] == PKT_QUIT) { warn("Server closed connection"); return -1; } else { warn("Not a reliable packet (%d) in setup", rbuf.ptr[0]); return -1; } } if (Receive_reliable() == -1) return -1; if (Sockbuf_flush(&wbuf) == -1) return -1; } if (cbuf.ptr != cbuf.buf) Sockbuf_advance(&cbuf, cbuf.ptr - cbuf.buf); if (cbuf.len > 0) continue; for (retries = 0;; retries++) { if (retries >= 10) { warn("Can't read setup after %d retries " "(todo=%d, left=%d)", retries, todo, cbuf.len - (cbuf.ptr - cbuf.buf)); return -1; } sock_set_timeout(&rbuf.sock, 2, 0); while (sock_readable(&rbuf.sock) > 0) { Sockbuf_clear(&rbuf); if (Sockbuf_read(&rbuf) == -1) { error("Can't read all setup data"); return -1; } if (rbuf.len > 0) break; sock_set_timeout(&rbuf.sock, 0, 0); } if (rbuf.len > 0) break; } } } if (oldServer && Setup->map_order != SETUP_MAP_UNCOMPRESSED) { if (Uncompress_map() == -1) return -1; } return 0; } /* * Send the first packet to the server with our name, * nick and display contained in it. * The server uses this data to verify that the packet * is from the right UDP connection, it already has * this info from the ENTER_GAME_pack. */ #define MAX_VERIFY_RETRIES 5 int Net_verify(char *user_name, char *nick_name, char *disp) { int n, type, result, retries; time_t last; for (retries = 0;;) { if (retries == 0 || time(NULL) - last >= 3) { if (retries++ >= MAX_VERIFY_RETRIES) { warn("Can't connect to server after %d retries", retries); return -1; } Sockbuf_clear(&wbuf); /* IFWINDOWS( Trace("Verifying to sock=%d\n", wbuf.sock) ); */ n = Packet_printf(&wbuf, "%c%s%s%s", PKT_VERIFY, user_name, nick_name, disp); if (n <= 0 || Sockbuf_flush(&wbuf) <= 0) { error("Can't send verify packet"); return -1; } time(&last); if (retries > 1) { printf("Waiting for verify response\n"); IFWINDOWS( Progress("Waiting for verify response") ); } } sock_set_timeout(&rbuf.sock, 1, 0); if (sock_readable(&rbuf.sock) == 0) continue; Sockbuf_clear(&rbuf); if (Sockbuf_read(&rbuf) == -1) { error("Can't read verify reply packet"); return -1; } if (rbuf.len <= 0) continue; if (rbuf.ptr[0] != PKT_RELIABLE) { if (rbuf.ptr[0] == PKT_QUIT) { warn("Server closed connection"); return -1; } else { warn("Bad packet type when verifying (%d)", rbuf.ptr[0]); return -1; } } if (Receive_reliable() == -1) return -1; if (Sockbuf_flush(&wbuf) == -1) return -1; if (cbuf.len == 0) continue; if (Receive_reply(&type, &result) <= 0) { warn("Can't receive verify reply packet"); return -1; } if (type != PKT_VERIFY) { warn("Verify wrong reply type (%d)", type); return -1; } if (result != PKT_SUCCESS) { warn("Verification failed (%d)", result); return -1; } if (Receive_magic() <= 0) { error("Can't receive magic packet after verify"); return -1; } break; } if (retries > 1) { printf("Verified correctly\n"); IFWINDOWS( Progress("Verified correctly") ); } return 0; } /* * Open the datagram socket and allocate the network data * structures like buffers. * Currently there are three different buffers used: * 1) wbuf is used only for sending packets (write/printf). * 2) rbuf is used for receiving packets in (read/scanf). * 3) cbuf is used to copy the reliable data stream * into from the raw and unreliable rbuf packets. */ int Net_init(char *server, int port) { int i; size_t size; sock_t sock; assert(server != NULL); #ifndef _WINDOWS signal(SIGPIPE, SIG_IGN); #endif server_display.view_width = 0; server_display.view_height = 0; server_display.spark_rand = 0; server_display.num_spark_colors = 0; Receive_init(); if (!clientPortStart || !clientPortEnd || (clientPortStart > clientPortEnd)) { if (sock_open_udp(&sock, NULL, 0) == SOCK_IS_ERROR) { error("Cannot create datagram socket (%d)", sock.error.error); return -1; } } else { int found_socket = 0; for (i = clientPortStart; i <= clientPortEnd; i++) { if (sock_open_udp(&sock, NULL, i) != SOCK_IS_ERROR) { found_socket = 1; break; } } if (found_socket == 0) { error("Could not find a usable port in given port range"); return -1; } } if (sock_connect(&sock, server, port) == -1) { error("Can't connect to server %s on port %d", server, port); sock_close(&sock); return -1; } wbuf.sock = sock; if (sock_set_non_blocking(&sock, 1) == -1) { error("Can't make socket non-blocking"); return -1; } if (sock_set_send_buffer_size(&sock, CLIENT_SEND_SIZE + 256) == -1) error("Can't set send buffer size to %d", CLIENT_SEND_SIZE + 256); if (sock_set_receive_buffer_size(&sock, CLIENT_RECV_SIZE + 256) == -1) error("Can't set receive buffer size to %d", CLIENT_RECV_SIZE + 256); size = receive_window_size * sizeof(frame_buf_t); if ((Frames = (frame_buf_t *) malloc(size)) == NULL) { error("No memory (%u)", size); return -1; } for (i = 0; i < receive_window_size; i++) { Frames[i].loops = 0; if (Sockbuf_init(&Frames[i].sbuf, &sock, CLIENT_RECV_SIZE, SOCKBUF_READ | SOCKBUF_DGRAM) == -1) { error("No memory for read buffer (%u)", CLIENT_RECV_SIZE); return -1; } } /* reliable data buffer, not a valid socket filedescriptor needed */ if (Sockbuf_init(&cbuf, NULL, CLIENT_RECV_SIZE, SOCKBUF_WRITE | SOCKBUF_READ | SOCKBUF_LOCK) == -1) { error("No memory for control buffer (%u)", CLIENT_RECV_SIZE); return -1; } /* write buffer */ if (Sockbuf_init(&wbuf, &sock, CLIENT_SEND_SIZE, SOCKBUF_WRITE | SOCKBUF_DGRAM) == -1) { error("No memory for write buffer (%u)", CLIENT_SEND_SIZE); return -1; } /* read buffer */ rbuf = Frames[0].sbuf; /* reliable data byte stream offset */ reliable_offset = 0; /* reset talk status */ talk_sequence_num = 0; talk_pending = 0; return 0; } /* * Cleanup all the network buffers and close the datagram socket. * Also try to send the server a quit packet if possible. * Because this quit packet may get lost we send one at the * beginning and one at the end. */ void Net_cleanup(void) { int i; sock_t sock = wbuf.sock; char ch; if (sock.fd > 2) { ch = PKT_QUIT; if (sock_write(&sock, &ch, 1) != 1) { sock_get_error(&sock); sock_write(&sock, &ch, 1); } micro_delay((unsigned)50*1000); } if (Frames != NULL) { for (i = 0; i < receive_window_size; i++) { if (Frames[i].sbuf.buf != NULL) Sockbuf_cleanup(&Frames[i].sbuf); else break; } XFREE(Frames); } Sockbuf_cleanup(&cbuf); Sockbuf_cleanup(&wbuf); XFREE(Setup); if (sock.fd > 2) { ch = PKT_QUIT; if (sock_write(&sock, &ch, 1) != 1) { sock_get_error(&sock); sock_write(&sock, &ch, 1); } micro_delay((unsigned)50*1000); if (sock_write(&sock, &ch, 1) != 1) { sock_get_error(&sock); sock_write(&sock, &ch, 1); } sock_close(&sock); } sock_cleanup(); } /* * Calculate a new 'keyboard-changed-id' which the server has to ack. */ void Net_key_change(void) { last_keyboard_change++; Key_update(); } /* * Flush the network output buffer if it has some data in it. * Called by the main loop before blocking on a select(2) call. */ int Net_flush(void) { if (wbuf.len == 0) { wbuf.ptr = wbuf.buf; return 0; } if (last_keyboard_ack != last_keyboard_change && last_keyboard_update != last_loops) /* * Since 3.2.10: just call Key_update to add our keyboard vector. * Key_update will call Send_keyboard to flush our buffer. */ return Key_update(); Send_talk(); if (Sockbuf_flush(&wbuf) == -1) return -1; Sockbuf_clear(&wbuf); last_send_anything = time(NULL); return 1; } /* * Return the socket filedescriptor for use in a select(2) call. */ int Net_fd(void) { return rbuf.sock.fd; } /* * Try to send a 'start play' packet to the server and get an * acknowledgement from the server. This is called after * we have initialized all our other stuff like the user interface * and we also have the map already. */ int Net_start(void) { int retries, type, result; time_t last; for (retries = 0;;) { if (retries == 0 || (time(NULL) - last) > 1) { if (retries++ >= 10) { warn("Can't start play after %d retries", retries); return -1; } Sockbuf_clear(&wbuf); /* * Some networks have trouble receiving big packets. * Therefore we don't transmit the shipshape when * we have had 5 unsuccesful attempts. */ if ((retries < 5 && Send_shape(shipShape) == -1) || Packet_printf(&wbuf, "%c", PKT_PLAY) <= 0 || Client_power() == -1 #ifdef SOUND || Send_audio_request(1) == -1 #endif || Client_fps_request() == -1 || Sockbuf_flush(&wbuf) == -1) { error("Can't send start play packet"); return -1; } time(&last); } if (cbuf.ptr > cbuf.buf) Sockbuf_advance(&cbuf, cbuf.ptr - cbuf.buf); sock_set_timeout(&rbuf.sock, 2, 0); while (cbuf.len <= 0 && sock_readable(&rbuf.sock) != 0) { Sockbuf_clear(&rbuf); if (Sockbuf_read(&rbuf) == -1) { error("Error reading play reply"); return -1; } if (rbuf.len <= 0) continue; if (rbuf.ptr[0] != PKT_RELIABLE) { if (rbuf.ptr[0] == PKT_QUIT) { warn("Server closed connection"); return -1; } else if (rbuf.ptr[0] == PKT_START) { /* * Packet out of order, drop it or... * Skip the frame and check for a Reliable Data Packet. * (HACK) * In a future version we may not want a reply to * the PKT_PLAY request and accept frames immediately. */ while (++rbuf.ptr < rbuf.buf + rbuf.len) { if (rbuf.ptr[0] == PKT_END && rbuf.ptr + 5 < rbuf.buf + rbuf.len && rbuf.ptr[5] == PKT_RELIABLE) { rbuf.ptr += 5; break; } } if (rbuf.ptr + 11 >= rbuf.buf + rbuf.len) { printf("skipping unexpected frame while starting\n"); continue; } printf("abusing unexpected frame while starting\n"); } else { printf("strange packet type while starting (%d)\n", rbuf.ptr[0]); /* * What the hack do we care when we wanna play. * Just drop the packet for now. */ Sockbuf_clear(&rbuf); continue; } } if (Receive_reliable() == -1) return -1; if (Sockbuf_flush(&wbuf) == -1) return -1; } if (cbuf.ptr - cbuf.buf >= cbuf.len) continue; if (cbuf.ptr[0] != PKT_REPLY) { warn("Not a reply packet after play (%d,%d,%d)", cbuf.ptr[0], cbuf.ptr - cbuf.buf, cbuf.len); return -1; } if (Receive_reply(&type, &result) <= 0) { warn("Can't receive reply packet after play"); return -1; } if (type != PKT_PLAY) { warn("Can't receive reply packet after play"); return -1; } if (result != PKT_SUCCESS) { warn("Start play not allowed (%d)", result); return -1; } break; } packet_measure = NULL; packetMeasurement = true; Net_init_measurement(); Net_init_lag_measurement(); errno = 0; return 0; } void Net_init_measurement(void) { packet_loss = 0; packet_drop = 0; packet_loop = 0; if (packetMeasurement) { if (packet_measure == NULL) { /* * Server FPS can change so we had better allocate enough. */ if ((packet_measure = XMALLOC(char, MAX_SUPPORTED_FPS)) == NULL) { error("No memory for packet measurement"); packetMeasurement = false; } else memset(packet_measure, PACKET_DRAW, MAX_SUPPORTED_FPS); } } else XFREE(packet_measure); } void Net_init_lag_measurement(void) { int i; packet_lag = 0; keyboard_delta = 0; for (i = 0; i < KEYBOARD_STORE; i++) { keyboard_change[i] = -1; keyboard_update[i] = -1; keyboard_acktime[i] = -1; } } /* * Process a packet which most likely is a frame update, * perhaps with some reliable data in it. */ static int Net_packet(void) { int type, prev_type = 0, result, replyto, status; while (rbuf.buf + rbuf.len > rbuf.ptr) { type = (*rbuf.ptr & 0xFF); if (receive_tbl[type] == NULL) { warn("Received unknown packet type (%d, %d), " "dropping frame.", type, prev_type); Sockbuf_clear(&rbuf); break; } else if ((result = (*receive_tbl[type])()) <= 0) { if (result == -1) { if (type != PKT_QUIT) warn("Processing packet type (%d, %d) failed", type, prev_type); return -1; } /* Drop rest of incomplete packet */ Sockbuf_clear(&rbuf); break; } prev_type = type; } while (cbuf.buf + cbuf.len > cbuf.ptr) { type = (*cbuf.ptr & 0xFF); if (type == PKT_REPLY) { if ((result = Receive_reply(&replyto, &status)) <= 0) { if (result == 0) break; return -1; } /* should do something more appropriate than this with the reply */ warn("Got reply packet (%d,%d)", replyto, status); } else if (reliable_tbl[type] == NULL) { int i; warn("Received unknown reliable data packet type (%d,%d,%d)", type, cbuf.ptr - cbuf.buf, cbuf.len); printf("\tdumping buffer for debugging:\n"); for (i = 0; i < cbuf.len; i++) { printf("%3d", cbuf.buf[i] & 0xFF); if (i % 20 == 0) printf("\n"); else printf(" "); } printf("\n"); return -1; } else if ((result = (*reliable_tbl[type])()) <= 0) { if (result == 0) break; return -1; } } return 0; } static void Net_keyboard_track(void) { int i, ind = -1; long maxtime = 0; /* look for a match for the keyboard change */ for (i = 0; i < KEYBOARD_STORE; i++) { if (keyboard_change[i] == last_keyboard_change) return; } /* look for a keyboard_change of -1 or an acknowledged change */ if (ind == -1) { for (i = 0; i < KEYBOARD_STORE; i++) { if (keyboard_change[i] == -1 || keyboard_acktime[i] != -1) { ind = i; break; } } } /* next, look for the oldest update */ if (ind == -1) { for (i = 0; i < KEYBOARD_STORE; i++) { if (keyboard_update[i] > maxtime) { ind = i; maxtime = keyboard_update[i]; } } } if (ind == -1) return; keyboard_change[ind] = last_keyboard_change; keyboard_update[ind] = last_keyboard_update; keyboard_acktime[ind] = -1; #if 0 printf("T;%d;%ld;%ld ", ind, last_keyboard_change, last_keyboard_update); #endif } /* * Do some (simple) packet loss/drop measurement * the results of which can be drawn on the display. * This is mainly for debugging and analysis. */ static void Net_measurement(long loop, int status) { int i; long delta; if (packet_measure == NULL) return; if ((delta = loop - packet_loop) < 0) { /* * Duplicate or out of order. */ return; } if (delta >= FPS) { if (packet_loop == 0) { packet_loop = loop - (loop % FPS); return; } packet_loop = loop - (loop % FPS); packet_loss = 0; packet_drop = 0; for (i = 0; i < FPS; i++) { switch (packet_measure[i]) { case PACKET_LOSS: packet_loss++; continue; case PACKET_DROP: packet_drop++; break; default: /* no drop or loss */ break; } packet_measure[i] = PACKET_LOSS; } delta = loop - packet_loop; } if (packet_measure[(int)delta] != PACKET_DRAW) packet_measure[(int)delta] = status; } /* Do some lag measurement the results of which can * be drawn on the display. This is mainly for * debugging and analysis. */ static void Net_lag_measurement(long key_ack) { int i, num; long sum; for (i = 0; i < KEYBOARD_STORE; i++) { if (keyboard_change[i] == key_ack && keyboard_acktime[i] == -1) { keyboard_acktime[i] =last_loops - 1; #if 0 printf("A;%d;%ld;%ld ", i, keyboard_change[i], keyboard_acktime[i]); #endif break; } } #if 0 if (i == KEYBOARD_STORE) printf("N "); #endif num = 0; sum = 0; for (i = 0; i < KEYBOARD_STORE; i++) { if (keyboard_acktime[i] != -1L) { num++; sum += keyboard_acktime[i] - keyboard_update[i]; } } if (num != 0) packet_lag = (int) (sum / num); } /* * Read a packet into one of the input buffers. * If it is a frame update then we check to see * if it is an old or duplicate one. If it isn't * a new frame then the packet is discarded and * we retry to read a packet once more. * It's a non-blocking read. */ static int Net_read(frame_buf_t *frame) { int n; long loop; u_byte ch; frame->loops = 0; for (;;) { Sockbuf_clear(&frame->sbuf); if (Sockbuf_read(&frame->sbuf) == -1) { error("Net input error"); return -1; } if (frame->sbuf.len <= 0) { Sockbuf_clear(&frame->sbuf); return 0; } /*IFWINDOWS( Trace("Net_read: read %d bytes type=%d\n", frame->sbuf.len, frame->sbuf.ptr[0]) ); */ if (frame->sbuf.ptr[0] != PKT_START) /* * Don't know which type of packet this is * and if it contains a frame at all (not likely). * It could be a quit packet. */ return 1; /* Peek at the frame loop number. */ n = Packet_scanf(&frame->sbuf, "%c%ld", &ch, &loop); /*IFWINDOWS( Trace("Net_read: frame # %d\n", loop) );*/ frame->sbuf.ptr = frame->sbuf.buf; if (n <= 0) { if (n == -1) { Sockbuf_clear(&frame->sbuf); return -1; } continue; } else if (loop > last_loops) { frame->loops = loop; return 2; } else { /* * Packet out of order. Drop it. * We may have already drawn it if it is duplicate. * Perhaps we should try to extract any reliable data * from it before dropping it. */ } } /*IFWINDOWS( Trace("Net_read: wbuf->len=%d\n", wbuf.len) );*/ } /* * Read frames from the net until there are no more available. * If the server has floaded us with frame updates then we should * discard everything except the most recent ones. The X server * may be too slow to keep up with the rate of the XPilot server * or there may have been a network hickup if the net is overloaded. */ int Net_input(void) { int i, j, n; int num_buffered_packets; frame_buf_t *frame, *last_frame, *oldest_frame = &Frames[0], tmpframe; time_t time_now; for (i = 0; i < receive_window_size; i++) { frame = &Frames[i]; if (!frame) continue; if (frame->loops != 0) { /* * Already contains a frame. */ if (frame->loops < oldest_frame->loops || oldest_frame->loops == 0) oldest_frame = frame; } else if (frame->sbuf.len > 0 && frame->sbuf.ptr == frame->sbuf.buf) { /* * Contains an unidentifiable packet. * No more input until this one is processed. */ break; } else { /* * Empty buffer. Read a frame. */ if ((n = Net_read(frame)) <= 0) { if (n == 0) { /* No more new packets available. */ if (i == 0) /* No frames to be processed. */ return 0; break; } else return n; } else if (n == 1) { /* * Contains an unidentifiable packet. * No more input until this one is processed. */ break; } else { /* * Check for duplicate packets. */ for (j = i - 1; j >= 0; j--) { if (frame->loops == Frames[j].loops) break; } if (j >= 0) { /* * Duplicate. Drop it. */ Net_measurement(frame->loops, PACKET_DROP); Sockbuf_clear(&frame->sbuf); frame->loops = 0; i--; /* correct for for loop increment. */ continue; } if (frame->loops < oldest_frame->loops) oldest_frame = frame; } } if ((i == receive_window_size - 1 && i > 0) #if 0 || drawPending || (ThreadedDraw && !WaitForSingleObject(dinfo.eventNotDrawing, 0) == WAIT_OBJECT_0) #endif ) { /* * Drop oldest packet. */ if (oldest_frame->loops < frame->loops) { /* * Switch buffers to prevent gaps. */ tmpframe = *oldest_frame; *oldest_frame = *frame; *frame = tmpframe; } Net_measurement(frame->loops, PACKET_DROP); Sockbuf_clear(&frame->sbuf); frame->loops = 0; oldest_frame = &Frames[0]; /* * Reset loop index. */ i = -1; /* correct for for loop increment. */ continue; } } /* * Find oldest packet. */ last_frame = oldest_frame = &Frames[0]; num_buffered_packets = 1; /* Could be 0, but returns before using this */ for (i = 1; i < receive_window_size; i++, last_frame++) { frame = &Frames[i]; if (frame->loops == 0) { if (frame->sbuf.len > 0) { /* * This is an unidentifiable packet. * Process it last, because it arrived last. */ num_buffered_packets++; continue; } else /* * Empty. The rest should be empty too, * because we have taken care not to have gaps. */ break; } else { num_buffered_packets++; if (frame->loops < oldest_frame->loops || oldest_frame->loops == 0) oldest_frame = frame; } } if (oldest_frame->sbuf.len <= 0) { /* * Couldn't find a non-empty packet. */ if (oldest_frame->loops > 0) { warn("bug %s,%d", __FILE__, __LINE__); oldest_frame->loops = 0; } return 0; } /* * Let the packet processing routines know which * packet they should process. */ rbuf = oldest_frame->sbuf; /* * Process the packet. */ n = Net_packet(); if (last_frame != oldest_frame) { /* * Switch buffers to prevent gaps. */ tmpframe = *oldest_frame; *oldest_frame = *last_frame; *last_frame = tmpframe; } Sockbuf_clear(&last_frame->sbuf); last_frame->loops = 0; rbuf = last_frame->sbuf; if (n == -1) return -1; /* * If the server hasn't yet acked our last keyboard change * and we haven't updated it in the (previous) current time frame * or if we haven't sent anything for a while (keepalive) * then we send our current keyboard state. */ time_now = time(NULL); if ((last_keyboard_ack != last_keyboard_change && last_keyboard_update /*+ 1*/ < last_loops) || time_now - last_send_anything > 5) { Key_update(); last_send_anything = time_now; } else /* * 4.5.4a2: flush if non-empty * This should help speedup the map update speed * for maps with large number of targets or cannons. */ Net_flush(); return num_buffered_packets; } /* * Receive the beginning of a new frame update packet, * which contains the loops number. */ int Receive_start(void) { int n; long loops_num; u_byte ch; long key_ack; if ((n = Packet_scanf(&rbuf, "%c%ld%ld", &ch, &loops_num, &key_ack)) <= 0) return n; if (last_loops >= loops_num) { /* * Packet is duplicate or out of order. */ Net_measurement(loops_num, PACKET_DROP); printf("ignoring frame (%ld)\n", last_loops - loops_num); return 0; } last_loops = loops_num; if (key_ack > last_keyboard_ack) { if (key_ack > last_keyboard_change) { printf("Premature keyboard ack by server (%ld,%ld,%ld)\n", last_keyboard_change, last_keyboard_ack, key_ack); /* * Packet could be corrupt??? * Some blokes turn off checksumming. */ return 0; } else last_keyboard_ack = key_ack; } Net_lag_measurement(key_ack); if ((n = Handle_start(loops_num)) == -1) return -1; return 1; } /* * Receive the end of a new frame update packet, * which should contain the same loops number * as the frame head. If this terminating packet * is missing then the packet is corrupt or incomplete. */ int Receive_end(void) { int n; long loops_num; u_byte ch; if ((n = Packet_scanf(&rbuf, "%c%ld", &ch, &loops_num)) <= 0) return n; Net_measurement(loops_num, PACKET_DRAW); if ((n = Handle_end(loops_num)) == -1) return -1; return 1; } /* * Receive a message string. This currently is rather * inefficiently encoded as an ascii string. */ int Receive_message(void) { int n; u_byte ch; char msg[MSG_LEN]; if ((n = Packet_scanf(&cbuf, "%c%S", &ch, msg)) <= 0) return n; if ((n = Handle_message(msg)) == -1) return -1; return 1; } /* * Receive the remaining playing time. */ int Receive_time_left(void) { int n; u_byte ch; long sec; if ((n = Packet_scanf(&rbuf, "%c%ld", &ch, &sec)) <= 0) return n; if ((n = Handle_time_left(sec)) == -1) return -1; return 1; } /* * Receive the id of the player we get frame updates for (game over mode). */ int Receive_eyes(void) { int n, id; u_byte ch; if ((n = Packet_scanf(&rbuf, "%c%hd", &ch, &id)) <= 0) return n; if ((n = Handle_eyes(id)) == -1) return -1; return 1; } /* * Receive the server MOTD. */ int Receive_motd(void) { u_byte ch; long off, size; short len; int n; char *cbuf_ptr = cbuf.ptr; if ((n = Packet_scanf(&cbuf, "%c%ld%hd%ld", &ch, &off, &len, &size)) <= 0) return n; if (cbuf.ptr + len > &cbuf.buf[cbuf.len]) { cbuf.ptr = cbuf_ptr; return 0; } Handle_motd(off, cbuf.ptr, (int)len, size); cbuf.ptr += len; return 1; } /* * Ask the server to send us the server MOTD. */ int Net_ask_for_motd(long offset, long maxlen) { if (offset < 0 || maxlen <= 0) { warn("Bad motd request (%ld, %ld)", offset, maxlen); return -1; } if (Packet_printf(&wbuf, "%c%ld%ld", PKT_MOTD, offset, maxlen) <= 0) { warn("Can't ask motd"); return -1; } return 0; } /* * Receive the packet with counts for all the items. * New since pack version 4203. */ int Receive_self_items(void) { unsigned mask; int i, n; u_byte ch; char *rbuf_ptr_start = rbuf.ptr; u_byte num_items[NUM_ITEMS]; n = Packet_scanf(&rbuf, "%c%u", &ch, &mask); if (n <= 0) return n; memset(num_items, 0, sizeof num_items); for (i = 0; mask != 0; i++) { if (mask & (1 << i)) { mask ^= (1 << i); if (rbuf.ptr - rbuf.buf < rbuf.len) { if (i < NUM_ITEMS) num_items[i] = *rbuf.ptr++; else rbuf.ptr++; } } } Handle_self_items(num_items); return (rbuf.ptr - rbuf_ptr_start); } /* * Receive the packet with all player information for the HUD. * If this packet is missing from the frame update then the player * isn't actively playing, which means he's either damaged, dead, * paused or has game over. */ int Receive_self(void) { int n; short x, y, vx, vy, lockId, lockDist, sFuelSum, sFuelMax, sViewWidth, sViewHeight; u_byte ch, sNumSparkColors, sHeading, sPower, sTurnSpeed, sTurnResistance, sNextCheckPoint, lockDir, sAutopilotLight, currentTank, sStat; u_byte num_items[NUM_ITEMS]; n = Packet_scanf(&rbuf, "%c" "%hd%hd%hd%hd%c" "%c%c%c" "%hd%hd%c%c", &ch, &x, &y, &vx, &vy, &sHeading, &sPower, &sTurnSpeed, &sTurnResistance, &lockId, &lockDist, &lockDir, &sNextCheckPoint); if (n <= 0) return n; memset(num_items, 0, sizeof num_items); n = Packet_scanf(&rbuf, "%c%hd%hd" "%hd%hd%c" "%c%c", ¤tTank, &sFuelSum, &sFuelMax, &sViewWidth, &sViewHeight, &sNumSparkColors, &sStat, &sAutopilotLight ); if (n <= 0) return n; /* * These assignments are done here because the server_display * structure members are not of the type that Packet_scanf() * expects, which breaks things on big endian architectures. */ server_display.view_width = sViewWidth; server_display.view_height = sViewHeight; LIMIT(server_display.view_width, MIN_VIEW_SIZE, MAX_VIEW_SIZE); if (sViewWidth != server_display.view_width) warn("unsupported view width from server"); LIMIT(server_display.view_height, MIN_VIEW_SIZE, MAX_VIEW_SIZE); if (sViewHeight != server_display.view_height) warn("unsupported view height from server"); server_display.num_spark_colors = sNumSparkColors; Handle_self(x, y, vx, vy, sHeading, (double) sPower, (double) sTurnSpeed, (double) sTurnResistance / 255.0, lockId, lockDist, lockDir, sNextCheckPoint, sAutopilotLight, num_items, currentTank, (double)sFuelSum, (double)sFuelMax, rbuf.len, (int)sStat); #ifdef _WINDOWS received_self = TRUE; #endif return 1; } int Receive_modifiers(void) { int n; char sMods[MAX_CHARS]; u_byte ch; if ((n = Packet_scanf(&rbuf, "%c%s", &ch, sMods)) <= 0) return n; if ((n = Handle_modifiers(sMods)) == -1) return -1; return 1; } int Receive_refuel(void) { int n; short x_0, y_0, x_1, y_1; u_byte ch; if ((n = Packet_scanf(&rbuf, "%c%hd%hd%hd%hd", &ch, &x_0, &y_0, &x_1, &y_1)) <= 0) return n; if ((n = Handle_refuel(x_0, y_0, x_1, y_1)) == -1) return -1; return 1; } int Receive_connector(void) { int n; short x_0, y_0, x_1, y_1; u_byte ch, tractor; n = Packet_scanf(&rbuf, "%c%hd%hd%hd%hd%c", &ch, &x_0, &y_0, &x_1, &y_1, &tractor); if (n <= 0) return n; if ((n = Handle_connector(x_0, y_0, x_1, y_1, tractor)) == -1) return -1; return 1; } int Receive_laser(void) { int n; short x, y, len; u_byte ch, color, dir; if ((n = Packet_scanf(&rbuf, "%c%c%hd%hd%hd%c", &ch, &color, &x, &y, &len, &dir)) <= 0) return n; if ((n = Handle_laser(color, x, y, len, dir)) == -1) return -1; return 1; } int Receive_missile(void) { int n; short x, y; u_byte ch, dir, len; if ((n = Packet_scanf(&rbuf, "%c%hd%hd%c%c", &ch, &x, &y, &len, &dir)) <= 0) return n; if ((n = Handle_missile(x, y, len, dir)) == -1) return -1; return 1; } int Receive_ball(void) { int n; short x, y, id; u_byte ch, style = 0xff /* no style */; if (version < 0x4F14) { if ((n = Packet_scanf(&rbuf, "%c%hd%hd%hd", &ch, &x, &y, &id)) <= 0) return n; } else { if ((n = Packet_scanf(&rbuf, "%c%hd%hd%hd%c", &ch, &x, &y, &id, &style)) <= 0) return n; } if ((n = Handle_ball(x, y, id, style)) == -1) return -1; return 1; } int Receive_ship(void) { int n, shield, cloak, eshield, phased, deflector; short x, y, id; u_byte ch, dir, flags; if ((n = Packet_scanf(&rbuf, "%c%hd%hd%hd" "%c%c", &ch, &x, &y, &id, &dir, &flags)) <= 0) return n; shield = ((flags & 1) != 0); cloak = ((flags & 2) != 0); eshield = ((flags & 4) != 0); phased = ((flags & 8) != 0); deflector = ((flags & 0x10) != 0); if ((n = Handle_ship(x, y, id, dir, shield, cloak, eshield, phased, deflector)) == -1) return -1; return 1; } int Receive_mine(void) { int n; short x, y, id; u_byte ch, teammine; n = Packet_scanf(&rbuf, "%c%hd%hd%c%hd", &ch, &x, &y, &teammine, &id); if (n <= 0) return n; if ((n = Handle_mine(x, y, teammine, id)) == -1) return -1; return 1; } int Receive_item(void) { int n; short x, y; u_byte ch, type; if ((n = Packet_scanf(&rbuf, "%c%hd%hd%c", &ch, &x, &y, &type)) <= 0) return n; if (type < NUM_ITEMS) { if ((n = Handle_item(x, y, type)) == -1) return -1; } return 1; } int Receive_destruct(void) { int n; short count; u_byte ch; if ((n = Packet_scanf(&rbuf, "%c%hd", &ch, &count)) <= 0) return n; if ((n = Handle_destruct(count)) == -1) return -1; return 1; } int Receive_shutdown(void) { int n; short count, delay; u_byte ch; if ((n = Packet_scanf(&rbuf, "%c%hd%hd", &ch, &count, &delay)) <= 0) return n; if ((n = Handle_shutdown(count, delay)) == -1) return -1; return 1; } int Receive_thrusttime(void) { int n; short count, max; u_byte ch; if ((n = Packet_scanf(&rbuf, "%c%hd%hd", &ch, &count, &max)) <= 0) return n; if ((n = Handle_thrusttime(count, max)) == -1) return -1; return 1; } int Receive_shieldtime(void) { int n; short count, max; u_byte ch; if ((n = Packet_scanf(&rbuf, "%c%hd%hd", &ch, &count, &max)) <= 0) return n; if ((n = Handle_shieldtime(count, max)) == -1) return -1; return 1; } int Receive_phasingtime(void) { int n; short count, max; u_byte ch; if ((n = Packet_scanf(&rbuf, "%c%hd%hd", &ch, &count, &max)) <= 0) return n; if ((n = Handle_phasingtime(count, max)) == -1) return -1; return 1; } int Receive_rounddelay(void) { int n; short count, max; u_byte ch; if ((n = Packet_scanf(&rbuf, "%c%hd%hd", &ch, &count, &max)) <= 0) return n; if ((n = Handle_rounddelay(count, max)) == -1) return -1; return 1; } int Receive_fastshot(void) { int n, r, type; rbuf.ptr++; /* skip PKT_FASTSHOT packet id */ if (rbuf.ptr - rbuf.buf + 2 >= rbuf.len) return 0; type = (*rbuf.ptr++ & 0xFF); n = (*rbuf.ptr++ & 0xFF); if (rbuf.ptr - rbuf.buf + (n * 2) > rbuf.len) return 0; r = Handle_fastshot(type, (u_byte*)rbuf.ptr, n); rbuf.ptr += n * 2; return (r == -1) ? -1 : 1; } int Receive_debris(void) { int n, r, type; if (rbuf.ptr - rbuf.buf + 2 >= rbuf.len) return 0; type = (*rbuf.ptr++ & 0xFF); n = (*rbuf.ptr++ & 0xFF); if (rbuf.ptr - rbuf.buf + (n * 2) > rbuf.len) return 0; r = Handle_debris(type - PKT_DEBRIS, (u_byte*)rbuf.ptr, n); rbuf.ptr += n * 2; return (r == -1) ? -1 : 1; } int Receive_wreckage(void) /* since 3.8.0 */ { int n; short x, y; u_byte ch, wrecktype, size, rot; if ((n = Packet_scanf(&rbuf, "%c%hd%hd%c%c%c", &ch, &x, &y, &wrecktype, &size, &rot)) <= 0) return n; if ((n = Handle_wreckage(x, y, wrecktype, size, rot)) == -1) return -1; return 1; } int Receive_asteroid(void) /* since 4.4.0 */ { int n; short x, y; u_byte ch, type_size, type, size, rot; if ((n = Packet_scanf(&rbuf, "%c%hd%hd%c%c", &ch, &x, &y, &type_size, &rot)) <= 0) return n; type = ((type_size >> 4) & 0x0F); size = (type_size & 0x0F); if ((n = Handle_asteroid(x, y, type, size, rot)) == -1) return -1; return 1; } int Receive_wormhole(void) /* since 4.5.0 */ { int n; short x, y; u_byte ch; if ((n = Packet_scanf(&rbuf, "%c%hd%hd", &ch, &x, &y)) <= 0) return n; if ((n = Handle_wormhole(x, y)) == -1) return -1; return 1; } int Receive_ecm(void) { int n; short x, y, size; u_byte ch; if ((n = Packet_scanf(&rbuf, "%c%hd%hd%hd", &ch, &x, &y, &size)) <= 0) return n; if ((n = Handle_ecm(x, y, size)) == -1) return -1; return 1; } int Receive_trans(void) { int n; short x_1, y_1, x_2, y_2; u_byte ch; if ((n = Packet_scanf(&rbuf, "%c%hd%hd%hd%hd", &ch, &x_1, &y_1, &x_2, &y_2)) <= 0) return n; if ((n = Handle_trans(x_1, y_1, x_2, y_2)) == -1) return -1; return 1; } int Receive_paused(void) { int n; short x, y, count; u_byte ch; if ((n = Packet_scanf(&rbuf, "%c%hd%hd%hd", &ch, &x, &y, &count)) <= 0) return n; if ((n = Handle_paused(x, y, count)) == -1) return -1; return 1; } int Receive_appearing(void) { int n; short x, y, id, count; u_byte ch; if ((n = Packet_scanf(&rbuf, "%c%hd%hd%hd%hd", &ch, &x, &y, &id, &count)) <= 0) return n; if ((n = Handle_appearing(x, y, id, count)) == -1) return -1; return 1; } int Receive_radar(void) { int n; short x, y; u_byte ch, size; if ((n = Packet_scanf(&rbuf, "%c%hd%hd%c", &ch, &x, &y, &size)) <= 0) return n; if ((n = Handle_radar(x, y, size)) == -1) return -1; return 1; } int Receive_fastradar(void) { int n, i, r = 1; int x, y, size; unsigned char *ptr; rbuf.ptr++; /* skip PKT_FASTRADAR packet id */ if (rbuf.ptr - rbuf.buf >= rbuf.len) return 0; n = (*rbuf.ptr++ & 0xFF); if (rbuf.ptr - rbuf.buf + (n * 3) > rbuf.len) return 0; ptr = (unsigned char *) rbuf.ptr; for (i = 0; i < n; i++) { x = *ptr++; y = *ptr++; y |= (*ptr & 0xC0) << 2; size = (*ptr & 0x07); if (*ptr & 0x20) size |= 0x80; ptr++; r = Handle_fastradar(x, y, size); if (r == -1) break; } rbuf.ptr += n * 3; return (r == -1) ? -1 : 1; } int Receive_damaged(void) { int n; u_byte ch, dmgd; if ((n = Packet_scanf(&rbuf, "%c%c", &ch, &dmgd)) <= 0) return n; if ((n = Handle_damaged(dmgd)) == -1) return -1; return 1; } int Receive_leave(void) { int n; short id; u_byte ch; if ((n = Packet_scanf(&cbuf, "%c%hd", &ch, &id)) <= 0) return n; if ((n = Handle_leave(id)) == -1) return -1; return 1; } int Receive_war(void) { int n; short robot_id, killer_id; u_byte ch; if ((n = Packet_scanf(&cbuf, "%c%hd%hd", &ch, &robot_id, &killer_id)) <= 0) return n; /* not interested */ return 1; } int Receive_seek(void) { int n; short programmer_id, robot_id, sought_id; u_byte ch; if ((n = Packet_scanf(&cbuf, "%c%hd%hd%hd", &ch, &programmer_id, &robot_id, &sought_id)) <= 0) return n; /* not interested */ return 1; } int Receive_player(void) { int n; short id; u_byte ch, myteam, mychar, myself = 0; char nick_name[MAX_CHARS], user_name[MAX_CHARS], host_name[MAX_CHARS], shape[2*MSG_LEN], *cbuf_ptr = cbuf.ptr; if ((n = Packet_scanf(&cbuf, "%c%hd%c%c" "%s%s%s" "%S", &ch, &id, &myteam, &mychar, nick_name, user_name, host_name, shape)) <= 0) return n; nick_name[MAX_NAME_LEN - 1] = '\0'; user_name[MAX_NAME_LEN - 1] = '\0'; host_name[MAX_HOST_LEN - 1] = '\0'; if (version < 0x4F10) n = Packet_scanf(&cbuf, "%S", &shape[strlen(shape)]); else n = Packet_scanf(&cbuf, "%S%c", &shape[strlen(shape)], &myself); if (n <= 0) { cbuf.ptr = cbuf_ptr; return n; } if ((n = Handle_player(id, myteam, mychar, nick_name, user_name, host_name, shape, myself)) == -1) return -1; return 1; } int Receive_team(void) { int n; short id; u_byte ch, pl_team; if ((n = Packet_scanf(&cbuf, "%c%hd%c", &ch, &id, &pl_team)) <= 0) return n; if (Handle_team(id, pl_team) == -1) return -1; return 1; } int Receive_score_object(void) { int n; unsigned short x, y; double score = 0; char msg[MAX_CHARS]; u_byte ch; if (version < 0x4500 || (version >= 0x4F09 && version < 0x4F11)) { short rcv_score; n = Packet_scanf(&cbuf, "%c%hd%hu%hu%s", &ch, &rcv_score, &x, &y, msg); score = rcv_score; } else { /* newer servers send scores with two decimals */ int rcv_score; n = Packet_scanf(&cbuf, "%c%d%hu%hu%s", &ch, &rcv_score, &x, &y, msg); score = (double)rcv_score / 100; } if (n <= 0) return n; if ((n = Handle_score_object(score, x, y, msg)) == -1) return -1; return 1; } int Receive_score(void) { int n; short id, life; double score = 0; u_byte ch, mychar, alliance = ' '; if (version < 0x4500 || (version >= 0x4F09 && version < 0x4F11)) { short rcv_score; n = Packet_scanf(&cbuf, "%c%hd%hd%hd%c", &ch, &id, &rcv_score, &life, &mychar); score = rcv_score; alliance = ' '; } else { /* newer servers send scores with two decimals */ int rcv_score; n = Packet_scanf(&cbuf, "%c%hd%d%hd%c%c", &ch, &id, &rcv_score, &life, &mychar, &alliance); score = (double)rcv_score / 100; } if (n <= 0) return n; if ((n = Handle_score(id, score, life, mychar, alliance)) == -1) return -1; return 1; } int Receive_team_score(void) { int n; u_byte ch; short team; int rcv_score; double score; if ((n = Packet_scanf(&cbuf, "%c%hd%d", &ch, &team, &rcv_score)) <= 0) return n; score = (double)rcv_score / 100; if ((n = Handle_team_score(team, score)) == -1) return -1; return 1; } int Receive_timing(void) { int n, check, round; short id; unsigned short timing; u_byte ch; n = Packet_scanf(&cbuf, "%c%hd%hu", &ch, &id, &timing); if (n <= 0) return n; check = timing % num_checks; round = timing / num_checks; if ((n = Handle_timing(id, check, round, last_loops)) == -1) return -1; return 1; } int Receive_fuel(void) { int n; unsigned short num, fuel; u_byte ch; if ((n = Packet_scanf(&rbuf, "%c%hu%hu", &ch, &num, &fuel)) <= 0) return n; if ((n = Handle_fuel(num, (double)fuel)) == -1) return -1; if (wbuf.len < MAX_MAP_ACK_LEN) Packet_printf(&wbuf, "%c%ld%hu", PKT_ACK_FUEL, last_loops, num); return 1; } int Receive_cannon(void) { int n; unsigned short num, dead_time; u_byte ch; if ((n = Packet_scanf(&rbuf, "%c%hu%hu", &ch, &num, &dead_time)) <= 0) return n; if ((n = Handle_cannon(num, dead_time)) == -1) return -1; if (wbuf.len < MAX_MAP_ACK_LEN) Packet_printf(&wbuf, "%c%ld%hu", PKT_ACK_CANNON, last_loops, num); return 1; } int Receive_target(void) { int n; unsigned short num, dead_time, damage; u_byte ch; if ((n = Packet_scanf(&rbuf, "%c%hu%hu%hu", &ch, &num, &dead_time, &damage)) <= 0) return n; if ((n = Handle_target(num, dead_time, (double)damage / 256.0)) == -1) return -1; if (wbuf.len < MAX_MAP_ACK_LEN) Packet_printf(&wbuf, "%c%ld%hu", PKT_ACK_TARGET, last_loops, num); return 1; } int Receive_polystyle(void) /* since ng 4.7.0 */ { int n; unsigned short num, newstyle; u_byte ch; if ((n = Packet_scanf(&rbuf, "%c%hu%hu", &ch, &num, &newstyle)) <= 0) return n; if ((n = Handle_polystyle(num, newstyle)) == -1) return -1; if (wbuf.len < MAX_MAP_ACK_LEN) Packet_printf(&wbuf, "%c%ld%hu", PKT_ACK_POLYSTYLE, last_loops, num); return 1; } int Receive_base(void) { int n; short id; unsigned short num; u_byte ch; if ((n = Packet_scanf(&cbuf, "%c%hd%hu", &ch, &id, &num)) <= 0) return n; if ((n = Handle_base(id, num)) == -1) return -1; return 1; } int Receive_magic(void) { int n; u_byte ch; if ((n = Packet_scanf(&cbuf, "%c%u", &ch, &magic)) <= 0) return n; return 1; } int Receive_string(void) { int n; u_byte ch, type; unsigned short arg1, arg2; if ((n = Packet_scanf(&cbuf, "%c%c%hu%hu", &ch, &type, &arg1, &arg2)) <= 0) return n; /* * Not implemented yet. */ return 1; } int Receive_loseitem(void) { int n; u_byte pkt; /* Most of the Receive_ funcs call a */ /* Handle_ func but that seems */ /* unecessary here */ if ((n = Packet_scanf(&rbuf, "%c%c", &pkt, &lose_item)) <= 0) return n; return 1; } int Send_ack(long rel_loops) { int n; if ((n = Packet_printf(&wbuf, "%c%ld%ld", PKT_ACK, reliable_offset, rel_loops)) <= 0) { if (n == 0) return 0; error("Can't ack reliable data"); return -1; } return 1; } int Receive_reliable(void) { int n; short len; u_byte ch; long rel, rel_loops; if ((n = Packet_scanf(&rbuf, "%c%hd%ld%ld", &ch, &len, &rel, &rel_loops)) == -1) return -1; if (n == 0) { warn("Incomplete reliable data packet"); return 0; } #ifdef DEBUG if (reliable_offset >= rel + len) printf("Reliable my=%ld pkt=%ld len=%d loops=%ld\n", reliable_offset, rel, len, rel_loops); #endif if (len <= 0) { warn("Bad reliable data length (%d)", len); return -1; } if (rbuf.ptr + len > rbuf.buf + rbuf.len) { warn("Not all reliable data in packet (%d,%d,%d)", rbuf.ptr - rbuf.buf, len, rbuf.len); rbuf.ptr += len; Sockbuf_advance(&rbuf, rbuf.ptr - rbuf.buf); return -1; } if (rel > reliable_offset) { /* * We miss one or more packets. * For now we drop this packet. * We could have kept it until the missing packet(s) arrived. */ rbuf.ptr += len; Sockbuf_advance(&rbuf, rbuf.ptr - rbuf.buf); if (Send_ack(rel_loops) == -1) return -1; return 1; } if (rel + len <= reliable_offset) { /* * Duplicate data. Probably an ack got lost. * Send an ack for our current stream position. */ rbuf.ptr += len; Sockbuf_advance(&rbuf, rbuf.ptr - rbuf.buf); if (Send_ack(rel_loops) == -1) return -1; return 1; } if (rel < reliable_offset) { len -= (short)(reliable_offset - rel); rbuf.ptr += reliable_offset - rel; rel = reliable_offset; } if (cbuf.ptr > cbuf.buf) Sockbuf_advance(&cbuf, cbuf.ptr - cbuf.buf); if (Sockbuf_write(&cbuf, rbuf.ptr, len) != len) { warn("Can't copy reliable data to buffer"); rbuf.ptr += len; Sockbuf_advance(&rbuf, rbuf.ptr - rbuf.buf); return -1; } reliable_offset += len; rbuf.ptr += len; Sockbuf_advance(&rbuf, rbuf.ptr - rbuf.buf); if (Send_ack(rel_loops) == -1) return -1; return 1; } int Receive_reply(int *replyto, int *result) { int n; u_byte type, ch1, ch2; n = Packet_scanf(&cbuf, "%c%c%c", &type, &ch1, &ch2); if (n <= 0) return n; if (n != 3 || type != PKT_REPLY) { error("Can't receive reply packet"); return -1; } *replyto = ch1; *result = ch2; return 1; } int Send_keyboard(u_byte *keyboard_vector) { int size = KEYBOARD_SIZE; if (wbuf.size - wbuf.len < size + 1 + 4) /* Not enough write buffer space for keyboard state */ return 0; Packet_printf(&wbuf, "%c%ld", PKT_KEYBOARD, last_keyboard_change); memcpy(&wbuf.buf[wbuf.len], keyboard_vector, (size_t)size); wbuf.len += size; last_keyboard_update = last_loops; Net_keyboard_track(); Send_talk(); if (Sockbuf_flush(&wbuf) == -1) { error("Can't send keyboard update"); return -1; } return 0; } int Send_shape(char *str) { shipshape_t *w; char buf[MSG_LEN], ext[MSG_LEN]; w = Convert_shape_str(str); Convert_ship_2_string(w, buf, ext, 0x3200); Free_ship_shape(w); if (Packet_printf(&wbuf, "%c%S", PKT_SHAPE, buf) <= 0) return -1; if (Packet_printf(&wbuf, "%S", ext) <= 0) return -1; return 0; } int Send_power(double pwr) { if (Packet_printf(&wbuf, "%c%hd", PKT_POWER, (int) (pwr * 256.0)) == -1) return -1; return 0; } int Send_power_s(double pwr_s) { if (Packet_printf(&wbuf, "%c%hd", PKT_POWER_S, (int)(pwr_s * 256.0)) == -1) return -1; return 0; } int Send_turnspeed(double turnspd) { if (Packet_printf(&wbuf, "%c%hd", PKT_TURNSPEED, (int) (turnspd * 256.0)) == -1) return -1; return 0; } int Send_turnspeed_s(double turnspd_s) { if (Packet_printf(&wbuf, "%c%hd", PKT_TURNSPEED_S, (int) (turnspd_s * 256.0)) == -1) return -1; return 0; } int Send_turnresistance(double turnres) { if (Packet_printf(&wbuf, "%c%hd", PKT_TURNRESISTANCE, (int) (turnres * 256.0)) == -1) return -1; return 0; } int Send_turnresistance_s(double turnres_s) { if (Packet_printf(&wbuf, "%c%hd", PKT_TURNRESISTANCE_S, (int) (turnres_s * 256.0)) == -1) return -1; return 0; } int Receive_quit(void) { unsigned char pkt; sockbuf_t *sbuf; char reason[MAX_CHARS]; if (rbuf.ptr < rbuf.buf + rbuf.len) sbuf = &rbuf; else sbuf = &cbuf; if (Packet_scanf(sbuf, "%c", &pkt) != 1) warn("Can't read quit packet"); else { if (Packet_scanf(sbuf, "%s", reason) <= 0) strlcpy(reason, "unknown reason", MAX_CHARS); warn("Got quit packet: \"%s\"", reason); } return -1; } int Receive_audio(void) { int n; unsigned char pkt, type, vol; if ((n = Packet_scanf(&rbuf, "%c%c%c", &pkt, &type, &vol)) <= 0) return n; #ifdef SOUND if ((n = Handle_audio(type, vol)) == -1) return -1; #endif /* SOUND */ return 1; } int Receive_talk_ack(void) { int n; unsigned char pkt; long talk_ack; if ((n = Packet_scanf(&cbuf, "%c%ld", &pkt, &talk_ack)) <= 0) return n; if (talk_ack >= talk_pending) talk_pending = 0; return 1; } int Net_talk(char *str) { strlcpy(talk_str, str, sizeof talk_str); if (talk_str[0] == '\\') /* it's a clientcommand! */ executeCommand(talk_str + 1); else { talk_pending = ++talk_sequence_num; talk_last_send = last_loops - TALK_RETRY; Send_talk(); } return 0; } int Send_talk(void) { if (talk_pending == 0) return 0; if (last_loops - talk_last_send < TALK_RETRY) return 0; if (Packet_printf(&wbuf, "%c%ld%s", PKT_TALK, talk_pending, talk_str) ==-1) return -1; talk_last_send = last_loops; return 0; } int Send_display(int width, int height, int sparks, int spark_colors) { int width_wanted = width; int height_wanted = height; if (width_wanted == server_display.view_width && height_wanted == server_display.view_height && spark_colors == server_display.num_spark_colors && sparks == server_display.spark_rand && last_loops != 0) { return 0; } if (Packet_printf(&wbuf, "%c%hd%hd%c%c", PKT_DISPLAY, width_wanted, height_wanted, spark_colors, sparks) == -1) return -1; server_display.spark_rand = sparks; return 0; } int Send_modifier_bank(int bank) { if (bank < 0 || bank >= NUM_MODBANKS) return -1; if (Packet_printf(&wbuf, "%c%c%s", PKT_MODIFIERBANK, bank, modBankStr[bank]) == -1) return -1; return 0; } int Send_pointer_move(int movement) { static int total; #if 0 struct timeval tv; static struct timeval old_tv; double s, u, t; static double oldt = 0; static int num = 1; gettimeofday(&tv, NULL); s = tv.tv_sec; u = tv.tv_usec; t = s + u * 1e-6; if (tv.tv_sec != old_tv.tv_sec) { warn("Send_pointer_moves = %d", num); num = 1; } else num ++; /*warn("%d %.2f: %d", num, t - oldt, movement);*/ oldt = t; old_tv = tv; #endif if (dirPrediction) { pointer_moves[pointer_move_next].movement = movement; pointer_moves[pointer_move_next].turnspeed = turnspeed; pointer_moves[pointer_move_next].id = last_keyboard_change + 1; pointer_move_next++; if (pointer_move_next >= MAX_POINTER_MOVES) pointer_move_next = 0; } if (version >= 0x4F13) { total += movement; movement = total; } if (Packet_printf(&wbuf, "%c%hd", PKT_POINTER_MOVE, movement) == -1) return -1; if (dirPrediction) Net_key_change(); return 0; } int Send_audio_request(int on) { #ifdef DEBUG_SOUND printf("Send_audio_request %d\n", on); #endif #ifndef SOUND on = 0; #endif if (Packet_printf(&wbuf, "%c%c", PKT_REQUEST_AUDIO, (on != 0)) == -1) return -1; return 0; } int Send_fps_request(int fps) { assert(fps > 0); assert(fps <= MAX_SUPPORTED_FPS); if (Packet_printf(&wbuf, "%c%c", PKT_ASYNC_FPS, fps) == -1) return -1; return 0; }