Added real pad sharing modes.

This commit is contained in:
Gregor Richards 2017-09-10 19:42:32 -04:00
parent e4029b72c1
commit fe80c4ce2c
7 changed files with 430 additions and 126 deletions

View File

@ -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

View File

@ -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);
netplay->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;
}

View File

@ -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)
{

View File

@ -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;

View File

@ -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<<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);
/* 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);
}
else
{
payload[0] = htonl(0);
/* They were confused */
return netplay_cmd_nak(netplay, connection);
}
/* Tell the player even if they were confused */
/* 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 << 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<<device);
if (connection->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<<client_num;
if (slave)
netplay->connected_slaves |= 1<<client_num;
netplay->client_devices[client_num] |= 1<<device;
netplay->device_clients[device] |= 1<<client_num;
/* Tell everyone */
payload[1] = htonl(NETPLAY_CMD_MODE_BIT_PLAYING |
(slave?NETPLAY_CMD_MODE_BIT_SLAVE:0) |
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", client_num+1);
RARCH_LOG("%s\n", msg);
runloop_msg_queue_push(msg, 1, 180, false);
if ((share_mode & NETPLAY_SHARE_DIGITAL_BITS) == 0)
share_mode |= NETPLAY_SHARE_DIGITAL_OR;
if ((share_mode & NETPLAY_SHARE_ANALOG_BITS) == 0)
share_mode |= NETPLAY_SHARE_ANALOG_MAX;
share_mode &= ~NETPLAY_SHARE_NO_PREFERENCE;
}
/* Tell the player even if they were confused */
/* Mark them as playing */
connection->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<<device);
netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload));
/* And expect their data */
@ -921,7 +963,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
case NETPLAY_CMD_MODE:
{
uint32_t payload[3];
uint32_t payload[7];
uint32_t frame, mode, client_num, devices, device;
size_t ptr;
struct delta_frame *dframe;
@ -937,8 +979,13 @@ static bool netplay_get_cmd(netplay_t *netplay,
dframe = &netplay->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)
{

View File

@ -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) */

View File

@ -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<<client))) continue;
client_count++;
simstate = netplay_device_client_state(resstate, simframe, device, client);
if (!simstate) continue;
for (word = 0; word < 3 /* FIXME */; word++)
{
if (!digital[word]) continue;
for (bit = 0; bit < 32; bit++)
{
if (!(digital[word] & (1<<bit))) continue;
if (simstate->data[word] & (1<<bit))
votes[word].votes[bit]++;
}
}
}
/* Now count all the bits */
client_count /= 2;
for (word = 0; word < 3 /* FIXME */; word++)
{
for (bit = 0; bit < 32; bit++)
{
if (votes[word].votes[bit] > client_count)
resstate->data[word] |= (1<<bit);
}
}
}
else /* !VOTE */
{
for (client = 0; client < MAX_CLIENTS; client++)
{
if (!(clients & (1<<client))) continue;
simstate = netplay_device_client_state(resstate, simframe, device, client);
if (!simstate) continue;
for (word = 0; word < 3 /* FIXME */; word++)
{
uint32_t part;
if (!digital[word]) continue;
part = simstate->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<<bit))) continue;
switch (share_mode)
{
case NETPLAY_SHARE_DIGITAL_XOR: resstate->data[word] ^= part & (1<<bit); break;
default: resstate->data[word] |= part & (1<<bit);
}
}
}
}
}
}
}
/**
* merge_analog_part
* @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
* @word : word to merge
* @bit : first bit to merge
*/
static void merge_analog_part(netplay_t *netplay,
netplay_input_state_t resstate, struct delta_frame *simframe,
uint32_t device, uint32_t clients, uint32_t word, uint8_t bit)
{
netplay_input_state_t simstate;
uint32_t client, client_count = 0;;
uint8_t share_mode = netplay->device_share_modes[device] & NETPLAY_SHARE_ANALOG_BITS;
int32_t value = 0, new_value;
for (client = 0; client < MAX_CLIENTS; client++)
{
if (!(clients & (1<<client))) continue;
client_count++;
simstate = netplay_device_client_state(resstate, simframe, device, client);
if (!simstate) continue;
new_value = (int16_t) ((simstate->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<<client))) continue;
clients = netplay->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<<device)))
continue;
if (!(clients & (1<<client))) continue;
client_count++;
/* Resolve this client-device */
simulated = false;
simstate = netplay_input_state_for(&simframe->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;
}
}