mirror of
https://github.com/CTCaer/RetroArch.git
synced 2025-01-18 17:04:34 +00:00
Reverse catch-up, i.e., server-demanded stalling
Previously, if two clients were connected to the same server and one of them was ahead of the server, the only way to rectify that situation was for the client to get so far ahead that it stalled, as the server could only catch up with an ahead client if all clients were ahead. That's unrealistic. This gives the server the alternate option of demanding that a client stall. This keeps things nicely in line even with >2 players.
This commit is contained in:
parent
958a028b0a
commit
cd281d5757
@ -304,6 +304,14 @@ Payload: None
|
||||
Description:
|
||||
Indicates that the core is no longer paused.
|
||||
|
||||
Command: STALL
|
||||
Payload:
|
||||
{
|
||||
frames: uint32
|
||||
}
|
||||
Description:
|
||||
Request that a client stall for the given number of frames.
|
||||
|
||||
Command: CHEATS
|
||||
Unused
|
||||
|
||||
|
@ -322,7 +322,7 @@ ssize_t netplay_recv(struct socket_buffer *sbuf, int sockfd, void *buf,
|
||||
if (block)
|
||||
{
|
||||
sbuf->start = sbuf->read;
|
||||
if (recvd < len)
|
||||
if (recvd < 0 || recvd < (ssize_t) len)
|
||||
{
|
||||
if (!socket_receive_all_blocking(sockfd, (unsigned char *) buf + recvd, len - recvd))
|
||||
return -1;
|
||||
|
@ -142,7 +142,7 @@ bool netplay_discovery_driver_ctl(enum rarch_netplay_discovery_ctl_state state,
|
||||
/* And send it off */
|
||||
if (sendto(lan_ad_client_fd, (const char *) &ad_packet_buffer,
|
||||
2*sizeof(uint32_t), 0, addr->ai_addr, addr->ai_addrlen) <
|
||||
2*sizeof(uint32_t))
|
||||
(ssize_t) (2*sizeof(uint32_t)))
|
||||
RARCH_WARN("Failed to send netplay discovery response.\n");
|
||||
|
||||
freeaddrinfo_retro(addr);
|
||||
|
@ -150,26 +150,6 @@ static bool get_self_input_state(netplay_t *netplay)
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint32_t netplay_max_ahead(netplay_t *netplay)
|
||||
{
|
||||
uint32_t max_ahead;
|
||||
|
||||
/* Figure out how many frames we're allowed to be ahead: Ideally we need to be
|
||||
* able to run our entire stall worth of frames in one real frame. In
|
||||
* practice, we'll allow a couple jitter frames. (FIXME: hard coded
|
||||
* as three 60FPS frames) */
|
||||
if (netplay_data->frame_run_time_avg)
|
||||
max_ahead = 50000 / netplay_data->frame_run_time_avg;
|
||||
else
|
||||
max_ahead = NETPLAY_MAX_STALL_FRAMES;
|
||||
if (max_ahead > NETPLAY_MAX_STALL_FRAMES)
|
||||
max_ahead = NETPLAY_MAX_STALL_FRAMES;
|
||||
if (max_ahead < 2)
|
||||
max_ahead = 2;
|
||||
|
||||
return max_ahead;
|
||||
}
|
||||
|
||||
/**
|
||||
* netplay_poll:
|
||||
* @netplay : pointer to netplay object
|
||||
@ -217,9 +197,8 @@ static bool netplay_poll(void)
|
||||
{
|
||||
case NETPLAY_STALL_RUNNING_FAST:
|
||||
{
|
||||
uint32_t max_ahead = netplay_max_ahead(netplay_data);
|
||||
netplay_update_unread_ptr(netplay_data);
|
||||
if (netplay_data->unread_frame_count + max_ahead - 2
|
||||
if (netplay_data->unread_frame_count + NETPLAY_MAX_STALL_FRAMES - 2
|
||||
> netplay_data->self_frame_count)
|
||||
{
|
||||
netplay_data->stall = NETPLAY_STALL_NONE;
|
||||
@ -233,17 +212,31 @@ static bool netplay_poll(void)
|
||||
break;
|
||||
}
|
||||
|
||||
case NETPLAY_STALL_SERVER_REQUESTED:
|
||||
{
|
||||
/* See if the stall is done */
|
||||
if (netplay_data->connections[0].stall_frame == 0)
|
||||
{
|
||||
/* Stop stalling! */
|
||||
netplay_data->connections[0].stall = NETPLAY_STALL_NONE;
|
||||
netplay_data->stall = NETPLAY_STALL_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
netplay_data->connections[0].stall_frame--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case NETPLAY_STALL_NO_CONNECTION:
|
||||
/* We certainly haven't fixed this */
|
||||
break;
|
||||
|
||||
default: /* not stalling */
|
||||
{
|
||||
uint32_t max_ahead = netplay_max_ahead(netplay_data);
|
||||
|
||||
/* Are we too far ahead? */
|
||||
netplay_update_unread_ptr(netplay_data);
|
||||
if (netplay_data->unread_frame_count + max_ahead
|
||||
if (netplay_data->unread_frame_count + NETPLAY_MAX_STALL_FRAMES
|
||||
<= netplay_data->self_frame_count)
|
||||
{
|
||||
netplay_data->stall = NETPLAY_STALL_RUNNING_FAST;
|
||||
@ -276,7 +269,7 @@ static bool netplay_poll(void)
|
||||
}
|
||||
|
||||
/* If we're stalling, consider disconnection */
|
||||
if (netplay_data->stall)
|
||||
if (netplay_data->stall && netplay_data->stall_time)
|
||||
{
|
||||
retro_time_t now = cpu_features_get_time_usec();
|
||||
|
||||
|
@ -257,7 +257,7 @@ struct info_buf_s
|
||||
|
||||
#define RECV(buf, sz) \
|
||||
recvd = netplay_recv(&connection->recv_packet_buffer, connection->fd, (buf), (sz), false); \
|
||||
if (recvd >= 0 && recvd < (sz)) \
|
||||
if (recvd >= 0 && recvd < (ssize_t) (sz)) \
|
||||
{ \
|
||||
netplay_recv_reset(&connection->recv_packet_buffer); \
|
||||
return true; \
|
||||
|
@ -379,11 +379,24 @@ bool netplay_cmd_mode(netplay_t *netplay,
|
||||
return netplay_send_raw_cmd(netplay, connection, cmd, NULL, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* netplay_cmd_stall
|
||||
*
|
||||
* Send a stall command.
|
||||
*/
|
||||
bool netplay_cmd_stall(netplay_t *netplay,
|
||||
struct netplay_connection *connection,
|
||||
uint32_t frames)
|
||||
{
|
||||
frames = htonl(frames);
|
||||
return netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_STALL, &frames, sizeof(frames));
|
||||
}
|
||||
|
||||
#undef RECV
|
||||
#define RECV(buf, sz) \
|
||||
recvd = netplay_recv(&connection->recv_packet_buffer, connection->fd, (buf), \
|
||||
(sz), false); \
|
||||
if (recvd >= 0 && recvd < (sz)) goto shrt; \
|
||||
if (recvd >= 0 && recvd < (ssize_t) (sz)) goto shrt; \
|
||||
else if (recvd < 0)
|
||||
|
||||
static bool netplay_get_cmd(netplay_t *netplay,
|
||||
@ -1199,6 +1212,42 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
remote_unpaused(netplay, connection);
|
||||
break;
|
||||
|
||||
case NETPLAY_CMD_STALL:
|
||||
{
|
||||
uint32_t frames;
|
||||
|
||||
if (cmd_size != sizeof(uint32_t))
|
||||
{
|
||||
RARCH_ERR("NETPLAY_CMD_STALL with incorrect payload size.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
|
||||
RECV(&frames, sizeof(frames))
|
||||
{
|
||||
RARCH_ERR("Failed to receive NETPLAY_CMD_STALL payload.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
frames = ntohl(frames);
|
||||
if (frames > NETPLAY_MAX_REQ_STALL_TIME)
|
||||
frames = NETPLAY_MAX_REQ_STALL_TIME;
|
||||
|
||||
if (netplay->is_server)
|
||||
{
|
||||
/* Only servers can request a stall! */
|
||||
RARCH_ERR("Netplay client requested a stall?\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
|
||||
/* We can only stall for one reason at a time */
|
||||
if (!netplay->stall)
|
||||
{
|
||||
connection->stall = netplay->stall = NETPLAY_STALL_SERVER_REQUESTED;
|
||||
netplay->stall_time = 0;
|
||||
connection->stall_frame = frames;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
RARCH_ERR("%s.\n", msg_hash_to_str(MSG_UNKNOWN_NETPLAY_COMMAND_RECEIVED));
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
|
@ -47,6 +47,8 @@
|
||||
|
||||
#define NETPLAY_MAX_STALL_FRAMES 60
|
||||
#define NETPLAY_FRAME_RUN_TIME_WINDOW 120
|
||||
#define NETPLAY_MAX_REQ_STALL_TIME 60
|
||||
#define NETPLAY_MAX_REQ_STALL_FREQUENCY 120
|
||||
|
||||
#define PREV_PTR(x) ((x) == 0 ? netplay->buffer_size - 1 : (x) - 1)
|
||||
#define NEXT_PTR(x) ((x + 1) % netplay->buffer_size)
|
||||
@ -148,8 +150,11 @@ enum netplay_cmd
|
||||
/* Resumes the game, takes no arguments */
|
||||
NETPLAY_CMD_RESUME = 0x0044,
|
||||
|
||||
/* Request that a client stall because it's running fast */
|
||||
NETPLAY_CMD_STALL = 0x0045,
|
||||
|
||||
/* Sends over cheats enabled on client (unsupported) */
|
||||
NETPLAY_CMD_CHEATS = 0x0045,
|
||||
NETPLAY_CMD_CHEATS = 0x0046,
|
||||
|
||||
/* Misc. commands */
|
||||
|
||||
@ -205,6 +210,7 @@ enum rarch_netplay_stall_reason
|
||||
{
|
||||
NETPLAY_STALL_NONE = 0,
|
||||
NETPLAY_STALL_RUNNING_FAST,
|
||||
NETPLAY_STALL_SERVER_REQUESTED,
|
||||
NETPLAY_STALL_NO_CONNECTION
|
||||
};
|
||||
|
||||
@ -273,7 +279,7 @@ struct netplay_connection
|
||||
enum rarch_netplay_connection_mode mode;
|
||||
|
||||
/* Player # of connected player */
|
||||
int player;
|
||||
uint32_t player;
|
||||
|
||||
/* What compression does this peer support? */
|
||||
uint32_t compression_supported;
|
||||
@ -284,6 +290,10 @@ struct netplay_connection
|
||||
/* Is this connection stalling? */
|
||||
enum rarch_netplay_stall_reason stall;
|
||||
retro_time_t stall_time;
|
||||
|
||||
/* For the server: When was the last time we requested this client to stall?
|
||||
* For the client: How many frames of stall do we have left? */
|
||||
uint32_t stall_frame;
|
||||
};
|
||||
|
||||
/* Compression transcoder */
|
||||
@ -717,6 +727,15 @@ bool netplay_cmd_mode(netplay_t *netplay,
|
||||
struct netplay_connection *connection,
|
||||
enum rarch_netplay_connection_mode mode);
|
||||
|
||||
/**
|
||||
* netplay_cmd_stall
|
||||
*
|
||||
* Send a stall command.
|
||||
*/
|
||||
bool netplay_cmd_stall(netplay_t *netplay,
|
||||
struct netplay_connection *connection,
|
||||
uint32_t frames);
|
||||
|
||||
/**
|
||||
* netplay_poll_net_input
|
||||
*
|
||||
|
@ -352,7 +352,7 @@ process:
|
||||
*/
|
||||
void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
||||
{
|
||||
uint32_t cmp_frame_count;
|
||||
uint32_t lo_frame_count, hi_frame_count;
|
||||
|
||||
/* Unless we're stalling, we've just finished running a frame */
|
||||
if (!stalled)
|
||||
@ -498,15 +498,29 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
||||
}
|
||||
|
||||
if (netplay->is_server)
|
||||
cmp_frame_count = netplay->unread_frame_count;
|
||||
{
|
||||
uint32_t player;
|
||||
|
||||
lo_frame_count = hi_frame_count = netplay->unread_frame_count;
|
||||
|
||||
/* Look for players that are ahead of us */
|
||||
for (player = 0; player < MAX_USERS; player++)
|
||||
{
|
||||
if (!(netplay->connected_players & (1<<player))) continue;
|
||||
if (netplay->read_frame_count[player] > hi_frame_count)
|
||||
hi_frame_count = netplay->read_frame_count[player];
|
||||
}
|
||||
}
|
||||
else
|
||||
cmp_frame_count = netplay->server_frame_count;
|
||||
{
|
||||
lo_frame_count = hi_frame_count = netplay->server_frame_count;
|
||||
}
|
||||
|
||||
/* If we're behind, try to catch up */
|
||||
if (netplay->catch_up)
|
||||
{
|
||||
/* Are we caught up? */
|
||||
if (netplay->self_frame_count >= cmp_frame_count)
|
||||
if (netplay->self_frame_count >= lo_frame_count)
|
||||
{
|
||||
netplay->catch_up = false;
|
||||
input_driver_unset_nonblock_state();
|
||||
@ -516,12 +530,43 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
||||
}
|
||||
else if (!stalled)
|
||||
{
|
||||
/* Are we falling behind? */
|
||||
if (netplay->self_frame_count < cmp_frame_count - 2)
|
||||
if (netplay->self_frame_count + 2 < lo_frame_count)
|
||||
{
|
||||
/* Are we falling behind? */
|
||||
netplay->catch_up = true;
|
||||
input_driver_set_nonblock_state();
|
||||
driver_ctl(RARCH_DRIVER_CTL_SET_NONBLOCK_STATE, NULL);
|
||||
|
||||
}
|
||||
else if (netplay->self_frame_count + 2 < hi_frame_count)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
/* We're falling behind some clients but not others, so request that
|
||||
* clients ahead of us stall */
|
||||
for (i = 0; i < netplay->connections_size; i++)
|
||||
{
|
||||
struct netplay_connection *connection = &netplay->connections[i];
|
||||
int player;
|
||||
if (!connection->active ||
|
||||
connection->mode != NETPLAY_CONNECTION_PLAYING)
|
||||
continue;
|
||||
player = connection->player;
|
||||
|
||||
/* Are they ahead? */
|
||||
if (netplay->self_frame_count + 2 < netplay->read_frame_count[player])
|
||||
{
|
||||
/* Tell them to stall */
|
||||
if (connection->stall_frame + NETPLAY_MAX_REQ_STALL_FREQUENCY <
|
||||
netplay->self_frame_count)
|
||||
{
|
||||
connection->stall_frame = netplay->self_frame_count;
|
||||
netplay_cmd_stall(netplay, connection,
|
||||
netplay->read_frame_count[player] -
|
||||
netplay->self_frame_count + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user