diff --git a/network/netplay/README b/network/netplay/README index 095854627e..a31fafe553 100644 --- a/network/netplay/README +++ b/network/netplay/README @@ -106,6 +106,10 @@ early (i.e., it only forwards data on the frame it's reached), it must also inform all clients of its own current frame even if it has no input. The NOINPUT command is provided for that purpose. +Each client has a client number, and the server is always client number 0. +Client numbers are currently limited to 0-31, as they're used in 32-bit +bitmaps. + The handshake procedure (this part is done by both server and client): 1) Send connection header 2) Receive and verify connection header @@ -156,7 +160,7 @@ Payload: { frame number: uint32 is server data: 1 bit - player: 31 bits + client number: 31 bits joypad input: uint32 analog 1 input: uint32 analog 2 input: uint32 @@ -213,9 +217,9 @@ Payload: { frame number: uint32 paused?: 1 bit - connected players: 31 bits - flip frame: uint32 + client number: 31 bits controller devices: uint32[16] + controller-client mapping: uint32[16] client nick: char[32] sram: variable } @@ -236,13 +240,16 @@ Description: Command: PLAY Payload: { - reserved: 31 bits as slave?: 1 bit + reserved: 15 bits + requested device(s): 16 bits } Description: Request to enter player mode. The client must wait for a MODE command before sending input. Server may refuse or force slave connections, so the - request is not necessarily honored. Payload may be elided if zero. + request is not necessarily honored. If no devices are explicitly requested, + the server may choose how to assign; the default is to assign the first + unassigned device. Payload may be elided if zero. Command: MODE Payload: @@ -252,7 +259,8 @@ Payload: slave: 1 bit playing: 1 bit you: 1 bit - player number: uint16 + client number: uint16 + device bitmap: uint32 } Description: Inform of a connection mode change (possibly of the receiving client). Only diff --git a/network/netplay/netplay_frontend.c b/network/netplay/netplay_frontend.c index c5d404dcba..fa305e03e8 100644 --- a/network/netplay/netplay_frontend.c +++ b/network/netplay/netplay_frontend.c @@ -71,7 +71,7 @@ static bool netplay_is_alive(void) { if (!netplay_data) return false; - return (netplay_data->is_server && !!netplay_data->connected_players) || + return (netplay_data->is_server && (netplay_data->connected_players1>1)) || (!netplay_data->is_server && netplay_data->self_mode >= NETPLAY_CONNECTION_CONNECTED); } @@ -115,8 +115,9 @@ static bool netplay_can_poll(netplay_t *netplay) */ static bool get_self_input_state(netplay_t *netplay) { - uint32_t state[WORDS_PER_INPUT] = {0, 0, 0}; struct delta_frame *ptr = &netplay->buffer[netplay->self_ptr]; + netplay_input_state_t istate = NULL; + uint32_t devices, devi; size_t i; if (!netplay_delta_frame_ready(netplay, ptr, netplay->self_frame_count)) @@ -128,39 +129,43 @@ static bool get_self_input_state(netplay_t *netplay) return true; } - if (!input_driver_is_libretro_input_blocked() && netplay->self_frame_count > 0) + devices = netplay->self_devices; + for (devi = 0; devi < MAX_INPUT_DEVICES; devi++) { - /* First frame we always give zero input since relying on - * input from first frame screws up when we use -F 0. */ - retro_input_state_t cb = netplay->cbs.state_cb; - for (i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++) - { - int16_t tmp = cb(0, - RETRO_DEVICE_JOYPAD, 0, (unsigned)i); - state[0] |= tmp ? 1 << i : 0; - } + if (!(devices & (1<real_input[devi], + netplay->self_client_num, 3 /* FIXME */, true); + if (!istate) + continue; /* FIXME: More severe? */ + + if (!input_driver_is_libretro_input_blocked() && netplay->self_frame_count > 0) { - int16_t tmp_x = cb(0, - RETRO_DEVICE_ANALOG, (unsigned)i, 0); - int16_t tmp_y = cb(0, - RETRO_DEVICE_ANALOG, (unsigned)i, 1); - state[1 + i] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16); + /* First frame we always give zero input since relying on + * input from first frame screws up when we use -F 0. */ + uint32_t *state = istate->data; + retro_input_state_t cb = netplay->cbs.state_cb; + for (i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++) + { + int16_t tmp = cb(0, + RETRO_DEVICE_JOYPAD, 0, (unsigned)i); + state[0] |= tmp ? 1 << i : 0; + } + + for (i = 0; i < 2; i++) + { + int16_t tmp_x = cb(0, + RETRO_DEVICE_ANALOG, (unsigned)i, 0); + int16_t tmp_y = cb(0, + RETRO_DEVICE_ANALOG, (unsigned)i, 1); + state[1 + i] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16); + } } } - memcpy(ptr->self_state, state, sizeof(state)); ptr->have_local = true; - /* If we're playing, copy it in as real input */ - if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING) - { - memcpy(ptr->real_input_state[netplay->self_player], state, - sizeof(state)); - ptr->have_real[netplay->self_player] = true; - } - /* And send this input to our peers */ for (i = 0; i < netplay->connections_size; i++) { @@ -203,7 +208,7 @@ bool init_netplay_deferred(const char* server, unsigned port) static bool netplay_poll(void) { int res; - uint32_t player; + uint32_t client; size_t i; netplay_data->can_poll = false; @@ -219,7 +224,7 @@ static bool netplay_poll(void) * frame */ netplay_update_unread_ptr(netplay_data); if (netplay_data->stateless_mode && - netplay_data->connected_players && + (netplay_data->connected_players1>1) && netplay_data->unread_frame_count <= netplay_data->run_frame_count) res = netplay_poll_net_input(netplay_data, true); else @@ -231,7 +236,7 @@ static bool netplay_poll(void) netplay_simulate_input(netplay_data, netplay_data->run_ptr, false); /* Handle any slaves */ - if (netplay_data->is_server && netplay_data->connected_slaves) + if (netplay_data->is_server && netplay_data->connected_slaves1) netplay_handle_slaves(netplay_data); netplay_update_unread_ptr(netplay_data); @@ -352,7 +357,7 @@ static bool netplay_poll(void) { /* Have we not read enough latency frames? */ if (netplay_data->self_mode == NETPLAY_CONNECTION_PLAYING && - netplay_data->connected_players && + netplay_data->connected_players1 && netplay_data->run_frame_count + netplay_data->input_latency_frames > netplay_data->self_frame_count) { netplay_data->stall = NETPLAY_STALL_INPUT_LATENCY; @@ -369,21 +374,19 @@ static bool netplay_poll(void) /* Figure out who to blame */ if (netplay_data->is_server) { - for (player = 0; player < MAX_USERS; player++) + for (client = 1; client < MAX_CLIENTS; client++) { - if (!(netplay_data->connected_players & (1<read_frame_count[player] > netplay_data->unread_frame_count) continue; - for (i = 0; i < netplay_data->connections_size; i++) + struct netplay_connection *connection; + if (!(netplay_data->connected_players1 & (1<read_frame_count1[client] > netplay_data->unread_frame_count) + continue; + connection = &netplay_data->connections[client-1]; + if (connection->active && + connection->mode == NETPLAY_CONNECTION_PLAYING) { - struct netplay_connection *connection = &netplay_data->connections[i]; - if (connection->active && - connection->mode == NETPLAY_CONNECTION_PLAYING && - connection->player == player) - { - connection->stall = NETPLAY_STALL_RUNNING_FAST; - connection->stall_time = netplay_data->stall_time; - break; - } + connection->stall = NETPLAY_STALL_RUNNING_FAST; + connection->stall_time = netplay_data->stall_time; } } } @@ -480,32 +483,30 @@ static int16_t netplay_input_state(netplay_t *netplay, { size_t ptr = netplay->is_replay ? netplay->replay_ptr : netplay->run_ptr; + struct delta_frame *delta; + netplay_input_state_t istate; const uint32_t *curr_input_state = NULL; - if (port <= 1) - { - /* Possibly flip the port */ - if (netplay_flip_port(netplay)) - port ^= 1; - } - else if (port >= MAX_USERS) + if (port >= MAX_INPUT_DEVICES) { return 0; } - if (port > netplay->player_max) - netplay->player_max = port; + if (port > netplay->input_device_max) + netplay->input_device_max = port; - if (netplay->buffer[ptr].have_real[port]) - { - netplay->buffer[ptr].used_real[port] = true; - curr_input_state = netplay->buffer[ptr].real_input_state[port]; - } + /* FIXME: Mixing */ + delta = &netplay->buffer[ptr]; + istate = delta->real_input[port]; + if (istate && istate->is_real) + delta->used_real[port] = true; else - { - curr_input_state = netplay->buffer[ptr].simulated_input_state[port]; - } + istate = delta->simulated_input[port]; + if (!istate) + return 0; + + curr_input_state = istate->data; switch (device) { @@ -889,7 +890,7 @@ bool netplay_pre_frame(netplay_t *netplay) } if (sync_stalled || - ((!netplay->is_server || netplay->connected_players) && + ((!netplay->is_server || (netplay->connected_players1>1)) && (netplay->stall || netplay->remote_paused))) { /* We may have received data even if we're stalled, so run post-frame @@ -942,19 +943,20 @@ static void netplay_force_future(netplay_t *netplay) netplay->run_ptr = netplay->self_ptr; netplay->run_frame_count = netplay->self_frame_count; + /* We need to ignore any intervening data from the other side, * and never rewind past this */ netplay_update_unread_ptr(netplay); if (netplay->unread_frame_count < netplay->run_frame_count) { - uint32_t player; - for (player = 0; player < MAX_USERS; player++) + uint32_t client; + for (client = 0; client < MAX_CLIENTS; client++) { - if (!(netplay->connected_players & (1<read_frame_count[player] < netplay->run_frame_count) + if (!(netplay->connected_players1 & (1<read_frame_count1[client] < netplay->run_frame_count) { - netplay->read_ptr[player] = netplay->run_ptr; - netplay->read_frame_count[player] = netplay->run_frame_count; + netplay->read_ptr1[client] = netplay->run_ptr; + netplay->read_frame_count1[client] = netplay->run_frame_count; } } if (netplay->server_frame_count < netplay->run_frame_count) @@ -1128,10 +1130,12 @@ static void netplay_core_reset(netplay_t *netplay) */ static void netplay_toggle_play_spectate(netplay_t *netplay) { + size_t i; + if (netplay->is_server) { /* FIXME: Duplication */ - uint32_t payload[2]; + uint32_t payload[3]; char msg[512]; const char *dmsg = NULL; payload[0] = htonl(netplay->self_frame_count); @@ -1139,28 +1143,41 @@ static void netplay_toggle_play_spectate(netplay_t *netplay) netplay->self_mode == NETPLAY_CONNECTION_SLAVE) { /* Mark us as no longer playing */ - payload[1] = htonl(netplay->self_player); + /* FIXME: Refactor this */ + payload[1] = htonl(netplay->self_client_num); + payload[2] = htonl(0); netplay->self_mode = NETPLAY_CONNECTION_SPECTATING; + netplay->connected_players1 |= ~(1L); + netplay->connected_slaves1 |= ~(1L); + netplay->client_devices[0] = 0; + for (i = 0; i < MAX_INPUT_DEVICES; i++) + netplay->device_clients[i] |= ~(1L); + netplay->self_devices = 0; dmsg = msg_hash_to_str(MSG_NETPLAY_YOU_HAVE_LEFT_THE_GAME); } else if (netplay->self_mode == NETPLAY_CONNECTION_SPECTATING) { - uint32_t player; + uint32_t device; - /* Take a player number */ - for (player = 0; player < MAX_USERS; player++) - if (!(netplay->connected_players & (1<device_clients[device])) + break; + if (device == MAX_INPUT_DEVICES) + return; /* Failure! */ - payload[1] = htonl(NETPLAY_CMD_MODE_BIT_PLAYING | player); + payload[1] = htonl(NETPLAY_CMD_MODE_BIT_PLAYING | device); + payload[2] = htonl(1<self_mode = NETPLAY_CONNECTION_PLAYING; - netplay->self_player = player; + netplay->connected_players1 |= 1; + netplay->client_devices[0] = (1<device_clients[device] = netplay->self_devices = 1; dmsg = msg; msg[sizeof(msg)-1] = '\0'; - snprintf(msg, sizeof(msg)-1, msg_hash_to_str(MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N), player+1); + snprintf(msg, sizeof(msg)-1, msg_hash_to_str(MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N), device+1); } RARCH_LOG("[netplay] %s\n", dmsg); diff --git a/network/netplay/netplay_handshake.c b/network/netplay/netplay_handshake.c index cdbc2f6f42..00cca88ce5 100644 --- a/network/netplay/netplay_handshake.c +++ b/network/netplay/netplay_handshake.c @@ -529,10 +529,10 @@ bool netplay_handshake_sync(netplay_t *netplay, /* If we're the server, now we send sync info */ size_t i; int matchct; - uint32_t cmd[5]; + uint32_t cmd[4]; retro_ctx_memory_info_t mem_info; + uint32_t client_num = 0; uint32_t device = 0; - uint32_t connected_players = 0; size_t nicklen, nickmangle = 0; bool nick_matched = false; @@ -543,30 +543,43 @@ bool netplay_handshake_sync(netplay_t *netplay, /* Send basic sync info */ cmd[0] = htonl(NETPLAY_CMD_SYNC); - cmd[1] = htonl(3*sizeof(uint32_t) + MAX_USERS*sizeof(uint32_t) + - NETPLAY_NICK_LEN + mem_info.size); + cmd[1] = htonl(2*sizeof(uint32_t) + /* Controller devices */ + + MAX_INPUT_DEVICES*sizeof(uint32_t) + + /* Device-client mapping */ + + MAX_INPUT_DEVICES*sizeof(uint32_t) + + /* Client nick */ + + NETPLAY_NICK_LEN + + /* And finally, sram */ + + mem_info.size); cmd[2] = htonl(netplay->self_frame_count); - connected_players = netplay->connected_players; - if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING) - connected_players |= 1<self_player; + client_num = connection - netplay->connections + 1; if (netplay->local_paused || netplay->remote_paused) - connected_players |= NETPLAY_CMD_SYNC_BIT_PAUSED; - cmd[3] = htonl(connected_players); - if (netplay->flip) - cmd[4] = htonl(netplay->flip_frame); - else - cmd[4] = htonl(0); + client_num |= NETPLAY_CMD_SYNC_BIT_PAUSED; + cmd[3] = htonl(client_num); if (!netplay_send(&connection->send_packet_buffer, connection->fd, cmd, sizeof(cmd))) return false; /* Now send the device info */ - for (i = 0; i < MAX_USERS; i++) + for (i = 0; i < MAX_INPUT_DEVICES; i++) { device = htonl(input_config_get_device((unsigned)i)); if (!netplay_send(&connection->send_packet_buffer, connection->fd, - &device, sizeof(device))) + &device, sizeof(device))) + return false; + } + + /* Then the device-client mapping */ + for (i = 0; i < MAX_INPUT_DEVICES; i++) + { + device = htonl(netplay->device_clients[i]); + if (!netplay_send(&connection->send_packet_buffer, connection->fd, + &device, sizeof(device))) return false; } @@ -901,10 +914,10 @@ bool netplay_handshake_pre_sync(netplay_t *netplay, struct netplay_connection *connection, bool *had_input) { uint32_t cmd[2]; - uint32_t new_frame_count, connected_players, flip_frame; + uint32_t new_frame_count, client_num, flip_frame; uint32_t device; uint32_t local_sram_size, remote_sram_size; - size_t i; + size_t i, j; ssize_t recvd; retro_ctx_controller_info_t pad; char new_nick[NETPLAY_NICK_LEN]; @@ -920,8 +933,8 @@ bool netplay_handshake_pre_sync(netplay_t *netplay, } /* Only expecting a sync command */ - if (ntohl(cmd[0]) != NETPLAY_CMD_SYNC || - ntohl(cmd[1]) < 3*sizeof(uint32_t) + MAX_USERS*sizeof(uint32_t) + + if (ntohl(cmd[0]) != NETPLAY_CMD_SYNC|| + ntohl(cmd[1]) < (2+2*MAX_INPUT_DEVICES)*sizeof(uint32_t) + NETPLAY_NICK_LEN) { RARCH_ERR("%s\n", @@ -934,30 +947,23 @@ bool netplay_handshake_pre_sync(netplay_t *netplay, return false; new_frame_count = ntohl(new_frame_count); - /* Get the connected players and pause mode */ - RECV(&connected_players, sizeof(connected_players)) + /* Get our client number and pause mode */ + RECV(&client_num, sizeof(client_num)) return false; - connected_players = ntohl(connected_players); - if (connected_players & NETPLAY_CMD_SYNC_BIT_PAUSED) + client_num = ntohl(client_num); + if (client_num & NETPLAY_CMD_SYNC_BIT_PAUSED) { netplay->remote_paused = true; - connected_players ^= NETPLAY_CMD_SYNC_BIT_PAUSED; + client_num ^= NETPLAY_CMD_SYNC_BIT_PAUSED; } - netplay->connected_players = connected_players; - - /* And the flip state */ - RECV(&flip_frame, sizeof(flip_frame)) - return false; - - flip_frame = ntohl(flip_frame); - netplay->flip = !!flip_frame; - netplay->flip_frame = flip_frame; + netplay->self_client_num = client_num; /* Set our frame counters as requested */ netplay->self_frame_count = netplay->run_frame_count = netplay->other_frame_count = netplay->unread_frame_count = netplay->server_frame_count = new_frame_count; + /* And clear out the framebuffer */ for (i = 0; i < netplay->buffer_size; i++) { struct delta_frame *ptr = &netplay->buffer[i]; @@ -977,17 +983,14 @@ bool netplay_handshake_pre_sync(netplay_t *netplay, } } - for (i = 0; i < MAX_USERS; i++) + for (i = 0; i < MAX_CLIENTS; i++) { - if (connected_players & (1<read_ptr[i] = netplay->self_ptr; - netplay->read_frame_count[i] = netplay->self_frame_count; - } + netplay->read_ptr1[i] = netplay->self_ptr; + netplay->read_frame_count1[i] = netplay->self_frame_count; } - /* Get and set each pad */ - for (i = 0; i < MAX_USERS; i++) + /* Get and set each input device */ + for (i = 0; i < MAX_INPUT_DEVICES; i++) { RECV(&device, sizeof(device)) return false; @@ -998,6 +1001,27 @@ bool netplay_handshake_pre_sync(netplay_t *netplay, core_set_controller_port_device(&pad); } + /* Get the client-controller mapping */ + netplay->connected_players1 = + netplay->connected_slaves1 = + netplay->self_devices = 0; + for (i = 0; i < MAX_CLIENTS; i++) + netplay->client_devices[i] = 0; + for (i = 0; i < MAX_INPUT_DEVICES; i++) + { + RECV(&device, sizeof(device)) + return false; + device = ntohl(device); + + netplay->device_clients[i] = device; + netplay->connected_players1 |= device; + for (j = 0; j < MAX_CLIENTS; j++) + { + if (device & (1<client_devices[j] |= 1<zbuffer_size + - NETPLAY_MAX_STALL_FRAMES * WORDS_PER_FRAME + (NETPLAY_MAX_STALL_FRAMES+1)*3; + NETPLAY_MAX_STALL_FRAMES * 16; netplay->packet_buffer_size = packet_buffer_size; for (i = 0; i < netplay->connections_size; i++) @@ -430,8 +430,7 @@ netplay_t *netplay_new(void *direct_host, const char *server, uint16_t port, netplay->listen_fd = -1; netplay->tcp_port = port; netplay->cbs = *cb; - netplay->connected_players = 0; - netplay->player_max = 1; + netplay->input_device_max = 1; netplay->is_server = (direct_host == NULL && server == NULL); netplay->is_connected = false;; netplay->nat_traversal = netplay->is_server ? nat_traversal : false; diff --git a/network/netplay/netplay_io.c b/network/netplay/netplay_io.c index 536c7a742d..0c77ba0b42 100644 --- a/network/netplay/netplay_io.c +++ b/network/netplay/netplay_io.c @@ -91,6 +91,7 @@ void netplay_hangup(netplay_t *netplay, struct netplay_connection *connection) { char msg[512]; const char *dmsg; + size_t i; if (!netplay) return; @@ -124,12 +125,22 @@ void netplay_hangup(netplay_t *netplay, struct netplay_connection *connection) if (!netplay->is_server) { netplay->self_mode = NETPLAY_CONNECTION_NONE; - netplay->connected_players = 0; + netplay->connected_players1 &= (1L<self_client_num); + for (i = 0; i < MAX_CLIENTS; i++) + { + if (i == netplay->self_client_num) + continue; + netplay->client_devices[i] = 0; + } + for (i = 0; i < MAX_INPUT_DEVICES; i++) + netplay->device_clients[i] &= (1L<self_client_num); netplay->stall = NETPLAY_STALL_NONE; } else { + uint32_t client_num = connection - netplay->connections + 1; + /* Mark the player for removal */ if (connection->mode == NETPLAY_CONNECTION_PLAYING || connection->mode == NETPLAY_CONNECTION_SLAVE) @@ -137,11 +148,14 @@ void netplay_hangup(netplay_t *netplay, struct netplay_connection *connection) /* This special mode keeps the connection object alive long enough to * send the disconnection message at the correct time */ connection->mode = NETPLAY_CONNECTION_DELAYED_DISCONNECT; - connection->delay_frame = netplay->read_frame_count[connection->player]; + connection->delay_frame = netplay->read_frame_count1[client_num]; /* Mark them as not playing anymore */ - netplay->connected_players &= ~(1<player); - netplay->connected_slaves &= ~(1<player); + netplay->connected_players1 &= ~(1L<connected_slaves1 &= ~(1L<client_devices[client_num] = 0; + for (i = 0; i < MAX_INPUT_DEVICES; i++) + netplay->device_clients[i] &= ~(1L<connections_size; i++) { + uint32_t client_num = i+1; connection = &netplay->connections[i]; if ((connection->active || connection->mode == NETPLAY_CONNECTION_DELAYED_DISCONNECT) && connection->delay_frame && connection->delay_frame <= netplay->self_frame_count) { /* Something was delayed! Prepare the MODE command */ - uint32_t payload[2]; + uint32_t payload[3]; payload[0] = htonl(connection->delay_frame); - payload[1] = htonl(connection->player); + payload[1] = htonl(client_num); + payload[2] = htonl(0); /* Remove the connection entirely if relevant */ if (connection->mode == NETPLAY_CONNECTION_DELAYED_DISCONNECT) @@ -189,24 +205,43 @@ void netplay_delayed_state_change(netplay_t *netplay) } /* Send the specified input data */ -static bool send_input_frame(netplay_t *netplay, - struct netplay_connection *only, struct netplay_connection *except, - uint32_t frame, uint32_t player, uint32_t *state) +static bool send_input_frame(netplay_t *netplay, struct delta_frame *dframe, + struct netplay_connection *only, struct netplay_connection *except, + uint32_t client_num) { - uint32_t buffer[2 + WORDS_PER_FRAME]; - size_t i; +#define BUFSZ 16 /* FIXME: Arbitrary restriction */ + uint32_t buffer[BUFSZ], devices, device; + size_t bufused, i; + /* Set up the basic buffer */ + bufused = 4; buffer[0] = htonl(NETPLAY_CMD_INPUT); - buffer[1] = htonl(WORDS_PER_FRAME * sizeof(uint32_t)); - buffer[2] = htonl(frame); - buffer[3] = htonl(player); - buffer[4] = htonl(state[0]); - buffer[5] = htonl(state[1]); - buffer[6] = htonl(state[2]); + buffer[2] = htonl(dframe->frame); + buffer[3] = htonl(client_num); + + /* Add the device data */ + devices = netplay->client_devices[client_num]; + for (device = 0; device < devices; device++) + { + netplay_input_state_t istate; + if (!(devices & (1<real_input[device]; + while (istate && istate->client_num != client_num) + istate = istate->next; + if (!istate) + continue; + if (bufused + istate->size >= BUFSZ) + continue; /* FIXME: More severe? */ + for (i = 0; i < istate->size; i++) + buffer[bufused+i] = htonl(istate->data[i]); + bufused += istate->size; + } + buffer[1] = htonl((bufused-2) * sizeof(uint32_t)); if (only) { - if (!netplay_send(&only->send_packet_buffer, only->fd, buffer, sizeof(buffer))) + if (!netplay_send(&only->send_packet_buffer, only->fd, buffer, bufused*sizeof(uint32_t))) { netplay_hangup(netplay, only); return false; @@ -221,16 +256,17 @@ static bool send_input_frame(netplay_t *netplay, if (connection->active && connection->mode >= NETPLAY_CONNECTION_CONNECTED && (connection->mode != NETPLAY_CONNECTION_PLAYING || - connection->player != player)) + i+1 != client_num)) { if (!netplay_send(&connection->send_packet_buffer, connection->fd, - buffer, sizeof(buffer))) + buffer, bufused*sizeof(uint32_t))) netplay_hangup(netplay, connection); } } } return true; +#undef BUFSZ } /** @@ -243,24 +279,50 @@ static bool send_input_frame(netplay_t *netplay, bool netplay_send_cur_input(netplay_t *netplay, struct netplay_connection *connection) { +#define BUFSZ 16 + uint32_t buffer[BUFSZ]; + size_t bufused; struct delta_frame *dframe = &netplay->buffer[netplay->self_ptr]; - uint32_t player; + uint32_t from_client, to_client; + uint32_t devices, device; + size_t i; + netplay_input_state_t istate; + + to_client = connection - netplay->connections + 1; if (netplay->is_server) { - /* Send the other players' input data */ - for (player = 0; player < MAX_USERS; player++) + /* Send the other players' input data (FIXME: This involves an + * unacceptable amount of recalculating) */ + for (from_client = 1; from_client < MAX_CLIENTS; from_client++) { - if (connection->mode == NETPLAY_CONNECTION_PLAYING && - connection->player == player) + if (from_client == to_client) continue; - if ((netplay->connected_players & (1<connected_players1 & (1<have_real[player]) + if (dframe->have_real[from_client]) { + bufused = 0; + devices = netplay->client_devices[from_client]; + for (device = 0; device < MAX_INPUT_DEVICES; device++) + { + if (!(devices & (1<real_input[device]; + while (istate && istate->client_num != from_client) + istate = istate->next; + if (!istate) + continue; + if (bufused + istate->size >= BUFSZ) + continue; + for (i = 0; i < istate->size; i++) + buffer[bufused+i] = istate->data[i]; + bufused += istate->size; + } if (!send_input_frame(netplay, connection, NULL, - netplay->self_frame_count, player, - dframe->real_input_state[player])) + netplay->self_frame_count, from_client, bufused, buffer)) return false; } } @@ -281,12 +343,32 @@ bool netplay_send_cur_input(netplay_t *netplay, if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING || netplay->self_mode == NETPLAY_CONNECTION_SLAVE) { + devices = netplay->self_devices; + bufused = 0; + for (device = 0; device < MAX_INPUT_DEVICES; device++) + { + if (!(devices & (1<real_input[device]; + while (istate && istate->client_num != netplay->self_client_num) + istate = istate->next; + if (!istate) + continue; + if (bufused + istate->size >= BUFSZ) + continue; + for (i = 0; i < istate->size; i++) + buffer[bufused+i] = istate->data[i]; + bufused += istate->size; + } if (!send_input_frame(netplay, connection, NULL, netplay->self_frame_count, - (netplay->is_server ? NETPLAY_CMD_INPUT_BIT_SERVER : 0) | netplay->self_player, - dframe->self_state)) + (netplay->is_server ? NETPLAY_CMD_INPUT_BIT_SERVER : 0) | netplay->self_client_num, + bufused, buffer)) return false; } +#undef BUFSZ if (!netplay_send_flush(&connection->send_packet_buffer, connection->fd, false)) @@ -436,7 +518,7 @@ bool netplay_cmd_mode(netplay_t *netplay, case NETPLAY_CONNECTION_SLAVE: payload = &payloadBuf; payloadBuf = htonl(NETPLAY_CMD_PLAY_BIT_SLAVE); - /* Intentional fallthrough */ + /* no break */ case NETPLAY_CONNECTION_PLAYING: cmd = NETPLAY_CMD_PLAY; @@ -506,43 +588,56 @@ static bool netplay_get_cmd(netplay_t *netplay, case NETPLAY_CMD_INPUT: { - uint32_t buffer[WORDS_PER_FRAME]; - uint32_t player; + uint32_t frame_num, client_num, input_size, devices, device; + bool is_server; unsigned i; struct delta_frame *dframe; - if (cmd_size != WORDS_PER_FRAME * sizeof(uint32_t)) + if (cmd_size < 2*sizeof(uint32_t)) { - RARCH_ERR("NETPLAY_CMD_INPUT received an unexpected payload size.\n"); + RARCH_ERR("NETPLAY_CMD_INPUT too short, no frame/client number."); return netplay_cmd_nak(netplay, connection); } - RECV(buffer, sizeof(buffer)) - { - RARCH_ERR("Failed to receive NETPLAY_CMD_INPUT input.\n"); - return netplay_cmd_nak(netplay, connection); - } - - for (i = 0; i < WORDS_PER_FRAME; i++) - buffer[i] = ntohl(buffer[i]); + RECV(&frame_num, sizeof(frame_num)) + return false; + RECV(&client_num, sizeof(client_num)) + return false; + frame_num = nhtohl(frame_num); + client_num = ntohl(client_num); + is_server = (client_num & NETPLAY_CMD_INPUT_BIT_SERVER)?true:false; + client_num &= 0xFFFF; if (netplay->is_server) { - /* Ignore the claimed player #, must be this client */ + /* Ignore the claimed client #, must be this client */ if (connection->mode != NETPLAY_CONNECTION_PLAYING && connection->mode != NETPLAY_CONNECTION_SLAVE) { RARCH_ERR("Netplay input from non-participating player.\n"); return netplay_cmd_nak(netplay, connection); } - player = connection->player; - } - else - { - player = buffer[1] & ~NETPLAY_CMD_INPUT_BIT_SERVER; + is_server = false; + client_num = connection - netplay->connections + 1; } - if (player >= MAX_USERS || !(netplay->connected_players & (1< MAX_CLIENTS) + { + RARCH_ERROR("NETPLAY_CMD_INPUT received data for an unsupported client.\n"); + return netplay_cmd_nak(netplay, connection); + } + + /* Figure out how much input is expected */ + devices = netplay->client_devices[client_num]; + input_size = netplay_expected_input_size(devices); + + if (cmd_size != (2+input_size) * sizeof(uint32_t)) + { + RARCH_ERR("NETPLAY_CMD_INPUT received an unexpected payload size.\n"); + return netplay_cmd_nak(netplay, connection); + } + + if (client_num >= MAX_CLIENTS || !(netplay->connected_players1 & (1<mode == NETPLAY_CONNECTION_PLAYING) { - if (buffer[0] < netplay->read_frame_count[player]) + if (frame_num < netplay->read_frame_count1[client_num]) { /* We already had this, so ignore the new transmission */ break; } - else if (buffer[0] > netplay->read_frame_count[player]) + else if (frame_num > netplay->read_frame_count1[client_num]) { /* Out of order = out of luck */ RARCH_ERR("Netplay input out of order.\n"); @@ -565,42 +660,57 @@ static bool netplay_get_cmd(netplay_t *netplay, } /* The data's good! */ - dframe = &netplay->buffer[netplay->read_ptr[player]]; - if (!netplay_delta_frame_ready(netplay, dframe, netplay->read_frame_count[player])) + dframe = &netplay->buffer[netplay->read_ptr1[client_num]]; + if (!netplay_delta_frame_ready(netplay, dframe, netplay->read_frame_count1[client_num])) { /* Hopefully we'll be ready after another round of input */ goto shrt; } - memcpy(dframe->real_input_state[player], buffer + 2, - WORDS_PER_INPUT*sizeof(uint32_t)); - dframe->have_real[player] = true; + + /* Copy in the input */ + for (device = 0; device < MAX_INPUT_DEVICES; device++) + { + netplay_input_state_t istate; + uint32_t dsize, di; + if (!(devices & (1<real_input[device], + client_num, dsize, true); + RECV(istate->data, dsize*sizeof(uint32_t)) + return false; + for (di = 0; di < dsize; di++) + istate->data[di] = ntohl(istate->data[di]); + istate->is_real = true; + } + dframe->have_real[client_num] = true; /* Slaves may go through several packets of data in the same frame * if latency is choppy, so we advance and send their data after * handling all network data this frame */ if (connection->mode == NETPLAY_CONNECTION_PLAYING) { - netplay->read_ptr[player] = NEXT_PTR(netplay->read_ptr[player]); - netplay->read_frame_count[player]++; + netplay->read_ptr1[client_num] = NEXT_PTR(netplay->read_ptr1[client_num]); + netplay->read_frame_count1[client_num]++; if (netplay->is_server) { - /* Forward it on if it's past data*/ + /* Forward it on if it's past data */ if (dframe->frame <= netplay->self_frame_count) - send_input_frame(netplay, NULL, connection, buffer[0], - player, dframe->real_input_state[player]); + send_input_frame(netplay, dframe, NULL, connection, client_num); } } /* If this was server data, advance our server pointer too */ - if (!netplay->is_server && (buffer[1] & NETPLAY_CMD_INPUT_BIT_SERVER)) + if (is_server) { - netplay->server_ptr = netplay->read_ptr[player]; - netplay->server_frame_count = netplay->read_frame_count[player]; + netplay->server_ptr = netplay->read_ptr1[client_num]; + netplay->server_frame_count = netplay->read_frame_count1[client_num]; } #ifdef DEBUG_NETPLAY_STEPS - RARCH_LOG("Received input from %u\n", player); + RARCH_LOG("Received input from %u\n", client_num); print_state(netplay); #endif break; @@ -685,7 +795,9 @@ static bool netplay_get_cmd(netplay_t *netplay, case NETPLAY_CMD_SPECTATE: { - uint32_t payload[2]; + uint32_t payload[3]; + uint32_t client_num; + size_t i; if (!netplay->is_server) { @@ -693,20 +805,25 @@ static bool netplay_get_cmd(netplay_t *netplay, return netplay_cmd_nak(netplay, connection); } + client_num = connection - netplay->connections + 1; + if (connection->mode == NETPLAY_CONNECTION_PLAYING || connection->mode == NETPLAY_CONNECTION_SLAVE) { /* The frame we haven't received is their end frame */ - connection->delay_frame = netplay->read_frame_count[connection->player]; + connection->delay_frame = netplay->read_frame_count1[client_num]; /* Mark them as not playing anymore */ connection->mode = NETPLAY_CONNECTION_SPECTATING; - netplay->connected_players &= ~(1<player); - netplay->connected_slaves &= ~(1<player); + netplay->connected_players1 &= ~(1<connected_slaves1 &= ~(1<client_devices[client_num] = 0; + for (i = 0; i < MAX_INPUT_DEVICES; i++) + netplay->device_clients[client_num] &= ~(1<player+1); + snprintf(msg, sizeof(msg)-1, "Player %d has left", client_num+1); RARCH_LOG("%s\n", msg); runloop_msg_queue_push(msg, 1, 180, false); } @@ -716,30 +833,32 @@ static bool netplay_get_cmd(netplay_t *netplay, } /* Tell the player even if they were confused */ - payload[1] = htonl(NETPLAY_CMD_MODE_BIT_YOU | connection->player); + payload[1] = htonl(NETPLAY_CMD_MODE_BIT_YOU | client_num); + payload[2] = htonl(0); netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload)); break; } case NETPLAY_CMD_PLAY: { - uint32_t payload[2]; - uint32_t player = 0; + uint32_t devices = 0, device, client_num; + uint32_t payload[3]; bool slave = false; settings_t *settings = config_get_ptr(); /* Check if they requested slave mode */ if (cmd_size == sizeof(uint32_t)) { - RECV(payload, sizeof(uint32_t)) + RECV(&devices, sizeof(devices)) { RARCH_ERR("Failed to receive NETPLAY_CMD_PLAY payload.\n"); return netplay_cmd_nak(netplay, connection); } - payload[0] = ntohl(payload[0]); - if (payload[0] & NETPLAY_CMD_PLAY_BIT_SLAVE) + devices = ntohl(devices); + if (devices & NETPLAY_CMD_PLAY_BIT_SLAVE) slave = true; + devices &= ~(NETPLAY_CMD_PLAY_BIT_SLAVE); } else if (cmd_size != 0) { @@ -760,6 +879,7 @@ static bool netplay_get_cmd(netplay_t *netplay, RARCH_ERR("NETPLAY_CMD_PLAY from a server.\n"); return netplay_cmd_nak(netplay, connection); } + client_num = connection - netplay->connections + 1; if (connection->delay_frame) { @@ -777,21 +897,20 @@ static bool netplay_get_cmd(netplay_t *netplay, break; } - /* Find an available player slot */ - for (player = 0; player <= netplay->player_max; player++) + /* Find an available device (FIXME: Honor device request) */ + for (device = 0; device <= netplay->input_device_max; device++) { - if (!(netplay->self_mode == NETPLAY_CONNECTION_PLAYING && - netplay->self_player == player) && - !(netplay->connected_players & (1<device_clients[device]) break; } - if (player > netplay->player_max) + if (device > netplay->input_device_max) { /* No slots free! */ payload[0] = htonl(NETPLAY_CMD_MODE_REFUSED_REASON_NO_SLOTS); netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE_REFUSED, payload, sizeof(uint32_t)); break; } + payload[2] = htonl(1<mode != NETPLAY_CONNECTION_PLAYING && connection->mode != NETPLAY_CONNECTION_SLAVE) @@ -799,20 +918,22 @@ static bool netplay_get_cmd(netplay_t *netplay, /* Mark them as playing */ connection->mode = slave ? NETPLAY_CONNECTION_SLAVE : NETPLAY_CONNECTION_PLAYING; - connection->player = player; - netplay->connected_players |= 1<connected_players1 |= 1<connected_slaves |= 1<connected_slaves1 |= 1<client_devices[client_num] |= 1<device_clients[device] |= 1<player); + client_num); netplay_send_raw_cmd_all(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload)); /* Announce it */ msg[sizeof(msg)-1] = '\0'; - snprintf(msg, sizeof(msg)-1, "Player %d has joined", player+1); + snprintf(msg, sizeof(msg)-1, "Player %d has joined", client_num+1); RARCH_LOG("%s\n", msg); runloop_msg_queue_push(msg, 1, 180, false); @@ -823,19 +944,20 @@ static bool netplay_get_cmd(netplay_t *netplay, ((connection->mode == NETPLAY_CONNECTION_SLAVE)? NETPLAY_CMD_MODE_BIT_SLAVE:0) | NETPLAY_CMD_MODE_BIT_YOU | - connection->player); + client_num); + payload[2] = htonl(1<read_ptr[player] = NEXT_PTR(netplay->self_ptr); - netplay->read_frame_count[player] = netplay->self_frame_count + 1; + netplay->read_ptr1[client_num] = NEXT_PTR(netplay->self_ptr); + netplay->read_frame_count1[client_num] = netplay->self_frame_count + 1; break; } case NETPLAY_CMD_MODE: { - uint32_t payload[2]; - uint32_t frame, mode, player; + uint32_t payload[3]; + uint32_t frame, mode, client_num, devices; size_t ptr; struct delta_frame *dframe; @@ -870,13 +992,15 @@ static bool netplay_get_cmd(netplay_t *netplay, netplay->force_rewind = true; mode = ntohl(payload[1]); - player = mode & 0xFFFF; - if (player >= MAX_USERS) + client_num = mode & 0xFFFF; + if (client_num >= MAX_CLIENTS) { RARCH_ERR("Received NETPLAY_CMD_MODE for a higher player number than we support.\n"); return netplay_cmd_nak(netplay, connection); } + devices = ntohl(payload[2]); + if (mode & NETPLAY_CMD_MODE_BIT_YOU) { /* A change to me! */ @@ -897,24 +1021,22 @@ static bool netplay_get_cmd(netplay_t *netplay, /* Our mode is based on whether we have the slave bit set */ if (mode & NETPLAY_CMD_MODE_BIT_SLAVE) - { netplay->self_mode = NETPLAY_CONNECTION_SLAVE; - - /* In slave mode we receive the data from the remote side, so - * we actually consider ourself a connected player */ - netplay->connected_players |= (1<read_ptr[player] = netplay->server_ptr; - netplay->read_frame_count[player] = netplay->server_frame_count; - } else - { netplay->self_mode = NETPLAY_CONNECTION_PLAYING; - } - netplay->self_player = player; + + netplay->connected_players1 |= (1<client_devices[client_num] = devices; + for (device = 0; device < MAX_INPUT_DEVICES; device++) + if (devices & (1<device_clients[device] |= (1<self_devices = devices; /* Fix up current frame info */ if (frame <= netplay->self_frame_count) { + /* FIXME: Must generate frames with 0 data */ +#if 0 /* It wanted past frames, better send 'em! */ START(netplay->server_ptr); while (dframe->used && dframe->frame <= netplay->self_frame_count) @@ -925,11 +1047,13 @@ static bool netplay_get_cmd(netplay_t *netplay, if (dframe->frame == netplay->self_frame_count) break; NEXT(); } +#endif } else { uint32_t frame_count; + netplay_input_state_t istate; /* It wants future frames, make sure we don't capture or send intermediate ones */ START(netplay->self_ptr); @@ -946,8 +1070,6 @@ static bool netplay_get_cmd(netplay_t *netplay, } } - memset(dframe->self_state, 0, sizeof(dframe->self_state)); - memset(dframe->real_input_state[player], 0, sizeof(dframe->self_state)); dframe->have_local = true; /* Go on to the next delta frame */ @@ -962,7 +1084,7 @@ static bool netplay_get_cmd(netplay_t *netplay, /* Announce it */ msg[sizeof(msg)-1] = '\0'; - snprintf(msg, sizeof(msg)-1, "You have joined as player %d", player+1); + snprintf(msg, sizeof(msg)-1, "You have joined as player %d", client_num+1); RARCH_LOG("%s\n", msg); runloop_msg_queue_push(msg, 1, 180, false); @@ -982,7 +1104,10 @@ static bool netplay_get_cmd(netplay_t *netplay, } /* Unmark ourself, in case we were in slave mode */ - netplay->connected_players &= ~(1<connected_players1 &= ~(1<client_devices[client_num] = 0; + for (device = 0; device < MAX_INPUT_DEVICES; device++) + netplay->device_clients[device] &= ~(1<connected_players |= (1<connected_players1 |= (1<client_devices[client_num] = devices; + for (device = 0; device < MAX_INPUT_DEVICES; device++) + if (devices & (1<device_clients[device] |= (1<read_ptr[player] = netplay->server_ptr; - netplay->read_frame_count[player] = netplay->server_frame_count; + netplay->read_ptr1[client_num] = netplay->server_ptr; + netplay->read_frame_count1[client_num] = netplay->server_frame_count; /* Announce it */ msg[sizeof(msg)-1] = '\0'; - snprintf(msg, sizeof(msg)-1, "Player %d has joined", player+1); + snprintf(msg, sizeof(msg)-1, "Player %d has joined", client_num+1); RARCH_LOG("%s\n", msg); runloop_msg_queue_push(msg, 1, 180, false); @@ -1027,11 +1156,14 @@ static bool netplay_get_cmd(netplay_t *netplay, } else { - netplay->connected_players &= ~(1<connected_players1 &= ~(1<client_devices[client_num] = 0; + for (device = 0; device < MAX_INPUT_DEVICES; device++) + netplay->device_clients[device] &= ~(1<is_server && !netplay->connected_players) + if (netplay->is_server && netplay->connected_players1<=1) { /* Nothing at all to read! */ netplay->unread_ptr = netplay->self_ptr; @@ -50,16 +50,16 @@ void netplay_update_unread_ptr(netplay_t *netplay) { size_t new_unread_ptr = 0; uint32_t new_unread_frame_count = (uint32_t) -1; - uint32_t player; + uint32_t client; - for (player = 0; player < MAX_USERS; player++) + for (client = 0; client < MAX_CLIENTS; client++) { - if (!(netplay->connected_players & (1<connected_slaves & (1<read_frame_count[player] < new_unread_frame_count) + if (!(netplay->connected_players1 & (1<connected_slaves1 & (1<read_frame_count1[client] < new_unread_frame_count) { - new_unread_ptr = netplay->read_ptr[player]; - new_unread_frame_count = netplay->read_frame_count[player]; + new_unread_ptr = netplay->read_ptr1[client]; + new_unread_frame_count = netplay->read_frame_count1[client]; } } @@ -93,19 +93,28 @@ void netplay_update_unread_ptr(netplay_t *netplay) */ void netplay_simulate_input(netplay_t *netplay, size_t sim_ptr, bool resim) { - uint32_t player; + uint32_t client; size_t prev; struct delta_frame *simframe, *pframe; + netplay_input_state_t simstate, pstate; simframe = &netplay->buffer[sim_ptr]; - for (player = 0; player < MAX_USERS; player++) + for (client = 0; client < MAX_CLIENTS; client++) { - if (!(netplay->connected_players & (1<have_real[player]) continue; + if (!(netplay->connected_players1 & (1<have_real[client]) continue; - prev = PREV_PTR(netplay->read_ptr[player]); + simstate = netplay_input_state_for(&simframe->simulated_input, client, 3 /* FIXME */, false); + if (!simstate) + continue; + + prev = PREV_PTR(netplay->read_ptr1[client]); pframe = &netplay->buffer[prev]; + pstate = netplay_input_state_for(&pframe->real_input, client, 3 /* FIXME */, false); + if (!pstate) + continue; if (resim) { @@ -128,15 +137,13 @@ void netplay_simulate_input(netplay_t *netplay, size_t sim_ptr, bool resim) (1U<simulated_input_state[player][0] & keep; - sim_state |= pframe->real_input_state[player][0] & ~keep; - simframe->simulated_input_state[player][0] = sim_state; + simstate->data[0] &= keep; + simstate->data[0] |= pstate->data[0] & ~keep; } else { - memcpy(simframe->simulated_input_state[player], - pframe->real_input_state[player], - WORDS_PER_INPUT * sizeof(uint32_t)); + memcpy(simstate->data, pstate->data, + simstate->size * sizeof(uint32_t)); } } } @@ -391,7 +398,7 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled) } /* Only relevant if we're connected and not in a desynching operation */ - if ((netplay->is_server && !netplay->connected_players) || + if ((netplay->is_server && (netplay->connected_players1>1)) || (netplay->self_mode < NETPLAY_CONNECTION_CONNECTED) || (netplay->desync)) {