Merge pull request #5036 from GregorR/netplay-disconnection-forwarding

Handle forwarding of netplay state demotions correctly.
This commit is contained in:
Twinaphex 2017-06-07 03:54:13 +02:00 committed by GitHub
commit 803ae53372
4 changed files with 79 additions and 17 deletions

View File

@ -165,6 +165,10 @@ static bool get_self_input_state(netplay_t *netplay)
netplay_send_cur_input(netplay, &netplay->connections[i]);
}
/* Handle any delayed state changes */
if (netplay->is_server)
netplay_delayed_state_change(netplay);
return true;
}
@ -847,7 +851,7 @@ void netplay_post_frame(netplay_t *netplay)
if (connection->active &&
!netplay_send_flush(&connection->send_packet_buffer, connection->fd,
false))
netplay_hangup(netplay, &netplay->connections[0]);
netplay_hangup(netplay, connection);
}
}

View File

@ -130,21 +130,19 @@ void netplay_hangup(netplay_t *netplay, struct netplay_connection *connection)
}
else
{
/* Remove this player */
/* Mark the player for removal */
if (connection->mode == NETPLAY_CONNECTION_PLAYING ||
connection->mode == NETPLAY_CONNECTION_SLAVE)
{
/* This special mode keeps the connection object alive long enough to
* send the disconnection message at the correct time */
connection->mode = NETPLAY_CONNECTION_DELAYED_DISCONNECT;
connection->delay_frame = netplay->read_frame_count[connection->player];
/* Mark them as not playing anymore */
netplay->connected_players &= ~(1<<connection->player);
netplay->connected_slaves &= ~(1<<connection->player);
/* FIXME: Duplication */
if (netplay->is_server)
{
uint32_t payload[2];
payload[0] = htonl(netplay->read_frame_count[connection->player]);
payload[1] = htonl(connection->player);
netplay_send_raw_cmd_all(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload));
}
}
}
@ -154,6 +152,42 @@ void netplay_hangup(netplay_t *netplay, struct netplay_connection *connection)
remote_unpaused(netplay, connection);
}
/**
* netplay_delayed_state_change:
*
* Handle any pending state changes which are ready as of the beginning of the
* current frame.
*/
void netplay_delayed_state_change(netplay_t *netplay)
{
struct netplay_connection *connection;
size_t i;
for (i = 0; i < netplay->connections_size; i++)
{
connection = &netplay->connections[i];
if ((connection->active || connection->mode == NETPLAY_CONNECTION_DELAYED_DISCONNECT) &&
connection->delay_frame &&
connection->delay_frame <= netplay->self_frame_count)
{
/* Something was delayed! Prepare the MODE command */
uint32_t payload[2];
payload[0] = htonl(connection->delay_frame);
payload[1] = htonl(connection->player);
/* Remove the connection entirely if relevant */
if (connection->mode == NETPLAY_CONNECTION_DELAYED_DISCONNECT)
connection->mode = NETPLAY_CONNECTION_NONE;
/* Then send the mode change packet */
netplay_send_raw_cmd_all(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload));
/* And forget the delay frame */
connection->delay_frame = 0;
}
}
}
/* Send the specified input data */
static bool send_input_frame(netplay_t *netplay,
struct netplay_connection *only, struct netplay_connection *except,
@ -663,17 +697,13 @@ static bool netplay_get_cmd(netplay_t *netplay,
connection->mode == NETPLAY_CONNECTION_SLAVE)
{
/* The frame we haven't received is their end frame */
payload[0] = htonl(netplay->read_frame_count[connection->player]);
connection->delay_frame = netplay->read_frame_count[connection->player];
/* Mark them as not playing anymore */
connection->mode = NETPLAY_CONNECTION_SPECTATING;
netplay->connected_players &= ~(1<<connection->player);
netplay->connected_slaves &= ~(1<<connection->player);
/* Tell everyone */
payload[1] = htonl(connection->player);
netplay_send_raw_cmd_all(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload));
/* Announce it */
msg[sizeof(msg)-1] = '\0';
snprintf(msg, sizeof(msg)-1, "Player %d has left", connection->player+1);
@ -731,6 +761,14 @@ static bool netplay_get_cmd(netplay_t *netplay,
return netplay_cmd_nak(netplay, connection);
}
if (connection->delay_frame)
{
/* Can't switch modes while a mode switch is already in progress. */
payload[0] = htonl(NETPLAY_CMD_MODE_REFUSED_REASON_TOO_FAST);
netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE_REFUSED, payload, sizeof(uint32_t));
break;
}
if (!connection->can_play)
{
/* Not allowed to play */

View File

@ -192,13 +192,19 @@ enum netplay_cmd_mode_reasons
NETPLAY_CMD_MODE_REFUSED_REASON_UNPRIVILEGED,
/* There are no free player slots */
NETPLAY_CMD_MODE_REFUSED_REASON_NO_SLOTS
NETPLAY_CMD_MODE_REFUSED_REASON_NO_SLOTS,
/* You're changing modes too fast */
NETPLAY_CMD_MODE_REFUSED_REASON_TOO_FAST
};
enum rarch_netplay_connection_mode
{
NETPLAY_CONNECTION_NONE = 0,
NETPLAY_CONNECTION_DELAYED_DISCONNECT, /* The connection is dead, but data
is still waiting to be forwarded */
/* Initialization: */
NETPLAY_CONNECTION_INIT, /* Waiting for header */
NETPLAY_CONNECTION_PRE_NICK, /* Waiting for nick */
@ -298,6 +304,11 @@ struct netplay_connection
/* Mode of the connection */
enum rarch_netplay_connection_mode mode;
/* If the mode is a DELAYED_DISCONNECT or SPECTATOR, the transmission of the
* mode change may have to wait for data to be forwarded. This is the frame
* to wait for, or 0 if no delay is active. */
uint32_t delay_frame;
/* Player # of connected player */
uint32_t player;
@ -720,6 +731,14 @@ void netplay_free(netplay_t *netplay);
*/
void netplay_hangup(netplay_t *netplay, struct netplay_connection *connection);
/**
* netplay_delayed_state_change:
*
* Handle any pending state changes which are ready as of the beginning of the
* current frame.
*/
void netplay_delayed_state_change(netplay_t *netplay);
/**
* netplay_send_cur_input
*

View File

@ -298,7 +298,8 @@ bool netplay_sync_pre_frame(netplay_t *netplay)
/* Allocate a connection */
for (connection_num = 0; connection_num < netplay->connections_size; connection_num++)
if (!netplay->connections[connection_num].active) break;
if (!netplay->connections[connection_num].active &&
netplay->connections[connection_num].mode != NETPLAY_CONNECTION_DELAYED_DISCONNECT) break;
if (connection_num == netplay->connections_size)
{
if (connection_num == 0)