First step (not yet compiling) of changing around Netplay input.

This commit is contained in:
Gregor Richards 2017-08-25 14:38:21 -04:00
parent f677a7ad7e
commit 6d4119690d
7 changed files with 528 additions and 295 deletions

View File

@ -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 inform all clients of its own current frame even if it has no input. The
NOINPUT command is provided for that purpose. 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): The handshake procedure (this part is done by both server and client):
1) Send connection header 1) Send connection header
2) Receive and verify connection header 2) Receive and verify connection header
@ -156,7 +160,7 @@ Payload:
{ {
frame number: uint32 frame number: uint32
is server data: 1 bit is server data: 1 bit
player: 31 bits client number: 31 bits
joypad input: uint32 joypad input: uint32
analog 1 input: uint32 analog 1 input: uint32
analog 2 input: uint32 analog 2 input: uint32
@ -213,9 +217,9 @@ Payload:
{ {
frame number: uint32 frame number: uint32
paused?: 1 bit paused?: 1 bit
connected players: 31 bits client number: 31 bits
flip frame: uint32
controller devices: uint32[16] controller devices: uint32[16]
controller-client mapping: uint32[16]
client nick: char[32] client nick: char[32]
sram: variable sram: variable
} }
@ -236,13 +240,16 @@ Description:
Command: PLAY Command: PLAY
Payload: Payload:
{ {
reserved: 31 bits
as slave?: 1 bit as slave?: 1 bit
reserved: 15 bits
requested device(s): 16 bits
} }
Description: Description:
Request to enter player mode. The client must wait for a MODE command 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 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 Command: MODE
Payload: Payload:
@ -252,7 +259,8 @@ Payload:
slave: 1 bit slave: 1 bit
playing: 1 bit playing: 1 bit
you: 1 bit you: 1 bit
player number: uint16 client number: uint16
device bitmap: uint32
} }
Description: Description:
Inform of a connection mode change (possibly of the receiving client). Only Inform of a connection mode change (possibly of the receiving client). Only

View File

@ -71,7 +71,7 @@ static bool netplay_is_alive(void)
{ {
if (!netplay_data) if (!netplay_data)
return false; 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); (!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) 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]; struct delta_frame *ptr = &netplay->buffer[netplay->self_ptr];
netplay_input_state_t istate = NULL;
uint32_t devices, devi;
size_t i; size_t i;
if (!netplay_delta_frame_ready(netplay, ptr, netplay->self_frame_count)) 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; 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 if (!(devices & (1<<devi)))
* input from first frame screws up when we use -F 0. */ continue;
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++) istate = netplay_input_state_for(&ptr->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, /* First frame we always give zero input since relying on
RETRO_DEVICE_ANALOG, (unsigned)i, 0); * input from first frame screws up when we use -F 0. */
int16_t tmp_y = cb(0, uint32_t *state = istate->data;
RETRO_DEVICE_ANALOG, (unsigned)i, 1); retro_input_state_t cb = netplay->cbs.state_cb;
state[1 + i] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16); 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; 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 */ /* And send this input to our peers */
for (i = 0; i < netplay->connections_size; i++) 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) static bool netplay_poll(void)
{ {
int res; int res;
uint32_t player; uint32_t client;
size_t i; size_t i;
netplay_data->can_poll = false; netplay_data->can_poll = false;
@ -219,7 +224,7 @@ static bool netplay_poll(void)
* frame */ * frame */
netplay_update_unread_ptr(netplay_data); netplay_update_unread_ptr(netplay_data);
if (netplay_data->stateless_mode && if (netplay_data->stateless_mode &&
netplay_data->connected_players && (netplay_data->connected_players1>1) &&
netplay_data->unread_frame_count <= netplay_data->run_frame_count) netplay_data->unread_frame_count <= netplay_data->run_frame_count)
res = netplay_poll_net_input(netplay_data, true); res = netplay_poll_net_input(netplay_data, true);
else else
@ -231,7 +236,7 @@ static bool netplay_poll(void)
netplay_simulate_input(netplay_data, netplay_data->run_ptr, false); netplay_simulate_input(netplay_data, netplay_data->run_ptr, false);
/* Handle any slaves */ /* 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_handle_slaves(netplay_data);
netplay_update_unread_ptr(netplay_data); netplay_update_unread_ptr(netplay_data);
@ -352,7 +357,7 @@ static bool netplay_poll(void)
{ {
/* Have we not read enough latency frames? */ /* Have we not read enough latency frames? */
if (netplay_data->self_mode == NETPLAY_CONNECTION_PLAYING && 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->run_frame_count + netplay_data->input_latency_frames > netplay_data->self_frame_count)
{ {
netplay_data->stall = NETPLAY_STALL_INPUT_LATENCY; netplay_data->stall = NETPLAY_STALL_INPUT_LATENCY;
@ -369,21 +374,19 @@ static bool netplay_poll(void)
/* Figure out who to blame */ /* Figure out who to blame */
if (netplay_data->is_server) 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<<player))) continue; struct netplay_connection *connection;
if (netplay_data->read_frame_count[player] > netplay_data->unread_frame_count) continue; if (!(netplay_data->connected_players1 & (1<<client)))
for (i = 0; i < netplay_data->connections_size; i++) continue;
if (netplay_data->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]; connection->stall = NETPLAY_STALL_RUNNING_FAST;
if (connection->active && connection->stall_time = netplay_data->stall_time;
connection->mode == NETPLAY_CONNECTION_PLAYING &&
connection->player == player)
{
connection->stall = NETPLAY_STALL_RUNNING_FAST;
connection->stall_time = netplay_data->stall_time;
break;
}
} }
} }
} }
@ -480,32 +483,30 @@ static int16_t netplay_input_state(netplay_t *netplay,
{ {
size_t ptr = netplay->is_replay ? size_t ptr = netplay->is_replay ?
netplay->replay_ptr : netplay->run_ptr; netplay->replay_ptr : netplay->run_ptr;
struct delta_frame *delta;
netplay_input_state_t istate;
const uint32_t *curr_input_state = NULL; const uint32_t *curr_input_state = NULL;
if (port <= 1) if (port >= MAX_INPUT_DEVICES)
{
/* Possibly flip the port */
if (netplay_flip_port(netplay))
port ^= 1;
}
else if (port >= MAX_USERS)
{ {
return 0; return 0;
} }
if (port > netplay->player_max) if (port > netplay->input_device_max)
netplay->player_max = port; netplay->input_device_max = port;
if (netplay->buffer[ptr].have_real[port]) /* FIXME: Mixing */
{ delta = &netplay->buffer[ptr];
netplay->buffer[ptr].used_real[port] = true; istate = delta->real_input[port];
curr_input_state = netplay->buffer[ptr].real_input_state[port]; if (istate && istate->is_real)
} delta->used_real[port] = true;
else else
{ istate = delta->simulated_input[port];
curr_input_state = netplay->buffer[ptr].simulated_input_state[port]; if (!istate)
} return 0;
curr_input_state = istate->data;
switch (device) switch (device)
{ {
@ -889,7 +890,7 @@ bool netplay_pre_frame(netplay_t *netplay)
} }
if (sync_stalled || if (sync_stalled ||
((!netplay->is_server || netplay->connected_players) && ((!netplay->is_server || (netplay->connected_players1>1)) &&
(netplay->stall || netplay->remote_paused))) (netplay->stall || netplay->remote_paused)))
{ {
/* We may have received data even if we're stalled, so run post-frame /* 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_ptr = netplay->self_ptr;
netplay->run_frame_count = netplay->self_frame_count; netplay->run_frame_count = netplay->self_frame_count;
/* We need to ignore any intervening data from the other side, /* We need to ignore any intervening data from the other side,
* and never rewind past this */ * and never rewind past this */
netplay_update_unread_ptr(netplay); netplay_update_unread_ptr(netplay);
if (netplay->unread_frame_count < netplay->run_frame_count) if (netplay->unread_frame_count < netplay->run_frame_count)
{ {
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<<player))) continue; if (!(netplay->connected_players1 & (1<<client))) continue;
if (netplay->read_frame_count[player] < netplay->run_frame_count) if (netplay->read_frame_count1[client] < netplay->run_frame_count)
{ {
netplay->read_ptr[player] = netplay->run_ptr; netplay->read_ptr1[client] = netplay->run_ptr;
netplay->read_frame_count[player] = netplay->run_frame_count; netplay->read_frame_count1[client] = netplay->run_frame_count;
} }
} }
if (netplay->server_frame_count < 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) static void netplay_toggle_play_spectate(netplay_t *netplay)
{ {
size_t i;
if (netplay->is_server) if (netplay->is_server)
{ {
/* FIXME: Duplication */ /* FIXME: Duplication */
uint32_t payload[2]; uint32_t payload[3];
char msg[512]; char msg[512];
const char *dmsg = NULL; const char *dmsg = NULL;
payload[0] = htonl(netplay->self_frame_count); 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) netplay->self_mode == NETPLAY_CONNECTION_SLAVE)
{ {
/* Mark us as no longer playing */ /* 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->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); dmsg = msg_hash_to_str(MSG_NETPLAY_YOU_HAVE_LEFT_THE_GAME);
} }
else if (netplay->self_mode == NETPLAY_CONNECTION_SPECTATING) else if (netplay->self_mode == NETPLAY_CONNECTION_SPECTATING)
{ {
uint32_t player; uint32_t device;
/* Take a player number */ /* Take an input device */
for (player = 0; player < MAX_USERS; player++) for (device = 0; device < MAX_INPUT_DEVICES; device++)
if (!(netplay->connected_players & (1<<player))) break; if (!(netplay->device_clients[device]))
if (player == MAX_USERS) return; /* Failure! */ 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<<device);
netplay->self_mode = NETPLAY_CONNECTION_PLAYING; netplay->self_mode = NETPLAY_CONNECTION_PLAYING;
netplay->self_player = player; netplay->connected_players1 |= 1;
netplay->client_devices[0] = (1<<device);
netplay->device_clients[device] = netplay->self_devices = 1;
dmsg = msg; dmsg = msg;
msg[sizeof(msg)-1] = '\0'; 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); RARCH_LOG("[netplay] %s\n", dmsg);

View File

@ -529,10 +529,10 @@ bool netplay_handshake_sync(netplay_t *netplay,
/* If we're the server, now we send sync info */ /* If we're the server, now we send sync info */
size_t i; size_t i;
int matchct; int matchct;
uint32_t cmd[5]; uint32_t cmd[4];
retro_ctx_memory_info_t mem_info; retro_ctx_memory_info_t mem_info;
uint32_t client_num = 0;
uint32_t device = 0; uint32_t device = 0;
uint32_t connected_players = 0;
size_t nicklen, nickmangle = 0; size_t nicklen, nickmangle = 0;
bool nick_matched = false; bool nick_matched = false;
@ -543,30 +543,43 @@ bool netplay_handshake_sync(netplay_t *netplay,
/* Send basic sync info */ /* Send basic sync info */
cmd[0] = htonl(NETPLAY_CMD_SYNC); cmd[0] = htonl(NETPLAY_CMD_SYNC);
cmd[1] = htonl(3*sizeof(uint32_t) + MAX_USERS*sizeof(uint32_t) + cmd[1] = htonl(2*sizeof(uint32_t)
NETPLAY_NICK_LEN + mem_info.size); /* 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); cmd[2] = htonl(netplay->self_frame_count);
connected_players = netplay->connected_players; client_num = connection - netplay->connections + 1;
if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING)
connected_players |= 1<<netplay->self_player;
if (netplay->local_paused || netplay->remote_paused) if (netplay->local_paused || netplay->remote_paused)
connected_players |= NETPLAY_CMD_SYNC_BIT_PAUSED; client_num |= NETPLAY_CMD_SYNC_BIT_PAUSED;
cmd[3] = htonl(connected_players); cmd[3] = htonl(client_num);
if (netplay->flip)
cmd[4] = htonl(netplay->flip_frame);
else
cmd[4] = htonl(0);
if (!netplay_send(&connection->send_packet_buffer, connection->fd, cmd, if (!netplay_send(&connection->send_packet_buffer, connection->fd, cmd,
sizeof(cmd))) sizeof(cmd)))
return false; return false;
/* Now send the device info */ /* 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)); device = htonl(input_config_get_device((unsigned)i));
if (!netplay_send(&connection->send_packet_buffer, connection->fd, 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; return false;
} }
@ -901,10 +914,10 @@ bool netplay_handshake_pre_sync(netplay_t *netplay,
struct netplay_connection *connection, bool *had_input) struct netplay_connection *connection, bool *had_input)
{ {
uint32_t cmd[2]; 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 device;
uint32_t local_sram_size, remote_sram_size; uint32_t local_sram_size, remote_sram_size;
size_t i; size_t i, j;
ssize_t recvd; ssize_t recvd;
retro_ctx_controller_info_t pad; retro_ctx_controller_info_t pad;
char new_nick[NETPLAY_NICK_LEN]; char new_nick[NETPLAY_NICK_LEN];
@ -920,8 +933,8 @@ bool netplay_handshake_pre_sync(netplay_t *netplay,
} }
/* Only expecting a sync command */ /* Only expecting a sync command */
if (ntohl(cmd[0]) != NETPLAY_CMD_SYNC || if (ntohl(cmd[0]) != NETPLAY_CMD_SYNC||
ntohl(cmd[1]) < 3*sizeof(uint32_t) + MAX_USERS*sizeof(uint32_t) + ntohl(cmd[1]) < (2+2*MAX_INPUT_DEVICES)*sizeof(uint32_t) +
NETPLAY_NICK_LEN) NETPLAY_NICK_LEN)
{ {
RARCH_ERR("%s\n", RARCH_ERR("%s\n",
@ -934,30 +947,23 @@ bool netplay_handshake_pre_sync(netplay_t *netplay,
return false; return false;
new_frame_count = ntohl(new_frame_count); new_frame_count = ntohl(new_frame_count);
/* Get the connected players and pause mode */ /* Get our client number and pause mode */
RECV(&connected_players, sizeof(connected_players)) RECV(&client_num, sizeof(client_num))
return false; return false;
connected_players = ntohl(connected_players); client_num = ntohl(client_num);
if (connected_players & NETPLAY_CMD_SYNC_BIT_PAUSED) if (client_num & NETPLAY_CMD_SYNC_BIT_PAUSED)
{ {
netplay->remote_paused = true; netplay->remote_paused = true;
connected_players ^= NETPLAY_CMD_SYNC_BIT_PAUSED; client_num ^= NETPLAY_CMD_SYNC_BIT_PAUSED;
} }
netplay->connected_players = connected_players; netplay->self_client_num = client_num;
/* 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;
/* Set our frame counters as requested */ /* Set our frame counters as requested */
netplay->self_frame_count = netplay->run_frame_count = netplay->self_frame_count = netplay->run_frame_count =
netplay->other_frame_count = netplay->unread_frame_count = netplay->other_frame_count = netplay->unread_frame_count =
netplay->server_frame_count = new_frame_count; netplay->server_frame_count = new_frame_count;
/* And clear out the framebuffer */
for (i = 0; i < netplay->buffer_size; i++) for (i = 0; i < netplay->buffer_size; i++)
{ {
struct delta_frame *ptr = &netplay->buffer[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<<i)) netplay->read_ptr1[i] = netplay->self_ptr;
{ netplay->read_frame_count1[i] = netplay->self_frame_count;
netplay->read_ptr[i] = netplay->self_ptr;
netplay->read_frame_count[i] = netplay->self_frame_count;
}
} }
/* Get and set each pad */ /* Get and set each input device */
for (i = 0; i < MAX_USERS; i++) for (i = 0; i < MAX_INPUT_DEVICES; i++)
{ {
RECV(&device, sizeof(device)) RECV(&device, sizeof(device))
return false; return false;
@ -998,6 +1001,27 @@ bool netplay_handshake_pre_sync(netplay_t *netplay,
core_set_controller_port_device(&pad); 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<<j))
netplay->client_devices[j] |= 1<<i;
}
}
/* Get our nick */ /* Get our nick */
RECV(new_nick, NETPLAY_NICK_LEN) RECV(new_nick, NETPLAY_NICK_LEN)
return false; return false;
@ -1018,8 +1042,8 @@ bool netplay_handshake_pre_sync(netplay_t *netplay,
core_get_memory(&mem_info); core_get_memory(&mem_info);
local_sram_size = (unsigned)mem_info.size; local_sram_size = (unsigned)mem_info.size;
remote_sram_size = ntohl(cmd[1]) - 3*sizeof(uint32_t) - remote_sram_size = ntohl(cmd[1]) -
MAX_USERS*sizeof(uint32_t) - NETPLAY_NICK_LEN; (2+2*MAX_INPUT_DEVICES)*sizeof(uint32_t) - NETPLAY_NICK_LEN;
if (local_sram_size != 0 && local_sram_size == remote_sram_size) if (local_sram_size != 0 && local_sram_size == remote_sram_size)
{ {

View File

@ -243,7 +243,7 @@ static bool netplay_init_socket_buffers(netplay_t *netplay)
* frames of input data, plus the headers for each of them */ * frames of input data, plus the headers for each of them */
size_t i; size_t i;
size_t packet_buffer_size = netplay->zbuffer_size + size_t packet_buffer_size = netplay->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; netplay->packet_buffer_size = packet_buffer_size;
for (i = 0; i < netplay->connections_size; i++) 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->listen_fd = -1;
netplay->tcp_port = port; netplay->tcp_port = port;
netplay->cbs = *cb; netplay->cbs = *cb;
netplay->connected_players = 0; netplay->input_device_max = 1;
netplay->player_max = 1;
netplay->is_server = (direct_host == NULL && server == NULL); netplay->is_server = (direct_host == NULL && server == NULL);
netplay->is_connected = false;; netplay->is_connected = false;;
netplay->nat_traversal = netplay->is_server ? nat_traversal : false; netplay->nat_traversal = netplay->is_server ? nat_traversal : false;

View File

@ -91,6 +91,7 @@ void netplay_hangup(netplay_t *netplay, struct netplay_connection *connection)
{ {
char msg[512]; char msg[512];
const char *dmsg; const char *dmsg;
size_t i;
if (!netplay) if (!netplay)
return; return;
@ -124,12 +125,22 @@ void netplay_hangup(netplay_t *netplay, struct netplay_connection *connection)
if (!netplay->is_server) if (!netplay->is_server)
{ {
netplay->self_mode = NETPLAY_CONNECTION_NONE; netplay->self_mode = NETPLAY_CONNECTION_NONE;
netplay->connected_players = 0; netplay->connected_players1 &= (1L<<netplay->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<<netplay->self_client_num);
netplay->stall = NETPLAY_STALL_NONE; netplay->stall = NETPLAY_STALL_NONE;
} }
else else
{ {
uint32_t client_num = connection - netplay->connections + 1;
/* Mark the player for removal */ /* Mark the player for removal */
if (connection->mode == NETPLAY_CONNECTION_PLAYING || if (connection->mode == NETPLAY_CONNECTION_PLAYING ||
connection->mode == NETPLAY_CONNECTION_SLAVE) 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 /* This special mode keeps the connection object alive long enough to
* send the disconnection message at the correct time */ * send the disconnection message at the correct time */
connection->mode = NETPLAY_CONNECTION_DELAYED_DISCONNECT; 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 */ /* Mark them as not playing anymore */
netplay->connected_players &= ~(1<<connection->player); netplay->connected_players1 &= ~(1L<<client_num);
netplay->connected_slaves &= ~(1<<connection->player); netplay->connected_slaves1 &= ~(1L<<client_num);
netplay->client_devices[client_num] = 0;
for (i = 0; i < MAX_INPUT_DEVICES; i++)
netplay->device_clients[i] &= ~(1L<<client_num);
} }
@ -165,15 +179,17 @@ void netplay_delayed_state_change(netplay_t *netplay)
for (i = 0; i < netplay->connections_size; i++) for (i = 0; i < netplay->connections_size; i++)
{ {
uint32_t client_num = i+1;
connection = &netplay->connections[i]; connection = &netplay->connections[i];
if ((connection->active || connection->mode == NETPLAY_CONNECTION_DELAYED_DISCONNECT) && if ((connection->active || connection->mode == NETPLAY_CONNECTION_DELAYED_DISCONNECT) &&
connection->delay_frame && connection->delay_frame &&
connection->delay_frame <= netplay->self_frame_count) connection->delay_frame <= netplay->self_frame_count)
{ {
/* Something was delayed! Prepare the MODE command */ /* Something was delayed! Prepare the MODE command */
uint32_t payload[2]; uint32_t payload[3];
payload[0] = htonl(connection->delay_frame); 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 */ /* Remove the connection entirely if relevant */
if (connection->mode == NETPLAY_CONNECTION_DELAYED_DISCONNECT) if (connection->mode == NETPLAY_CONNECTION_DELAYED_DISCONNECT)
@ -189,24 +205,43 @@ void netplay_delayed_state_change(netplay_t *netplay)
} }
/* Send the specified input data */ /* Send the specified input data */
static bool send_input_frame(netplay_t *netplay, static bool send_input_frame(netplay_t *netplay, struct delta_frame *dframe,
struct netplay_connection *only, struct netplay_connection *except, struct netplay_connection *only, struct netplay_connection *except,
uint32_t frame, uint32_t player, uint32_t *state) uint32_t client_num)
{ {
uint32_t buffer[2 + WORDS_PER_FRAME]; #define BUFSZ 16 /* FIXME: Arbitrary restriction */
size_t i; uint32_t buffer[BUFSZ], devices, device;
size_t bufused, i;
/* Set up the basic buffer */
bufused = 4;
buffer[0] = htonl(NETPLAY_CMD_INPUT); buffer[0] = htonl(NETPLAY_CMD_INPUT);
buffer[1] = htonl(WORDS_PER_FRAME * sizeof(uint32_t)); buffer[2] = htonl(dframe->frame);
buffer[2] = htonl(frame); buffer[3] = htonl(client_num);
buffer[3] = htonl(player);
buffer[4] = htonl(state[0]); /* Add the device data */
buffer[5] = htonl(state[1]); devices = netplay->client_devices[client_num];
buffer[6] = htonl(state[2]); for (device = 0; device < devices; device++)
{
netplay_input_state_t istate;
if (!(devices & (1<<device)))
continue;
istate = dframe->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 (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); netplay_hangup(netplay, only);
return false; return false;
@ -221,16 +256,17 @@ static bool send_input_frame(netplay_t *netplay,
if (connection->active && if (connection->active &&
connection->mode >= NETPLAY_CONNECTION_CONNECTED && connection->mode >= NETPLAY_CONNECTION_CONNECTED &&
(connection->mode != NETPLAY_CONNECTION_PLAYING || (connection->mode != NETPLAY_CONNECTION_PLAYING ||
connection->player != player)) i+1 != client_num))
{ {
if (!netplay_send(&connection->send_packet_buffer, connection->fd, if (!netplay_send(&connection->send_packet_buffer, connection->fd,
buffer, sizeof(buffer))) buffer, bufused*sizeof(uint32_t)))
netplay_hangup(netplay, connection); netplay_hangup(netplay, connection);
} }
} }
} }
return true; return true;
#undef BUFSZ
} }
/** /**
@ -243,24 +279,50 @@ static bool send_input_frame(netplay_t *netplay,
bool netplay_send_cur_input(netplay_t *netplay, bool netplay_send_cur_input(netplay_t *netplay,
struct netplay_connection *connection) struct netplay_connection *connection)
{ {
#define BUFSZ 16
uint32_t buffer[BUFSZ];
size_t bufused;
struct delta_frame *dframe = &netplay->buffer[netplay->self_ptr]; 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) if (netplay->is_server)
{ {
/* Send the other players' input data */ /* Send the other players' input data (FIXME: This involves an
for (player = 0; player < MAX_USERS; player++) * unacceptable amount of recalculating) */
for (from_client = 1; from_client < MAX_CLIENTS; from_client++)
{ {
if (connection->mode == NETPLAY_CONNECTION_PLAYING && if (from_client == to_client)
connection->player == player)
continue; continue;
if ((netplay->connected_players & (1<<player))) if ((netplay->connected_players1 & (1<<from_client)))
{ {
if (dframe->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<<device)))
continue;
// Add this device's input
istate = dframe->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, if (!send_input_frame(netplay, connection, NULL,
netplay->self_frame_count, player, netplay->self_frame_count, from_client, bufused, buffer))
dframe->real_input_state[player]))
return false; return false;
} }
} }
@ -281,12 +343,32 @@ bool netplay_send_cur_input(netplay_t *netplay,
if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING || if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING ||
netplay->self_mode == NETPLAY_CONNECTION_SLAVE) netplay->self_mode == NETPLAY_CONNECTION_SLAVE)
{ {
devices = netplay->self_devices;
bufused = 0;
for (device = 0; device < MAX_INPUT_DEVICES; device++)
{
if (!(devices & (1<<device)))
continue;
// Add this device's input (FIXME: refactor)
istate = dframe->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, if (!send_input_frame(netplay, connection, NULL,
netplay->self_frame_count, netplay->self_frame_count,
(netplay->is_server ? NETPLAY_CMD_INPUT_BIT_SERVER : 0) | netplay->self_player, (netplay->is_server ? NETPLAY_CMD_INPUT_BIT_SERVER : 0) | netplay->self_client_num,
dframe->self_state)) bufused, buffer))
return false; return false;
} }
#undef BUFSZ
if (!netplay_send_flush(&connection->send_packet_buffer, connection->fd, if (!netplay_send_flush(&connection->send_packet_buffer, connection->fd,
false)) false))
@ -436,7 +518,7 @@ bool netplay_cmd_mode(netplay_t *netplay,
case NETPLAY_CONNECTION_SLAVE: case NETPLAY_CONNECTION_SLAVE:
payload = &payloadBuf; payload = &payloadBuf;
payloadBuf = htonl(NETPLAY_CMD_PLAY_BIT_SLAVE); payloadBuf = htonl(NETPLAY_CMD_PLAY_BIT_SLAVE);
/* Intentional fallthrough */ /* no break */
case NETPLAY_CONNECTION_PLAYING: case NETPLAY_CONNECTION_PLAYING:
cmd = NETPLAY_CMD_PLAY; cmd = NETPLAY_CMD_PLAY;
@ -506,43 +588,56 @@ static bool netplay_get_cmd(netplay_t *netplay,
case NETPLAY_CMD_INPUT: case NETPLAY_CMD_INPUT:
{ {
uint32_t buffer[WORDS_PER_FRAME]; uint32_t frame_num, client_num, input_size, devices, device;
uint32_t player; bool is_server;
unsigned i; unsigned i;
struct delta_frame *dframe; 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); return netplay_cmd_nak(netplay, connection);
} }
RECV(buffer, sizeof(buffer)) RECV(&frame_num, sizeof(frame_num))
{ return false;
RARCH_ERR("Failed to receive NETPLAY_CMD_INPUT input.\n"); RECV(&client_num, sizeof(client_num))
return netplay_cmd_nak(netplay, connection); return false;
} frame_num = nhtohl(frame_num);
client_num = ntohl(client_num);
for (i = 0; i < WORDS_PER_FRAME; i++) is_server = (client_num & NETPLAY_CMD_INPUT_BIT_SERVER)?true:false;
buffer[i] = ntohl(buffer[i]); client_num &= 0xFFFF;
if (netplay->is_server) 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 && if (connection->mode != NETPLAY_CONNECTION_PLAYING &&
connection->mode != NETPLAY_CONNECTION_SLAVE) connection->mode != NETPLAY_CONNECTION_SLAVE)
{ {
RARCH_ERR("Netplay input from non-participating player.\n"); RARCH_ERR("Netplay input from non-participating player.\n");
return netplay_cmd_nak(netplay, connection); return netplay_cmd_nak(netplay, connection);
} }
player = connection->player; is_server = false;
} client_num = connection - netplay->connections + 1;
else
{
player = buffer[1] & ~NETPLAY_CMD_INPUT_BIT_SERVER;
} }
if (player >= MAX_USERS || !(netplay->connected_players & (1<<player))) if (client_num > 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<<client_num)))
{ {
RARCH_ERR("Invalid NETPLAY_CMD_INPUT player number.\n"); RARCH_ERR("Invalid NETPLAY_CMD_INPUT player number.\n");
return netplay_cmd_nak(netplay, connection); return netplay_cmd_nak(netplay, connection);
@ -551,12 +646,12 @@ static bool netplay_get_cmd(netplay_t *netplay,
/* Check the frame number only if they're not in slave mode */ /* Check the frame number only if they're not in slave mode */
if (connection->mode == NETPLAY_CONNECTION_PLAYING) if (connection->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 */ /* We already had this, so ignore the new transmission */
break; 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 */ /* Out of order = out of luck */
RARCH_ERR("Netplay input out of order.\n"); RARCH_ERR("Netplay input out of order.\n");
@ -565,42 +660,57 @@ static bool netplay_get_cmd(netplay_t *netplay,
} }
/* The data's good! */ /* The data's good! */
dframe = &netplay->buffer[netplay->read_ptr[player]]; dframe = &netplay->buffer[netplay->read_ptr1[client_num]];
if (!netplay_delta_frame_ready(netplay, dframe, netplay->read_frame_count[player])) if (!netplay_delta_frame_ready(netplay, dframe, netplay->read_frame_count1[client_num]))
{ {
/* Hopefully we'll be ready after another round of input */ /* Hopefully we'll be ready after another round of input */
goto shrt; goto shrt;
} }
memcpy(dframe->real_input_state[player], buffer + 2,
WORDS_PER_INPUT*sizeof(uint32_t)); /* Copy in the input */
dframe->have_real[player] = true; for (device = 0; device < MAX_INPUT_DEVICES; device++)
{
netplay_input_state_t istate;
uint32_t dsize, di;
if (!(devices & (1<<device)))
continue;
dsize = netplay_expected_input_size(1 << device);
istate = netplay_input_state_for(&dframe->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 /* Slaves may go through several packets of data in the same frame
* if latency is choppy, so we advance and send their data after * if latency is choppy, so we advance and send their data after
* handling all network data this frame */ * handling all network data this frame */
if (connection->mode == NETPLAY_CONNECTION_PLAYING) if (connection->mode == NETPLAY_CONNECTION_PLAYING)
{ {
netplay->read_ptr[player] = NEXT_PTR(netplay->read_ptr[player]); netplay->read_ptr1[client_num] = NEXT_PTR(netplay->read_ptr1[client_num]);
netplay->read_frame_count[player]++; netplay->read_frame_count1[client_num]++;
if (netplay->is_server) 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) if (dframe->frame <= netplay->self_frame_count)
send_input_frame(netplay, NULL, connection, buffer[0], send_input_frame(netplay, dframe, NULL, connection, client_num);
player, dframe->real_input_state[player]);
} }
} }
/* If this was server data, advance our server pointer too */ /* 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_ptr = netplay->read_ptr1[client_num];
netplay->server_frame_count = netplay->read_frame_count[player]; netplay->server_frame_count = netplay->read_frame_count1[client_num];
} }
#ifdef DEBUG_NETPLAY_STEPS #ifdef DEBUG_NETPLAY_STEPS
RARCH_LOG("Received input from %u\n", player); RARCH_LOG("Received input from %u\n", client_num);
print_state(netplay); print_state(netplay);
#endif #endif
break; break;
@ -685,7 +795,9 @@ static bool netplay_get_cmd(netplay_t *netplay,
case NETPLAY_CMD_SPECTATE: case NETPLAY_CMD_SPECTATE:
{ {
uint32_t payload[2]; uint32_t payload[3];
uint32_t client_num;
size_t i;
if (!netplay->is_server) if (!netplay->is_server)
{ {
@ -693,20 +805,25 @@ static bool netplay_get_cmd(netplay_t *netplay,
return netplay_cmd_nak(netplay, connection); return netplay_cmd_nak(netplay, connection);
} }
client_num = connection - netplay->connections + 1;
if (connection->mode == NETPLAY_CONNECTION_PLAYING || if (connection->mode == NETPLAY_CONNECTION_PLAYING ||
connection->mode == NETPLAY_CONNECTION_SLAVE) connection->mode == NETPLAY_CONNECTION_SLAVE)
{ {
/* The frame we haven't received is their end frame */ /* 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 */ /* Mark them as not playing anymore */
connection->mode = NETPLAY_CONNECTION_SPECTATING; connection->mode = NETPLAY_CONNECTION_SPECTATING;
netplay->connected_players &= ~(1<<connection->player); netplay->connected_players1 &= ~(1<<client_num);
netplay->connected_slaves &= ~(1<<connection->player); netplay->connected_slaves1 &= ~(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 */ /* Announce it */
msg[sizeof(msg)-1] = '\0'; msg[sizeof(msg)-1] = '\0';
snprintf(msg, sizeof(msg)-1, "Player %d has left", connection->player+1); snprintf(msg, sizeof(msg)-1, "Player %d has left", client_num+1);
RARCH_LOG("%s\n", msg); RARCH_LOG("%s\n", msg);
runloop_msg_queue_push(msg, 1, 180, false); 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 */ /* 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)); netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload));
break; break;
} }
case NETPLAY_CMD_PLAY: case NETPLAY_CMD_PLAY:
{ {
uint32_t payload[2]; uint32_t devices = 0, device, client_num;
uint32_t player = 0; uint32_t payload[3];
bool slave = false; bool slave = false;
settings_t *settings = config_get_ptr(); settings_t *settings = config_get_ptr();
/* Check if they requested slave mode */ /* Check if they requested slave mode */
if (cmd_size == sizeof(uint32_t)) 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"); RARCH_ERR("Failed to receive NETPLAY_CMD_PLAY payload.\n");
return netplay_cmd_nak(netplay, connection); return netplay_cmd_nak(netplay, connection);
} }
payload[0] = ntohl(payload[0]); devices = ntohl(devices);
if (payload[0] & NETPLAY_CMD_PLAY_BIT_SLAVE) if (devices & NETPLAY_CMD_PLAY_BIT_SLAVE)
slave = true; slave = true;
devices &= ~(NETPLAY_CMD_PLAY_BIT_SLAVE);
} }
else if (cmd_size != 0) 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"); RARCH_ERR("NETPLAY_CMD_PLAY from a server.\n");
return netplay_cmd_nak(netplay, connection); return netplay_cmd_nak(netplay, connection);
} }
client_num = connection - netplay->connections + 1;
if (connection->delay_frame) if (connection->delay_frame)
{ {
@ -777,21 +897,20 @@ static bool netplay_get_cmd(netplay_t *netplay,
break; break;
} }
/* Find an available player slot */ /* Find an available device (FIXME: Honor device request) */
for (player = 0; player <= netplay->player_max; player++) for (device = 0; device <= netplay->input_device_max; device++)
{ {
if (!(netplay->self_mode == NETPLAY_CONNECTION_PLAYING && if (!netplay->device_clients[device])
netplay->self_player == player) &&
!(netplay->connected_players & (1<<player)))
break; break;
} }
if (player > netplay->player_max) if (device > netplay->input_device_max)
{ {
/* No slots free! */ /* No slots free! */
payload[0] = htonl(NETPLAY_CMD_MODE_REFUSED_REASON_NO_SLOTS); payload[0] = htonl(NETPLAY_CMD_MODE_REFUSED_REASON_NO_SLOTS);
netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE_REFUSED, payload, sizeof(uint32_t)); netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE_REFUSED, payload, sizeof(uint32_t));
break; break;
} }
payload[2] = htonl(1<<device);
if (connection->mode != NETPLAY_CONNECTION_PLAYING && if (connection->mode != NETPLAY_CONNECTION_PLAYING &&
connection->mode != NETPLAY_CONNECTION_SLAVE) connection->mode != NETPLAY_CONNECTION_SLAVE)
@ -799,20 +918,22 @@ static bool netplay_get_cmd(netplay_t *netplay,
/* Mark them as playing */ /* Mark them as playing */
connection->mode = slave ? NETPLAY_CONNECTION_SLAVE : connection->mode = slave ? NETPLAY_CONNECTION_SLAVE :
NETPLAY_CONNECTION_PLAYING; NETPLAY_CONNECTION_PLAYING;
connection->player = player;
netplay->connected_players |= 1<<player; netplay->connected_players1 |= 1<<client_num;
if (slave) if (slave)
netplay->connected_slaves |= 1<<player; netplay->connected_slaves1 |= 1<<client_num;
netplay->client_devices[client_num] |= 1<<device;
netplay->device_clients[device] |= 1<<client_num;
/* Tell everyone */ /* Tell everyone */
payload[1] = htonl(NETPLAY_CMD_MODE_BIT_PLAYING | payload[1] = htonl(NETPLAY_CMD_MODE_BIT_PLAYING |
(slave?NETPLAY_CMD_MODE_BIT_SLAVE:0) | (slave?NETPLAY_CMD_MODE_BIT_SLAVE:0) |
connection->player); client_num);
netplay_send_raw_cmd_all(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload)); netplay_send_raw_cmd_all(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload));
/* Announce it */ /* Announce it */
msg[sizeof(msg)-1] = '\0'; 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); RARCH_LOG("%s\n", msg);
runloop_msg_queue_push(msg, 1, 180, false); 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)? ((connection->mode == NETPLAY_CONNECTION_SLAVE)?
NETPLAY_CMD_MODE_BIT_SLAVE:0) | NETPLAY_CMD_MODE_BIT_SLAVE:0) |
NETPLAY_CMD_MODE_BIT_YOU | NETPLAY_CMD_MODE_BIT_YOU |
connection->player); client_num);
payload[2] = htonl(1<<device);
netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload)); netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload));
/* And expect their data */ /* And expect their data */
netplay->read_ptr[player] = NEXT_PTR(netplay->self_ptr); netplay->read_ptr1[client_num] = NEXT_PTR(netplay->self_ptr);
netplay->read_frame_count[player] = netplay->self_frame_count + 1; netplay->read_frame_count1[client_num] = netplay->self_frame_count + 1;
break; break;
} }
case NETPLAY_CMD_MODE: case NETPLAY_CMD_MODE:
{ {
uint32_t payload[2]; uint32_t payload[3];
uint32_t frame, mode, player; uint32_t frame, mode, client_num, devices;
size_t ptr; size_t ptr;
struct delta_frame *dframe; struct delta_frame *dframe;
@ -870,13 +992,15 @@ static bool netplay_get_cmd(netplay_t *netplay,
netplay->force_rewind = true; netplay->force_rewind = true;
mode = ntohl(payload[1]); mode = ntohl(payload[1]);
player = mode & 0xFFFF; client_num = mode & 0xFFFF;
if (player >= MAX_USERS) if (client_num >= MAX_CLIENTS)
{ {
RARCH_ERR("Received NETPLAY_CMD_MODE for a higher player number than we support.\n"); RARCH_ERR("Received NETPLAY_CMD_MODE for a higher player number than we support.\n");
return netplay_cmd_nak(netplay, connection); return netplay_cmd_nak(netplay, connection);
} }
devices = ntohl(payload[2]);
if (mode & NETPLAY_CMD_MODE_BIT_YOU) if (mode & NETPLAY_CMD_MODE_BIT_YOU)
{ {
/* A change to me! */ /* 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 */ /* Our mode is based on whether we have the slave bit set */
if (mode & NETPLAY_CMD_MODE_BIT_SLAVE) if (mode & NETPLAY_CMD_MODE_BIT_SLAVE)
{
netplay->self_mode = NETPLAY_CONNECTION_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<<player);
netplay->read_ptr[player] = netplay->server_ptr;
netplay->read_frame_count[player] = netplay->server_frame_count;
}
else else
{
netplay->self_mode = NETPLAY_CONNECTION_PLAYING; netplay->self_mode = NETPLAY_CONNECTION_PLAYING;
}
netplay->self_player = player; netplay->connected_players1 |= (1<<client_num);
netplay->client_devices[client_num] = devices;
for (device = 0; device < MAX_INPUT_DEVICES; device++)
if (devices & (1<<device))
netplay->device_clients[device] |= (1<<client_num);
netplay->self_devices = devices;
/* Fix up current frame info */ /* Fix up current frame info */
if (frame <= netplay->self_frame_count) if (frame <= netplay->self_frame_count)
{ {
/* FIXME: Must generate frames with 0 data */
#if 0
/* It wanted past frames, better send 'em! */ /* It wanted past frames, better send 'em! */
START(netplay->server_ptr); START(netplay->server_ptr);
while (dframe->used && dframe->frame <= netplay->self_frame_count) 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; if (dframe->frame == netplay->self_frame_count) break;
NEXT(); NEXT();
} }
#endif
} }
else else
{ {
uint32_t frame_count; uint32_t frame_count;
netplay_input_state_t istate;
/* It wants future frames, make sure we don't capture or send intermediate ones */ /* It wants future frames, make sure we don't capture or send intermediate ones */
START(netplay->self_ptr); 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; dframe->have_local = true;
/* Go on to the next delta frame */ /* Go on to the next delta frame */
@ -962,7 +1084,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
/* Announce it */ /* Announce it */
msg[sizeof(msg)-1] = '\0'; 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); RARCH_LOG("%s\n", msg);
runloop_msg_queue_push(msg, 1, 180, false); 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 */ /* Unmark ourself, in case we were in slave mode */
netplay->connected_players &= ~(1<<player); netplay->connected_players1 &= ~(1<<client_num);
netplay->client_devices[client_num] = 0;
for (device = 0; device < MAX_INPUT_DEVICES; device++)
netplay->device_clients[device] &= ~(1<<client_num);
/* Announce it */ /* Announce it */
strlcpy(msg, "You have left the game", sizeof(msg)); strlcpy(msg, "You have left the game", sizeof(msg));
@ -1008,14 +1133,18 @@ static bool netplay_get_cmd(netplay_t *netplay,
return netplay_cmd_nak(netplay, connection); return netplay_cmd_nak(netplay, connection);
} }
netplay->connected_players |= (1<<player); netplay->connected_players1 |= (1<<client_num);
netplay->client_devices[client_num] = devices;
for (device = 0; device < MAX_INPUT_DEVICES; device++)
if (devices & (1<<device))
netplay->device_clients[device] |= (1<<client_num);
netplay->read_ptr[player] = netplay->server_ptr; netplay->read_ptr1[client_num] = netplay->server_ptr;
netplay->read_frame_count[player] = netplay->server_frame_count; netplay->read_frame_count1[client_num] = netplay->server_frame_count;
/* Announce it */ /* Announce it */
msg[sizeof(msg)-1] = '\0'; 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); RARCH_LOG("%s\n", msg);
runloop_msg_queue_push(msg, 1, 180, false); runloop_msg_queue_push(msg, 1, 180, false);
@ -1027,11 +1156,14 @@ static bool netplay_get_cmd(netplay_t *netplay,
} }
else else
{ {
netplay->connected_players &= ~(1<<player); netplay->connected_players1 &= ~(1<<client_num);
netplay->client_devices[client_num] = 0;
for (device = 0; device < MAX_INPUT_DEVICES; device++)
netplay->device_clients[device] &= ~(1<<client_num);
/* Announce it */ /* Announce it */
msg[sizeof(msg)-1] = '\0'; msg[sizeof(msg)-1] = '\0';
snprintf(msg, sizeof(msg)-1, "Player %d has left", player+1); snprintf(msg, sizeof(msg)-1, "Player %d has left", client_num+1);
RARCH_LOG("%s\n", msg); RARCH_LOG("%s\n", msg);
runloop_msg_queue_push(msg, 1, 180, false); runloop_msg_queue_push(msg, 1, 180, false);

View File

@ -28,10 +28,7 @@
#include "../../msg_hash.h" #include "../../msg_hash.h"
#include "../../verbosity.h" #include "../../verbosity.h"
#define WORDS_PER_INPUT 3 /* Buttons, left stick, right stick */ #define NETPLAY_PROTOCOL_VERSION 5
#define WORDS_PER_FRAME (WORDS_PER_INPUT+2) /* + frameno, playerno */
#define NETPLAY_PROTOCOL_VERSION 4
#define RARCH_DEFAULT_PORT 55435 #define RARCH_DEFAULT_PORT 55435
#define RARCH_DEFAULT_NICK "Anonymous" #define RARCH_DEFAULT_NICK "Anonymous"
@ -45,6 +42,17 @@
#define CATCH_UP_CHECK_TIME_USEC (500*1000) #define CATCH_UP_CHECK_TIME_USEC (500*1000)
#define MAX_RETRIES 16 #define MAX_RETRIES 16
#define RETRY_MS 500 #define RETRY_MS 500
#define MAX_INPUT_DEVICES 16
#undef MAX_USERS /* FIXME: Temporary */
/* We allow only 32 clients to fit into a 32-bit bitmap */
#define MAX_CLIENTS 32
typedef uint32_t client_bitmap_t;
/* For now we only support the normal or analog gamepad */
#define NETPLAY_SUPPORTED_DEVICES ( \
(1<<RETRO_DEVICE_JOYPAD) | \
(1<<RETRO_DEVICE_ANALOG))
#define NETPLAY_MAX_STALL_FRAMES 60 #define NETPLAY_MAX_STALL_FRAMES 60
#define NETPLAY_FRAME_RUN_TIME_WINDOW 120 #define NETPLAY_FRAME_RUN_TIME_WINDOW 120
@ -177,7 +185,7 @@ enum netplay_cmd
#define NETPLAY_CMD_INPUT_BIT_SERVER (1U<<31) #define NETPLAY_CMD_INPUT_BIT_SERVER (1U<<31)
#define NETPLAY_CMD_SYNC_BIT_PAUSED (1U<<31) #define NETPLAY_CMD_SYNC_BIT_PAUSED (1U<<31)
#define NETPLAY_CMD_PLAY_BIT_SLAVE (1U) #define NETPLAY_CMD_PLAY_BIT_SLAVE (1U<<31)
#define NETPLAY_CMD_MODE_BIT_SLAVE (1U<<18) #define NETPLAY_CMD_MODE_BIT_SLAVE (1U<<18)
#define NETPLAY_CMD_MODE_BIT_PLAYING (1U<<17) #define NETPLAY_CMD_MODE_BIT_PLAYING (1U<<17)
#define NETPLAY_CMD_MODE_BIT_YOU (1U<<16) #define NETPLAY_CMD_MODE_BIT_YOU (1U<<16)
@ -240,7 +248,24 @@ enum rarch_netplay_stall_reason
NETPLAY_STALL_NO_CONNECTION NETPLAY_STALL_NO_CONNECTION
}; };
typedef uint32_t netplay_input_state_t[WORDS_PER_INPUT]; /* Input state for a particular client-device pair */
typedef struct netplay_input_state
{
/* The next input state (forming a list) */
struct netplay_input_state *next;
/* Whose data is this? */
uint32_t client_num;
/* Is this real data? */
bool is_real;
/* How many words of input data do we have? */
uint32_t size;
/* The input data itself (note: should expand beyond 1 by overallocating). */
uint32_t data[1];
} *netplay_input_state_t;
struct delta_frame struct delta_frame
{ {
@ -253,20 +278,26 @@ struct delta_frame
/* The CRC-32 of the serialized state if we've calculated it, else 0 */ /* The CRC-32 of the serialized state if we've calculated it, else 0 */
uint32_t crc; uint32_t crc;
/* The real, simulated and local input. If we're playing, self_state is /* The processed input, i.e., what's actually going to the core. is_real
* mirrored to the appropriate real_input_state player. */ * here means all input came from real players, none simulated. One list per
netplay_input_state_t real_input_state[MAX_USERS]; * input device. */
netplay_input_state_t simulated_input_state[MAX_USERS]; netplay_input_state_t processed_input[MAX_INPUT_DEVICES];
netplay_input_state_t self_state;
/* The real input */
netplay_input_state_t real_input[MAX_INPUT_DEVICES];
/* The simulated input. is_real here means the simulation is done, i.e.,
* it's a real simulation, not real input. */
netplay_input_state_t simulated_input[MAX_INPUT_DEVICES];
/* Have we read local input? */ /* Have we read local input? */
bool have_local; bool have_local;
/* Have we read the real (remote) input? */ /* Have we read the real (remote) input? */
bool have_real[MAX_USERS]; bool have_real[MAX_CLIENTS];
/* Is the current state as of self_frame_count using the real (remote) data? */ /* Is the current state as of self_frame_count using the real (remote) data? */
bool used_real[MAX_USERS]; bool used_real[MAX_CLIENTS];
}; };
struct socket_buffer struct socket_buffer
@ -309,9 +340,6 @@ struct netplay_connection
* to wait for, or 0 if no delay is active. */ * to wait for, or 0 if no delay is active. */
uint32_t delay_frame; uint32_t delay_frame;
/* Player # of connected player */
uint32_t player;
/* What compression does this peer support? */ /* What compression does this peer support? */
uint32_t compression_supported; uint32_t compression_supported;
@ -350,8 +378,8 @@ struct netplay
/* TCP connection for listening (server only) */ /* TCP connection for listening (server only) */
int listen_fd; int listen_fd;
/* Our player number */ /* Our client number */
uint32_t self_player; uint32_t self_client_num;
/* Our mode and status */ /* Our mode and status */
enum rarch_netplay_connection_mode self_mode; enum rarch_netplay_connection_mode self_mode;
@ -361,19 +389,28 @@ struct netplay
size_t connections_size; size_t connections_size;
struct netplay_connection one_connection; /* Client only */ struct netplay_connection one_connection; /* Client only */
/* Bitmap of players with controllers (low bit is player 1) */ /* Bitmap of clients with input devices */
uint32_t connected_players; uint32_t connected_players1;
/* Bitmap of players playing in slave mode (should be a subset of /* Bitmap of clients playing in slave mode (should be a subset of
* connected_players) */ * connected_players) */
uint32_t connected_slaves; uint32_t connected_slaves1;
/* For each client, the bitmap of devices they're connected to */
uint32_t client_devices[MAX_CLIENTS];
/* For each device, the bitmap of clients connected */
client_bitmap_t device_clients[MAX_INPUT_DEVICES];
/* Our own device bitmap */
uint32_t self_devices;
/* Number of desync operations we're currently performing. If set, we don't /* Number of desync operations we're currently performing. If set, we don't
* attempt to stay in sync. */ * attempt to stay in sync. */
uint32_t desync; uint32_t desync;
/* Maximum player number */ /* Maximum input device number */
uint32_t player_max; uint32_t input_device_max;
struct retro_callbacks cbs; struct retro_callbacks cbs;
@ -418,9 +455,9 @@ struct netplay
size_t unread_ptr; size_t unread_ptr;
uint32_t unread_frame_count; uint32_t unread_frame_count;
/* Pointer to the next frame to read from each player */ /* Pointer to the next frame to read from each client */
size_t read_ptr[MAX_USERS]; size_t read_ptr1[MAX_CLIENTS];
uint32_t read_frame_count[MAX_USERS]; uint32_t read_frame_count1[MAX_CLIENTS];
/* Pointer to the next frame to read from the server (as it might not be a /* Pointer to the next frame to read from the server (as it might not be a
* player but still synchronizes) */ * player but still synchronizes) */
@ -457,7 +494,8 @@ struct netplay
bool savestate_request_outstanding; bool savestate_request_outstanding;
/* A buffer for outgoing input packets. */ /* A buffer for outgoing input packets. */
uint32_t input_packet_buffer[2 + WORDS_PER_FRAME]; size_t input_packet_buffer_size;
uint32_t *input_packet_buffer1;
/* Our local socket info */ /* Our local socket info */
struct addrinfo *addr; struct addrinfo *addr;
@ -608,6 +646,14 @@ bool netplay_delta_frame_ready(netplay_t *netplay, struct delta_frame *delta,
*/ */
uint32_t netplay_delta_frame_crc(netplay_t *netplay, struct delta_frame *delta); uint32_t netplay_delta_frame_crc(netplay_t *netplay, struct delta_frame *delta);
/**
* netplay_input_state_for
*
* Get an input state for a particular client
*/
netplay_input_state_t netplay_input_state_for(netplay_input_state_t *list,
uint32_t client_num, size_t size, bool mustCreate);
/*************************************************************** /***************************************************************
* NETPLAY-DISCOVERY.C * NETPLAY-DISCOVERY.C

View File

@ -39,7 +39,7 @@
*/ */
void netplay_update_unread_ptr(netplay_t *netplay) void netplay_update_unread_ptr(netplay_t *netplay)
{ {
if (netplay->is_server && !netplay->connected_players) if (netplay->is_server && netplay->connected_players1<=1)
{ {
/* Nothing at all to read! */ /* Nothing at all to read! */
netplay->unread_ptr = netplay->self_ptr; netplay->unread_ptr = netplay->self_ptr;
@ -50,16 +50,16 @@ void netplay_update_unread_ptr(netplay_t *netplay)
{ {
size_t new_unread_ptr = 0; size_t new_unread_ptr = 0;
uint32_t new_unread_frame_count = (uint32_t) -1; 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<<player))) continue; if (!(netplay->connected_players1 & (1<<client))) continue;
if ((netplay->connected_slaves & (1<<player))) continue; if ((netplay->connected_slaves1 & (1<<client))) continue;
if (netplay->read_frame_count[player] < new_unread_frame_count) if (netplay->read_frame_count1[client] < new_unread_frame_count)
{ {
new_unread_ptr = netplay->read_ptr[player]; new_unread_ptr = netplay->read_ptr1[client];
new_unread_frame_count = netplay->read_frame_count[player]; 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) void netplay_simulate_input(netplay_t *netplay, size_t sim_ptr, bool resim)
{ {
uint32_t player; uint32_t client;
size_t prev; size_t prev;
struct delta_frame *simframe, *pframe; struct delta_frame *simframe, *pframe;
netplay_input_state_t simstate, pstate;
simframe = &netplay->buffer[sim_ptr]; simframe = &netplay->buffer[sim_ptr];
for (player = 0; player < MAX_USERS; player++) for (client = 0; client < MAX_CLIENTS; client++)
{ {
if (!(netplay->connected_players & (1<<player))) continue; if (!(netplay->connected_players1 & (1<<client))) continue;
if (simframe->have_real[player]) continue; // FIXME: Maybe this is the right time to do resolved data?
if (simframe->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]; pframe = &netplay->buffer[prev];
pstate = netplay_input_state_for(&pframe->real_input, client, 3 /* FIXME */, false);
if (!pstate)
continue;
if (resim) if (resim)
{ {
@ -128,15 +137,13 @@ void netplay_simulate_input(netplay_t *netplay, size_t sim_ptr, bool resim)
(1U<<RETRO_DEVICE_ID_JOYPAD_DOWN) | (1U<<RETRO_DEVICE_ID_JOYPAD_DOWN) |
(1U<<RETRO_DEVICE_ID_JOYPAD_LEFT) | (1U<<RETRO_DEVICE_ID_JOYPAD_LEFT) |
(1U<<RETRO_DEVICE_ID_JOYPAD_RIGHT); (1U<<RETRO_DEVICE_ID_JOYPAD_RIGHT);
uint32_t sim_state = simframe->simulated_input_state[player][0] & keep; simstate->data[0] &= keep;
sim_state |= pframe->real_input_state[player][0] & ~keep; simstate->data[0] |= pstate->data[0] & ~keep;
simframe->simulated_input_state[player][0] = sim_state;
} }
else else
{ {
memcpy(simframe->simulated_input_state[player], memcpy(simstate->data, pstate->data,
pframe->real_input_state[player], simstate->size * sizeof(uint32_t));
WORDS_PER_INPUT * 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 */ /* 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->self_mode < NETPLAY_CONNECTION_CONNECTED) ||
(netplay->desync)) (netplay->desync))
{ {