(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,
"Chat Supported"
)
MSG_HASH(
MSG_NETPLAY_SLOWDOWNS_CAUSED,
"Slowdowns Caused"
)
MSG_HASH(
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((client->protocol >= 6) ?
MENU_ENUM_LABEL_VALUE_YES : MENU_ENUM_LABEL_VALUE_NO));
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)
{
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_CLIENT_DEVICES,
MSG_NETPLAY_CHAT_SUPPORTED,
MSG_NETPLAY_SLOWDOWNS_CAUSED,
MSG_RESAMPLER_QUALITY_LOWEST,
MSG_RESAMPLER_QUALITY_LOWER,
MSG_RESAMPLER_QUALITY_NORMAL,

View File

@ -154,6 +154,7 @@ typedef struct netplay_client_info
{
uint32_t protocol;
uint32_t devices;
uint32_t slowdowns;
int32_t ping;
int id;
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)
{
size_t i;
int res;
uint32_t client;
if (!get_self_input_state(block_libretro_input, netplay))
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)
return true;
/* Read Netplay input, block if we're configured to stall for input every
* frame */
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);
/* Handle any slaves */
/* Handle slaves. */
if (netplay->is_server && netplay->connected_slaves)
netplay_handle_slaves(netplay);
netplay_update_unread_ptr(netplay);
/* Figure out how many frames of input latency we should be using to hide
* network latency */
if (netplay->frame_run_time_avg || netplay->stateless_mode)
/* Figure out how many frames of input latency we should be using to
hide network latency. */
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 */
unsigned frames_per_frame = netplay->frame_run_time_avg ?
(16666 / netplay->frame_run_time_avg) :
0;
unsigned frames_ahead = (netplay->run_frame_count > netplay->unread_frame_count) ?
(netplay->run_frame_count - netplay->unread_frame_count) :
0;
int input_latency_frames_min = netplay->input_latency_frames_min;
int input_latency_frames_max = netplay->input_latency_frames_max;
(unsigned)(16666 / netplay->frame_run_time_avg) : 0;
unsigned frames_ahead =
(netplay->run_frame_count > netplay->unread_frame_count) ?
(unsigned)(netplay->run_frame_count - netplay->unread_frame_count)
: 0;
int input_latency_frames_min = (int)netplay->input_latency_frames_min;
int input_latency_frames_max = (int)netplay->input_latency_frames_max;
/* Assume we need a couple frames worth of time to actually run the
* current frame */
/* 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;
frames_per_frame = 0;
/* Shall we adjust our latency? */
if (netplay->stateless_mode)
{
/* 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->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 */
/* We can't hide this much network latency with replay,
so hide some with input latency. */
if (netplay->input_latency_frames < input_latency_frames_min ||
(frames_per_frame < frames_ahead &&
netplay->input_latency_frames < input_latency_frames_max))
netplay->input_latency_frames++;
}
/* We don't need this much latency (any more). */
else if (netplay->input_latency_frames > input_latency_frames_max ||
(frames_per_frame > frames_ahead + 2 &&
netplay->input_latency_frames > input_latency_frames_min))
{
/* We don't need this much latency (any more) */
(frames_per_frame > (frames_ahead + 2) &&
netplay->input_latency_frames > input_latency_frames_min))
netplay->input_latency_frames--;
}
}
/* If we're stalled, consider unstalling */
/* If we're stalled, consider unstalling. */
switch (netplay->stall)
{
case NETPLAY_STALL_RUNNING_FAST:
if (netplay->unread_frame_count + NETPLAY_MAX_STALL_FRAMES - 2
> netplay->self_frame_count)
if ((netplay->unread_frame_count + NETPLAY_MAX_STALL_FRAMES - 2) >
netplay->self_frame_count)
{
netplay->stall = NETPLAY_STALL_NONE;
struct netplay_connection *connection;
for (i = 0; i < netplay->connections_size; i++)
{
struct netplay_connection *connection = &netplay->connections[i];
if (connection->active && connection->stall)
connection = &netplay->connections[i];
if (connection->active)
connection->stall = NETPLAY_STALL_NONE;
}
netplay->stall = NETPLAY_STALL_NONE;
}
break;
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;
break;
case NETPLAY_STALL_INPUT_LATENCY:
/* Just let it recalculate momentarily */
/* Just let it recalculate momentarily. */
netplay->stall = NETPLAY_STALL_NONE;
break;
case NETPLAY_STALL_SERVER_REQUESTED:
/* See if the stall is done */
if (netplay->connections[0].stall_frame == 0)
{
/* Stop stalling! */
netplay->connections[0].stall = NETPLAY_STALL_NONE;
netplay->stall = NETPLAY_STALL_NONE;
struct netplay_connection *connection = &netplay->connections[0];
/* 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;
case NETPLAY_STALL_NO_CONNECTION:
/* We certainly haven't fixed this */
/* We certainly haven't fixed this. */
break;
default: /* not stalling */
default:
/* Not stalling. */
break;
}
/* If we're not stalled, consider stalling */
if (!netplay->stall)
/* If we're not stalled, consider stalling. */
if (netplay->stall == NETPLAY_STALL_NONE)
{
/* Have we not read enough latency frames? */
if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING &&
netplay->connected_players &&
netplay->run_frame_count + netplay->input_latency_frames > netplay->self_frame_count)
switch (netplay->self_mode)
{
netplay->stall = NETPLAY_STALL_INPUT_LATENCY;
netplay->stall_time = 0;
case NETPLAY_CONNECTION_SPECTATING:
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? */
if (netplay->unread_frame_count + NETPLAY_MAX_STALL_FRAMES
<= netplay->self_frame_count)
if (netplay->stall == NETPLAY_STALL_NONE &&
netplay->self_frame_count > NETPLAY_MAX_STALL_FRAMES)
{
netplay->stall = NETPLAY_STALL_RUNNING_FAST;
netplay->stall_time = cpu_features_get_time_usec();
uint32_t min_frame_count = netplay->self_frame_count -
NETPLAY_MAX_STALL_FRAMES;
/* Figure out who to blame */
if (netplay->is_server)
if (netplay->unread_frame_count <= min_frame_count)
{
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;
if (!(netplay->connected_players & (1 << client)))
continue;
if (netplay->read_frame_count[client] > netplay->unread_frame_count)
continue;
connection = &netplay->connections[client-1];
if (connection->active &&
connection->mode == NETPLAY_CONNECTION_PLAYING)
for (i = 0; i < netplay->connections_size; i++)
{
connection->stall = NETPLAY_STALL_RUNNING_FAST;
connection->stall_time = netplay->stall_time;
connection = &netplay->connections[i];
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 (netplay->stall && netplay->stall_time)
/* If we're stalling, consider disconnection. */
if (netplay->stall != NETPLAY_STALL_NONE && netplay->stall_time)
{
retro_time_t now = cpu_features_get_time_usec();
/* Don't stall out while they're 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))
if (!netplay->remote_paused)
{
/* Stalled out! */
retro_time_t delta = now - netplay->stall_time;
if (netplay->is_server)
{
bool fixed = false;
for (i = 0; i < netplay->connections_size; i++)
if (delta >= MAX_SERVER_STALL_TIME_USEC)
{
struct netplay_connection *connection = &netplay->connections[i];
if (connection->active &&
connection->mode == NETPLAY_CONNECTION_PLAYING &&
connection->stall)
{
netplay_hangup(netplay, connection);
fixed = true;
}
}
/* Stalled out! */
struct netplay_connection *connection;
for (i = 0; i < netplay->connections_size; i++)
{
connection = &netplay->connections[i];
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;
return true;
}
}
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;
@ -8019,6 +8032,7 @@ static bool netplay_poll(netplay_t *netplay, bool block_libretro_input)
catastrophe:
for (i = 0; i < netplay->connections_size; i++)
netplay_hangup(netplay, &netplay->connections[i]);
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++];
info->id = (int)i;
info->protocol = connection->netplay_protocol;
info->mode = connection->mode;
info->ping = connection->ping;
info->devices = netplay->client_devices[i + 1];
info->id = (int)i;
info->protocol = connection->netplay_protocol;
info->mode = connection->mode;
info->ping = connection->ping;
info->slowdowns = connection->stall_slow;
info->devices = netplay->client_devices[i + 1];
strlcpy(info->name, connection->nick, sizeof(info->name));
}
}

View File

@ -365,9 +365,6 @@ typedef struct netplay_address
/* Each connection gets a connection struct */
struct netplay_connection
{
/* Is this connection stalling? */
retro_time_t stall_time;
/* Timer used to estimate a connection's latency */
retro_time_t ping_timer;
@ -399,6 +396,10 @@ struct netplay_connection
* For the client: How many frames of stall do we have left? */
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?
* Network latency has limited precision as we estimate it
* once every pre-frame. */