Merge pull request #4691 from GregorR/netplay-slave-mode
Netplay slave mode
This commit is contained in:
commit
fdbdcfee65
|
@ -821,6 +821,12 @@ static const bool netplay_public_announce = true;
|
|||
/* Start netplay in spectator mode */
|
||||
static const bool netplay_start_as_spectator = false;
|
||||
|
||||
/* Allow connections in slave mode */
|
||||
static const bool netplay_allow_slaves = true;
|
||||
|
||||
/* Require connections only in slave mode */
|
||||
static const bool netplay_require_slaves = false;
|
||||
|
||||
/* Netplay without savestates/rewind */
|
||||
static const bool netplay_stateless_mode = false;
|
||||
|
||||
|
|
|
@ -730,6 +730,8 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings,
|
|||
#ifdef HAVE_NETWORKING
|
||||
SETTING_BOOL("netplay_public_announce", &settings->netplay.public_announce, true, netplay_public_announce, false);
|
||||
SETTING_BOOL("netplay_start_as_spectator", &settings->netplay.start_as_spectator, false, netplay_start_as_spectator, false);
|
||||
SETTING_BOOL("netplay_allow_slaves", &settings->netplay.allow_slaves, true, netplay_allow_slaves, false);
|
||||
SETTING_BOOL("netplay_require_slaves", &settings->netplay.require_slaves, true, netplay_require_slaves, false);
|
||||
SETTING_BOOL("netplay_stateless_mode", &settings->netplay.stateless_mode, false, netplay_stateless_mode, false);
|
||||
SETTING_BOOL("netplay_client_swap_input", &settings->netplay.swap_input, true, netplay_client_swap_input, false);
|
||||
#endif
|
||||
|
|
|
@ -407,6 +407,8 @@ typedef struct settings
|
|||
char server[255];
|
||||
unsigned port;
|
||||
bool start_as_spectator;
|
||||
bool allow_slaves;
|
||||
bool require_slaves;
|
||||
bool stateless_mode;
|
||||
int check_frames;
|
||||
unsigned input_latency_frames_min;
|
||||
|
|
|
@ -599,6 +599,8 @@ MSG_HASH(MENU_ENUM_LABEL_NETPLAY_ENABLE_CLIENT,
|
|||
"menu_netplay_enable_client")
|
||||
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_ENABLE_HOST,
|
||||
"menu_netplay_enable_host")
|
||||
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_ALLOW_SLAVES,
|
||||
"netplay_allow_slaves")
|
||||
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_IP_ADDRESS,
|
||||
"netplay_ip_address")
|
||||
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_MODE,
|
||||
|
@ -613,6 +615,8 @@ MSG_HASH(MENU_ENUM_LABEL_NETPLAY_SETTINGS,
|
|||
"menu_netplay_settings")
|
||||
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_PUBLIC_ANNOUNCE,
|
||||
"netplay_public_announce")
|
||||
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_REQUIRE_SLAVES,
|
||||
"netplay_require_slaves")
|
||||
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_SPECTATE_PASSWORD,
|
||||
"netplay_spectate_password")
|
||||
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_SPECTATOR_MODE_ENABLE,
|
||||
|
|
|
@ -1540,6 +1540,21 @@ int menu_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len) {
|
|||
"on start. It's always possible to change mode \n"
|
||||
"later.");
|
||||
break;
|
||||
case MENU_ENUM_LABEL_NETPLAY_ALLOW_SLAVES:
|
||||
snprintf(s, len,
|
||||
"Whether to allow connections in slave mode. \n"
|
||||
" \n"
|
||||
"Slave-mode clients require very little processing \n"
|
||||
"power on either side, but will suffer \n"
|
||||
"significantly from network latency.");
|
||||
break;
|
||||
case MENU_ENUM_LABEL_NETPLAY_REQUIRE_SLAVES:
|
||||
snprintf(s, len,
|
||||
"Whether to disallow connections not in slave mode. \n"
|
||||
" \n"
|
||||
"Not recommended except for very fast networks \n"
|
||||
"with very weak machines. \n");
|
||||
break;
|
||||
case MENU_ENUM_LABEL_NETPLAY_STATELESS_MODE:
|
||||
snprintf(s, len,
|
||||
"Whether to run netplay in a mode not requiring\n"
|
||||
|
|
|
@ -961,6 +961,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NEAREST,
|
|||
"Nearest")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY,
|
||||
"Netplay")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_ALLOW_SLAVES,
|
||||
"Allow Slave-Mode Clients")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CHECK_FRAMES,
|
||||
"Netplay Check Frames")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_MIN,
|
||||
|
@ -991,6 +993,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_PASSWORD,
|
|||
"Server Password")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_PUBLIC_ANNOUNCE,
|
||||
"Publicly Announce Netplay")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_REQUIRE_SLAVES,
|
||||
"Disallow Non-Slave-Mode Clients")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SETTINGS,
|
||||
"Netplay settings")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_START_AS_SPECTATOR,
|
||||
|
@ -2638,6 +2642,14 @@ MSG_HASH(
|
|||
MENU_ENUM_SUBLABEL_NETPLAY_START_AS_SPECTATOR,
|
||||
"Whether to start netplay in spectator mode."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_NETPLAY_ALLOW_SLAVES,
|
||||
"Whether to allow connections in slave mode. Slave-mode clients require very little processing power on either side, but will suffer significantly from network latency."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_NETPLAY_REQUIRE_SLAVES,
|
||||
"Whether to disallow connections not in slave mode. Not recommended except for very fast networks with very weak machines."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_NETPLAY_STATELESS_MODE,
|
||||
"Whether to run netplay in a mode not requiring save states. If set to true, a very fast network is required, but no rewinding is performed, so there will be no netplay jitter."
|
||||
|
|
|
@ -190,6 +190,8 @@ default_sublabel_macro(action_bind_sublabel_netplay_tcp_udp_port, MENU_
|
|||
default_sublabel_macro(action_bind_sublabel_netplay_password, MENU_ENUM_SUBLABEL_NETPLAY_PASSWORD)
|
||||
default_sublabel_macro(action_bind_sublabel_netplay_spectate_password, MENU_ENUM_SUBLABEL_NETPLAY_SPECTATE_PASSWORD)
|
||||
default_sublabel_macro(action_bind_sublabel_netplay_start_as_spectator, MENU_ENUM_SUBLABEL_NETPLAY_START_AS_SPECTATOR)
|
||||
default_sublabel_macro(action_bind_sublabel_netplay_allow_slaves, MENU_ENUM_SUBLABEL_NETPLAY_ALLOW_SLAVES)
|
||||
default_sublabel_macro(action_bind_sublabel_netplay_require_slaves, MENU_ENUM_SUBLABEL_NETPLAY_REQUIRE_SLAVES)
|
||||
default_sublabel_macro(action_bind_sublabel_netplay_stateless_mode, MENU_ENUM_SUBLABEL_NETPLAY_STATELESS_MODE)
|
||||
default_sublabel_macro(action_bind_sublabel_netplay_check_frames, MENU_ENUM_SUBLABEL_NETPLAY_CHECK_FRAMES)
|
||||
default_sublabel_macro(action_bind_sublabel_netplay_nat_traversal, MENU_ENUM_SUBLABEL_NETPLAY_NAT_TRAVERSAL)
|
||||
|
@ -750,6 +752,12 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
|
|||
case MENU_ENUM_LABEL_NETPLAY_START_AS_SPECTATOR:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_netplay_start_as_spectator);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_NETPLAY_ALLOW_SLAVES:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_netplay_allow_slaves);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_NETPLAY_REQUIRE_SLAVES:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_netplay_require_slaves);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_NETPLAY_STATELESS_MODE:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_netplay_stateless_mode);
|
||||
break;
|
||||
|
|
|
@ -4854,6 +4854,14 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data)
|
|||
MENU_ENUM_LABEL_NETPLAY_START_AS_SPECTATOR,
|
||||
PARSE_ONLY_BOOL, false) != -1)
|
||||
count++;
|
||||
if (menu_displaylist_parse_settings_enum(menu, info,
|
||||
MENU_ENUM_LABEL_NETPLAY_ALLOW_SLAVES,
|
||||
PARSE_ONLY_BOOL, false) != -1)
|
||||
count++;
|
||||
if (menu_displaylist_parse_settings_enum(menu, info,
|
||||
MENU_ENUM_LABEL_NETPLAY_REQUIRE_SLAVES,
|
||||
PARSE_ONLY_BOOL, false) != -1)
|
||||
count++;
|
||||
if (menu_displaylist_parse_settings_enum(menu, info,
|
||||
MENU_ENUM_LABEL_NETPLAY_STATELESS_MODE,
|
||||
PARSE_ONLY_BOOL, false) != -1)
|
||||
|
|
|
@ -5674,6 +5674,7 @@ static bool setting_append_list(
|
|||
general_write_handler,
|
||||
general_read_handler);
|
||||
settings_data_list_current_add_flags(list, list_info, SD_FLAG_ALLOW_INPUT);
|
||||
settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED);
|
||||
|
||||
CONFIG_UINT(
|
||||
list, list_info,
|
||||
|
@ -5688,6 +5689,7 @@ static bool setting_append_list(
|
|||
general_read_handler);
|
||||
menu_settings_list_current_add_range(list, list_info, 0, 65535, 1, true, true);
|
||||
settings_data_list_current_add_flags(list, list_info, SD_FLAG_ALLOW_INPUT);
|
||||
settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED);
|
||||
|
||||
CONFIG_STRING(
|
||||
list, list_info,
|
||||
|
@ -5732,6 +5734,38 @@ static bool setting_append_list(
|
|||
general_read_handler,
|
||||
SD_FLAG_NONE);
|
||||
|
||||
CONFIG_BOOL(
|
||||
list, list_info,
|
||||
&settings->netplay.allow_slaves,
|
||||
MENU_ENUM_LABEL_NETPLAY_ALLOW_SLAVES,
|
||||
MENU_ENUM_LABEL_VALUE_NETPLAY_ALLOW_SLAVES,
|
||||
true,
|
||||
MENU_ENUM_LABEL_VALUE_OFF,
|
||||
MENU_ENUM_LABEL_VALUE_ON,
|
||||
&group_info,
|
||||
&subgroup_info,
|
||||
parent_group,
|
||||
general_write_handler,
|
||||
general_read_handler,
|
||||
SD_FLAG_NONE);
|
||||
settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED);
|
||||
|
||||
CONFIG_BOOL(
|
||||
list, list_info,
|
||||
&settings->netplay.require_slaves,
|
||||
MENU_ENUM_LABEL_NETPLAY_REQUIRE_SLAVES,
|
||||
MENU_ENUM_LABEL_VALUE_NETPLAY_REQUIRE_SLAVES,
|
||||
false,
|
||||
MENU_ENUM_LABEL_VALUE_OFF,
|
||||
MENU_ENUM_LABEL_VALUE_ON,
|
||||
&group_info,
|
||||
&subgroup_info,
|
||||
parent_group,
|
||||
general_write_handler,
|
||||
general_read_handler,
|
||||
SD_FLAG_NONE);
|
||||
settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED);
|
||||
|
||||
CONFIG_BOOL(
|
||||
list, list_info,
|
||||
&settings->netplay.stateless_mode,
|
||||
|
@ -5746,6 +5780,7 @@ static bool setting_append_list(
|
|||
general_write_handler,
|
||||
general_read_handler,
|
||||
SD_FLAG_NONE);
|
||||
settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED);
|
||||
|
||||
CONFIG_INT(
|
||||
list, list_info,
|
||||
|
@ -5773,7 +5808,6 @@ static bool setting_append_list(
|
|||
general_write_handler,
|
||||
general_read_handler);
|
||||
menu_settings_list_current_add_range(list, list_info, 0, 15, 1, true, true);
|
||||
settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED);
|
||||
|
||||
CONFIG_INT(
|
||||
list, list_info,
|
||||
|
@ -5787,14 +5821,13 @@ static bool setting_append_list(
|
|||
general_write_handler,
|
||||
general_read_handler);
|
||||
menu_settings_list_current_add_range(list, list_info, 0, 15, 1, true, true);
|
||||
settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED);
|
||||
|
||||
CONFIG_BOOL(
|
||||
list, list_info,
|
||||
&settings->netplay.nat_traversal,
|
||||
MENU_ENUM_LABEL_NETPLAY_NAT_TRAVERSAL,
|
||||
MENU_ENUM_LABEL_VALUE_NETPLAY_NAT_TRAVERSAL,
|
||||
false,
|
||||
true,
|
||||
MENU_ENUM_LABEL_VALUE_OFF,
|
||||
MENU_ENUM_LABEL_VALUE_ON,
|
||||
&group_info,
|
||||
|
@ -5803,6 +5836,7 @@ static bool setting_append_list(
|
|||
general_write_handler,
|
||||
general_read_handler,
|
||||
SD_FLAG_NONE);
|
||||
settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED);
|
||||
|
||||
CONFIG_BOOL(
|
||||
list, list_info,
|
||||
|
@ -5818,6 +5852,7 @@ static bool setting_append_list(
|
|||
general_write_handler,
|
||||
general_read_handler,
|
||||
SD_FLAG_NONE);
|
||||
settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED);
|
||||
|
||||
END_SUB_GROUP(list, list_info, parent_group);
|
||||
|
||||
|
|
|
@ -1020,6 +1020,8 @@ enum msg_hash_enums
|
|||
MENU_LABEL(NETPLAY_DELAY_FRAMES),
|
||||
MENU_LABEL(NETPLAY_PUBLIC_ANNOUNCE),
|
||||
MENU_LABEL(NETPLAY_START_AS_SPECTATOR),
|
||||
MENU_LABEL(NETPLAY_ALLOW_SLAVES),
|
||||
MENU_LABEL(NETPLAY_REQUIRE_SLAVES),
|
||||
MENU_LABEL(NETPLAY_STATELESS_MODE),
|
||||
MENU_LABEL(NETPLAY_CHECK_FRAMES),
|
||||
MENU_LABEL(NETPLAY_INPUT_LATENCY_FRAMES_MIN),
|
||||
|
|
|
@ -234,16 +234,22 @@ Description:
|
|||
itself to be in spectator mode and send no further input.
|
||||
|
||||
Command: PLAY
|
||||
Payload: None
|
||||
Payload:
|
||||
{
|
||||
reserved: 31 bits
|
||||
as slave?: 1 bit
|
||||
}
|
||||
Description:
|
||||
Request to enter player mode. The client must wait for a MODE command
|
||||
before sending input.
|
||||
before sending input. Server may refuse or force slave connections, so the
|
||||
request is not necessarily honored. Payload may be elided if zero.
|
||||
|
||||
Command: MODE
|
||||
Payload:
|
||||
{
|
||||
frame number: uint32
|
||||
reserved: 14 bits
|
||||
reserved: 13 bits
|
||||
slave: 1 bit
|
||||
playing: 1 bit
|
||||
you: 1 bit
|
||||
player number: uint16
|
||||
|
|
|
@ -162,8 +162,7 @@ bool netplay_send(struct socket_buffer *sbuf, int sockfd, const void *buf,
|
|||
|
||||
}
|
||||
|
||||
/* Flush what we can immediately */
|
||||
return netplay_send_flush(sbuf, sockfd, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -220,6 +220,10 @@ static bool netplay_poll(void)
|
|||
/* Simulate the input if we don't have real input */
|
||||
netplay_simulate_input(netplay_data, netplay_data->run_ptr, false);
|
||||
|
||||
/* Handle any slaves */
|
||||
if (netplay_data->is_server && netplay_data->connected_slaves)
|
||||
netplay_handle_slaves(netplay_data);
|
||||
|
||||
netplay_update_unread_ptr(netplay_data);
|
||||
|
||||
/* Figure out how many frames of input latency we should be using to hide
|
||||
|
@ -299,6 +303,11 @@ static bool netplay_poll(void)
|
|||
break;
|
||||
}
|
||||
|
||||
case NETPLAY_STALL_SPECTATOR_WAIT:
|
||||
if (netplay_data->unread_frame_count > netplay_data->self_frame_count)
|
||||
netplay_data->stall = NETPLAY_STALL_NONE;
|
||||
break;
|
||||
|
||||
case NETPLAY_STALL_INPUT_LATENCY:
|
||||
/* Just let it recalculate momentarily */
|
||||
netplay_data->stall = NETPLAY_STALL_NONE;
|
||||
|
@ -331,7 +340,7 @@ static bool netplay_poll(void)
|
|||
/* If we're not stalled, consider stalling */
|
||||
if (!netplay_data->stall)
|
||||
{
|
||||
/* Have we not reat enough latency frames? */
|
||||
/* Have we not read enough latency frames? */
|
||||
if (netplay_data->self_mode == NETPLAY_CONNECTION_PLAYING &&
|
||||
netplay_data->connected_players &&
|
||||
netplay_data->run_frame_count + netplay_data->input_latency_frames > netplay_data->self_frame_count)
|
||||
|
@ -370,6 +379,16 @@ static bool netplay_poll(void)
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
/* If we're a spectator, are we ahead at all? */
|
||||
if (!netplay_data->is_server &&
|
||||
(netplay_data->self_mode == NETPLAY_CONNECTION_SPECTATING ||
|
||||
netplay_data->self_mode == NETPLAY_CONNECTION_SLAVE) &&
|
||||
netplay_data->unread_frame_count <= netplay_data->self_frame_count)
|
||||
{
|
||||
netplay_data->stall = NETPLAY_STALL_SPECTATOR_WAIT;
|
||||
netplay_data->stall_time = cpu_features_get_time_usec();
|
||||
}
|
||||
}
|
||||
|
||||
/* If we're stalling, consider disconnection */
|
||||
|
@ -943,7 +962,8 @@ static void netplay_toggle_play_spectate(netplay_t *netplay)
|
|||
char msg[512];
|
||||
const char *dmsg = NULL;
|
||||
payload[0] = htonl(netplay->self_frame_count);
|
||||
if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING)
|
||||
if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING ||
|
||||
netplay->self_mode == NETPLAY_CONNECTION_SLAVE)
|
||||
{
|
||||
/* Mark us as no longer playing */
|
||||
payload[1] = htonl(netplay->self_player);
|
||||
|
@ -980,7 +1000,8 @@ static void netplay_toggle_play_spectate(netplay_t *netplay)
|
|||
{
|
||||
uint32_t cmd;
|
||||
|
||||
if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING)
|
||||
if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING ||
|
||||
netplay->self_mode == NETPLAY_CONNECTION_SLAVE)
|
||||
{
|
||||
/* Switch to spectator mode immediately */
|
||||
netplay->self_mode = NETPLAY_CONNECTION_SPECTATING;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "netplay_private.h"
|
||||
|
||||
#include "../../configuration.h"
|
||||
#include "../../runloop.h"
|
||||
#include "../../tasks/tasks_internal.h"
|
||||
|
||||
|
@ -129,9 +130,11 @@ void netplay_hangup(netplay_t *netplay, struct netplay_connection *connection)
|
|||
else
|
||||
{
|
||||
/* Remove this player */
|
||||
if (connection->mode == NETPLAY_CONNECTION_PLAYING)
|
||||
if (connection->mode == NETPLAY_CONNECTION_PLAYING ||
|
||||
connection->mode == NETPLAY_CONNECTION_SLAVE)
|
||||
{
|
||||
netplay->connected_players &= ~(1<<connection->player);
|
||||
netplay->connected_slaves &= ~(1<<connection->player);
|
||||
|
||||
/* FIXME: Duplication */
|
||||
if (netplay->is_server)
|
||||
|
@ -240,11 +243,12 @@ bool netplay_send_cur_input(netplay_t *netplay,
|
|||
}
|
||||
|
||||
/* Send our own data */
|
||||
if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING)
|
||||
if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING ||
|
||||
netplay->self_mode == NETPLAY_CONNECTION_SLAVE)
|
||||
{
|
||||
if (!send_input_frame(netplay, connection, NULL,
|
||||
netplay->self_frame_count,
|
||||
(netplay->is_server ? NETPLAY_CMD_INPUT_BIT_SERVER : 0) | netplay->self_player,
|
||||
(netplay->is_server ? NETPLAY_CMD_INPUT_BIT_SERVER : 0) | netplay->self_player,
|
||||
dframe->self_state))
|
||||
return false;
|
||||
}
|
||||
|
@ -364,12 +368,18 @@ bool netplay_cmd_mode(netplay_t *netplay,
|
|||
enum rarch_netplay_connection_mode mode)
|
||||
{
|
||||
uint32_t cmd;
|
||||
uint32_t payloadBuf, *payload = NULL;
|
||||
switch (mode)
|
||||
{
|
||||
case NETPLAY_CONNECTION_SPECTATING:
|
||||
cmd = NETPLAY_CMD_SPECTATE;
|
||||
break;
|
||||
|
||||
case NETPLAY_CONNECTION_SLAVE:
|
||||
payload = &payloadBuf;
|
||||
payloadBuf = htonl(NETPLAY_CMD_PLAY_BIT_SLAVE);
|
||||
/* Intentional fallthrough */
|
||||
|
||||
case NETPLAY_CONNECTION_PLAYING:
|
||||
cmd = NETPLAY_CMD_PLAY;
|
||||
break;
|
||||
|
@ -377,7 +387,8 @@ bool netplay_cmd_mode(netplay_t *netplay,
|
|||
default:
|
||||
return false;
|
||||
}
|
||||
return netplay_send_raw_cmd(netplay, connection, cmd, NULL, 0);
|
||||
return netplay_send_raw_cmd(netplay, connection, cmd, payload,
|
||||
payload ? sizeof(uint32_t) : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -460,7 +471,8 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
|||
if (netplay->is_server)
|
||||
{
|
||||
/* Ignore the claimed player #, must be this client */
|
||||
if (connection->mode != NETPLAY_CONNECTION_PLAYING)
|
||||
if (connection->mode != NETPLAY_CONNECTION_PLAYING &&
|
||||
connection->mode != NETPLAY_CONNECTION_SLAVE)
|
||||
{
|
||||
RARCH_ERR("Netplay input from non-participating player.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
|
@ -478,16 +490,20 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
|||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
|
||||
if (buffer[0] < netplay->read_frame_count[player])
|
||||
/* Check the frame number only if they're not in slave mode */
|
||||
if (connection->mode == NETPLAY_CONNECTION_PLAYING)
|
||||
{
|
||||
/* We already had this, so ignore the new transmission */
|
||||
break;
|
||||
}
|
||||
else if (buffer[0] > netplay->read_frame_count[player])
|
||||
{
|
||||
/* Out of order = out of luck */
|
||||
RARCH_ERR("Netplay input out of order.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
if (buffer[0] < netplay->read_frame_count[player])
|
||||
{
|
||||
/* We already had this, so ignore the new transmission */
|
||||
break;
|
||||
}
|
||||
else if (buffer[0] > netplay->read_frame_count[player])
|
||||
{
|
||||
/* Out of order = out of luck */
|
||||
RARCH_ERR("Netplay input out of order.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
}
|
||||
|
||||
/* The data's good! */
|
||||
|
@ -500,15 +516,22 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
|||
memcpy(dframe->real_input_state[player], buffer + 2,
|
||||
WORDS_PER_INPUT*sizeof(uint32_t));
|
||||
dframe->have_real[player] = true;
|
||||
netplay->read_ptr[player] = NEXT_PTR(netplay->read_ptr[player]);
|
||||
netplay->read_frame_count[player]++;
|
||||
|
||||
if (netplay->is_server)
|
||||
/* Slaves may go through several packets of data in the same frame
|
||||
* if latency is choppy, so we advance and send their data after
|
||||
* handling all network data this frame */
|
||||
if (connection->mode == NETPLAY_CONNECTION_PLAYING)
|
||||
{
|
||||
/* Forward it on if it's past data*/
|
||||
if (dframe->frame <= netplay->self_frame_count)
|
||||
send_input_frame(netplay, NULL, connection, buffer[0],
|
||||
player, dframe->real_input_state[player]);
|
||||
netplay->read_ptr[player] = NEXT_PTR(netplay->read_ptr[player]);
|
||||
netplay->read_frame_count[player]++;
|
||||
|
||||
if (netplay->is_server)
|
||||
{
|
||||
/* Forward it on if it's past data*/
|
||||
if (dframe->frame <= netplay->self_frame_count)
|
||||
send_input_frame(netplay, NULL, connection, buffer[0],
|
||||
player, dframe->real_input_state[player]);
|
||||
}
|
||||
}
|
||||
|
||||
/* If this was server data, advance our server pointer too */
|
||||
|
@ -605,7 +628,8 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
|||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
|
||||
if (connection->mode == NETPLAY_CONNECTION_PLAYING)
|
||||
if (connection->mode == NETPLAY_CONNECTION_PLAYING ||
|
||||
connection->mode == NETPLAY_CONNECTION_SLAVE)
|
||||
{
|
||||
/* The frame we haven't received is their end frame */
|
||||
payload[0] = htonl(netplay->read_frame_count[connection->player]);
|
||||
|
@ -613,6 +637,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
|||
/* 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);
|
||||
|
@ -639,6 +664,34 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
|||
{
|
||||
uint32_t payload[2];
|
||||
uint32_t player = 0;
|
||||
bool slave = false;
|
||||
settings_t *settings = config_get_ptr();
|
||||
|
||||
/* Check if they requested slave mode */
|
||||
if (cmd_size == sizeof(uint32_t))
|
||||
{
|
||||
RECV(payload, sizeof(uint32_t))
|
||||
{
|
||||
RARCH_ERR("Failed to receive NETPLAY_CMD_PLAY payload.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
|
||||
payload[0] = ntohl(payload[0]);
|
||||
if (payload[0] & NETPLAY_CMD_PLAY_BIT_SLAVE)
|
||||
slave = true;
|
||||
}
|
||||
else if (cmd_size != 0)
|
||||
{
|
||||
RARCH_ERR("Invalid payload size for NETPLAY_CMD_PLAY.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
|
||||
/* Check if their slave mode request corresponds with what we allow */
|
||||
if (settings->netplay.require_slaves)
|
||||
slave = true;
|
||||
else if (!settings->netplay.allow_slaves)
|
||||
slave = false;
|
||||
|
||||
payload[0] = htonl(netplay->self_frame_count + 1);
|
||||
|
||||
if (!netplay->is_server)
|
||||
|
@ -671,15 +724,21 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
|||
break;
|
||||
}
|
||||
|
||||
if (connection->mode != NETPLAY_CONNECTION_PLAYING)
|
||||
if (connection->mode != NETPLAY_CONNECTION_PLAYING &&
|
||||
connection->mode != NETPLAY_CONNECTION_SLAVE)
|
||||
{
|
||||
/* Mark them as playing */
|
||||
connection->mode = NETPLAY_CONNECTION_PLAYING;
|
||||
connection->mode = slave ? NETPLAY_CONNECTION_SLAVE :
|
||||
NETPLAY_CONNECTION_PLAYING;
|
||||
connection->player = player;
|
||||
netplay->connected_players |= 1<<player;
|
||||
if (slave)
|
||||
netplay->connected_slaves |= 1<<player;
|
||||
|
||||
/* Tell everyone */
|
||||
payload[1] = htonl(NETPLAY_CMD_MODE_BIT_PLAYING | connection->player);
|
||||
payload[1] = htonl(NETPLAY_CMD_MODE_BIT_PLAYING |
|
||||
(slave?NETPLAY_CMD_MODE_BIT_SLAVE:0) |
|
||||
connection->player);
|
||||
netplay_send_raw_cmd_all(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload));
|
||||
|
||||
/* Announce it */
|
||||
|
@ -692,7 +751,10 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
|||
|
||||
/* Tell the player even if they were confused */
|
||||
payload[1] = htonl(NETPLAY_CMD_MODE_BIT_PLAYING |
|
||||
NETPLAY_CMD_MODE_BIT_YOU | connection->player);
|
||||
((connection->mode == NETPLAY_CONNECTION_SLAVE)?
|
||||
NETPLAY_CMD_MODE_BIT_SLAVE:0) |
|
||||
NETPLAY_CMD_MODE_BIT_YOU |
|
||||
connection->player);
|
||||
netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload));
|
||||
|
||||
/* And expect their data */
|
||||
|
@ -764,7 +826,21 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
|||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
|
||||
netplay->self_mode = NETPLAY_CONNECTION_PLAYING;
|
||||
/* Our mode is based on whether we have the slave bit set */
|
||||
if (mode & NETPLAY_CMD_MODE_BIT_SLAVE)
|
||||
{
|
||||
netplay->self_mode = NETPLAY_CONNECTION_SLAVE;
|
||||
|
||||
/* In slave mode we receive the data from the remote side, so
|
||||
* we actually consider ourself a connected player */
|
||||
netplay->connected_players |= (1<<player);
|
||||
netplay->read_ptr[player] = netplay->server_ptr;
|
||||
netplay->read_frame_count[player] = netplay->server_frame_count;
|
||||
}
|
||||
else
|
||||
{
|
||||
netplay->self_mode = NETPLAY_CONNECTION_PLAYING;
|
||||
}
|
||||
netplay->self_player = player;
|
||||
|
||||
/* Fix up current frame info */
|
||||
|
@ -836,6 +912,9 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
|||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
|
||||
/* Unmark ourself, in case we were in slave mode */
|
||||
netplay->connected_players &= ~(1<<player);
|
||||
|
||||
/* Announce it */
|
||||
strlcpy(msg, "You have left the game", sizeof(msg));
|
||||
RARCH_LOG("%s\n", msg);
|
||||
|
@ -1049,7 +1128,8 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
|||
}
|
||||
|
||||
/* Only players may load states */
|
||||
if (connection->mode != NETPLAY_CONNECTION_PLAYING)
|
||||
if (connection->mode != NETPLAY_CONNECTION_PLAYING &&
|
||||
connection->mode != NETPLAY_CONNECTION_SLAVE)
|
||||
{
|
||||
RARCH_ERR("Netplay state load from a spectator.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
|
@ -1212,7 +1292,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
|||
}
|
||||
nick[sizeof(nick)-1] = '\0';
|
||||
|
||||
/* We outright ignore pausing from spectators */
|
||||
/* We outright ignore pausing from spectators and slaves */
|
||||
if (connection->mode != NETPLAY_CONNECTION_PLAYING)
|
||||
break;
|
||||
|
||||
|
@ -1367,6 +1447,49 @@ int netplay_poll_net_input(netplay_t *netplay, bool block)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* netplay_handle_slaves
|
||||
*
|
||||
* Handle any slave connections
|
||||
*/
|
||||
void netplay_handle_slaves(netplay_t *netplay)
|
||||
{
|
||||
struct delta_frame *frame = &netplay->buffer[netplay->self_ptr];
|
||||
size_t i;
|
||||
for (i = 0; i < netplay->connections_size; i++)
|
||||
{
|
||||
struct netplay_connection *connection = &netplay->connections[i];
|
||||
if (connection->active &&
|
||||
connection->mode == NETPLAY_CONNECTION_SLAVE)
|
||||
{
|
||||
int player = connection->player;
|
||||
|
||||
/* This is a slave connection. First, should we do anything at all? If
|
||||
* we've already "read" this data, then we can just ignore it */
|
||||
if (netplay->read_frame_count[player] > netplay->self_frame_count)
|
||||
continue;
|
||||
|
||||
/* Alright, we have to send something. Do we need to generate it first? */
|
||||
if (!frame->have_real[player])
|
||||
{
|
||||
/* Copy the previous frame's data */
|
||||
memcpy(frame->real_input_state[player],
|
||||
netplay->buffer[PREV_PTR(netplay->self_ptr)].real_input_state[player],
|
||||
WORDS_PER_INPUT*sizeof(uint32_t));
|
||||
frame->have_real[player] = true;
|
||||
}
|
||||
|
||||
/* Send it along */
|
||||
send_input_frame(netplay, NULL, NULL, netplay->self_frame_count,
|
||||
player, frame->real_input_state[player]);
|
||||
|
||||
/* And mark it as "read" */
|
||||
netplay->read_ptr[player] = NEXT_PTR(netplay->self_ptr);
|
||||
netplay->read_frame_count[player] = netplay->self_frame_count + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* netplay_flip_port
|
||||
*
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
|
||||
#define MAX_SERVER_STALL_TIME_USEC (5*1000*1000)
|
||||
#define MAX_CLIENT_STALL_TIME_USEC (10*1000*1000)
|
||||
#define CATCH_UP_CHECK_TIME_USEC (500*1000)
|
||||
#define MAX_RETRIES 16
|
||||
#define RETRY_MS 500
|
||||
|
||||
|
@ -176,6 +177,8 @@ enum netplay_cmd
|
|||
|
||||
#define NETPLAY_CMD_INPUT_BIT_SERVER (1U<<31)
|
||||
#define NETPLAY_CMD_SYNC_BIT_PAUSED (1U<<31)
|
||||
#define NETPLAY_CMD_PLAY_BIT_SLAVE (1U)
|
||||
#define NETPLAY_CMD_MODE_BIT_SLAVE (1U<<18)
|
||||
#define NETPLAY_CMD_MODE_BIT_PLAYING (1U<<17)
|
||||
#define NETPLAY_CMD_MODE_BIT_YOU (1U<<16)
|
||||
|
||||
|
@ -206,15 +209,28 @@ enum rarch_netplay_connection_mode
|
|||
/* Ready: */
|
||||
NETPLAY_CONNECTION_CONNECTED, /* Modes above this are connected */
|
||||
NETPLAY_CONNECTION_SPECTATING, /* Spectator mode */
|
||||
NETPLAY_CONNECTION_SLAVE, /* Playing in slave mode */
|
||||
NETPLAY_CONNECTION_PLAYING /* Normal ready state */
|
||||
};
|
||||
|
||||
enum rarch_netplay_stall_reason
|
||||
{
|
||||
NETPLAY_STALL_NONE = 0,
|
||||
|
||||
/* We're so far ahead that we can't read more data without overflowing the
|
||||
* buffer */
|
||||
NETPLAY_STALL_RUNNING_FAST,
|
||||
|
||||
/* We're in spectator or slave mode and are running ahead at all */
|
||||
NETPLAY_STALL_SPECTATOR_WAIT,
|
||||
|
||||
/* Our actual execution is catching up with latency-adjusted input frames */
|
||||
NETPLAY_STALL_INPUT_LATENCY,
|
||||
|
||||
/* The server asked us to stall */
|
||||
NETPLAY_STALL_SERVER_REQUESTED,
|
||||
|
||||
/* We have no connection and must have one to proceed */
|
||||
NETPLAY_STALL_NO_CONNECTION
|
||||
};
|
||||
|
||||
|
@ -331,10 +347,13 @@ struct netplay
|
|||
size_t connections_size;
|
||||
struct netplay_connection one_connection; /* Client only */
|
||||
|
||||
/* Bitmap of players with controllers (whether local or remote) (low bit is
|
||||
* player 1) */
|
||||
/* Bitmap of players with controllers (low bit is player 1) */
|
||||
uint32_t connected_players;
|
||||
|
||||
/* Bitmap of players playing in slave mode (should be a subset of
|
||||
* connected_players) */
|
||||
uint32_t connected_slaves;
|
||||
|
||||
/* Maximum player number */
|
||||
uint32_t player_max;
|
||||
|
||||
|
@ -461,6 +480,12 @@ struct netplay
|
|||
/* Opposite of stalling, should we be catching up? */
|
||||
bool catch_up;
|
||||
|
||||
/* When did we start falling behind? */
|
||||
retro_time_t catch_up_time;
|
||||
|
||||
/* How far behind did we fall? */
|
||||
uint32_t catch_up_behind;
|
||||
|
||||
/* Frequency with which to check CRCs */
|
||||
int check_frames;
|
||||
|
||||
|
@ -758,6 +783,13 @@ bool netplay_cmd_stall(netplay_t *netplay,
|
|||
*/
|
||||
int netplay_poll_net_input(netplay_t *netplay, bool block);
|
||||
|
||||
/**
|
||||
* netplay_handle_slaves
|
||||
*
|
||||
* Handle any slave connections
|
||||
*/
|
||||
void netplay_handle_slaves(netplay_t *netplay);
|
||||
|
||||
/**
|
||||
* netplay_flip_port
|
||||
*
|
||||
|
|
|
@ -55,6 +55,7 @@ void netplay_update_unread_ptr(netplay_t *netplay)
|
|||
for (player = 0; player < MAX_USERS; player++)
|
||||
{
|
||||
if (!(netplay->connected_players & (1<<player))) continue;
|
||||
if ((netplay->connected_slaves & (1<<player))) continue;
|
||||
if (netplay->read_frame_count[player] < new_unread_frame_count)
|
||||
{
|
||||
new_unread_ptr = netplay->read_ptr[player];
|
||||
|
@ -68,8 +69,16 @@ void netplay_update_unread_ptr(netplay_t *netplay)
|
|||
new_unread_frame_count = netplay->server_frame_count;
|
||||
}
|
||||
|
||||
netplay->unread_ptr = new_unread_ptr;
|
||||
netplay->unread_frame_count = new_unread_frame_count;
|
||||
if (new_unread_frame_count != (uint32_t) -1)
|
||||
{
|
||||
netplay->unread_ptr = new_unread_ptr;
|
||||
netplay->unread_frame_count = new_unread_frame_count;
|
||||
}
|
||||
else
|
||||
{
|
||||
netplay->unread_ptr = netplay->self_ptr;
|
||||
netplay->unread_frame_count = netplay->self_frame_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -546,7 +555,7 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
|||
if (netplay->catch_up)
|
||||
{
|
||||
/* Are we caught up? */
|
||||
if (netplay->self_frame_count >= lo_frame_count)
|
||||
if (netplay->self_frame_count + 1 >= lo_frame_count)
|
||||
{
|
||||
netplay->catch_up = false;
|
||||
input_driver_unset_nonblock_state();
|
||||
|
@ -556,17 +565,44 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
|||
}
|
||||
else if (!stalled)
|
||||
{
|
||||
if (netplay->self_frame_count + 2 < lo_frame_count)
|
||||
if (netplay->self_frame_count + 3 < lo_frame_count)
|
||||
{
|
||||
/* Are we falling behind? */
|
||||
netplay->catch_up = true;
|
||||
input_driver_set_nonblock_state();
|
||||
driver_set_nonblock_state();
|
||||
retro_time_t cur_time = cpu_features_get_time_usec();
|
||||
uint32_t cur_behind = lo_frame_count - netplay->self_frame_count;
|
||||
|
||||
/* We're behind, but we'll only try to catch up if we're actually
|
||||
* falling behind, i.e. if we're more behind after some time */
|
||||
if (netplay->catch_up_time == 0)
|
||||
{
|
||||
/* Record our current time to check for catch-up later */
|
||||
netplay->catch_up_time = cur_time;
|
||||
netplay->catch_up_behind = cur_behind;
|
||||
|
||||
}
|
||||
else if (cur_time - netplay->catch_up_time > CATCH_UP_CHECK_TIME_USEC)
|
||||
{
|
||||
/* Time to check how far behind we are */
|
||||
if (netplay->catch_up_behind <= cur_behind)
|
||||
{
|
||||
/* We're definitely falling behind! */
|
||||
netplay->catch_up = true;
|
||||
netplay->catch_up_time = 0;
|
||||
input_driver_set_nonblock_state();
|
||||
driver_set_nonblock_state();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Check again in another period */
|
||||
netplay->catch_up_time = cur_time;
|
||||
netplay->catch_up_behind = cur_behind;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else if (netplay->self_frame_count + 2 < hi_frame_count)
|
||||
else if (netplay->self_frame_count + 3 < hi_frame_count)
|
||||
{
|
||||
size_t i;
|
||||
netplay->catch_up_time = 0;
|
||||
|
||||
/* We're falling behind some clients but not others, so request that
|
||||
* clients ahead of us stall */
|
||||
|
@ -580,7 +616,7 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
|||
player = connection->player;
|
||||
|
||||
/* Are they ahead? */
|
||||
if (netplay->self_frame_count + 2 < netplay->read_frame_count[player])
|
||||
if (netplay->self_frame_count + 3 < netplay->read_frame_count[player])
|
||||
{
|
||||
/* Tell them to stall */
|
||||
if (connection->stall_frame + NETPLAY_MAX_REQ_STALL_FREQUENCY <
|
||||
|
@ -594,5 +630,9 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
|||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
netplay->catch_up_time = 0;
|
||||
}
|
||||
else
|
||||
netplay->catch_up_time = 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue