diff --git a/config.def.h b/config.def.h index 6b01bc9e59..82a99c79b5 100644 --- a/config.def.h +++ b/config.def.h @@ -818,6 +818,9 @@ static const unsigned autosave_interval = 0; /* Publicly announce netplay */ static const bool netplay_public_announce = true; +/* Start netplay in spectator mode */ +static const bool netplay_start_as_spectator = false; + /* Netplay without savestates/rewind */ static const bool netplay_stateless_mode = false; diff --git a/configuration.c b/configuration.c index b40de26d25..1812c2bef1 100644 --- a/configuration.c +++ b/configuration.c @@ -729,6 +729,7 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings, SETTING_BOOL("menu_swap_ok_cancel_buttons", &settings->input.menu_swap_ok_cancel_buttons, true, menu_swap_ok_cancel_buttons, false); #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_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 diff --git a/configuration.h b/configuration.h index a8f1e20e64..3b6c51306f 100644 --- a/configuration.h +++ b/configuration.h @@ -406,6 +406,7 @@ typedef struct settings bool public_announce; char server[255]; unsigned port; + bool start_as_spectator; bool stateless_mode; int check_frames; unsigned input_latency_frames_min; diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 277d837046..8772de1888 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -617,6 +617,8 @@ MSG_HASH(MENU_ENUM_LABEL_NETPLAY_SPECTATE_PASSWORD, "netplay_spectate_password") MSG_HASH(MENU_ENUM_LABEL_NETPLAY_SPECTATOR_MODE_ENABLE, "netplay_spectator_mode_enable") +MSG_HASH(MENU_ENUM_LABEL_NETPLAY_START_AS_SPECTATOR, + "netplay_start_as_spectator") MSG_HASH(MENU_ENUM_LABEL_NETPLAY_STATELESS_MODE, "netplay_stateless_mode") MSG_HASH(MENU_ENUM_LABEL_NETPLAY_TCP_UDP_PORT, diff --git a/intl/msg_hash_us.c b/intl/msg_hash_us.c index dfd157ff38..6a7cdc4be1 100644 --- a/intl/msg_hash_us.c +++ b/intl/msg_hash_us.c @@ -1542,6 +1542,14 @@ int menu_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len) { "If set to false, clients must manually connect \n" "rather than using the public lobby."); break; + case MENU_ENUM_LABEL_NETPLAY_START_AS_SPECTATOR: + snprintf(s, len, + "Whether to start netplay in spectator mode. \n" + " \n" + "If set to true, netplay will be in spectator mode \n" + "on start. It's always possible to change mode \n" + "later."); + break; case MENU_ENUM_LABEL_NETPLAY_STATELESS_MODE: snprintf(s, len, "Whether to run netplay in a mode not requiring\n" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index d370efde10..7ca3391f13 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -993,6 +993,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_PUBLIC_ANNOUNCE, "Publicly Announce Netplay") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SETTINGS, "Netplay settings") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_START_AS_SPECTATOR, + "Netplay Spectator Mode") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_STATELESS_MODE, "Netplay Stateless Mode") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SPECTATE_PASSWORD, @@ -2632,6 +2634,10 @@ MSG_HASH( MENU_ENUM_SUBLABEL_NETPLAY_SPECTATE_PASSWORD, "The password for connecting to the netplay host with only spectator privileges. Used only in host mode." ) +MSG_HASH( + MENU_ENUM_SUBLABEL_NETPLAY_START_AS_SPECTATOR, + "Whether to start netplay in spectator mode." + ) 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." diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index e57e66043e..161ff63e57 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -189,6 +189,7 @@ default_sublabel_macro(action_bind_sublabel_netplay_ip_address, MENU_ default_sublabel_macro(action_bind_sublabel_netplay_tcp_udp_port, MENU_ENUM_SUBLABEL_NETPLAY_TCP_UDP_PORT) 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_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) @@ -746,6 +747,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_NETPLAY_CHECK_FRAMES: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_netplay_check_frames); break; + 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_STATELESS_MODE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_netplay_stateless_mode); break; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index a1a2b319fb..641c8847d5 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -4813,6 +4813,10 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) MENU_ENUM_LABEL_NETPLAY_SPECTATE_PASSWORD, PARSE_ONLY_STRING, false) != -1) count++; + if (menu_displaylist_parse_settings_enum(menu, info, + 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_STATELESS_MODE, PARSE_ONLY_BOOL, false) != -1) diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 6cba6258f5..ae82515b83 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -5661,6 +5661,21 @@ static bool setting_append_list( general_read_handler); settings_data_list_current_add_flags(list, list_info, SD_FLAG_ALLOW_INPUT); + CONFIG_BOOL( + list, list_info, + &settings->netplay.start_as_spectator, + MENU_ENUM_LABEL_NETPLAY_START_AS_SPECTATOR, + MENU_ENUM_LABEL_VALUE_NETPLAY_START_AS_SPECTATOR, + 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); + CONFIG_BOOL( list, list_info, &settings->netplay.stateless_mode, diff --git a/msg_hash.h b/msg_hash.h index fba5261515..52b421b503 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -1019,6 +1019,7 @@ enum msg_hash_enums MENU_LABEL(NETPLAY_CLIENT_SWAP_INPUT), MENU_LABEL(NETPLAY_DELAY_FRAMES), MENU_LABEL(NETPLAY_PUBLIC_ANNOUNCE), + MENU_LABEL(NETPLAY_START_AS_SPECTATOR), MENU_LABEL(NETPLAY_STATELESS_MODE), MENU_LABEL(NETPLAY_CHECK_FRAMES), MENU_LABEL(NETPLAY_INPUT_LATENCY_FRAMES_MIN), diff --git a/network/netplay/README b/network/netplay/README index 460dbc91cc..2f32dfd98f 100644 --- a/network/netplay/README +++ b/network/netplay/README @@ -319,7 +319,7 @@ Description: Command: RESET Payload: { - frame: uint32 + frame number: uint32 } Description: Indicate that the core was reset at the beginning of the given frame. diff --git a/network/netplay/netplay_frontend.c b/network/netplay/netplay_frontend.c index 0afbbcb353..e02332fed0 100644 --- a/network/netplay/netplay_frontend.c +++ b/network/netplay/netplay_frontend.c @@ -1095,7 +1095,11 @@ bool init_netplay(void *direct_host, const char *server, unsigned port) quirks); if (netplay_data) + { + if (netplay_data->is_server && !settings->netplay.start_as_spectator) + netplay_data->self_mode = NETPLAY_CONNECTION_PLAYING; return true; + } RARCH_WARN("%s\n", msg_hash_to_str(MSG_NETPLAY_FAILED)); diff --git a/network/netplay/netplay_handshake.c b/network/netplay/netplay_handshake.c index 4df482e0e9..6350770520 100644 --- a/network/netplay/netplay_handshake.c +++ b/network/netplay/netplay_handshake.c @@ -878,6 +878,7 @@ bool netplay_handshake_pre_sync(netplay_t *netplay, retro_ctx_controller_info_t pad; char new_nick[NETPLAY_NICK_LEN]; retro_ctx_memory_info_t mem_info; + settings_t *settings = config_get_ptr(); RECV(cmd, sizeof(cmd)) { @@ -1021,8 +1022,11 @@ bool netplay_handshake_pre_sync(netplay_t *netplay, netplay_handshake_ready(netplay, connection); netplay_recv_flush(&connection->recv_packet_buffer); - /* Ask to go to player mode */ - return netplay_cmd_mode(netplay, connection, NETPLAY_CONNECTION_PLAYING); + /* Ask to switch to playing mode if we should */ + if (!settings->netplay.start_as_spectator) + return netplay_cmd_mode(netplay, connection, NETPLAY_CONNECTION_PLAYING); + else + return true; } /** diff --git a/network/netplay/netplay_init.c b/network/netplay/netplay_init.c index e96d72178b..cf02a2d001 100644 --- a/network/netplay/netplay_init.c +++ b/network/netplay/netplay_init.c @@ -430,7 +430,7 @@ netplay_t *netplay_new(void *direct_host, const char *server, uint16_t port, netplay->crcs_valid = true; netplay->quirks = quirks; netplay->self_mode = netplay->is_server ? - NETPLAY_CONNECTION_PLAYING : + NETPLAY_CONNECTION_SPECTATING : NETPLAY_CONNECTION_NONE; if (netplay->is_server)