diff --git a/network/netplay/README b/network/netplay/README index 00291d1850..dda433a9b5 100644 --- a/network/netplay/README +++ b/network/netplay/README @@ -357,3 +357,40 @@ Unused Command: CFG_ACK Unused + + +Input types + +Each input device uses a number of words fixed by the type of device. When +buttons are listed, they are listed from lowest bit to highest bit, i.e., +reverse order. When insufficient buttons are present to represent to represent +a full 32-bit word, the remainder are reserved and unused. + +JOYPAD + { + B, Y, Select, Start, Up, Down, Left, Right, A, X, L, R, L2, R2, L3, R3 + } + +MOUSE + { + unused, unused, Left, Right, Wheel up, Wheel down, Middle, Horizontal + wheel up, Horizontal wheel down + Y: int16 + X: int16 + } + +LIGHTGUN + { + unused, unused, Trigger, Cursor, Turbo, Pause, Start + Y: int16 + X: int16 + } + +ANALOG + { + buttons: uint32, as JOYPAD + Left analog Y: int16 + Left analog X: int16 + Right analog Y: int16 + Right analog X: int16 + } diff --git a/network/netplay/netplay_delta.c b/network/netplay/netplay_delta.c index 7828cb9f9a..de4737b7c1 100644 --- a/network/netplay/netplay_delta.c +++ b/network/netplay/netplay_delta.c @@ -167,14 +167,22 @@ netplay_input_state_t netplay_input_state_for(netplay_input_state_t *list, * * Size in words for a given set of devices. */ -uint32_t netplay_expected_input_size(uint32_t devices) +uint32_t netplay_expected_input_size(netplay_t *netplay, uint32_t devices) { - /* FIXME: For now, we assume all devices are three words, because in the implementation, they are. */ - uint32_t ret = 0; - while (devices) + uint32_t ret = 0, device; + for (device = 0; device < MAX_INPUT_DEVICES; device++) { - if (devices & 1) ret += 3; - devices >>= 1; + if (!(devices & (1<config_devices[device]&RETRO_DEVICE_MASK) + { + /* These are all essentially magic numbers, but each device has a + * fixed size, documented in network/netplay/README */ + case RETRO_DEVICE_JOYPAD: ret += 1; break; + case RETRO_DEVICE_MOUSE: ret += 2; break; + case RETRO_DEVICE_LIGHTGUN: ret += 2; break; + case RETRO_DEVICE_ANALOG: ret += 3; break; + default: break; /* Unsupported */ + } } return ret; } diff --git a/network/netplay/netplay_frontend.c b/network/netplay/netplay_frontend.c index e2124fc7d9..89b914f646 100644 --- a/network/netplay/netplay_frontend.c +++ b/network/netplay/netplay_frontend.c @@ -137,41 +137,70 @@ static bool get_self_input_state(netplay_t *netplay) continue; /* Find an appropriate local device */ - dev_type = input_config_get_device(devi)&RETRO_DEVICE_MASK; + dev_type = netplay->config_devices[devi]&RETRO_DEVICE_MASK; for (local_device = 0; local_device < MAX_INPUT_DEVICES; local_device++) { if (used_devices & (1<config_devices[local_device]&RETRO_DEVICE_MASK) == dev_type) break; } if (local_device == MAX_INPUT_DEVICES) local_device = 0; used_devices |= (1<real_input[devi], - netplay->self_client_num, 3 /* FIXME */, true, false); + netplay->self_client_num, netplay_expected_input_size(netplay, 1 << devi), + true, false); if (!istate) continue; /* FIXME: More severe? */ + /* First frame we always give zero input since relying on + * input from first frame screws up when we use -F 0. */ if (!input_driver_is_libretro_input_blocked() && netplay->self_frame_count > 0) { - /* 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(local_device, - RETRO_DEVICE_JOYPAD, 0, (unsigned)i); - state[0] |= tmp ? 1 << i : 0; - } + unsigned dtype = netplay->config_devices[devi]&RETRO_DEVICE_MASK; - for (i = 0; i < 2; i++) + switch (dtype) { - int16_t tmp_x = cb(local_device, - RETRO_DEVICE_ANALOG, (unsigned)i, 0); - int16_t tmp_y = cb(local_device, - RETRO_DEVICE_ANALOG, (unsigned)i, 1); - state[1 + i] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16); + case RETRO_DEVICE_ANALOG: + for (i = 0; i < 2; i++) + { + int16_t tmp_x = cb(local_device, + RETRO_DEVICE_ANALOG, (unsigned)i, 0); + int16_t tmp_y = cb(local_device, + RETRO_DEVICE_ANALOG, (unsigned)i, 1); + state[1 + i] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16); + } + /* no break */ + + case RETRO_DEVICE_JOYPAD: + for (i = 0; i <= RETRO_DEVICE_ID_JOYPAD_R3; i++) + { + int16_t tmp = cb(local_device, + RETRO_DEVICE_JOYPAD, 0, (unsigned)i); + state[0] |= tmp ? 1 << i : 0; + } + break; + + case RETRO_DEVICE_MOUSE: + case RETRO_DEVICE_LIGHTGUN: + { + int16_t tmp_x = cb(local_device, dtype, 0, 0); + int16_t tmp_y = cb(local_device, dtype, 0, 1); + state[1] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16); + for (i = 2; + i <= ((dtype == RETRO_DEVICE_MOUSE) ? + RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN : + RETRO_DEVICE_ID_LIGHTGUN_START); + i++) + { + int16_t tmp = cb(local_device, dtype, 0, + (unsigned) i); + state[0] |= tmp ? 1 << i : 0; + } + break; + } } } } @@ -504,8 +533,20 @@ static int16_t netplay_input_state(netplay_t *netplay, const uint32_t *curr_input_state = NULL; if (port >= MAX_INPUT_DEVICES) - { return 0; + + /* If the port doesn't seem to correspond to the device, "correct" it. This + * is common with e.g. zappers. */ + if (device != RETRO_DEVICE_JOYPAD && + (netplay->config_devices[port]&RETRO_DEVICE_MASK) != device) + { + for (port = 0; port < MAX_INPUT_DEVICES; port++) + { + if ((netplay->config_devices[port]&RETRO_DEVICE_MASK) == device) + break; + } + if (port == MAX_INPUT_DEVICES) + return 0; } delta = &netplay->buffer[ptr]; @@ -513,6 +554,8 @@ static int16_t netplay_input_state(netplay_t *netplay, if (!istate || !istate->used) return 0; + if (istate->size == 0) + return 0; curr_input_state = istate->data; switch (device) @@ -522,10 +565,23 @@ static int16_t netplay_input_state(netplay_t *netplay, case RETRO_DEVICE_ANALOG: { - uint32_t state = curr_input_state[1 + idx]; + uint32_t state; + if (istate->size != 3) + return 0; + state = curr_input_state[1 + idx]; return (int16_t)(uint16_t)(state >> (id * 16)); } + case RETRO_DEVICE_MOUSE: + case RETRO_DEVICE_LIGHTGUN: + { + if (istate->size != 2) + return 0; + if (id <= RETRO_DEVICE_ID_MOUSE_Y) + return (int16_t)(uint16_t)(curr_input_state[1] >> (id * 16)); + return ((1 << id) & curr_input_state[0]) ? 1 : 0; + } + default: return 0; } diff --git a/network/netplay/netplay_handshake.c b/network/netplay/netplay_handshake.c index a369bca35e..2f4dd40dc1 100644 --- a/network/netplay/netplay_handshake.c +++ b/network/netplay/netplay_handshake.c @@ -35,7 +35,6 @@ #include "../../content.h" #include "../../retroarch.h" #include "../../version.h" -#include "../../input/input_driver.h" #ifdef HAVE_MENU #include "../../menu/widgets/menu_input_dialog.h" @@ -594,7 +593,7 @@ bool netplay_handshake_sync(netplay_t *netplay, /* Now send the device info */ for (i = 0; i < MAX_INPUT_DEVICES; i++) { - device = htonl(input_config_get_device((unsigned)i)); + device = htonl(netplay->config_devices[i]); if (!netplay_send(&connection->send_packet_buffer, connection->fd, &device, sizeof(device))) return false; @@ -1022,6 +1021,7 @@ bool netplay_handshake_pre_sync(netplay_t *netplay, pad.port = (unsigned)i; pad.device = ntohl(device); + netplay->config_devices[i] = pad.device; core_set_controller_port_device(&pad); } diff --git a/network/netplay/netplay_init.c b/network/netplay/netplay_init.c index 18e6af47ee..893e3d89e9 100644 --- a/network/netplay/netplay_init.c +++ b/network/netplay/netplay_init.c @@ -32,6 +32,7 @@ #include "../../autosave.h" #include "../../retroarch.h" +#include "../../input/input_driver.h" #if defined(AF_INET6) && !defined(HAVE_SOCKET_LEGACY) #define HAVE_INET6 1 @@ -466,8 +467,21 @@ netplay_t *netplay_new(void *direct_host, const char *server, uint16_t port, return NULL; } - if (!netplay->is_server) + if (netplay->is_server) { + /* Clients get device info from the server */ + unsigned i; + for (i = 0; i < MAX_INPUT_DEVICES; i++) + { + uint32_t dtype = input_config_get_device(i); + netplay->config_devices[i] = dtype; + if (dtype != RETRO_DEVICE_NONE && !netplay_expected_input_size(netplay, 1<connections[0]); netplay->connections[0].mode = NETPLAY_CONNECTION_INIT; diff --git a/network/netplay/netplay_io.c b/network/netplay/netplay_io.c index 9498efb8e1..c611397ef9 100644 --- a/network/netplay/netplay_io.c +++ b/network/netplay/netplay_io.c @@ -232,7 +232,7 @@ static bool send_input_frame(netplay_t *netplay, struct delta_frame *dframe, if (!(devices & (1<real_input[device]; - while (istate && istate->client_num != client_num) + while (istate && (!istate->used || istate->client_num != client_num)) istate = istate->next; if (!istate) continue; @@ -810,7 +810,7 @@ static void handle_play_spectate(netplay_t *netplay, uint32_t client_num, /* Find an available device */ for (device = 0; device < MAX_INPUT_DEVICES; device++) { - if (input_config_get_device(device) == RETRO_DEVICE_NONE) + if (netplay->config_devices[device] == RETRO_DEVICE_NONE) { device = MAX_INPUT_DEVICES; break; @@ -985,7 +985,7 @@ static bool netplay_get_cmd(netplay_t *netplay, /* Figure out how much input is expected */ devices = netplay->client_devices[client_num]; - input_size = netplay_expected_input_size(devices); + input_size = netplay_expected_input_size(netplay, devices); if (cmd_size != (2+input_size) * sizeof(uint32_t)) { @@ -1037,7 +1037,7 @@ static bool netplay_get_cmd(netplay_t *netplay, if (!(devices & (1<real_input[device], client_num, dsize, true, false); if (!istate) @@ -1297,12 +1297,15 @@ static bool netplay_get_cmd(netplay_t *netplay, { for (device = 0; device < MAX_INPUT_DEVICES; device++) { + uint32_t dsize; + netplay_input_state_t istate; if (!(devices & (1<real_input[device], client_num, - 3 /* FIXME */, false, false); + dsize = netplay_expected_input_size(netplay, 1 << device); + istate = netplay_input_state_for( + &dframe->real_input[device], client_num, dsize, + false, false); if (!istate) continue; - memset(istate->data, 0, istate->size*sizeof(uint32_t)); + memset(istate->data, 0, dsize*sizeof(uint32_t)); } dframe->have_local = true; dframe->have_real[client_num] = true; diff --git a/network/netplay/netplay_private.h b/network/netplay/netplay_private.h index eada25d118..cb87db74c1 100644 --- a/network/netplay/netplay_private.h +++ b/network/netplay/netplay_private.h @@ -436,6 +436,10 @@ struct netplay * attempt to stay in sync. */ uint32_t desync; + /* The device types for every connected device. We store them and ignore any + * menu changes, as netplay needs fixed devices. */ + uint32_t config_devices[MAX_INPUT_DEVICES]; + struct retro_callbacks cbs; /* TCP port (only set if serving) */ @@ -680,7 +684,7 @@ netplay_input_state_t netplay_input_state_for(netplay_input_state_t *list, * * Size in words for a given set of devices. */ -uint32_t netplay_expected_input_size(uint32_t devices); +uint32_t netplay_expected_input_size(netplay_t *netplay, uint32_t devices); /*************************************************************** diff --git a/network/netplay/netplay_sync.c b/network/netplay/netplay_sync.c index 9d349c72ed..6dab8938e5 100644 --- a/network/netplay/netplay_sync.c +++ b/network/netplay/netplay_sync.c @@ -96,15 +96,17 @@ struct vote_count { netplay_input_state_t netplay_device_client_state(netplay_t *netplay, struct delta_frame *simframe, uint32_t device, uint32_t client) { + uint32_t dsize = netplay_expected_input_size(netplay, 1 << device); netplay_input_state_t simstate = netplay_input_state_for( - &simframe->real_input[device], client, 3 /* FIXME */, false, true); + &simframe->real_input[device], client, + dsize, false, true); if (!simstate) { if (netplay->read_frame_count[client] > simframe->frame) return NULL; simstate = netplay_input_state_for(&simframe->simlated_input[device], - client, 3 /* FIXME */, false, true); + client, dsize, false, true); } return simstate; } @@ -129,7 +131,7 @@ static void netplay_merge_digital(netplay_t *netplay, /* Make sure all real clients are accounted for */ for (simstate = simframe->real_input[device]; simstate; simstate = simstate->next) { - if (!simstate->used || simstate->size != 3 /* FIXME */) continue; + if (!simstate->used || simstate->size != resstate->size) continue; clients |= 1<client_num; } @@ -137,6 +139,8 @@ static void netplay_merge_digital(netplay_t *netplay, { /* Vote mode requires counting all the bits */ uint32_t client_count = 0; + + /* This just assumes we have no more than three words, will need to be adjusted for new devices */ struct vote_count votes[3] = {0}; for (client = 0; client < MAX_CLIENTS; client++) { @@ -145,7 +149,7 @@ static void netplay_merge_digital(netplay_t *netplay, if (!simstate) continue; client_count++; - for (word = 0; word < 3 /* FIXME */; word++) + for (word = 0; word < resstate->size; word++) { if (!digital[word]) continue; for (bit = 0; bit < 32; bit++) @@ -159,7 +163,7 @@ static void netplay_merge_digital(netplay_t *netplay, /* Now count all the bits */ client_count /= 2; - for (word = 0; word < 3 /* FIXME */; word++) + for (word = 0; word < resstate->size; word++) { for (bit = 0; bit < 32; bit++) { @@ -176,7 +180,7 @@ static void netplay_merge_digital(netplay_t *netplay, if (!(clients & (1<size; word++) { uint32_t part; if (!digital[word]) continue; @@ -231,7 +235,7 @@ static void merge_analog_part(netplay_t *netplay, /* Make sure all real clients are accounted for */ for (simstate = simframe->real_input[device]; simstate; simstate = simstate->next) { - if (!simstate->used || simstate->size != 3 /* FIXME */) continue; + if (!simstate->used || simstate->size != resstate->size) continue; clients |= 1<client_num; } @@ -267,16 +271,23 @@ static void merge_analog_part(netplay_t *netplay, * @simframe : frame in which merging is being performed * @device : device being merged * @clients : bitmap of clients being merged + * @dtype : device type */ static void netplay_merge_analog(netplay_t *netplay, netplay_input_state_t resstate, struct delta_frame *simframe, - uint32_t device, uint32_t clients) + uint32_t device, uint32_t clients, unsigned dtype) { - /* FIXME: Assumes 3-byte gamepad */ + if (dtype == RETRO_DEVICE_JOYPAD) + return; + /* All other devices have analog */ + merge_analog_part(netplay, resstate, simframe, device, clients, 1, 0); merge_analog_part(netplay, resstate, simframe, device, clients, 1, 16); - merge_analog_part(netplay, resstate, simframe, device, clients, 2, 0); - merge_analog_part(netplay, resstate, simframe, device, clients, 2, 16); + if (dtype == RETRO_DEVICE_ANALOG) + { + merge_analog_part(netplay, resstate, simframe, device, clients, 2, 0); + merge_analog_part(netplay, resstate, simframe, device, clients, 2, 16); + } } /** @@ -303,13 +314,15 @@ bool netplay_resolve_input(netplay_t *netplay, size_t sim_ptr, bool resim) for (device = 0; device < MAX_INPUT_DEVICES; device++) { + unsigned dtype = netplay->config_devices[device]&RETRO_DEVICE_MASK; + uint32_t dsize = netplay_expected_input_size(netplay, 1 << device); clients = netplay->device_clients[device]; client_count = 0; /* Make sure all real clients are accounted for */ for (simstate = simframe->real_input[device]; simstate; simstate = simstate->next) { - if (!simstate->used || simstate->size != 3 /* FIXME */) continue; + if (!simstate->used || simstate->size != dsize) continue; clients |= 1<client_num; } @@ -318,19 +331,19 @@ bool netplay_resolve_input(netplay_t *netplay, size_t sim_ptr, bool resim) if (!(clients & (1<real_input[device], client, 3 /* FIXME */, false, true); + simstate = netplay_input_state_for(&simframe->real_input[device], client, dsize, false, true); if (!simstate) { /* Don't already have this input, so must simulate if we're supposed to have it at all */ if (netplay->read_frame_count[client] > simframe->frame) continue; - simstate = netplay_input_state_for(&simframe->simlated_input[device], client, 3 /* FIXME */, false, false); + simstate = netplay_input_state_for(&simframe->simlated_input[device], client, dsize, false, false); if (!simstate) continue; prev = PREV_PTR(netplay->read_ptr[client]); pframe = &netplay->buffer[prev]; - pstate = netplay_input_state_for(&pframe->real_input[device], client, 3 /* FIXME */, false, true); + pstate = netplay_input_state_for(&pframe->real_input[device], client, dsize, false, true); if (!pstate) continue; @@ -351,17 +364,28 @@ bool netplay_resolve_input(netplay_t *netplay, size_t sim_ptr, bool resim) * which will seem to jerkily be pressed numerous times with those * wavefronts. */ - const uint32_t keep = (1U<data[0] &= keep; simstate->data[0] |= pstate->data[0] & ~keep; } else { memcpy(simstate->data, pstate->data, - simstate->size * sizeof(uint32_t)); + dsize * sizeof(uint32_t)); } } @@ -369,23 +393,35 @@ bool netplay_resolve_input(netplay_t *netplay, size_t sim_ptr, bool resim) client_count++; } + /* The frontend always uses the first resolved input, so make sure it's right */ + while (simframe->resolved_input[device] + && (simframe->resolved_input[device]->size != dsize + || simframe->resolved_input[device]->client_num != 0)) + { + /* The default resolved input is of the wrong size! */ + netplay_input_state_t nextistate = simframe->resolved_input[device]->next; + free(simframe->resolved_input[device]); + simframe->resolved_input[device] = nextistate; + } + /* Now we copy the state, whether real or simulated, out into the resolved state */ - resstate = netplay_input_state_for(&simframe->resolved_input[device], 0, 3 /* FIXME */, false, false); + resstate = netplay_input_state_for(&simframe->resolved_input[device], 0, + dsize, false, false); if (!resstate) continue; if (client_count == 1) { /* Trivial in the common 1-client case */ - if (memcmp(resstate->data, client_state->data, resstate->size * sizeof(uint32_t))) + if (memcmp(resstate->data, client_state->data, dsize * sizeof(uint32_t))) ret = true; - memcpy(resstate->data, client_state->data, resstate->size * sizeof(uint32_t)); + memcpy(resstate->data, client_state->data, dsize * sizeof(uint32_t)); } else if (client_count == 0) { uint32_t word; - for (word = 0; word < resstate->size; word++) + for (word = 0; word < dsize; word++) { if (resstate->data[word]) ret = true; @@ -396,17 +432,20 @@ bool netplay_resolve_input(netplay_t *netplay, size_t sim_ptr, bool resim) else { /* Merge them */ + /* NOTE: As it happens, all of our devices keep the digital data in + * the first word, so this is fine, but it'll have to change when + * other devices are supported. */ static const uint32_t digital[3] = {-1, 0, 0}; - oldresstate = netplay_input_state_for(&simframe->resolved_input[device], 1, 3 /* FIXME */, false, false); + oldresstate = netplay_input_state_for(&simframe->resolved_input[device], 1, dsize, false, false); if (!oldresstate) continue; - memcpy(oldresstate->data, resstate->data, oldresstate->size * sizeof(uint32_t)); - memset(resstate->data, 0, resstate->size * sizeof(uint32_t)); + memcpy(oldresstate->data, resstate->data, dsize * sizeof(uint32_t)); + memset(resstate->data, 0, dsize * sizeof(uint32_t)); netplay_merge_digital(netplay, resstate, simframe, device, clients, digital); - netplay_merge_analog(netplay, resstate, simframe, device, clients); + netplay_merge_analog(netplay, resstate, simframe, device, clients, dtype); - if (memcmp(resstate->data, oldresstate->data, resstate->size * sizeof(uint32_t))) + if (memcmp(resstate->data, oldresstate->data, dsize * sizeof(uint32_t))) ret = true; }