Merge pull request #4547 from GregorR/netplay-input-latency-2

Netplay input latency
This commit is contained in:
Twinaphex 2017-02-02 09:47:44 +01:00 committed by GitHub
commit 089ed2f215
15 changed files with 266 additions and 70 deletions

View File

@ -964,6 +964,8 @@ static struct config_int_setting *populate_settings_int(settings_t *settings, in
#ifdef HAVE_NETWORKING
SETTING_INT("netplay_ip_port", &settings->netplay.port, true, RARCH_DEFAULT_PORT, false);
SETTING_INT("netplay_check_frames", (unsigned*)&settings->netplay.check_frames, true, netplay_check_frames, false);
SETTING_INT("netplay_input_latency_frames_min",&settings->netplay.input_latency_frames_min, true, 0, false);
SETTING_INT("netplay_input_latency_frames_range",&settings->netplay.input_latency_frames_range, true, 0, false);
#endif
#ifdef HAVE_LANGEXTRA
SETTING_INT("user_language", &settings->user_language, true, RETRO_LANGUAGE_ENGLISH, false);

View File

@ -407,6 +407,8 @@ typedef struct settings
unsigned port;
bool stateless_mode;
int check_frames;
unsigned input_latency_frames_min;
unsigned input_latency_frames_range;
bool swap_input;
bool nat_traversal;
char password[128];

View File

@ -587,6 +587,10 @@ MSG_HASH(MENU_ENUM_LABEL_NETPLAY_CHECK_FRAMES,
"netplay_check_frames")
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_CLIENT_SWAP_INPUT,
"netplay_client_swap_input")
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_INPUT_LATENCY_FRAMES_MIN,
"netplay_input_latency_frames_min")
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_INPUT_LATENCY_FRAMES_RANGE,
"netplay_input_latency_frames_range")
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_DISCONNECT,
"menu_netplay_disconnect")
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_ENABLE,

View File

@ -1560,6 +1560,31 @@ int menu_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len) {
"no checks. This value is only used on the \n"
"netplay host. \n");
break;
case MENU_ENUM_LABEL_NETPLAY_INPUT_LATENCY_FRAMES_MIN:
snprintf(s, len,
"The number of frames of input latency for \n"
"netplay to use to hide network latency. \n"
" \n"
"When in netplay, this option delays local \n"
"input, so that the frame being run is \n"
"closer to the frames being received from \n"
"the network. This reduces jitter and makes \n"
"netplay less CPU-intensive, but at the \n"
"price of noticeable input lag. \n");
break;
case MENU_ENUM_LABEL_NETPLAY_INPUT_LATENCY_FRAMES_RANGE:
snprintf(s, len,
"The range of frames of input latency that \n"
"may be used by netplay to hide network \n"
"latency. \n"
"\n"
"If set, netplay will adjust the number of \n"
"frames of input latency dynamically to \n"
"balance CPU time, input latency and \n"
"network latency. This reduces jitter and \n"
"makes netplay less CPU-intensive, but at \n"
"the price of unpredictable input lag. \n");
break;
case MENU_ENUM_LABEL_NETPLAY_NAT_TRAVERSAL:
snprintf(s, len,
"When hosting, attempt to listen for\n"

View File

@ -964,6 +964,10 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY,
"Netplay")
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CHECK_FRAMES,
"Netplay Check Frames")
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_MIN,
"Input Latency Frames")
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_RANGE,
"Input Latency Frames Range")
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CLIENT_SWAP_INPUT,
"Netplay P2 Uses C1")
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DELAY_FRAMES,

View File

@ -4813,6 +4813,14 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data)
MENU_ENUM_LABEL_NETPLAY_CHECK_FRAMES,
PARSE_ONLY_INT, false) != -1)
count++;
if (menu_displaylist_parse_settings_enum(menu, info,
MENU_ENUM_LABEL_NETPLAY_INPUT_LATENCY_FRAMES_MIN,
PARSE_ONLY_INT, false) != -1)
count++;
if (menu_displaylist_parse_settings_enum(menu, info,
MENU_ENUM_LABEL_NETPLAY_INPUT_LATENCY_FRAMES_RANGE,
PARSE_ONLY_INT, false) != -1)
count++;
if (menu_displaylist_parse_settings_enum(menu, info,
MENU_ENUM_LABEL_NETPLAY_NAT_TRAVERSAL,
PARSE_ONLY_BOOL, false) != -1)

