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 */
|
/* Start netplay in spectator mode */
|
||||||
static const bool netplay_start_as_spectator = false;
|
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 */
|
/* Netplay without savestates/rewind */
|
||||||
static const bool netplay_stateless_mode = false;
|
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
|
#ifdef HAVE_NETWORKING
|
||||||
SETTING_BOOL("netplay_public_announce", &settings->netplay.public_announce, true, netplay_public_announce, false);
|
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_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_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);
|
SETTING_BOOL("netplay_client_swap_input", &settings->netplay.swap_input, true, netplay_client_swap_input, false);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -407,6 +407,8 @@ typedef struct settings
|
||||||
char server[255];
|
char server[255];
|
||||||
unsigned port;
|
unsigned port;
|
||||||
bool start_as_spectator;
|
bool start_as_spectator;
|
||||||
|
bool allow_slaves;
|
||||||
|
bool require_slaves;
|
||||||
bool stateless_mode;
|
bool stateless_mode;
|
||||||
int check_frames;
|
int check_frames;
|
||||||
unsigned input_latency_frames_min;
|
unsigned input_latency_frames_min;
|
||||||
|
|
|
@ -599,6 +599,8 @@ MSG_HASH(MENU_ENUM_LABEL_NETPLAY_ENABLE_CLIENT,
|
||||||
"menu_netplay_enable_client")
|
"menu_netplay_enable_client")
|
||||||
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_ENABLE_HOST,
|
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_ENABLE_HOST,
|
||||||
"menu_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,
|
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_IP_ADDRESS,
|
||||||
"netplay_ip_address")
|
"netplay_ip_address")
|
||||||
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_MODE,
|
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_MODE,
|
||||||
|
@ -613,6 +615,8 @@ MSG_HASH(MENU_ENUM_LABEL_NETPLAY_SETTINGS,
|
||||||
"menu_netplay_settings")
|
"menu_netplay_settings")
|
||||||
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_PUBLIC_ANNOUNCE,
|
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_PUBLIC_ANNOUNCE,
|
||||||
"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,
|
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_SPECTATE_PASSWORD,
|
||||||
"netplay_spectate_password")
|
"netplay_spectate_password")
|
||||||
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_SPECTATOR_MODE_ENABLE,
|
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"
|
"on start. It's always possible to change mode \n"
|
||||||
"later.");
|
"later.");
|
||||||
break;
|
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:
|
case MENU_ENUM_LABEL_NETPLAY_STATELESS_MODE:
|
||||||
snprintf(s, len,
|
snprintf(s, len,
|
||||||
"Whether to run netplay in a mode not requiring\n"
|
"Whether to run netplay in a mode not requiring\n"
|
||||||
|
|
|
@ -961,6 +961,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NEAREST,
|
||||||
"Nearest")
|
"Nearest")
|
||||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY,
|
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY,
|
||||||
"Netplay")
|
"Netplay")
|
||||||
|
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_ALLOW_SLAVES,
|
||||||
|
"Allow Slave-Mode Clients")
|
||||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CHECK_FRAMES,
|
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CHECK_FRAMES,
|
||||||
"Netplay Check Frames")
|
"Netplay Check Frames")
|
||||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_MIN,
|
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")
|
"Server Password")
|
||||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_PUBLIC_ANNOUNCE,
|
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_PUBLIC_ANNOUNCE,
|
||||||
"Publicly Announce Netplay")
|
"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,
|
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SETTINGS,
|
||||||
"Netplay settings")
|
"Netplay settings")
|
||||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_START_AS_SPECTATOR,
|
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_START_AS_SPECTATOR,
|
||||||
|
@ -2638,6 +2642,14 @@ MSG_HASH(
|
||||||
MENU_ENUM_SUBLABEL_NETPLAY_START_AS_SPECTATOR,
|
MENU_ENUM_SUBLABEL_NETPLAY_START_AS_SPECTATOR,
|
||||||
"Whether to start netplay in spectator mode."
|
"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(
|
MSG_HASH(
|
||||||
MENU_ENUM_SUBLABEL_NETPLAY_STATELESS_MODE,
|
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."
|
"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_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_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_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_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_check_frames, MENU_ENUM_SUBLABEL_NETPLAY_CHECK_FRAMES)
|
||||||
default_sublabel_macro(action_bind_sublabel_netplay_nat_traversal, MENU_ENUM_SUBLABEL_NETPLAY_NAT_TRAVERSAL)
|
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:
|
case MENU_ENUM_LABEL_NETPLAY_START_AS_SPECTATOR:
|
||||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_netplay_start_as_spectator);
|
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_netplay_start_as_spectator);
|
||||||
break;
|
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:
|
case MENU_ENUM_LABEL_NETPLAY_STATELESS_MODE:
|
||||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_netplay_stateless_mode);
|
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_netplay_stateless_mode);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -4854,6 +4854,14 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data)
|
||||||
MENU_ENUM_LABEL_NETPLAY_START_AS_SPECTATOR,
|
MENU_ENUM_LABEL_NETPLAY_START_AS_SPECTATOR,
|
||||||
PARSE_ONLY_BOOL, false) != -1)
|
PARSE_ONLY_BOOL, false) != -1)
|
||||||
count++;
|
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,
|
if (menu_displaylist_parse_settings_enum(menu, info,
|
||||||
MENU_ENUM_LABEL_NETPLAY_STATELESS_MODE,
|
MENU_ENUM_LABEL_NETPLAY_STATELESS_MODE,
|
||||||
PARSE_ONLY_BOOL, false) != -1)
|
PARSE_ONLY_BOOL, false) != -1)
|
||||||
|
|
|
@ -5674,6 +5674,7 @@ static bool setting_append_list(
|
||||||
general_write_handler,
|
general_write_handler,
|
||||||
general_read_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_ALLOW_INPUT);
|
||||||
|
settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED);
|
||||||
|
|
||||||
CONFIG_UINT(
|
CONFIG_UINT(
|
||||||
list, list_info,
|
list, list_info,
|
||||||
|
@ -5688,6 +5689,7 @@ static bool setting_append_list(
|
||||||
general_read_handler);
|
general_read_handler);
|
||||||
menu_settings_list_current_add_range(list, list_info, 0, 65535, 1, true, true);
|
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_ALLOW_INPUT);
|
||||||
|
settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED);
|
||||||
|
|
||||||
CONFIG_STRING(
|
CONFIG_STRING(
|
||||||
list, list_info,
|
list, list_info,
|
||||||
|
@ -5732,6 +5734,38 @@ static bool setting_append_list(
|
||||||
general_read_handler,
|
general_read_handler,
|
||||||
SD_FLAG_NONE);
|
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(
|
CONFIG_BOOL(
|
||||||
list, list_info,
|
list, list_info,
|
||||||
&settings->netplay.stateless_mode,
|
&settings->netplay.stateless_mode,
|
||||||
|
@ -5746,6 +5780,7 @@ static bool setting_append_list(
|
||||||
general_write_handler,
|
general_write_handler,
|
||||||
general_read_handler,
|
general_read_handler,
|
||||||
SD_FLAG_NONE);
|
SD_FLAG_NONE);
|
||||||
|
settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED);
|
||||||
|
|
||||||
CONFIG_INT(
|
CONFIG_INT(
|
||||||
list, list_info,
|
list, list_info,
|
||||||
|
@ -5773,7 +5808,6 @@ static bool setting_append_list(
|
||||||
general_write_handler,
|
general_write_handler,
|
||||||
general_read_handler);
|
general_read_handler);
|
||||||
menu_settings_list_current_add_range(list, list_info, 0, 15, 1, true, true);
|
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(
|
CONFIG_INT(
|
||||||
list, list_info,
|
list, list_info,
|
||||||
|
@ -5787,14 +5821,13 @@ static bool setting_append_list(
|
||||||
general_write_handler,
|
general_write_handler,
|
||||||
general_read_handler);
|
general_read_handler);
|
||||||
menu_settings_list_current_add_range(list, list_info, 0, 15, 1, true, true);
|
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(
|
CONFIG_BOOL(
|
||||||
list, list_info,
|
list, list_info,
|
||||||
&settings->netplay.nat_traversal,
|
&settings->netplay.nat_traversal,
|
||||||
MENU_ENUM_LABEL_NETPLAY_NAT_TRAVERSAL,
|
MENU_ENUM_LABEL_NETPLAY_NAT_TRAVERSAL,
|
||||||
MENU_ENUM_LABEL_VALUE_NETPLAY_NAT_TRAVERSAL,
|
MENU_ENUM_LABEL_VALUE_NETPLAY_NAT_TRAVERSAL,
|
||||||
false,
|
true,
|
||||||
MENU_ENUM_LABEL_VALUE_OFF,
|
MENU_ENUM_LABEL_VALUE_OFF,
|
||||||
MENU_ENUM_LABEL_VALUE_ON,
|
MENU_ENUM_LABEL_VALUE_ON,
|
||||||
&group_info,
|
&group_info,
|
||||||
|
@ -5803,6 +5836,7 @@ static bool setting_append_list(
|
||||||
general_write_handler,
|
general_write_handler,
|
||||||
general_read_handler,
|
general_read_handler,
|
||||||
SD_FLAG_NONE);
|
SD_FLAG_NONE);
|
||||||
|
settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED);
|
||||||
|
|
||||||
CONFIG_BOOL(
|
CONFIG_BOOL(
|
||||||
list, list_info,
|
list, list_info,
|
||||||
|
@ -5818,6 +5852,7 @@ static bool setting_append_list(
|
||||||
general_write_handler,
|
general_write_handler,
|
||||||
general_read_handler,
|
general_read_handler,
|
||||||
SD_FLAG_NONE);
|
SD_FLAG_NONE);
|
||||||
|
settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED);
|
||||||
|
|
||||||
END_SUB_GROUP(list, list_info, parent_group);
|
END_SUB_GROUP(list, list_info, parent_group);
|
||||||
|
|
||||||
|
|
|
@ -1020,6 +1020,8 @@ enum msg_hash_enums
|
||||||
MENU_LABEL(NETPLAY_DELAY_FRAMES),
|
MENU_LABEL(NETPLAY_DELAY_FRAMES),
|
||||||
MENU_LABEL(NETPLAY_PUBLIC_ANNOUNCE),
|
MENU_LABEL(NETPLAY_PUBLIC_ANNOUNCE),
|
||||||
MENU_LABEL(NETPLAY_START_AS_SPECTATOR),
|
MENU_LABEL(NETPLAY_START_AS_SPECTATOR),
|
||||||
|
MENU_LABEL(NETPLAY_ALLOW_SLAVES),
|
||||||
|
MENU_LABEL(NETPLAY_REQUIRE_SLAVES),
|
||||||
MENU_LABEL(NETPLAY_STATELESS_MODE),
|
MENU_LABEL(NETPLAY_STATELESS_MODE),
|
||||||
MENU_LABEL(NETPLAY_CHECK_FRAMES),
|
MENU_LABEL(NETPLAY_CHECK_FRAMES),
|
||||||
MENU_LABEL(NETPLAY_INPUT_LATENCY_FRAMES_MIN),
|
MENU_LABEL(NETPLAY_INPUT_LATENCY_FRAMES_MIN),
|
||||||
|
|
|
@ -234,16 +234,22 @@ Description:
|
||||||
itself to be in spectator mode and send no further input.
|
itself to be in spectator mode and send no further input.
|
||||||
|
|
||||||
Command: PLAY
|
Command: PLAY
|
||||||
Payload: None
|
Payload:
|
||||||
|
{
|
||||||
|
reserved: 31 bits
|
||||||
|
as slave?: 1 bit
|
||||||
|
}
|
||||||
Description:
|
Description:
|
||||||
Request to enter player mode. The client must wait for a MODE command
|
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
|
Command: MODE
|
||||||
Payload:
|
Payload:
|
||||||
{
|
{
|
||||||
frame number: uint32
|
frame number: uint32
|
||||||
reserved: 14 bits
|
reserved: 13 bits
|
||||||
|
slave: 1 bit
|
||||||
playing: 1 bit
|
playing: 1 bit
|
||||||
you: 1 bit
|
you: 1 bit
|
||||||
player number: uint16
|
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 true;
|
||||||
return netplay_send_flush(sbuf, sockfd, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -220,6 +220,10 @@ static bool netplay_poll(void)
|
||||||
/* Simulate the input if we don't have real input */
|
/* Simulate the input if we don't have real input */
|
||||||
netplay_simulate_input(netplay_data, netplay_data->run_ptr, false);
|
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);
|
netplay_update_unread_ptr(netplay_data);
|
||||||
|
|
||||||
/* Figure out how many frames of input latency we should be using to hide
|
/* Figure out how many frames of input latency we should be using to hide
|
||||||
|
@ -299,6 +303,11 @@ static bool netplay_poll(void)
|
||||||
break;
|
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:
|
case NETPLAY_STALL_INPUT_LATENCY:
|
||||||
/* Just let it recalculate momentarily */
|
/* Just let it recalculate momentarily */
|
||||||
netplay_data->stall = NETPLAY_STALL_NONE;
|
netplay_data->stall = NETPLAY_STALL_NONE;
|
||||||
|
@ -331,7 +340,7 @@ static bool netplay_poll(void)
|
||||||
/* If we're not stalled, consider stalling */
|
/* If we're not stalled, consider stalling */
|
||||||
if (!netplay_data->stall)
|
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 &&
|
if (netplay_data->self_mode == NETPLAY_CONNECTION_PLAYING &&
|
||||||
netplay_data->connected_players &&
|
netplay_data->connected_players &&
|
||||||
netplay_data->run_frame_count + netplay_data->input_latency_frames > netplay_data->self_frame_count)
|
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 */
|
/* If we're stalling, consider disconnection */
|
||||||
|
@ -943,7 +962,8 @@ static void netplay_toggle_play_spectate(netplay_t *netplay)
|
||||||
char msg[512];
|
char msg[512];
|
||||||
const char *dmsg = NULL;
|
const char *dmsg = NULL;
|
||||||
payload[0] = htonl(netplay->self_frame_count);
|
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 */
|
/* Mark us as no longer playing */
|
||||||
payload[1] = htonl(netplay->self_player);
|
payload[1] = htonl(netplay->self_player);
|
||||||
|
@ -980,7 +1000,8 @@ static void netplay_toggle_play_spectate(netplay_t *netplay)
|
||||||
{
|
{
|
||||||
uint32_t cmd;
|
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 */
|
/* Switch to spectator mode immediately */
|
||||||
netplay->self_mode = NETPLAY_CONNECTION_SPECTATING;
|
netplay->self_mode = NETPLAY_CONNECTION_SPECTATING;
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
#include "netplay_private.h"
|
#include "netplay_private.h"
|
||||||
|
|
||||||
|
#include "../../configuration.h"
|
||||||
#include "../../runloop.h"
|
#include "../../runloop.h"
|
||||||
#include "../../tasks/tasks_internal.h"
|
#include "../../tasks/tasks_internal.h"
|
||||||
|
|
||||||
|
@ -129,9 +130,11 @@ void netplay_hangup(netplay_t *netplay, struct netplay_connection *connection)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Remove this player */
|
/* 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_players &= ~(1<<connection->player);
|
||||||
|
netplay->connected_slaves &= ~(1<<connection->player);
|
||||||
|
|
||||||
/* FIXME: Duplication */
|
/* FIXME: Duplication */
|
||||||
if (netplay->is_server)
|
if (netplay->is_server)
|
||||||
|
@ -240,11 +243,12 @@ bool netplay_send_cur_input(netplay_t *netplay,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send our own data */
|
/* 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,
|
if (!send_input_frame(netplay, connection, NULL,
|
||||||
netplay->self_frame_count,
|
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))
|
dframe->self_state))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -364,12 +368,18 @@ bool netplay_cmd_mode(netplay_t *netplay,
|
||||||
enum rarch_netplay_connection_mode mode)
|
enum rarch_netplay_connection_mode mode)
|
||||||
{
|
{
|
||||||
uint32_t cmd;
|
uint32_t cmd;
|
||||||
|
uint32_t payloadBuf, *payload = NULL;
|
||||||
switch (mode)
|
switch (mode)
|
||||||
{
|
{
|
||||||
case NETPLAY_CONNECTION_SPECTATING:
|
case NETPLAY_CONNECTION_SPECTATING:
|
||||||
cmd = NETPLAY_CMD_SPECTATE;
|
cmd = NETPLAY_CMD_SPECTATE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NETPLAY_CONNECTION_SLAVE:
|
||||||
|
payload = &payloadBuf;
|
||||||
|
payloadBuf = htonl(NETPLAY_CMD_PLAY_BIT_SLAVE);
|
||||||
|
/* Intentional fallthrough */
|
||||||
|
|
||||||
case NETPLAY_CONNECTION_PLAYING:
|
case NETPLAY_CONNECTION_PLAYING:
|
||||||
cmd = NETPLAY_CMD_PLAY;
|
cmd = NETPLAY_CMD_PLAY;
|
||||||
break;
|
break;
|
||||||
|
@ -377,7 +387,8 @@ bool netplay_cmd_mode(netplay_t *netplay,
|
||||||
default:
|
default:
|
||||||
return false;
|
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)
|
if (netplay->is_server)
|
||||||
{
|
{
|
||||||
/* Ignore the claimed player #, must be this client */
|
/* 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");
|
RARCH_ERR("Netplay input from non-participating player.\n");
|
||||||
return netplay_cmd_nak(netplay, connection);
|
return netplay_cmd_nak(netplay, connection);
|
||||||
|
@ -478,16 +490,20 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||||
return netplay_cmd_nak(netplay, connection);
|
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 */
|
if (buffer[0] < netplay->read_frame_count[player])
|
||||||
break;
|
{
|
||||||
}
|
/* We already had this, so ignore the new transmission */
|
||||||
else if (buffer[0] > netplay->read_frame_count[player])
|
break;
|
||||||
{
|
}
|
||||||
/* Out of order = out of luck */
|
else if (buffer[0] > netplay->read_frame_count[player])
|
||||||
RARCH_ERR("Netplay input out of order.\n");
|
{
|
||||||
return netplay_cmd_nak(netplay, connection);
|
/* Out of order = out of luck */
|
||||||
|
RARCH_ERR("Netplay input out of order.\n");
|
||||||
|
return netplay_cmd_nak(netplay, connection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The data's good! */
|
/* The data's good! */
|
||||||
|
@ -500,15 +516,22 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||||
memcpy(dframe->real_input_state[player], buffer + 2,
|
memcpy(dframe->real_input_state[player], buffer + 2,
|
||||||
WORDS_PER_INPUT*sizeof(uint32_t));
|
WORDS_PER_INPUT*sizeof(uint32_t));
|
||||||
dframe->have_real[player] = true;
|
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*/
|
netplay->read_ptr[player] = NEXT_PTR(netplay->read_ptr[player]);
|
||||||
if (dframe->frame <= netplay->self_frame_count)
|
netplay->read_frame_count[player]++;
|
||||||
send_input_frame(netplay, NULL, connection, buffer[0],
|
|
||||||
player, dframe->real_input_state[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 */
|
/* 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);
|
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 */
|
/* The frame we haven't received is their end frame */
|
||||||
payload[0] = htonl(netplay->read_frame_count[connection->player]);
|
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 */
|
/* Mark them as not playing anymore */
|
||||||
connection->mode = NETPLAY_CONNECTION_SPECTATING;
|
connection->mode = NETPLAY_CONNECTION_SPECTATING;
|
||||||
netplay->connected_players &= ~(1<<connection->player);
|
netplay->connected_players &= ~(1<<connection->player);
|
||||||
|
netplay->connected_slaves &= ~(1<<connection->player);
|
||||||
|
|
||||||
/* Tell everyone */
|
/* Tell everyone */
|
||||||
payload[1] = htonl(connection->player);
|
payload[1] = htonl(connection->player);
|
||||||
|
@ -639,6 +664,34 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||||
{
|
{
|
||||||
uint32_t payload[2];
|
uint32_t payload[2];
|
||||||
uint32_t player = 0;
|
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);
|
payload[0] = htonl(netplay->self_frame_count + 1);
|
||||||
|
|
||||||
if (!netplay->is_server)
|
if (!netplay->is_server)
|
||||||
|
@ -671,15 +724,21 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connection->mode != NETPLAY_CONNECTION_PLAYING)
|
if (connection->mode != NETPLAY_CONNECTION_PLAYING &&
|
||||||
|
connection->mode != NETPLAY_CONNECTION_SLAVE)
|
||||||
{
|
{
|
||||||
/* Mark them as playing */
|
/* Mark them as playing */
|
||||||
connection->mode = NETPLAY_CONNECTION_PLAYING;
|
connection->mode = slave ? NETPLAY_CONNECTION_SLAVE :
|
||||||
|
NETPLAY_CONNECTION_PLAYING;
|
||||||
connection->player = player;
|
connection->player = player;
|
||||||
netplay->connected_players |= 1<<player;
|
netplay->connected_players |= 1<<player;
|
||||||
|
if (slave)
|
||||||
|
netplay->connected_slaves |= 1<<player;
|
||||||
|
|
||||||
/* Tell everyone */
|
/* 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));
|
netplay_send_raw_cmd_all(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload));
|
||||||
|
|
||||||
/* Announce it */
|
/* Announce it */
|
||||||
|
@ -692,7 +751,10 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||||
|
|
||||||
/* Tell the player even if they were confused */
|
/* Tell the player even if they were confused */
|
||||||
payload[1] = htonl(NETPLAY_CMD_MODE_BIT_PLAYING |
|
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));
|
netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload));
|
||||||
|
|
||||||
/* And expect their data */
|
/* And expect their data */
|
||||||
|
@ -764,7 +826,21 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||||
return netplay_cmd_nak(netplay, connection);
|
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;
|
netplay->self_player = player;
|
||||||
|
|
||||||
/* Fix up current frame info */
|
/* Fix up current frame info */
|
||||||
|
@ -836,6 +912,9 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||||
return netplay_cmd_nak(netplay, connection);
|
return netplay_cmd_nak(netplay, connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Unmark ourself, in case we were in slave mode */
|
||||||
|
netplay->connected_players &= ~(1<<player);
|
||||||
|
|
||||||
/* Announce it */
|
/* Announce it */
|
||||||
strlcpy(msg, "You have left the game", sizeof(msg));
|
strlcpy(msg, "You have left the game", sizeof(msg));
|
||||||
RARCH_LOG("%s\n", msg);
|
RARCH_LOG("%s\n", msg);
|
||||||
|
@ -1049,7 +1128,8 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Only players may load states */
|
/* 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");
|
RARCH_ERR("Netplay state load from a spectator.\n");
|
||||||
return netplay_cmd_nak(netplay, connection);
|
return netplay_cmd_nak(netplay, connection);
|
||||||
|
@ -1212,7 +1292,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||||
}
|
}
|
||||||
nick[sizeof(nick)-1] = '\0';
|
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)
|
if (connection->mode != NETPLAY_CONNECTION_PLAYING)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1367,6 +1447,49 @@ int netplay_poll_net_input(netplay_t *netplay, bool block)
|
||||||
return 0;
|
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
|
* netplay_flip_port
|
||||||
*
|
*
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
|
|
||||||
#define MAX_SERVER_STALL_TIME_USEC (5*1000*1000)
|
#define MAX_SERVER_STALL_TIME_USEC (5*1000*1000)
|
||||||
#define MAX_CLIENT_STALL_TIME_USEC (10*1000*1000)
|
#define MAX_CLIENT_STALL_TIME_USEC (10*1000*1000)
|
||||||
|
#define CATCH_UP_CHECK_TIME_USEC (500*1000)
|
||||||
#define MAX_RETRIES 16
|
#define MAX_RETRIES 16
|
||||||
#define RETRY_MS 500
|
#define RETRY_MS 500
|
||||||
|
|
||||||
|
@ -176,6 +177,8 @@ enum netplay_cmd
|
||||||
|
|
||||||
#define NETPLAY_CMD_INPUT_BIT_SERVER (1U<<31)
|
#define NETPLAY_CMD_INPUT_BIT_SERVER (1U<<31)
|
||||||
#define NETPLAY_CMD_SYNC_BIT_PAUSED (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_PLAYING (1U<<17)
|
||||||
#define NETPLAY_CMD_MODE_BIT_YOU (1U<<16)
|
#define NETPLAY_CMD_MODE_BIT_YOU (1U<<16)
|
||||||
|
|
||||||
|
@ -206,15 +209,28 @@ enum rarch_netplay_connection_mode
|
||||||
/* Ready: */
|
/* Ready: */
|
||||||
NETPLAY_CONNECTION_CONNECTED, /* Modes above this are connected */
|
NETPLAY_CONNECTION_CONNECTED, /* Modes above this are connected */
|
||||||
NETPLAY_CONNECTION_SPECTATING, /* Spectator mode */
|
NETPLAY_CONNECTION_SPECTATING, /* Spectator mode */
|
||||||
|
NETPLAY_CONNECTION_SLAVE, /* Playing in slave mode */
|
||||||
NETPLAY_CONNECTION_PLAYING /* Normal ready state */
|
NETPLAY_CONNECTION_PLAYING /* Normal ready state */
|
||||||
};
|
};
|
||||||
|
|
||||||
enum rarch_netplay_stall_reason
|
enum rarch_netplay_stall_reason
|
||||||
{
|
{
|
||||||
NETPLAY_STALL_NONE = 0,
|
NETPLAY_STALL_NONE = 0,
|
||||||
|
|
||||||
|
/* We're so far ahead that we can't read more data without overflowing the
|
||||||
|
* buffer */
|
||||||
NETPLAY_STALL_RUNNING_FAST,
|
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,
|
NETPLAY_STALL_INPUT_LATENCY,
|
||||||
|
|
||||||
|
/* The server asked us to stall */
|
||||||
NETPLAY_STALL_SERVER_REQUESTED,
|
NETPLAY_STALL_SERVER_REQUESTED,
|
||||||
|
|
||||||
|
/* We have no connection and must have one to proceed */
|
||||||
NETPLAY_STALL_NO_CONNECTION
|
NETPLAY_STALL_NO_CONNECTION
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -331,10 +347,13 @@ struct netplay
|
||||||
size_t connections_size;
|
size_t connections_size;
|
||||||
struct netplay_connection one_connection; /* Client only */
|
struct netplay_connection one_connection; /* Client only */
|
||||||
|
|
||||||
/* Bitmap of players with controllers (whether local or remote) (low bit is
|
/* Bitmap of players with controllers (low bit is player 1) */
|
||||||
* player 1) */
|
|
||||||
uint32_t connected_players;
|
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 */
|
/* Maximum player number */
|
||||||
uint32_t player_max;
|
uint32_t player_max;
|
||||||
|
|
||||||
|
@ -461,6 +480,12 @@ struct netplay
|
||||||
/* Opposite of stalling, should we be catching up? */
|
/* Opposite of stalling, should we be catching up? */
|
||||||
bool catch_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 */
|
/* Frequency with which to check CRCs */
|
||||||
int check_frames;
|
int check_frames;
|
||||||
|
|
||||||
|
@ -758,6 +783,13 @@ bool netplay_cmd_stall(netplay_t *netplay,
|
||||||
*/
|
*/
|
||||||
int netplay_poll_net_input(netplay_t *netplay, bool block);
|
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
|
* netplay_flip_port
|
||||||
*
|
*
|
||||||
|
|
|
@ -55,6 +55,7 @@ void netplay_update_unread_ptr(netplay_t *netplay)
|
||||||
for (player = 0; player < MAX_USERS; player++)
|
for (player = 0; player < MAX_USERS; player++)
|
||||||
{
|
{
|
||||||
if (!(netplay->connected_players & (1<<player))) continue;
|
if (!(netplay->connected_players & (1<<player))) continue;
|
||||||
|
if ((netplay->connected_slaves & (1<<player))) continue;
|
||||||
if (netplay->read_frame_count[player] < new_unread_frame_count)
|
if (netplay->read_frame_count[player] < new_unread_frame_count)
|
||||||
{
|
{
|
||||||
new_unread_ptr = netplay->read_ptr[player];
|
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;
|
new_unread_frame_count = netplay->server_frame_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
netplay->unread_ptr = new_unread_ptr;
|
if (new_unread_frame_count != (uint32_t) -1)
|
||||||
netplay->unread_frame_count = new_unread_frame_count;
|
{
|
||||||
|
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)
|
if (netplay->catch_up)
|
||||||
{
|
{
|
||||||
/* Are we caught 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;
|
netplay->catch_up = false;
|
||||||
input_driver_unset_nonblock_state();
|
input_driver_unset_nonblock_state();
|
||||||
|
@ -556,17 +565,44 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
||||||
}
|
}
|
||||||
else if (!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? */
|
retro_time_t cur_time = cpu_features_get_time_usec();
|
||||||
netplay->catch_up = true;
|
uint32_t cur_behind = lo_frame_count - netplay->self_frame_count;
|
||||||
input_driver_set_nonblock_state();
|
|
||||||
driver_set_nonblock_state();
|
/* 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;
|
size_t i;
|
||||||
|
netplay->catch_up_time = 0;
|
||||||
|
|
||||||
/* We're falling behind some clients but not others, so request that
|
/* We're falling behind some clients but not others, so request that
|
||||||
* clients ahead of us stall */
|
* clients ahead of us stall */
|
||||||
|
@ -580,7 +616,7 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
||||||
player = connection->player;
|
player = connection->player;
|
||||||
|
|
||||||
/* Are they ahead? */
|
/* 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 */
|
/* Tell them to stall */
|
||||||
if (connection->stall_frame + NETPLAY_MAX_REQ_STALL_FREQUENCY <
|
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