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
NOINPUT command is provided for that purpose.
Each client has a client number, and the server is always client number 0.
Client numbers are currently limited to 0-31, as they're used in 32-bit
bitmaps.
The handshake procedure (this part is done by both server and client):
1) Send connection header
2) Receive and verify connection header
@ -156,7 +160,7 @@ Payload:
{
frame number: uint32
is server data: 1 bit
player: 31 bits
client number: 31 bits
joypad input: uint32
analog 1 input: uint32
analog 2 input: uint32
@ -213,9 +217,9 @@ Payload:
{
frame number: uint32
paused?: 1 bit
connected players: 31 bits
flip frame: uint32
client number: 31 bits
controller devices: uint32[16]
controller-client mapping: uint32[16]
client nick: char[32]
sram: variable
}
@ -236,13 +240,16 @@ Description:
Command: PLAY
Payload:
{
reserved: 31 bits
as slave?: 1 bit
reserved: 15 bits
requested device(s): 16 bits
}
Description:
Request to enter player mode. The client must wait for a MODE command
before sending input. Server may refuse or force slave connections, so the
request is not necessarily honored. Payload may be elided if zero.
request is not necessarily honored. If no devices are explicitly requested,
the server may choose how to assign; the default is to assign the first
unassigned device. Payload may be elided if zero.
Command: MODE
Payload:
@ -252,7 +259,8 @@ Payload:
slave: 1 bit
playing: 1 bit
you: 1 bit
player number: uint16
client number: uint16
device bitmap: uint32
}
Description:
Inform of a connection mode change (possibly of the receiving client). Only

View File

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

View File

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

View File

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

View File

