mirror of
https://github.com/libretro/RetroArch
synced 2025-04-10 15:45:19 +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:
|
Description:
|
||||||
Indicates that the core is no longer paused.
|
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
|
Command: CHEATS
|
||||||
Unused
|
Unused
|
||||||
|
|
||||||
|
@ -322,7 +322,7 @@ ssize_t netplay_recv(struct socket_buffer *sbuf, int sockfd, void *buf,
|
|||||||
if (block)
|
if (block)
|
||||||
{
|
{
|
||||||
sbuf->start = sbuf->read;
|
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))
|
if (!socket_receive_all_blocking(sockfd, (unsigned char *) buf + recvd, len - recvd))
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -142,7 +142,7 @@ bool netplay_discovery_driver_ctl(enum rarch_netplay_discovery_ctl_state state,
|
|||||||
/* And send it off */
|
/* And send it off */
|
||||||
if (sendto(lan_ad_client_fd, (const char *) &ad_packet_buffer,
|
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), 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");
|
RARCH_WARN("Failed to send netplay discovery response.\n");
|
||||||
|
|
||||||
freeaddrinfo_retro(addr);
|
freeaddrinfo_retro(addr);
|
||||||
|
@ -150,26 +150,6 @@ static bool get_self_input_state(netplay_t *netplay)
|
|||||||
return true;
|
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_poll:
|
||||||
* @netplay : pointer to netplay object
|
* @netplay : pointer to netplay object
|
||||||
@ -217,9 +197,8 @@ static bool netplay_poll(void)
|
|||||||
{
|
{
|
||||||
case NETPLAY_STALL_RUNNING_FAST:
|
case NETPLAY_STALL_RUNNING_FAST:
|
||||||
{
|
{
|
||||||
uint32_t max_ahead = netplay_max_ahead(netplay_data);
|
|
||||||
netplay_update_unread_ptr(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->self_frame_count)
|
||||||
{
|
{
|
||||||
netplay_data->stall = NETPLAY_STALL_NONE;
|
netplay_data->stall = NETPLAY_STALL_NONE;
|
||||||
@ -233,17 +212,31 @@ static bool netplay_poll(void)
|
|||||||
break;
|
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:
|
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 */
|
||||||
{
|
{
|
||||||
uint32_t max_ahead = netplay_max_ahead(netplay_data);
|
|
||||||
|
|
||||||
/* Are we too far ahead? */
|
/* Are we too far ahead? */
|
||||||
netplay_update_unread_ptr(netplay_data);
|
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->self_frame_count)
|
||||||
{
|
{
|
||||||
netplay_data->stall = NETPLAY_STALL_RUNNING_FAST;
|
netplay_data->stall = NETPLAY_STALL_RUNNING_FAST;
|
||||||
@ -276,7 +269,7 @@ static bool netplay_poll(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* If we're stalling, consider disconnection */
|
/* 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();
|
retro_time_t now = cpu_features_get_time_usec();
|
||||||
|
|
||||||
|
@ -257,7 +257,7 @@ struct info_buf_s
|
|||||||
|
|
||||||
#define RECV(buf, sz) \
|
#define RECV(buf, sz) \
|
||||||
recvd = netplay_recv(&connection->recv_packet_buffer, connection->fd, (buf), (sz), false); \
|
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); \
|
netplay_recv_reset(&connection->recv_packet_buffer); \
|
||||||
return true; \
|
return true; \
|
||||||
|
@ -379,11 +379,24 @@ bool netplay_cmd_mode(netplay_t *netplay,
|
|||||||
return netplay_send_raw_cmd(netplay, connection, cmd, NULL, 0);
|
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
|
#undef RECV
|
||||||
#define RECV(buf, sz) \
|
#define RECV(buf, sz) \
|
||||||
recvd = netplay_recv(&connection->recv_packet_buffer, connection->fd, (buf), \
|
recvd = netplay_recv(&connection->recv_packet_buffer, connection->fd, (buf), \
|
||||||
(sz), false); \
|
(sz), false); \
|
||||||
if (recvd >= 0 && recvd < (sz)) goto shrt; \
|
if (recvd >= 0 && recvd < (ssize_t) (sz)) goto shrt; \
|
||||||
else if (recvd < 0)
|
else if (recvd < 0)
|
||||||
|
|
||||||
static bool netplay_get_cmd(netplay_t *netplay,
|
static bool netplay_get_cmd(netplay_t *netplay,
|
||||||
@ -1199,6 +1212,42 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
|||||||
remote_unpaused(netplay, connection);
|
remote_unpaused(netplay, connection);
|
||||||
break;
|
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:
|
default:
|
||||||
RARCH_ERR("%s.\n", msg_hash_to_str(MSG_UNKNOWN_NETPLAY_COMMAND_RECEIVED));
|
RARCH_ERR("%s.\n", msg_hash_to_str(MSG_UNKNOWN_NETPLAY_COMMAND_RECEIVED));
|
||||||
return netplay_cmd_nak(netplay, connection);
|
return netplay_cmd_nak(netplay, connection);
|
||||||
|
@ -47,6 +47,8 @@
|
|||||||
|
|
||||||
#define NETPLAY_MAX_STALL_FRAMES 60
|
#define NETPLAY_MAX_STALL_FRAMES 60
|
||||||
#define NETPLAY_FRAME_RUN_TIME_WINDOW 120
|
#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 PREV_PTR(x) ((x) == 0 ? netplay->buffer_size - 1 : (x) - 1)
|
||||||
#define NEXT_PTR(x) ((x + 1) % netplay->buffer_size)
|
#define NEXT_PTR(x) ((x + 1) % netplay->buffer_size)
|
||||||
@ -148,8 +150,11 @@ enum netplay_cmd
|
|||||||
/* Resumes the game, takes no arguments */
|
/* Resumes the game, takes no arguments */
|
||||||
NETPLAY_CMD_RESUME = 0x0044,
|
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) */
|
/* Sends over cheats enabled on client (unsupported) */
|
||||||
NETPLAY_CMD_CHEATS = 0x0045,
|
NETPLAY_CMD_CHEATS = 0x0046,
|
||||||
|
|
||||||
/* Misc. commands */
|
/* Misc. commands */
|
||||||
|
|
||||||
@ -205,6 +210,7 @@ enum rarch_netplay_stall_reason
|
|||||||
{
|
{
|
||||||
NETPLAY_STALL_NONE = 0,
|
NETPLAY_STALL_NONE = 0,
|
||||||
NETPLAY_STALL_RUNNING_FAST,
|
NETPLAY_STALL_RUNNING_FAST,
|
||||||
|
NETPLAY_STALL_SERVER_REQUESTED,
|
||||||
NETPLAY_STALL_NO_CONNECTION
|
NETPLAY_STALL_NO_CONNECTION
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -273,7 +279,7 @@ struct netplay_connection
|
|||||||
enum rarch_netplay_connection_mode mode;
|
enum rarch_netplay_connection_mode mode;
|
||||||
|
|
||||||
/* Player # of connected player */
|
/* Player # of connected player */
|
||||||
int player;
|
uint32_t player;
|
||||||
|
|
||||||
/* What compression does this peer support? */
|
/* What compression does this peer support? */
|
||||||
uint32_t compression_supported;
|
uint32_t compression_supported;
|
||||||
@ -284,6 +290,10 @@ struct netplay_connection
|
|||||||
/* Is this connection stalling? */
|
/* Is this connection stalling? */
|
||||||
enum rarch_netplay_stall_reason stall;
|
enum rarch_netplay_stall_reason stall;
|
||||||
retro_time_t stall_time;
|
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 */
|
/* Compression transcoder */
|
||||||
@ -717,6 +727,15 @@ bool netplay_cmd_mode(netplay_t *netplay,
|
|||||||
struct netplay_connection *connection,
|
struct netplay_connection *connection,
|
||||||
enum rarch_netplay_connection_mode mode);
|
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
|
* netplay_poll_net_input
|
||||||
*
|
*
|
||||||
|
@ -352,7 +352,7 @@ process:
|
|||||||
*/
|
*/
|
||||||
void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
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 */
|
/* Unless we're stalling, we've just finished running a frame */
|
||||||
if (!stalled)
|
if (!stalled)
|
||||||
@ -498,15 +498,29 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (netplay->is_server)
|
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
|
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 we're behind, try to catch up */
|
||||||
if (netplay->catch_up)
|
if (netplay->catch_up)
|
||||||
{
|
{
|
||||||
/* Are we caught 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;
|
netplay->catch_up = false;
|
||||||
input_driver_unset_nonblock_state();
|
input_driver_unset_nonblock_state();
|
||||||
@ -516,12 +530,43 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
|||||||
}
|
}
|
||||||
else if (!stalled)
|
else if (!stalled)
|
||||||
{
|
{
|
||||||
/* Are we falling behind? */
|
if (netplay->self_frame_count + 2 < lo_frame_count)
|
||||||
if (netplay->self_frame_count < cmp_frame_count - 2)
|
|
||||||
{
|
{
|
||||||
|
/* Are we falling behind? */
|
||||||
netplay->catch_up = true;
|
netplay->catch_up = true;
|
||||||
input_driver_set_nonblock_state();
|
input_driver_set_nonblock_state();
|
||||||
driver_ctl(RARCH_DRIVER_CTL_SET_NONBLOCK_STATE, NULL);
|
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