(Netplay) Show client slowdown information (#14272)

This commit is contained in:
Cthulhu-throwaway 2022-08-02 08:31:55 -03:00 committed by GitHub
parent e6f3a387b2
commit 8105688a99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 171 additions and 144 deletions

View File

@ -11731,6 +11731,10 @@ MSG_HASH(
MSG_NETPLAY_CHAT_SUPPORTED, MSG_NETPLAY_CHAT_SUPPORTED,
"Chat Supported" "Chat Supported"
) )
MSG_HASH(
MSG_NETPLAY_SLOWDOWNS_CAUSED,
"Slowdowns Caused"
)
MSG_HASH( MSG_HASH(
MSG_AUDIO_VOLUME, MSG_AUDIO_VOLUME,

View File

@ -1620,12 +1620,17 @@ static int action_bind_sublabel_netplay_kick_client(file_list_t *list,
} }
} }
snprintf(buf, sizeof(buf), "%s: %s", snprintf(buf, sizeof(buf), "%s: %s\n",
msg_hash_to_str(MSG_NETPLAY_CHAT_SUPPORTED), msg_hash_to_str(MSG_NETPLAY_CHAT_SUPPORTED),
msg_hash_to_str((client->protocol >= 6) ? msg_hash_to_str((client->protocol >= 6) ?
MENU_ENUM_LABEL_VALUE_YES : MENU_ENUM_LABEL_VALUE_NO)); MENU_ENUM_LABEL_VALUE_YES : MENU_ENUM_LABEL_VALUE_NO));
strlcat(s, buf, len); strlcat(s, buf, len);
snprintf(buf, sizeof(buf), "%s: %lu",
msg_hash_to_str(MSG_NETPLAY_SLOWDOWNS_CAUSED),
(unsigned long)client->slowdowns);
strlcat(s, buf, len);
if (client->ping >= 0) if (client->ping >= 0)
{ {
snprintf(buf, sizeof(buf), "\nPing: %u ms", (unsigned)client->ping); snprintf(buf, sizeof(buf), "\nPing: %u ms", (unsigned)client->ping);

View File

@ -226,6 +226,7 @@ enum msg_hash_enums
MSG_NETPLAY_STATUS_SPECTATING, MSG_NETPLAY_STATUS_SPECTATING,
MSG_NETPLAY_CLIENT_DEVICES, MSG_NETPLAY_CLIENT_DEVICES,
MSG_NETPLAY_CHAT_SUPPORTED, MSG_NETPLAY_CHAT_SUPPORTED,
MSG_NETPLAY_SLOWDOWNS_CAUSED,
MSG_RESAMPLER_QUALITY_LOWEST, MSG_RESAMPLER_QUALITY_LOWEST,
MSG_RESAMPLER_QUALITY_LOWER, MSG_RESAMPLER_QUALITY_LOWER,
MSG_RESAMPLER_QUALITY_NORMAL, MSG_RESAMPLER_QUALITY_NORMAL,

View File

@ -154,6 +154,7 @@ typedef struct netplay_client_info
{ {
uint32_t protocol; uint32_t protocol;
uint32_t devices; uint32_t devices;
uint32_t slowdowns;
int32_t ping; int32_t ping;
int id; int id;
enum rarch_netplay_connection_mode mode; enum rarch_netplay_connection_mode mode;

View File

@ -7793,225 +7793,238 @@ static bool get_self_input_state(
static bool netplay_poll(netplay_t *netplay, bool block_libretro_input) static bool netplay_poll(netplay_t *netplay, bool block_libretro_input)
{ {
size_t i; size_t i;
int res;
uint32_t client;
if (!get_self_input_state(block_libretro_input, netplay)) if (!get_self_input_state(block_libretro_input, netplay))
goto catastrophe; goto catastrophe;
/* If we're not connected, we're done */ /* If we're not connected, we're done. */
if (netplay->self_mode == NETPLAY_CONNECTION_NONE) if (netplay->self_mode == NETPLAY_CONNECTION_NONE)
return true; return true;
/* Read Netplay input, block if we're configured to stall for input every
* frame */
netplay_update_unread_ptr(netplay); netplay_update_unread_ptr(netplay);
if (netplay->stateless_mode &&
(netplay->connected_players>1) &&
netplay->unread_frame_count <= netplay->run_frame_count)
res = netplay_poll_net_input(netplay, true);
else
res = netplay_poll_net_input(netplay, false);
if (res == -1)
goto catastrophe;
/* Resolve and/or simulate the input if we don't have real input */ /* Read netplay input,
block if we're configured to stall for input every frame. */
{
bool block = netplay->stateless_mode && netplay->connected_players > 1 &&
netplay->unread_frame_count <= netplay->run_frame_count;
if (netplay_poll_net_input(netplay, block) == -1)
goto catastrophe;
}
/* Resolve and/or simulate the input if we don't have real input. */
netplay_resolve_input(netplay, netplay->run_ptr, false); netplay_resolve_input(netplay, netplay->run_ptr, false);
/* Handle any slaves */ /* Handle slaves. */
if (netplay->is_server && netplay->connected_slaves) if (netplay->is_server && netplay->connected_slaves)
netplay_handle_slaves(netplay); netplay_handle_slaves(netplay);
netplay_update_unread_ptr(netplay); netplay_update_unread_ptr(netplay);
/* Figure out how many frames of input latency we should be using to hide /* Figure out how many frames of input latency we should be using to
* network latency */ hide network latency. */
if (netplay->frame_run_time_avg || netplay->stateless_mode) if (netplay->stateless_mode)
{
int input_latency_frames_min = (int)netplay->input_latency_frames_min;
int input_latency_frames_max = (int)netplay->input_latency_frames_max;
/* In stateless mode, we adjust up if we're "close"
and down if we have a lot of slack. */
if (netplay->input_latency_frames < input_latency_frames_min ||
(netplay->unread_frame_count == (netplay->run_frame_count + 1) &&
netplay->input_latency_frames < input_latency_frames_max))
netplay->input_latency_frames++;
else if (netplay->input_latency_frames > input_latency_frames_max ||
(netplay->unread_frame_count > (netplay->run_frame_count + 2) &&
netplay->input_latency_frames > input_latency_frames_min))
netplay->input_latency_frames--;
}
else if (netplay->frame_run_time_avg)
{ {
/* FIXME: Using fixed 60fps for this calculation */ /* FIXME: Using fixed 60fps for this calculation */
unsigned frames_per_frame = netplay->frame_run_time_avg ? unsigned frames_per_frame = netplay->frame_run_time_avg ?
(16666 / netplay->frame_run_time_avg) : (unsigned)(16666 / netplay->frame_run_time_avg) : 0;
0; unsigned frames_ahead =
unsigned frames_ahead = (netplay->run_frame_count > netplay->unread_frame_count) ? (netplay->run_frame_count > netplay->unread_frame_count) ?
(netplay->run_frame_count - netplay->unread_frame_count) : (unsigned)(netplay->run_frame_count - netplay->unread_frame_count)
0; : 0;
int input_latency_frames_min = netplay->input_latency_frames_min; int input_latency_frames_min = (int)netplay->input_latency_frames_min;
int input_latency_frames_max = netplay->input_latency_frames_max; int input_latency_frames_max = (int)netplay->input_latency_frames_max;
/* Assume we need a couple frames worth of time to actually run the /* Assume we need a couple frames worth of time
* current frame */ to actually run the current frame. */
if (frames_per_frame > 2) if (frames_per_frame > 2)
frames_per_frame -= 2; frames_per_frame -= 2;
else else
frames_per_frame = 0; frames_per_frame = 0;
/* Shall we adjust our latency? */ /* We can't hide this much network latency with replay,
if (netplay->stateless_mode) so hide some with input latency. */
{ if (netplay->input_latency_frames < input_latency_frames_min ||
/* In stateless mode, we adjust up if we're "close" and down if we (frames_per_frame < frames_ahead &&
* have a lot of slack */ netplay->input_latency_frames < input_latency_frames_max))
if (netplay->input_latency_frames < input_latency_frames_min ||
(netplay->unread_frame_count == netplay->run_frame_count + 1 &&
netplay->input_latency_frames < input_latency_frames_max))
netplay->input_latency_frames++;
else if (netplay->input_latency_frames > input_latency_frames_max ||
(netplay->unread_frame_count > netplay->run_frame_count + 2 &&
netplay->input_latency_frames > input_latency_frames_min))
netplay->input_latency_frames--;
}
else if (netplay->input_latency_frames < input_latency_frames_min ||
(frames_per_frame < frames_ahead &&
netplay->input_latency_frames < input_latency_frames_max))
{
/* We can't hide this much network latency with replay, so hide some
* with input latency */
netplay->input_latency_frames++; netplay->input_latency_frames++;
} /* We don't need this much latency (any more). */
else if (netplay->input_latency_frames > input_latency_frames_max || else if (netplay->input_latency_frames > input_latency_frames_max ||
(frames_per_frame > frames_ahead + 2 && (frames_per_frame > (frames_ahead + 2) &&
netplay->input_latency_frames > input_latency_frames_min)) netplay->input_latency_frames > input_latency_frames_min))
{
/* We don't need this much latency (any more) */
netplay->input_latency_frames--; netplay->input_latency_frames--;
}
} }
/* If we're stalled, consider unstalling */ /* If we're stalled, consider unstalling. */
switch (netplay->stall) switch (netplay->stall)
{ {
case NETPLAY_STALL_RUNNING_FAST: case NETPLAY_STALL_RUNNING_FAST:
if (netplay->unread_frame_count + NETPLAY_MAX_STALL_FRAMES - 2 if ((netplay->unread_frame_count + NETPLAY_MAX_STALL_FRAMES - 2) >
> netplay->self_frame_count) netplay->self_frame_count)
{ {
netplay->stall = NETPLAY_STALL_NONE; struct netplay_connection *connection;
for (i = 0; i < netplay->connections_size; i++) for (i = 0; i < netplay->connections_size; i++)
{ {
struct netplay_connection *connection = &netplay->connections[i]; connection = &netplay->connections[i];
if (connection->active && connection->stall) if (connection->active)
connection->stall = NETPLAY_STALL_NONE; connection->stall = NETPLAY_STALL_NONE;
} }
netplay->stall = NETPLAY_STALL_NONE;
} }
break; break;
case NETPLAY_STALL_SPECTATOR_WAIT: case NETPLAY_STALL_SPECTATOR_WAIT:
if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING || netplay->unread_frame_count > netplay->self_frame_count) if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING ||
netplay->unread_frame_count > netplay->self_frame_count)
netplay->stall = NETPLAY_STALL_NONE; netplay->stall = NETPLAY_STALL_NONE;
break; break;
case NETPLAY_STALL_INPUT_LATENCY: case NETPLAY_STALL_INPUT_LATENCY:
/* Just let it recalculate momentarily */ /* Just let it recalculate momentarily. */
netplay->stall = NETPLAY_STALL_NONE; netplay->stall = NETPLAY_STALL_NONE;
break; break;
case NETPLAY_STALL_SERVER_REQUESTED: case NETPLAY_STALL_SERVER_REQUESTED:
/* See if the stall is done */
if (netplay->connections[0].stall_frame == 0)
{ {
/* Stop stalling! */ struct netplay_connection *connection = &netplay->connections[0];
netplay->connections[0].stall = NETPLAY_STALL_NONE;
netplay->stall = NETPLAY_STALL_NONE; /* See if the stall is done. */
if (!connection->stall_frame)
{
/* Stop stalling! */
connection->stall = NETPLAY_STALL_NONE;
netplay->stall = NETPLAY_STALL_NONE;
}
else
connection->stall_frame--;
} }
else
netplay->connections[0].stall_frame--;
break; break;
case NETPLAY_STALL_NO_CONNECTION: case NETPLAY_STALL_NO_CONNECTION:
/* We certainly haven't fixed this */ /* We certainly haven't fixed this. */
break; break;
default: /* not stalling */ default:
/* Not stalling. */
break; break;
} }
/* If we're not stalled, consider stalling */ /* If we're not stalled, consider stalling. */
if (!netplay->stall) if (netplay->stall == NETPLAY_STALL_NONE)
{ {
/* Have we not read enough latency frames? */ switch (netplay->self_mode)
if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING &&
netplay->connected_players &&
netplay->run_frame_count + netplay->input_latency_frames > netplay->self_frame_count)
{ {
netplay->stall = NETPLAY_STALL_INPUT_LATENCY; case NETPLAY_CONNECTION_SPECTATING:
netplay->stall_time = 0; case NETPLAY_CONNECTION_SLAVE:
/* If we're a spectator, are we ahead at all? */
if (!netplay->is_server &&
netplay->unread_frame_count <= netplay->self_frame_count)
{
netplay->stall = NETPLAY_STALL_SPECTATOR_WAIT;
netplay->stall_time = cpu_features_get_time_usec();
}
break;
case NETPLAY_CONNECTION_PLAYING:
/* Have we not read enough latency frames? */
if (netplay->connected_players &&
(netplay->run_frame_count + netplay->input_latency_frames) >
netplay->self_frame_count)
{
netplay->stall = NETPLAY_STALL_INPUT_LATENCY;
netplay->stall_time = 0;
}
break;
default:
break;
} }
/* Are we too far ahead? */ /* Are we too far ahead? */
if (netplay->unread_frame_count + NETPLAY_MAX_STALL_FRAMES if (netplay->stall == NETPLAY_STALL_NONE &&
<= netplay->self_frame_count) netplay->self_frame_count > NETPLAY_MAX_STALL_FRAMES)
{ {
netplay->stall = NETPLAY_STALL_RUNNING_FAST; uint32_t min_frame_count = netplay->self_frame_count -
netplay->stall_time = cpu_features_get_time_usec(); NETPLAY_MAX_STALL_FRAMES;
/* Figure out who to blame */ if (netplay->unread_frame_count <= min_frame_count)
if (netplay->is_server)
{ {
for (client = 1; client < MAX_CLIENTS; client++) netplay->stall = NETPLAY_STALL_RUNNING_FAST;
netplay->stall_time = cpu_features_get_time_usec();
/* Figure out who to blame. */
if (netplay->is_server)
{ {
struct netplay_connection *connection; struct netplay_connection *connection;
if (!(netplay->connected_players & (1 << client)))
continue; for (i = 0; i < netplay->connections_size; i++)
if (netplay->read_frame_count[client] > netplay->unread_frame_count)
continue;
connection = &netplay->connections[client-1];
if (connection->active &&
connection->mode == NETPLAY_CONNECTION_PLAYING)
{ {
connection->stall = NETPLAY_STALL_RUNNING_FAST; connection = &netplay->connections[i];
connection->stall_time = netplay->stall_time; if (!connection->active ||
connection->mode != NETPLAY_CONNECTION_PLAYING)
continue;
if (netplay->read_frame_count[i + 1] < min_frame_count)
{
connection->stall = NETPLAY_STALL_RUNNING_FAST;
connection->stall_slow++;
}
} }
} }
} }
}
/* If we're a spectator, are we ahead at all? */
if (!netplay->is_server &&
(netplay->self_mode == NETPLAY_CONNECTION_SPECTATING ||
netplay->self_mode == NETPLAY_CONNECTION_SLAVE) &&
netplay->unread_frame_count <= netplay->self_frame_count)
{
netplay->stall = NETPLAY_STALL_SPECTATOR_WAIT;
netplay->stall_time = cpu_features_get_time_usec();
} }
} }
/* If we're stalling, consider disconnection */ /* If we're stalling, consider disconnection. */
if (netplay->stall && netplay->stall_time) if (netplay->stall != NETPLAY_STALL_NONE && netplay->stall_time)
{ {
retro_time_t now = cpu_features_get_time_usec(); retro_time_t now = cpu_features_get_time_usec();
/* Don't stall out while they're paused */ if (!netplay->remote_paused)
if (netplay->remote_paused)
netplay->stall_time = now;
else if (now - netplay->stall_time >=
(netplay->is_server ? MAX_SERVER_STALL_TIME_USEC :
MAX_CLIENT_STALL_TIME_USEC))
{ {
/* Stalled out! */ retro_time_t delta = now - netplay->stall_time;
if (netplay->is_server) if (netplay->is_server)
{ {
bool fixed = false; if (delta >= MAX_SERVER_STALL_TIME_USEC)
for (i = 0; i < netplay->connections_size; i++)
{ {
struct netplay_connection *connection = &netplay->connections[i]; /* Stalled out! */
if (connection->active && struct netplay_connection *connection;
connection->mode == NETPLAY_CONNECTION_PLAYING &&
connection->stall) for (i = 0; i < netplay->connections_size; i++)
{ {
netplay_hangup(netplay, connection); connection = &netplay->connections[i];
fixed = true; if (!connection->active ||
} connection->mode != NETPLAY_CONNECTION_PLAYING)
} continue;
if (connection->stall != NETPLAY_STALL_NONE)
netplay_hangup(netplay, connection);
}
if (fixed)
{
/* Not stalled now :) */
netplay->stall = NETPLAY_STALL_NONE; netplay->stall = NETPLAY_STALL_NONE;
return true;
} }
} }
else else
goto catastrophe; {
return false; if (delta >= MAX_CLIENT_STALL_TIME_USEC)
/* Stalled out! */
goto catastrophe;
}
} }
else
/* Don't stall out while they're paused. */
netplay->stall_time = now;
} }
return true; return true;
@ -8019,6 +8032,7 @@ static bool netplay_poll(netplay_t *netplay, bool block_libretro_input)
catastrophe: catastrophe:
for (i = 0; i < netplay->connections_size; i++) for (i = 0; i < netplay->connections_size; i++)
netplay_hangup(netplay, &netplay->connections[i]); netplay_hangup(netplay, &netplay->connections[i]);
return false; return false;
} }
@ -8818,11 +8832,12 @@ static size_t retrieve_client_info(netplay_t *netplay, netplay_client_info_t *bu
{ {
netplay_client_info_t *info = &buf[j++]; netplay_client_info_t *info = &buf[j++];
info->id = (int)i; info->id = (int)i;
info->protocol = connection->netplay_protocol; info->protocol = connection->netplay_protocol;
info->mode = connection->mode; info->mode = connection->mode;
info->ping = connection->ping; info->ping = connection->ping;
info->devices = netplay->client_devices[i + 1]; info->slowdowns = connection->stall_slow;
info->devices = netplay->client_devices[i + 1];
strlcpy(info->name, connection->nick, sizeof(info->name)); strlcpy(info->name, connection->nick, sizeof(info->name));
} }
} }

View File

@ -365,9 +365,6 @@ typedef struct netplay_address
/* Each connection gets a connection struct */ /* Each connection gets a connection struct */
struct netplay_connection struct netplay_connection
{ {
/* Is this connection stalling? */
retro_time_t stall_time;
/* Timer used to estimate a connection's latency */ /* Timer used to estimate a connection's latency */
retro_time_t ping_timer; retro_time_t ping_timer;
@ -399,6 +396,10 @@ struct netplay_connection
* For the client: How many frames of stall do we have left? */ * For the client: How many frames of stall do we have left? */
uint32_t stall_frame; uint32_t stall_frame;
/* How many times has this connection caused a stall because it's running
too slow? */
uint32_t stall_slow;
/* What latency is this connection running on? /* What latency is this connection running on?
* Network latency has limited precision as we estimate it * Network latency has limited precision as we estimate it
* once every pre-frame. */ * once every pre-frame. */