Merge pull request #4309 from GregorR/netplay-reverse-catchup

Reverse catch-up, i.e., server-demanded stalling
This commit is contained in:
Twinaphex 2016-12-24 21:55:12 +01:00 committed by GitHub
commit ad0707d289
8 changed files with 152 additions and 38 deletions

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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();

View File

@ -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; \

View File

@ -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);

View File

@ -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
*

View File

@ -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);
}
}
}
}
}
}