View File

@ -5672,7 +5672,35 @@ static bool setting_append_list(
parent_group,
general_write_handler,
general_read_handler);
menu_settings_list_current_add_range(list, list_info, -600, 600, 1, true, false);
menu_settings_list_current_add_range(list, list_info, -600, 600, 1, false, false);
settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED);
CONFIG_INT(
list, list_info,
(int *) &settings->netplay.input_latency_frames_min,
MENU_ENUM_LABEL_NETPLAY_INPUT_LATENCY_FRAMES_MIN,
MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_MIN,
0,
&group_info,
&subgroup_info,
parent_group,
general_write_handler,
general_read_handler);
menu_settings_list_current_add_range(list, list_info, 0, 15, 1, true, true);
settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED);
CONFIG_INT(
list, list_info,
(int *) &settings->netplay.input_latency_frames_range,
MENU_ENUM_LABEL_NETPLAY_INPUT_LATENCY_FRAMES_RANGE,
MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_RANGE,
0,
&group_info,
&subgroup_info,
parent_group,
general_write_handler,
general_read_handler);
menu_settings_list_current_add_range(list, list_info, 0, 15, 1, true, true);
settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED);
CONFIG_BOOL(

View File

@ -1020,6 +1020,8 @@ enum msg_hash_enums
MENU_LABEL(NETPLAY_DELAY_FRAMES),
MENU_LABEL(NETPLAY_STATELESS_MODE),
MENU_LABEL(NETPLAY_CHECK_FRAMES),
MENU_LABEL(NETPLAY_INPUT_LATENCY_FRAMES_MIN),
MENU_LABEL(NETPLAY_INPUT_LATENCY_FRAMES_RANGE),
MENU_LABEL(NETPLAY_SPECTATOR_MODE_ENABLE),
MENU_LABEL(NETPLAY_TCP_UDP_PORT),
MENU_LABEL(NETPLAY_NAT_TRAVERSAL),

View File

@ -42,6 +42,10 @@ In general, other ≤ unread and other ≤ self. In all likelihood, unread ≤ s
but it is both possible and supported for the remote host to get ahead of the
local host.
There is an additional location, run, used when input latency is desired. In
that case, self points to where input is being read, and run points to the
frame actually being executed. Run is purely local.
The server has a slightly more complicated job as it can handle multiple
clients, however it is not vastly more complicated: For each connection which
is playing (i.e., has a controller), it maintains a per-player unread frame,

View File

@ -205,7 +205,7 @@ static bool netplay_poll(void)
netplay_update_unread_ptr(netplay_data);
if (netplay_data->stateless_mode &&
netplay_data->connected_players &&
netplay_data->unread_frame_count <= netplay_data->self_frame_count)
netplay_data->unread_frame_count <= netplay_data->run_frame_count)
res = netplay_poll_net_input(netplay_data, true);
else
res = netplay_poll_net_input(netplay_data, false);
@ -218,14 +218,73 @@ static bool netplay_poll(void)
}
/* Simulate the input if we don't have real input */
netplay_simulate_input(netplay_data, netplay_data->self_ptr, false);
netplay_simulate_input(netplay_data, netplay_data->run_ptr, false);
/* Consider stalling */
netplay_update_unread_ptr(netplay_data);
/* Figure out how many frames of input latency we should be using to hide
* network latency */
if (netplay_data->frame_run_time_avg || netplay_data->stateless_mode)
{
/* FIXME: Using fixed 60fps for this calculation */
unsigned frames_per_frame = netplay_data->frame_run_time_avg ?
(16666/netplay_data->frame_run_time_avg) :
0;
unsigned frames_ahead = (netplay_data->run_frame_count > netplay_data->unread_frame_count) ?
(netplay_data->run_frame_count - netplay_data->unread_frame_count) :
0;
settings_t *settings = config_get_ptr();
unsigned input_latency_frames_min = settings->netplay.input_latency_frames_min;
unsigned input_latency_frames_max = input_latency_frames_min + settings->netplay.input_latency_frames_range;
/* Assume we need a couple frames worth of time to actually run the
* current frame */
if (frames_per_frame > 2)
frames_per_frame -= 2;
else
frames_per_frame = 0;
/* Shall we adjust our latency? */
if (netplay_data->stateless_mode)
{
/* In stateless mode, we adjust up if we're "close" and down if we
* have a lot of slack */
if (netplay_data->input_latency_frames < input_latency_frames_min ||
(netplay_data->unread_frame_count == netplay_data->run_frame_count + 1 &&
netplay_data->input_latency_frames < input_latency_frames_max))
{
netplay_data->input_latency_frames++;
}
else if (netplay_data->input_latency_frames > input_latency_frames_max ||
(netplay_data->unread_frame_count > netplay_data->run_frame_count + 2 &&
netplay_data->input_latency_frames > input_latency_frames_min))
{
netplay_data->input_latency_frames--;
}
}
else if (netplay_data->input_latency_frames < input_latency_frames_min ||
(frames_per_frame < frames_ahead &&
netplay_data->input_latency_frames < input_latency_frames_max))
{
/* We can't hide this much network latency with replay, so hide some
* with input latency */
netplay_data->input_latency_frames++;
}
else if (netplay_data->input_latency_frames > input_latency_frames_max ||
(frames_per_frame > frames_ahead + 2 &&
netplay_data->input_latency_frames > input_latency_frames_min))
{
/* We don't need this much latency (any more) */
netplay_data->input_latency_frames--;
}
}
/* If we're stalled, consider unstalling */
switch (netplay_data->stall)
{
case NETPLAY_STALL_RUNNING_FAST:
{
netplay_update_unread_ptr(netplay_data);
if (netplay_data->unread_frame_count + NETPLAY_MAX_STALL_FRAMES - 2
> netplay_data->self_frame_count)
{
@ -240,6 +299,11 @@ static bool netplay_poll(void)
break;
}
case NETPLAY_STALL_INPUT_LATENCY:
/* Just let it recalculate momentarily */
netplay_data->stall = NETPLAY_STALL_NONE;
break;
case NETPLAY_STALL_SERVER_REQUESTED:
{
/* See if the stall is done */
@ -261,38 +325,50 @@ static bool netplay_poll(void)
break;
default: /* not stalling */
{
/* Are we too far ahead? */
netplay_update_unread_ptr(netplay_data);
if (netplay_data->unread_frame_count + NETPLAY_MAX_STALL_FRAMES
<= netplay_data->self_frame_count)
{
netplay_data->stall = NETPLAY_STALL_RUNNING_FAST;
netplay_data->stall_time = cpu_features_get_time_usec();
break;
}
/* Figure out who to blame */
if (netplay_data->is_server)
/* If we're not stalled, consider stalling */
if (!netplay_data->stall)
{
/* Have we not reat enough latency frames? */
if (netplay_data->self_mode == NETPLAY_CONNECTION_PLAYING &&
netplay_data->connected_players &&
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_time = 0;
}
/* Are we too far ahead? */
if (netplay_data->unread_frame_count + NETPLAY_MAX_STALL_FRAMES
<= netplay_data->self_frame_count)
{
netplay_data->stall = NETPLAY_STALL_RUNNING_FAST;
netplay_data->stall_time = cpu_features_get_time_usec();
/* Figure out who to blame */
if (netplay_data->is_server)
{
for (player = 0; player < MAX_USERS; player++)
{
for (player = 0; player < MAX_USERS; player++)
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++)
{
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 = &netplay_data->connections[i];
if (connection->active &&
connection->mode == NETPLAY_CONNECTION_PLAYING &&
connection->player == player)
{
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;
break;
}
}
}
}
}
}
@ -371,7 +447,7 @@ static int16_t netplay_input_state(netplay_t *netplay,
unsigned idx, unsigned id)
{
size_t ptr = netplay->is_replay ?
netplay->replay_ptr : netplay->self_ptr;
netplay->replay_ptr : netplay->run_ptr;
const uint32_t *curr_input_state = NULL;
@ -688,7 +764,7 @@ void netplay_send_savestate(netplay_t *netplay,
/* Send it to relevant peers */
header[0] = htonl(NETPLAY_CMD_LOAD_SAVESTATE);
header[1] = htonl(wn + 2*sizeof(uint32_t));
header[2] = htonl(netplay->self_frame_count);
header[2] = htonl(netplay->run_frame_count);
header[3] = htonl(serial_info->size);
for (i = 0; i < netplay->connections_size; i++)
@ -721,16 +797,21 @@ void netplay_load_savestate(netplay_t *netplay,
{
retro_ctx_serialize_info_t tmp_serial_info;
/* Wherever we're inputting, that's where we consider our state to be loaded
* (FIXME: Need to be more careful about saving it?) */
netplay->run_ptr = netplay->self_ptr;
netplay->run_frame_count = netplay->self_frame_count;
/* Record it in our own buffer */
if (save || !serial_info)
{
if (netplay_delta_frame_ready(netplay,
&netplay->buffer[netplay->self_ptr], netplay->self_frame_count))
&netplay->buffer[netplay->run_ptr], netplay->run_frame_count))
{
if (!serial_info)
{
tmp_serial_info.size = netplay->state_size;
tmp_serial_info.data = netplay->buffer[netplay->self_ptr].state;
tmp_serial_info.data = netplay->buffer[netplay->run_ptr].state;
if (!core_serialize(&tmp_serial_info))
return;
tmp_serial_info.data_const = tmp_serial_info.data;
@ -740,7 +821,7 @@ void netplay_load_savestate(netplay_t *netplay,
{
if (serial_info->size <= netplay->state_size)
{
memcpy(netplay->buffer[netplay->self_ptr].state,
memcpy(netplay->buffer[netplay->run_ptr].state,
serial_info->data_const, serial_info->size);
}
}
@ -755,29 +836,29 @@ void netplay_load_savestate(netplay_t *netplay,
/* 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->self_frame_count)
if (netplay->unread_frame_count < netplay->run_frame_count)
{
uint32_t player;
for (player = 0; player < MAX_USERS; player++)
{
if (!(netplay->connected_players & (1<<player))) continue;
if (netplay->read_frame_count[player] < netplay->self_frame_count)
if (netplay->read_frame_count[player] < netplay->run_frame_count)
{
netplay->read_ptr[player] = netplay->self_ptr;
netplay->read_frame_count[player] = netplay->self_frame_count;
netplay->read_ptr[player] = netplay->run_ptr;
netplay->read_frame_count[player] = netplay->run_frame_count;
}
}
if (netplay->server_frame_count < netplay->self_frame_count)
if (netplay->server_frame_count < netplay->run_frame_count)
{
netplay->server_ptr = netplay->self_ptr;
netplay->server_frame_count = netplay->self_frame_count;
netplay->server_ptr = netplay->run_ptr;
netplay->server_frame_count = netplay->run_frame_count;
}
netplay_update_unread_ptr(netplay);
}
if (netplay->other_frame_count < netplay->self_frame_count)
if (netplay->other_frame_count < netplay->run_frame_count)
{
netplay->other_ptr = netplay->self_ptr;
netplay->other_frame_count = netplay->self_frame_count;
netplay->other_ptr = netplay->run_ptr;
netplay->other_frame_count = netplay->run_frame_count;
}
/* If we can't send it to the peer, loading a state was a bad idea */

View File

@ -904,9 +904,9 @@ bool netplay_handshake_pre_sync(netplay_t *netplay,
netplay->flip_frame = flip_frame;
/* Set our frame counters as requested */
netplay->self_frame_count = netplay->other_frame_count =
netplay->unread_frame_count = netplay->server_frame_count =
new_frame_count;
netplay->self_frame_count = netplay->run_frame_count =
netplay->other_frame_count = netplay->unread_frame_count =
netplay->server_frame_count = new_frame_count;
for (i = 0; i < netplay->buffer_size; i++)
{
struct delta_frame *ptr = &netplay->buffer[i];
@ -919,7 +919,8 @@ bool netplay_handshake_pre_sync(netplay_t *netplay,
return false;
ptr->frame = new_frame_count;
ptr->have_local = true;
netplay->other_ptr = netplay->unread_ptr = netplay->server_ptr = i;
netplay->run_ptr = netplay->other_ptr = netplay->unread_ptr =
netplay->server_ptr = i;
}
}

View File

@ -321,7 +321,7 @@ bool netplay_try_init_serialization(netplay_t *netplay)
/* Check if we can actually save */
serial_info.data_const = NULL;
serial_info.data = netplay->buffer[netplay->self_ptr].state;
serial_info.data = netplay->buffer[netplay->run_ptr].state;
serial_info.size = netplay->state_size;
if (!core_serialize(&serial_info))

View File

@ -954,7 +954,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
case NETPLAY_CMD_CRC:
{
uint32_t buffer[2];
size_t tmp_ptr = netplay->self_ptr;
size_t tmp_ptr = netplay->run_ptr;
bool found = false;
if (cmd_size != sizeof(buffer))
@ -985,7 +985,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
}
tmp_ptr = PREV_PTR(tmp_ptr);
} while (tmp_ptr != netplay->self_ptr);
} while (tmp_ptr != netplay->run_ptr);
if (!found)
{
@ -1035,8 +1035,8 @@ static bool netplay_get_cmd(netplay_t *netplay,
if (!netplay->is_replay)
{
netplay->is_replay = true;
netplay->replay_ptr = netplay->self_ptr;
netplay->replay_frame_count = netplay->self_frame_count;
netplay->replay_ptr = netplay->run_ptr;
netplay->replay_frame_count = netplay->run_frame_count;
netplay_wait_and_init_serialization(netplay);
netplay->is_replay = false;
}
@ -1134,15 +1134,20 @@ static bool netplay_get_cmd(netplay_t *netplay,
true, &rd, &wn, NULL);
/* Skip ahead if it's past where we are */
if (frame > netplay->self_frame_count)
if (frame > netplay->run_frame_count)
{
/* This is squirrely: We need to assure that when we advance the
* frame in post_frame, THEN we're referring to the frame to
* load into. If we refer directly to read_ptr, then we'll end
* up never reading the input for read_frame_count itself, which
* will make the other side unhappy. */
netplay->self_ptr = PREV_PTR(netplay->read_ptr[connection->player]);
netplay->self_frame_count = frame - 1;
netplay->run_ptr = PREV_PTR(netplay->read_ptr[connection->player]);
netplay->run_frame_count = frame - 1;
if (frame > netplay->self_frame_count)
{
netplay->self_ptr = netplay->run_ptr;
netplay->self_frame_count = netplay->run_frame_count;
}
}
/* Don't expect earlier data from other clients */
@ -1289,6 +1294,8 @@ int netplay_poll_net_input(netplay_t *netplay, bool block)
if (max_fd == 0)
return 0;
netplay->timeout_cnt = 0;
do
{
had_input = false;
@ -1308,7 +1315,7 @@ int netplay_poll_net_input(netplay_t *netplay, bool block)
netplay_update_unread_ptr(netplay);
/* If we were blocked for input, pass if we have this frame's input */
if (netplay->unread_frame_count > netplay->self_frame_count)
if (netplay->unread_frame_count > netplay->run_frame_count)
break;
/* If we're supposed to block but we didn't have enough input, wait for it */
@ -1330,7 +1337,7 @@ int netplay_poll_net_input(netplay_t *netplay, bool block)
return -1;
RARCH_LOG("Network is stalling at frame %u, count %u of %d ...\n",
netplay->self_frame_count, netplay->timeout_cnt, MAX_RETRIES);
netplay->run_frame_count, netplay->timeout_cnt, MAX_RETRIES);
if (netplay->timeout_cnt >= MAX_RETRIES && !netplay->remote_paused)
return -1;

View File

@ -210,6 +210,7 @@ enum rarch_netplay_stall_reason
{
NETPLAY_STALL_NONE = 0,
NETPLAY_STALL_RUNNING_FAST,
NETPLAY_STALL_INPUT_LATENCY,
NETPLAY_STALL_SERVER_REQUESTED,
NETPLAY_STALL_NO_CONNECTION
};
@ -357,10 +358,15 @@ struct netplay
/* The size of our packet buffers */
size_t packet_buffer_size;
/* The current frame seen by the frontend */
/* The frame we're currently inputting */
size_t self_ptr;
uint32_t self_frame_count;
/* The frame we're currently running, which may be behind the frame we're
* currently inputting if we're using input latency */
size_t run_ptr;
uint32_t run_frame_count;
/* The first frame at which some data might be unreliable */
size_t other_ptr;
uint32_t other_frame_count;
@ -437,6 +443,9 @@ struct netplay
int frame_run_time_ptr;
retro_time_t frame_run_time_sum, frame_run_time_avg;
/* Latency frames and limits */
unsigned input_latency_frames;
/* Are we stalled? */
enum rarch_netplay_stall_reason stall;

View File

@ -186,14 +186,14 @@ bool netplay_sync_pre_frame(netplay_t *netplay)
{
retro_ctx_serialize_info_t serial_info;
if (netplay_delta_frame_ready(netplay, &netplay->buffer[netplay->self_ptr], netplay->self_frame_count))
if (netplay_delta_frame_ready(netplay, &netplay->buffer[netplay->run_ptr], netplay->run_frame_count))
{
serial_info.data_const = NULL;
serial_info.data = netplay->buffer[netplay->self_ptr].state;
serial_info.data = netplay->buffer[netplay->run_ptr].state;
serial_info.size = netplay->state_size;
memset(serial_info.data, 0, serial_info.size);
if ((netplay->quirks & NETPLAY_QUIRK_INITIALIZATION) || netplay->self_frame_count == 0)
if ((netplay->quirks & NETPLAY_QUIRK_INITIALIZATION) || netplay->run_frame_count == 0)
{
/* Don't serialize until it's safe */
}
@ -201,8 +201,19 @@ bool netplay_sync_pre_frame(netplay_t *netplay)
{
if (netplay->force_send_savestate && !netplay->stall && !netplay->remote_paused)
{
/* Bring our running frame and input frames into parity so we don't
* send old info */
if (netplay->run_ptr != netplay->self_ptr)
{
memcpy(netplay->buffer[netplay->self_ptr].state,
netplay->buffer[netplay->run_ptr].state,
netplay->state_size);
netplay->run_ptr = netplay->self_ptr;
netplay->run_frame_count = netplay->self_frame_count;
}
/* Send this along to the other side */
serial_info.data_const = netplay->buffer[netplay->self_ptr].state;
serial_info.data_const = netplay->buffer[netplay->run_ptr].state;
netplay_load_savestate(netplay, &serial_info, false);
netplay->force_send_savestate = false;
}
@ -216,7 +227,7 @@ bool netplay_sync_pre_frame(netplay_t *netplay)
}
/* If we can't transmit savestates, we must stall until the client is ready */
if (netplay->self_frame_count > 0 &&
if (netplay->run_frame_count > 0 &&
(netplay->quirks & (NETPLAY_QUIRK_NO_SAVESTATES|NETPLAY_QUIRK_NO_TRANSMISSION)) &&
(netplay->connections_size == 0 || !netplay->connections[0].active ||
netplay->connections[0].mode < NETPLAY_CONNECTION_CONNECTED))
@ -356,6 +367,14 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
/* Unless we're stalling, we've just finished running a frame */
if (!stalled)
{
netplay->run_ptr = NEXT_PTR(netplay->run_ptr);
netplay->run_frame_count++;
}
/* We've finished an input frame even if we're stalling */
if ((!stalled || netplay->stall == NETPLAY_STALL_INPUT_LATENCY) &&
netplay->self_frame_count < netplay->run_frame_count + netplay->input_latency_frames)
{
netplay->self_ptr = NEXT_PTR(netplay->self_ptr);
netplay->self_frame_count++;
@ -383,7 +402,7 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
/* Skip ahead if we predicted correctly.
* Skip until our simulation failed. */
while (netplay->other_frame_count < netplay->unread_frame_count &&
netplay->other_frame_count < netplay->self_frame_count)
netplay->other_frame_count < netplay->run_frame_count)
{
struct delta_frame *ptr = &netplay->buffer[netplay->other_ptr];
size_t i;
@ -406,7 +425,7 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
/* Now replay the real input if we've gotten ahead of it */
if (netplay->force_rewind ||
(netplay->other_frame_count < netplay->unread_frame_count &&
netplay->other_frame_count < netplay->self_frame_count))
netplay->other_frame_count < netplay->run_frame_count))
{
retro_ctx_serialize_info_t serial_info;
@ -428,7 +447,7 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
RARCH_ERR("Netplay savestate loading failed: Prepare for desync!\n");
}
while (netplay->replay_frame_count < netplay->self_frame_count)
while (netplay->replay_frame_count < netplay->run_frame_count)
{
retro_time_t start, tm;
@ -483,15 +502,15 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
/* Average our time */
netplay->frame_run_time_avg = netplay->frame_run_time_sum / NETPLAY_FRAME_RUN_TIME_WINDOW;
if (netplay->unread_frame_count < netplay->self_frame_count)
if (netplay->unread_frame_count < netplay->run_frame_count)
{
netplay->other_ptr = netplay->unread_ptr;
netplay->other_frame_count = netplay->unread_frame_count;
}
else
{
netplay->other_ptr = netplay->self_ptr;
netplay->other_frame_count = netplay->self_frame_count;
netplay->other_ptr = netplay->run_ptr;
netplay->other_frame_count = netplay->run_frame_count;
}
netplay->is_replay = false;
netplay->force_rewind = false;