diff --git a/network/netplay/README b/network/netplay/README index b73c7b5550..5b22590851 100644 --- a/network/netplay/README +++ b/network/netplay/README @@ -160,15 +160,15 @@ Payload: { frame number: uint32 client number: 32 bits - joypad input: uint32 - analog 1 input: uint32 - analog 2 input: uint32 + input data: variable } Description: Input state for each frame. Netplay must send an INPUT command for every frame in order to function at all. When sent from the server (client 0), INPUT is a synchronization point: No synchronization events from the given - frame may arrive after the server's input for the frame. + frame may arrive after the server's input for the frame. The actual size of + the input data is variable, but must match the expected size for each of + the devices the given client controls. Command: NOINPUT Payload: @@ -217,6 +217,7 @@ Payload: paused?: 1 bit client number: 31 bits controller devices: uint32[16] + share modes: uint8[16] controller-client mapping: uint32[16] client nick: char[32] sram: variable @@ -239,7 +240,8 @@ Command: PLAY Payload: { as slave?: 1 bit - reserved: 15 bits + reserved: 7 bits + preferred share mode: 8 bits requested device(s): 16 bits } Description: @@ -247,18 +249,20 @@ Description: before sending input. Server may refuse or force slave connections, so the 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. + unassigned device, and share the first device if all devices are assigned + and the first device is shareable. Command: MODE Payload: { frame number: uint32 - reserved: 13 bits - slave: 1 bit - playing: 1 bit you: 1 bit + playing: 1 bit + slave: 1 bit + reserved: 13 bits client number: uint16 device bitmap: uint32 + share modes: uint8[16] } 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 0450d216f2..e72ac80a89 100644 --- a/network/netplay/netplay_frontend.c +++ b/network/netplay/netplay_frontend.c @@ -496,9 +496,6 @@ static int16_t netplay_input_state(netplay_t *netplay, return 0; } - if (port > netplay->input_device_max) - netplay->input_device_max = port; - /* FIXME: Mixing */ delta = &netplay->buffer[ptr]; istate = delta->resolved_input[port]; @@ -1161,12 +1158,32 @@ static void netplay_toggle_play_spectate(netplay_t *netplay) else if (netplay->self_mode == NETPLAY_CONNECTION_SPECTATING) { uint32_t device; + uint8_t share_mode = NETPLAY_SHARE_DIGITAL_OR|NETPLAY_SHARE_ANALOG_MAX; /* Take an input device */ for (device = 0; device < MAX_INPUT_DEVICES; device++) - if (!(netplay->device_clients[device])) + { + if (input_config_get_device(device) == RETRO_DEVICE_NONE) + { + device = MAX_INPUT_DEVICES; break; - if (device == MAX_INPUT_DEVICES) + } + if (!netplay->device_clients[device]) + break; + } + if (device >= MAX_INPUT_DEVICES) + { + /* Share one */ + for (device = 0; device < MAX_INPUT_DEVICES; device++) + { + if (netplay->device_clients[device] && netplay->device_share_modes[device]) + { + share_mode = netplay->device_share_modes[device]; + break; + } + } + } + if (device >= MAX_INPUT_DEVICES) return; /* Failure! */ payload[1] = htonl(NETPLAY_CMD_MODE_BIT_PLAYING | device); @@ -1175,6 +1192,7 @@ static void netplay_toggle_play_spectate(netplay_t *netplay) netplay->connected_players |= 1; netplay->client_devices[0] = netplay->self_devices = (1<device_clients[device] = 1; + netplay->device_share_modes[device] = share_mode; netplay->read_ptr1[0] = netplay->self_ptr; netplay->read_frame_count1[0] = netplay->self_frame_count; @@ -1320,13 +1338,7 @@ bool init_netplay(void *direct_host, const char *server, unsigned port) if (netplay_data) { if (netplay_data->is_server && !settings->bools.netplay_start_as_spectator) - { - netplay_data->self_mode = NETPLAY_CONNECTION_PLAYING; - netplay_data->self_devices = 1; - netplay_data->client_devices[0] = 1; - netplay_data->device_clients[0] = 1; - netplay_data->connected_players = 1; - } + netplay_toggle_play_spectate(netplay_data); return true; } diff --git a/network/netplay/netplay_handshake.c b/network/netplay/netplay_handshake.c index 5600732d37..006c7ebd17 100644 --- a/network/netplay/netplay_handshake.c +++ b/network/netplay/netplay_handshake.c @@ -547,6 +547,9 @@ bool netplay_handshake_sync(netplay_t *netplay, /* Controller devices */ + MAX_INPUT_DEVICES*sizeof(uint32_t) + /* Share modes */ + + MAX_INPUT_DEVICES*sizeof(uint8_t) + /* Device-client mapping */ + MAX_INPUT_DEVICES*sizeof(uint32_t) @@ -574,6 +577,11 @@ bool netplay_handshake_sync(netplay_t *netplay, return false; } + /* Then the share mode */ + if (!netplay_send(&connection->send_packet_buffer, connection->fd, + netplay->device_share_modes, sizeof(netplay->device_share_modes))) + return false; + /* Then the device-client mapping */ for (i = 0; i < MAX_INPUT_DEVICES; i++) { @@ -933,8 +941,8 @@ bool netplay_handshake_pre_sync(netplay_t *netplay, } /* Only expecting a sync command */ - if (ntohl(cmd[0]) != NETPLAY_CMD_SYNC|| - ntohl(cmd[1]) < (2+2*MAX_INPUT_DEVICES)*sizeof(uint32_t) + + if (ntohl(cmd[0]) != NETPLAY_CMD_SYNC || + ntohl(cmd[1]) < (2+2*MAX_INPUT_DEVICES)*sizeof(uint32_t) + (MAX_INPUT_DEVICES)*sizeof(uint8_t) + NETPLAY_NICK_LEN) { RARCH_ERR("%s\n", @@ -1001,6 +1009,10 @@ bool netplay_handshake_pre_sync(netplay_t *netplay, core_set_controller_port_device(&pad); } + /* Get the share modes */ + RECV(netplay->device_share_modes, sizeof(netplay->device_share_modes)) + return false; + /* Get the client-controller mapping */ netplay->connected_players = netplay->connected_slaves = @@ -1012,7 +1024,6 @@ bool netplay_handshake_pre_sync(netplay_t *netplay, RECV(&device, sizeof(device)) return false; device = ntohl(device); - fprintf(stderr, "Device %d: %d\n", (int) i, (int) device); netplay->device_clients[i] = device; netplay->connected_players |= device; @@ -1044,7 +1055,7 @@ bool netplay_handshake_pre_sync(netplay_t *netplay, local_sram_size = (unsigned)mem_info.size; remote_sram_size = ntohl(cmd[1]) - - (2+2*MAX_INPUT_DEVICES)*sizeof(uint32_t) - NETPLAY_NICK_LEN; + (2+2*MAX_INPUT_DEVICES)*sizeof(uint32_t) - (MAX_INPUT_DEVICES)*sizeof(uint8_t) - NETPLAY_NICK_LEN; if (local_sram_size != 0 && local_sram_size == remote_sram_size) { diff --git a/network/netplay/netplay_init.c b/network/netplay/netplay_init.c index 899ff7a23a..18e6af47ee 100644 --- a/network/netplay/netplay_init.c +++ b/network/netplay/netplay_init.c @@ -426,7 +426,6 @@ 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->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 7eb909de17..a1ad814c44 100644 --- a/network/netplay/netplay_io.c +++ b/network/netplay/netplay_io.c @@ -28,7 +28,7 @@ #include "../../retroarch.h" #include "../../tasks/tasks_internal.h" -#if 1 +#if 0 #define DEBUG_NETPLAY_STEPS 1 static void print_state(netplay_t *netplay) @@ -185,10 +185,11 @@ void netplay_delayed_state_change(netplay_t *netplay) connection->delay_frame <= netplay->self_frame_count) { /* Something was delayed! Prepare the MODE command */ - uint32_t payload[3]; + uint32_t payload[7]; payload[0] = htonl(connection->delay_frame); payload[1] = htonl(client_num); payload[2] = htonl(0); + memcpy(payload + 3, netplay->device_share_modes, sizeof(netplay->device_share_modes)); /* Remove the connection entirely if relevant */ if (connection->mode == NETPLAY_CONNECTION_DELAYED_DISCONNECT) @@ -468,7 +469,7 @@ bool netplay_cmd_mode(netplay_t *netplay, enum rarch_netplay_connection_mode mode) { uint32_t cmd; - uint32_t payloadBuf, *payload = NULL; + uint32_t payloadBuf = 0, *payload = NULL; switch (mode) { case NETPLAY_CONNECTION_SPECTATING: @@ -476,11 +477,13 @@ bool netplay_cmd_mode(netplay_t *netplay, break; case NETPLAY_CONNECTION_SLAVE: - payload = &payloadBuf; - payloadBuf = htonl(NETPLAY_CMD_PLAY_BIT_SLAVE); + payloadBuf = NETPLAY_CMD_PLAY_BIT_SLAVE; /* no break */ case NETPLAY_CONNECTION_PLAYING: + payload = &payloadBuf; + payloadBuf |= NETPLAY_SHARE_NO_PREFERENCE<<16; + payloadBuf = htonl(payloadBuf); cmd = NETPLAY_CMD_PLAY; break; @@ -534,6 +537,10 @@ static bool netplay_get_cmd(netplay_t *netplay, cmd_size = ntohl(cmd_size); +#ifdef DEBUG_NETPLAY_STEPS + RARCH_LOG("Received netplay command %X (%X)\n", cmd, cmd_size); +#endif + netplay->timeout_cnt = 0; switch (cmd) @@ -760,7 +767,7 @@ static bool netplay_get_cmd(netplay_t *netplay, case NETPLAY_CMD_SPECTATE: { - uint32_t payload[3]; + uint32_t payload[7]; uint32_t client_num; size_t i; @@ -772,64 +779,65 @@ static bool netplay_get_cmd(netplay_t *netplay, client_num = connection - netplay->connections + 1; - if (connection->mode == NETPLAY_CONNECTION_PLAYING || - connection->mode == NETPLAY_CONNECTION_SLAVE) + 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_count1[client_num]; - - /* Mark them as not playing anymore */ - connection->mode = NETPLAY_CONNECTION_SPECTATING; - netplay->connected_players &= ~(1<connected_slaves &= ~(1<client_devices[client_num] = 0; - for (i = 0; i < MAX_INPUT_DEVICES; i++) - netplay->device_clients[client_num] &= ~(1<delay_frame = netplay->read_frame_count1[client_num]; + + /* Mark them as not playing anymore */ + connection->mode = NETPLAY_CONNECTION_SPECTATING; + netplay->connected_players &= ~(1 << client_num); + netplay->connected_slaves &= ~(1 << client_num); + netplay->client_devices[client_num] = 0; + for (i = 0; i < MAX_INPUT_DEVICES; i++) + netplay->device_clients[client_num] &= ~(1 << client_num); + + /* Tell the player */ + payload[0] = htonl(connection->delay_frame); payload[1] = htonl(NETPLAY_CMD_MODE_BIT_YOU | client_num); payload[2] = htonl(0); + memcpy(payload + 3, netplay->device_share_modes, sizeof(netplay->device_share_modes)); netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload)); + + /* Announce it */ + msg[sizeof(msg) - 1] = '\0'; + 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); break; } case NETPLAY_CMD_PLAY: { - uint32_t devices = 0, device, client_num; - uint32_t payload[3]; + uint32_t mode, devices = 0, device, client_num; + uint8_t share_mode; + uint32_t payload[7]; bool slave = false; settings_t *settings = config_get_ptr(); - /* Check if they requested slave mode */ - if (cmd_size == sizeof(uint32_t)) + if (cmd_size != sizeof(uint32_t)) { - RECV(&devices, sizeof(devices)) - { - RARCH_ERR("Failed to receive NETPLAY_CMD_PLAY payload.\n"); - return netplay_cmd_nak(netplay, connection); - } - - devices = ntohl(devices); - if (devices & NETPLAY_CMD_PLAY_BIT_SLAVE) - slave = true; - devices &= ~(NETPLAY_CMD_PLAY_BIT_SLAVE); - } - else if (cmd_size != 0) - { - RARCH_ERR("Invalid payload size for NETPLAY_CMD_PLAY.\n"); + RARCH_ERR("Incorrect NETPLAY_CMD_PLAY payload size.\n"); return netplay_cmd_nak(netplay, connection); } + RECV(&mode, sizeof(uint32_t)) + { + RARCH_ERR("Failed to receive NETPLAY_CMD_PLAY payload.\n"); + return netplay_cmd_nak(netplay, connection); + } + mode = ntohl(mode); + + /* Check the requested mode */ + slave = (mode&NETPLAY_CMD_PLAY_BIT_SLAVE)?true:false; + share_mode = (mode>>16)&0xFF; + + /* And the requested devices */ + devices = mode&0xFFFF; /* Check if their slave mode request corresponds with what we allow */ if (settings->bools.netplay_require_slaves) @@ -837,6 +845,7 @@ static bool netplay_get_cmd(netplay_t *netplay, else if (!settings->bools.netplay_allow_slaves) slave = false; + /* Start forming our response */ payload[0] = htonl(netplay->self_frame_count + 1); if (!netplay->is_server) @@ -862,13 +871,38 @@ static bool netplay_get_cmd(netplay_t *netplay, break; } - /* Find an available device (FIXME: Honor device request) */ - for (device = 0; device <= netplay->input_device_max; device++) + if (connection->mode == NETPLAY_CONNECTION_PLAYING + || connection->mode == NETPLAY_CONNECTION_SLAVE) { + /* They were obviously confused */ + return netplay_cmd_nak(netplay, connection); + } + + /* Find an available device (FIXME: Honor device request) */ + (void) devices; + for (device = 0; device < MAX_INPUT_DEVICES; device++) + { + if (input_config_get_device(device) == RETRO_DEVICE_NONE) + { + device = MAX_INPUT_DEVICES; + break; + } if (!netplay->device_clients[device]) break; } - if (device > netplay->input_device_max) + if (device >= MAX_INPUT_DEVICES && share_mode) + { + /* No device was totally free, maybe one is shareable? */ + for (device = 0; device < MAX_INPUT_DEVICES; device++) + { + if (netplay->device_clients[device] && netplay->device_share_modes[device]) + { + share_mode = netplay->device_share_modes[device]; + break; + } + } + } + if (device >= MAX_INPUT_DEVICES) { /* No slots free! */ payload[0] = htonl(NETPLAY_CMD_MODE_REFUSED_REASON_NO_SLOTS); @@ -877,40 +911,48 @@ static bool netplay_get_cmd(netplay_t *netplay, } payload[2] = htonl(1<mode != NETPLAY_CONNECTION_PLAYING && - connection->mode != NETPLAY_CONNECTION_SLAVE) + /* Fix our share mode */ + if (share_mode) { - /* Mark them as playing */ - connection->mode = slave ? NETPLAY_CONNECTION_SLAVE : - NETPLAY_CONNECTION_PLAYING; - - netplay->connected_players |= 1<connected_slaves |= 1<client_devices[client_num] |= 1<device_clients[device] |= 1<mode = + slave ? NETPLAY_CONNECTION_SLAVE : NETPLAY_CONNECTION_PLAYING; + + netplay->connected_players |= 1 << client_num; + if (slave) + netplay->connected_slaves |= 1 << client_num; + netplay->client_devices[client_num] |= 1 << device; + netplay->device_clients[device] |= 1 << client_num; + netplay->device_share_modes[device] = share_mode; + + /* Tell everyone */ + payload[1] = htonl( + NETPLAY_CMD_MODE_BIT_PLAYING + | (slave ? NETPLAY_CMD_MODE_BIT_SLAVE : 0) | client_num); + memcpy(payload + 3, netplay->device_share_modes, sizeof(netplay->device_share_modes)); + 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", client_num + 1); + RARCH_LOG("%s\n", msg); + runloop_msg_queue_push(msg, 1, 180, false); + + /* Tell the player */ payload[1] = htonl(NETPLAY_CMD_MODE_BIT_PLAYING | ((connection->mode == NETPLAY_CONNECTION_SLAVE)? NETPLAY_CMD_MODE_BIT_SLAVE:0) | NETPLAY_CMD_MODE_BIT_YOU | client_num); - payload[2] = htonl(1<buffer[ptr]; \ } while(0) - if (cmd_size != sizeof(payload) || - netplay->is_server) + if (netplay->is_server) + { + RARCH_ERR("NETPLAY_CMD_MODE from client.\n"); + return netplay_cmd_nak(netplay, connection); + } + + if (cmd_size != sizeof(payload)) { RARCH_ERR("Invalid payload size for NETPLAY_CMD_MODE.\n"); return netplay_cmd_nak(netplay, connection); @@ -965,6 +1012,7 @@ static bool netplay_get_cmd(netplay_t *netplay, } devices = ntohl(payload[2]); + memcpy(netplay->device_share_modes, payload + 3, sizeof(netplay->device_share_modes)); if (mode & NETPLAY_CMD_MODE_BIT_YOU) { diff --git a/network/netplay/netplay_private.h b/network/netplay/netplay_private.h index c07c860b98..0d1ffc4647 100644 --- a/network/netplay/netplay_private.h +++ b/network/netplay/netplay_private.h @@ -184,9 +184,9 @@ enum netplay_cmd #define NETPLAY_CMD_SYNC_BIT_PAUSED (1U<<31) #define NETPLAY_CMD_PLAY_BIT_SLAVE (1U<<31) -#define NETPLAY_CMD_MODE_BIT_SLAVE (1U<<18) -#define NETPLAY_CMD_MODE_BIT_PLAYING (1U<<17) -#define NETPLAY_CMD_MODE_BIT_YOU (1U<<16) +#define NETPLAY_CMD_MODE_BIT_YOU (1U<<31) +#define NETPLAY_CMD_MODE_BIT_PLAYING (1U<<30) +#define NETPLAY_CMD_MODE_BIT_SLAVE (1U<<29) /* These are the reasons given for mode changes to be rejected */ enum netplay_cmd_mode_reasons @@ -201,9 +201,35 @@ enum netplay_cmd_mode_reasons NETPLAY_CMD_MODE_REFUSED_REASON_NO_SLOTS, /* You're changing modes too fast */ - NETPLAY_CMD_MODE_REFUSED_REASON_TOO_FAST + NETPLAY_CMD_MODE_REFUSED_REASON_TOO_FAST, + + /* You requested a particular port but it's not available */ + NETPLAY_CMD_MODE_REFUSED_REASON_NOT_AVAILABLE }; +/* Preferences for sharing devices */ +enum rarch_netplay_share_preference +{ + /* Prefer not to share, shouldn't be set as a sharing mode for an shared device */ + NETPLAY_SHARE_NO_SHARING = 0x0, + + /* No preference. Only for requests. Set if sharing is requested but either + * digital or analog doesn't have a preference. */ + NETPLAY_SHARE_NO_PREFERENCE = 0x1, + + /* For digital devices */ + NETPLAY_SHARE_DIGITAL_BITS = 0x1C, + NETPLAY_SHARE_DIGITAL_OR = 0x4, + NETPLAY_SHARE_DIGITAL_XOR = 0x8, + NETPLAY_SHARE_DIGITAL_VOTE = 0xC, + + /* For analog devices */ + NETPLAY_SHARE_ANALOG_BITS = 0xE0, + NETPLAY_SHARE_ANALOG_MAX = 0x20, + NETPLAY_SHARE_ANALOG_AVERAGE = 0x40 +}; + +/* The current status of a connection */ enum rarch_netplay_connection_mode { NETPLAY_CONNECTION_NONE = 0, @@ -399,6 +425,9 @@ struct netplay /* For each device, the bitmap of clients connected */ client_bitmap_t device_clients[MAX_INPUT_DEVICES]; + /* The sharing mode for each device */ + uint8_t device_share_modes[MAX_INPUT_DEVICES]; + /* Our own device bitmap */ uint32_t self_devices; @@ -406,9 +435,6 @@ struct netplay * attempt to stay in sync. */ uint32_t desync; - /* Maximum input device number */ - uint32_t input_device_max; - struct retro_callbacks cbs; /* TCP port (only set if serving) */ diff --git a/network/netplay/netplay_sync.c b/network/netplay/netplay_sync.c index 7bae5ab458..df0e45316d 100644 --- a/network/netplay/netplay_sync.c +++ b/network/netplay/netplay_sync.c @@ -82,6 +82,188 @@ void netplay_update_unread_ptr(netplay_t *netplay) } } +struct vote_count { + uint16_t votes[32]; +}; + +/** + * netplay_device_client_state + * @resstate : state being resolved + * @simframe : frame in which merging is being performed + * @device : device being merged + * @client : client to find state for + */ +netplay_input_state_t netplay_device_client_state( + netplay_input_state_t resstate, struct delta_frame *simframe, + uint32_t device, uint32_t client) +{ + netplay_input_state_t simstate = + netplay_input_state_for( + &simframe->real_input[device], client, 3 /* FIXME */, false, true); + if (!simstate) + { + resstate->used = false; + simstate = netplay_input_state_for(&simframe->simlated_input[device], + client, 3 /* FIXME */, false, true); + } + return simstate; +} + +/** + * netplay_merge_digital + * @netplay : pointer to netplay object + * @resstate : state being resolved + * @simframe : frame in which merging is being performed + * @device : device being merged + * @clients : bitmap of clients being merged + * @digital : bitmap of digital bits + */ +static void netplay_merge_digital(netplay_t *netplay, + netplay_input_state_t resstate, struct delta_frame *simframe, + uint32_t device, uint32_t clients, const uint32_t *digital) +{ + netplay_input_state_t simstate; + uint32_t word, bit, client; + uint8_t share_mode = netplay->device_share_modes[device] & NETPLAY_SHARE_DIGITAL_BITS; + if (share_mode == NETPLAY_SHARE_DIGITAL_VOTE) + { + /* Vote mode requires counting all the bits */ + uint32_t client_count = 0; + struct vote_count votes[3] = {0}; + for (client = 0; client < MAX_CLIENTS; client++) + { + if (!(clients & (1<data[word] & (1< client_count) + resstate->data[word] |= (1<data[word]; + if (digital[word] == (uint32_t) -1) + { + /* Combine the whole word */ + switch (share_mode) + { + case NETPLAY_SHARE_DIGITAL_XOR: resstate->data[word] ^= part; break; + default: resstate->data[word] |= part; + } + + } + else /* !whole word */ + { + for (bit = 0; bit < 32; bit++) + { + if (!(digital[word] & (1<data[word] ^= part & (1<data[word] |= part & (1<device_share_modes[device] & NETPLAY_SHARE_ANALOG_BITS; + int32_t value = 0, new_value; + + for (client = 0; client < MAX_CLIENTS; client++) + { + if (!(clients & (1<data[word]>>bit) & 0xFFFF); + switch (share_mode) + { + case NETPLAY_SHARE_ANALOG_AVERAGE: + value += (int32_t) new_value; + break; + default: + if (abs(new_value) > abs(value) || + (abs(new_value) == abs(value) && new_value > value)) + value = new_value; + } + } + + if (share_mode == NETPLAY_SHARE_ANALOG_AVERAGE) + value /= client_count; + + resstate->data[word] |= ((uint32_t) (uint16_t) value) << bit; +} + +/** + * netplay_merge_analog + * @netplay : pointer to netplay object + * @resstate : state being resolved + * @simframe : frame in which merging is being performed + * @device : device being merged + * @clients : bitmap of clients being merged + */ +static void netplay_merge_analog(netplay_t *netplay, + netplay_input_state_t resstate, struct delta_frame *simframe, + uint32_t device, uint32_t clients) +{ + /* FIXME: Assumes 3-byte gamepad */ + 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); +} + /** * netplay_resolve_input * @netplay : pointer to netplay object @@ -95,26 +277,27 @@ void netplay_update_unread_ptr(netplay_t *netplay) */ bool netplay_resolve_input(netplay_t *netplay, size_t sim_ptr, bool resim) { - uint32_t client; size_t prev; struct delta_frame *simframe, *pframe; - netplay_input_state_t simstate, resstate, pstate; - uint32_t devices, device; + netplay_input_state_t simstate, resstate, oldresstate, pstate; + uint32_t clients, client, client_count; + uint32_t device; bool ret = false, simulated; simframe = &netplay->buffer[sim_ptr]; - for (client = 0; client < MAX_CLIENTS; client++) + for (device = 0; device < MAX_INPUT_DEVICES; device++) { - if (!(netplay->connected_players & (1<device_clients[device]; + if (!clients) continue; + client_count = 0; - devices = netplay->client_devices[client]; - - for (device = 0; device < MAX_INPUT_DEVICES; device++) + for (client = 0; client < MAX_CLIENTS; client++) { - if (!(devices & (1<real_input[device], client, 3 /* FIXME */, false, true); if (!simstate) @@ -162,15 +345,36 @@ bool netplay_resolve_input(netplay_t *netplay, size_t sim_ptr, bool resim) simstate->size * sizeof(uint32_t)); } } + } - /* Now we copy the state, whether real or simulated, out into the resolved state (FIXME: Merging) */ - resstate = netplay_input_state_for(&simframe->resolved_input[device], 0, 3 /* FIXME */, false, false); - if (!resstate) - continue; + /* 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); + if (!resstate) + continue; + + if (client_count == 1) + { + /* Trivial in the common 1-client case */ resstate->used = !simulated; /* We reuse "used" to mean "real" */ if (memcmp(resstate->data, simstate->data, resstate->size * sizeof(uint32_t))) ret = true; memcpy(resstate->data, simstate->data, resstate->size * sizeof(uint32_t)); + + } else { + /* Merge them */ + static const uint32_t digital[3] = {-1, 0, 0}; + oldresstate = netplay_input_state_for(&simframe->resolved_input[device], 1, 3 /* FIXME */, false, false); + if (!oldresstate) + continue; + memcpy(oldresstate->data, resstate->data, oldresstate->size * sizeof(uint32_t)); + memset(resstate->data, 0, resstate->size * sizeof(uint32_t)); + + netplay_merge_digital(netplay, resstate, simframe, device, clients, digital); + netplay_merge_analog(netplay, resstate, simframe, device, clients); + + if (memcmp(resstate->data, oldresstate->data, resstate->size * sizeof(uint32_t))) + ret = true; + } }