mirror of
https://github.com/CTCaer/RetroArch.git
synced 2024-12-24 11:45:16 +00:00
Merge pull request #4309 from GregorR/netplay-reverse-catchup
Reverse catch-up, i.e., server-demanded stalling
This commit is contained in:
commit
ad0707d289
@ -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…
Reference in New Issue
Block a user