@ -28,10 +28,7 @@
#include "../../msg_hash.h"
#include "../../verbosity.h"
#define WORDS_PER_INPUT 3 /* Buttons, left stick, right stick */
#define WORDS_PER_FRAME (WORDS_PER_INPUT+2) /* + frameno, playerno */
#define NETPLAY_PROTOCOL_VERSION 4
#define NETPLAY_PROTOCOL_VERSION 5
#define RARCH_DEFAULT_PORT 55435
#define RARCH_DEFAULT_NICK "Anonymous"
@ -45,6 +42,17 @@
#define CATCH_UP_CHECK_TIME_USEC (500*1000)
#define MAX_RETRIES 16
#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_FRAME_RUN_TIME_WINDOW 120
@ -177,7 +185,7 @@ enum netplay_cmd
#define NETPLAY_CMD_INPUT_BIT_SERVER (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_PLAYING (1U<<17)
#define NETPLAY_CMD_MODE_BIT_YOU (1U<<16)
@ -240,7 +248,24 @@ enum rarch_netplay_stall_reason
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
{
@ -253,20 +278,26 @@ struct delta_frame
/* The CRC-32 of the serialized state if we've calculated it, else 0 */
uint32_t crc;
/* The real, simulated and local input. If we're playing, self_state is
* mirrored to the appropriate real_input_state player. */
netplay_input_state_t real_input_state[MAX_USERS];
netplay_input_state_t simulated_input_state[MAX_USERS];
netplay_input_state_t self_state;
/* The processed input, i.e., what's actually going to the core. is_real
* here means all input came from real players, none simulated. One list per
* input device. */
netplay_input_state_t processed_input[MAX_INPUT_DEVICES];
/* 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? */
bool have_local;
/* 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? */
bool used_real[MAX_USERS];
bool used_real[MAX_CLIENTS];
};
struct socket_buffer
@ -309,9 +340,6 @@ struct netplay_connection
* to wait for, or 0 if no delay is active. */
uint32_t delay_frame;
/* Player # of connected player */
uint32_t player;
/* What compression does this peer support? */
uint32_t compression_supported;
@ -350,8 +378,8 @@ struct netplay
/* TCP connection for listening (server only) */
int listen_fd;
/* Our player number */
uint32_t self_player;
/* Our client number */
uint32_t self_client_num;
/* Our mode and status */
enum rarch_netplay_connection_mode self_mode;
@ -361,19 +389,28 @@ struct netplay
size_t connections_size;
struct netplay_connection one_connection; /* Client only */
/* Bitmap of players with controllers (low bit is player 1) */
uint32_t connected_players;
/* Bitmap of clients with input devices */
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) */
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
* attempt to stay in sync. */
uint32_t desync;
/* Maximum player number */
uint32_t player_max;
/* Maximum input device number */
uint32_t input_device_max;
struct retro_callbacks cbs;
@ -418,9 +455,9 @@ struct netplay
size_t unread_ptr;
uint32_t unread_frame_count;
/* Pointer to the next frame to read from each player */
size_t read_ptr[MAX_USERS];
uint32_t read_frame_count[MAX_USERS];
/* Pointer to the next frame to read from each client */
size_t read_ptr1[MAX_CLIENTS];
uint32_t read_frame_count1[MAX_CLIENTS];
/* Pointer to the next frame to read from the server (as it might not be a
* player but still synchronizes) */
@ -457,7 +494,8 @@ struct netplay
bool savestate_request_outstanding;
/* 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 */
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);
/**
* 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

View File

@ -39,7 +39,7 @@
*/
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! */
netplay->unread_ptr = netplay->self_ptr;
@ -50,16 +50,16 @@ void netplay_update_unread_ptr(netplay_t *netplay)
{
size_t new_unread_ptr = 0;
uint32_t new_unread_frame_count = (uint32_t) -1;
uint32_t player;
uint32_t client;
for (player = 0; player < MAX_USERS; player++)
for (client = 0; client < MAX_CLIENTS; client++)
{
if (!(netplay->connected_players & (1<<player))) continue;
if ((netplay->connected_slaves & (1<<player))) continue;
if (netplay->read_frame_count[player] < new_unread_frame_count)
if (!(netplay->connected_players1 & (1<<client))) continue;
if ((netplay->connected_slaves1 & (1<<client))) continue;
if (netplay->read_frame_count1[client] < new_unread_frame_count)
{
new_unread_ptr = netplay->read_ptr[player];
new_unread_frame_count = netplay->read_frame_count[player];
new_unread_ptr = netplay->read_ptr1[client];
new_unread_frame_count = netplay->read_frame_count1[client];
}
}
@ -93,19 +93,28 @@ void netplay_update_unread_ptr(netplay_t *netplay)
*/
void netplay_simulate_input(netplay_t *netplay, size_t sim_ptr, bool resim)
{
uint32_t player;
uint32_t client;
size_t prev;
struct delta_frame *simframe, *pframe;
netplay_input_state_t simstate, pstate;
simframe = &netplay->buffer[sim_ptr];
for (player = 0; player < MAX_USERS; player++)
for (client = 0; client < MAX_CLIENTS; client++)
{
if (!(netplay->connected_players & (1<<player))) continue;
if (simframe->have_real[player]) continue;
if (!(netplay->connected_players1 & (1<<client))) 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];
pstate = netplay_input_state_for(&pframe->real_input, client, 3 /* FIXME */, false);
if (!pstate)
continue;
if (resim)
{
@ -128,15 +137,13 @@ void netplay_simulate_input(netplay_t *netplay, size_t sim_ptr, bool resim)
(1U<<RETRO_DEVICE_ID_JOYPAD_DOWN) |
(1U<<RETRO_DEVICE_ID_JOYPAD_LEFT) |
(1U<<RETRO_DEVICE_ID_JOYPAD_RIGHT);
uint32_t sim_state = simframe->simulated_input_state[player][0] & keep;
sim_state |= pframe->real_input_state[player][0] & ~keep;
simframe->simulated_input_state[player][0] = sim_state;
simstate->data[0] &= keep;
simstate->data[0] |= pstate->data[0] & ~keep;
}
else
{
memcpy(simframe->simulated_input_state[player],
pframe->real_input_state[player],
WORDS_PER_INPUT * sizeof(uint32_t));
memcpy(simstate->data, pstate->data,
simstate->size * sizeof(uint32_t));
}
}
}
@ -391,7 +398,7 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
}
/* Only relevant if we're connected and not in a desynching operation */
if ((netplay->is_server && !netplay->connected_players) ||
if ((netplay->is_server && (netplay->connected_players1>1)) ||
(netplay->self_mode < NETPLAY_CONNECTION_CONNECTED) ||
(netplay->desync))
{