From 7f33a03423b8a962a9eb8ce2f87a93b546fa16e0 Mon Sep 17 00:00:00 2001 From: twinaphex Date: Fri, 5 Nov 2021 18:52:56 +0100 Subject: [PATCH] Revert "Revert "Backport netplay changes from forum member"" This reverts commit 38a6b9f0866e8f9c1985557352ca1a25613fc862. --- config.def.h | 5 + configuration.c | 4 + configuration.h | 2 + intl/msg_hash_es.h | 12 + intl/msg_hash_lbl.h | 8 + intl/msg_hash_pt_br.h | 12 + intl/msg_hash_us.h | 28 + menu/cbs/menu_cbs_sublabel.c | 8 + menu/menu_displaylist.c | 5 + menu/menu_setting.c | 33 ++ msg_hash.h | 6 + network/netplay/netplay_frontend.c | 918 ++++++++++++++++------------- ui/drivers/qt/qt_options.cpp | 2 + 13 files changed, 641 insertions(+), 402 deletions(-) diff --git a/config.def.h b/config.def.h index 6c5e6d410f..a94fb5bb88 100644 --- a/config.def.h +++ b/config.def.h @@ -1019,6 +1019,10 @@ static const bool audio_enable_menu_bgm = false; #define DEFAULT_NOTIFICATION_SHOW_REFRESH_RATE true #endif +#ifdef HAVE_NETWORKING +#define DEFAULT_NOTIFICATION_SHOW_NETPLAY_EXTRA false +#endif + /* Output samplerate. */ #ifdef GEKKO #define DEFAULT_OUTPUT_RATE 32000 @@ -1170,6 +1174,7 @@ static const bool netplay_use_mitm_server = false; #define DEFAULT_NETPLAY_MITM_SERVER "nyc" #ifdef HAVE_NETWORKING +static const unsigned netplay_max_connections = 3; static const unsigned netplay_share_digital = RARCH_NETPLAY_SHARE_DIGITAL_NO_PREFERENCE; static const unsigned netplay_share_analog = RARCH_NETPLAY_SHARE_ANALOG_NO_PREFERENCE; diff --git a/configuration.c b/configuration.c index f407649dcd..59eee6e35e 100644 --- a/configuration.c +++ b/configuration.c @@ -1702,6 +1702,9 @@ static struct config_bool_setting *populate_settings_bool( SETTING_BOOL("notification_show_screenshot", &settings->bools.notification_show_screenshot, true, DEFAULT_NOTIFICATION_SHOW_SCREENSHOT, false); #endif SETTING_BOOL("notification_show_refresh_rate", &settings->bools.notification_show_refresh_rate, true, DEFAULT_NOTIFICATION_SHOW_REFRESH_RATE, false); +#ifdef HAVE_NETWORKING + SETTING_BOOL("notification_show_netplay_extra", &settings->bools.notification_show_netplay_extra, true, DEFAULT_NOTIFICATION_SHOW_NETPLAY_EXTRA, false); +#endif SETTING_BOOL("menu_widget_scale_auto", &settings->bools.menu_widget_scale_auto, true, DEFAULT_MENU_WIDGET_SCALE_AUTO, false); SETTING_BOOL("audio_enable_menu", &settings->bools.audio_enable_menu, true, audio_enable_menu, false); SETTING_BOOL("audio_enable_menu_ok", &settings->bools.audio_enable_menu_ok, true, audio_enable_menu_ok, false); @@ -2200,6 +2203,7 @@ static struct config_uint_setting *populate_settings_uint( #endif #ifdef HAVE_NETWORKING SETTING_UINT("netplay_ip_port", &settings->uints.netplay_port, true, RARCH_DEFAULT_PORT, false); + SETTING_UINT("netplay_max_connections", &settings->uints.netplay_max_connections, true, netplay_max_connections, false); SETTING_OVERRIDE(RARCH_OVERRIDE_SETTING_NETPLAY_IP_PORT); SETTING_UINT("netplay_input_latency_frames_min",&settings->uints.netplay_input_latency_frames_min, true, 0, false); SETTING_UINT("netplay_input_latency_frames_range",&settings->uints.netplay_input_latency_frames_range, true, 0, false); diff --git a/configuration.h b/configuration.h index 621e0027c3..e59711d5d2 100644 --- a/configuration.h +++ b/configuration.h @@ -200,6 +200,7 @@ typedef struct settings unsigned input_max_users; unsigned netplay_port; + unsigned netplay_max_connections; unsigned netplay_input_latency_frames_min; unsigned netplay_input_latency_frames_range; unsigned netplay_share_digital; @@ -624,6 +625,7 @@ typedef struct settings bool notification_show_screenshot; #endif bool notification_show_refresh_rate; + bool notification_show_netplay_extra; bool menu_widget_scale_auto; bool menu_show_start_screen; bool menu_pause_libretro; diff --git a/intl/msg_hash_es.h b/intl/msg_hash_es.h index 0b30c0c61d..2bb9776a1f 100644 --- a/intl/msg_hash_es.h +++ b/intl/msg_hash_es.h @@ -3945,6 +3945,14 @@ MSG_HASH( MENU_ENUM_SUBLABEL_NOTIFICATION_SHOW_REFRESH_RATE, "Muestra un mensaje en pantalla al cambiar la frecuencia de actualización." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_NOTIFICATION_SHOW_NETPLAY_EXTRA, + "Notificaciones extras de netplay" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_NOTIFICATION_SHOW_NETPLAY_EXTRA, + "Muestra mensajes no esenciales de netplay en la pantalla." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_VIDEO_FONT_PATH, "Fuente de notificaciones" @@ -5142,6 +5150,10 @@ MSG_HASH( MENU_ENUM_SUBLABEL_NETPLAY_TCP_UDP_PORT, "Indica el puerto del servidor a conectar. Puede ser TCP o UDP." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_NETPLAY_MAX_CONNECTIONS, + "Max Conexiones Simultáneas" + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_NETPLAY_PASSWORD, "Contraseña del servidor" diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 5779d6a72a..ff6d1db8a9 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -1974,6 +1974,10 @@ MSG_HASH( MENU_ENUM_LABEL_NETPLAY_TCP_UDP_PORT, "netplay_tcp_udp_port" ) +MSG_HASH( + MENU_ENUM_LABEL_NETPLAY_MAX_CONNECTIONS, + "netplay_max_connections" + ) MSG_HASH( MENU_ENUM_LABEL_NETPLAY_LAN_SCAN_SETTINGS, "Search for and connect to netplay hosts on the local network." @@ -4768,6 +4772,10 @@ MSG_HASH( MENU_ENUM_LABEL_NOTIFICATION_SHOW_REFRESH_RATE, "notification_show_refresh_rate" ) +MSG_HASH( + MENU_ENUM_LABEL_NOTIFICATION_SHOW_NETPLAY_EXTRA, + "notification_show_netplay_extra" + ) MSG_HASH( MENU_ENUM_LABEL_VIDEO_SHADERS_ENABLE, "video_shader_enable" diff --git a/intl/msg_hash_pt_br.h b/intl/msg_hash_pt_br.h index e485de1273..40c957954d 100644 --- a/intl/msg_hash_pt_br.h +++ b/intl/msg_hash_pt_br.h @@ -3921,6 +3921,14 @@ MSG_HASH( MENU_ENUM_SUBLABEL_NOTIFICATION_SHOW_REFRESH_RATE, "Exibe uma mensagem na tela ao definir a taxa de atualização." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_NOTIFICATION_SHOW_NETPLAY_EXTRA, + "Notificações extras de netplay" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_NOTIFICATION_SHOW_NETPLAY_EXTRA, + "Exibe mensagens não essenciais de netplay na tela." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_VIDEO_FONT_PATH, "Fonte das notificações na tela" @@ -5114,6 +5122,10 @@ MSG_HASH( MENU_ENUM_SUBLABEL_NETPLAY_TCP_UDP_PORT, "Porta do endereço de IP do anfitrião. Pode ser uma porta TCP ou uma porta UDP." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_NETPLAY_MAX_CONNECTIONS, + "Max Conexões Simultâneas" + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_NETPLAY_PASSWORD, "Senha do servidor" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index e0904a468a..8d421ec032 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -4009,6 +4009,14 @@ MSG_HASH( MENU_ENUM_SUBLABEL_NOTIFICATION_SHOW_REFRESH_RATE, "Display an on-screen message when setting the refresh rate." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_NOTIFICATION_SHOW_NETPLAY_EXTRA, + "Extra Netplay Notifications" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_NOTIFICATION_SHOW_NETPLAY_EXTRA, + "Display non-essential netplay on-screen messages." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_VIDEO_FONT_PATH, "Notification Font" @@ -5206,6 +5214,10 @@ MSG_HASH( MENU_ENUM_SUBLABEL_NETPLAY_TCP_UDP_PORT, "The port of the host IP address. Can be either a TCP or UDP port." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_NETPLAY_MAX_CONNECTIONS, + "Max Simultaneous Connections" + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_NETPLAY_PASSWORD, "Server Password" @@ -12057,6 +12069,22 @@ MSG_HASH( MSG_FAILED_TO_SET_INITIAL_DISK, "Failed to set last used disc..." ) +MSG_HASH( + MSG_FAILED_TO_CONNECT_TO_CLIENT, + "Failed to connect to client" + ) +MSG_HASH( + MSG_FAILED_TO_CONNECT_TO_HOST, + "Failed to connect to host" + ) +MSG_HASH( + MSG_NETPLAY_HOST_FULL, + "Netplay host full" + ) +MSG_HASH( + MSG_FAILED_TO_RECEIVE_HEADER_FROM_HOST, + "Failed to receive header from host" + ) MSG_HASH( MSG_CHEEVOS_HARDCORE_MODE_DISABLED, "A save state was loaded. Achievements Hardcore Mode disabled for the current session." diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 111f533c1e..ae6babc26a 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -441,6 +441,9 @@ DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_notification_show_screenshot_duratio DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_notification_show_screenshot_flash, MENU_ENUM_SUBLABEL_NOTIFICATION_SHOW_SCREENSHOT_FLASH) #endif DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_notification_show_refresh_rate, MENU_ENUM_SUBLABEL_NOTIFICATION_SHOW_REFRESH_RATE) +#ifdef HAVE_NETWORKING +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_notification_show_netplay_extra, MENU_ENUM_SUBLABEL_NOTIFICATION_SHOW_NETPLAY_EXTRA) +#endif DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_window_width, MENU_ENUM_SUBLABEL_VIDEO_WINDOW_WIDTH) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_window_height, MENU_ENUM_SUBLABEL_VIDEO_WINDOW_HEIGHT) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_video_window_auto_width_max, MENU_ENUM_SUBLABEL_VIDEO_WINDOW_AUTO_WIDTH_MAX) @@ -3477,6 +3480,11 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_NOTIFICATION_SHOW_REFRESH_RATE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_notification_show_refresh_rate); break; +#ifdef HAVE_NETWORKING + case MENU_ENUM_LABEL_NOTIFICATION_SHOW_NETPLAY_EXTRA: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_notification_show_netplay_extra); + break; +#endif case MENU_ENUM_LABEL_RESTART_RETROARCH: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_restart_retroarch); break; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 4efb4893d5..bd94d0ce4b 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -7295,6 +7295,7 @@ unsigned menu_displaylist_build_list( {MENU_ENUM_LABEL_NETPLAY_MITM_SERVER, PARSE_ONLY_STRING, false}, {MENU_ENUM_LABEL_NETPLAY_IP_ADDRESS, PARSE_ONLY_STRING, true}, {MENU_ENUM_LABEL_NETPLAY_TCP_UDP_PORT, PARSE_ONLY_UINT, true}, + {MENU_ENUM_LABEL_NETPLAY_MAX_CONNECTIONS, PARSE_ONLY_UINT, true}, {MENU_ENUM_LABEL_NETPLAY_PASSWORD, PARSE_ONLY_STRING, true}, {MENU_ENUM_LABEL_NETPLAY_SPECTATE_PASSWORD, PARSE_ONLY_STRING, true}, {MENU_ENUM_LABEL_NETPLAY_START_AS_SPECTATOR, PARSE_ONLY_BOOL, true}, @@ -8629,6 +8630,9 @@ unsigned menu_displaylist_build_list( #endif #endif {MENU_ENUM_LABEL_NOTIFICATION_SHOW_REFRESH_RATE, PARSE_ONLY_BOOL, false }, +#ifdef HAVE_NETWORKING + {MENU_ENUM_LABEL_NOTIFICATION_SHOW_NETPLAY_EXTRA, PARSE_ONLY_BOOL, false }, +#endif }; for (i = 0; i < ARRAY_SIZE(build_list); i++) @@ -9870,6 +9874,7 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, file_list_t *list = info->list; menu_displaylist_build_info_selective_t build_list[] = { {MENU_ENUM_LABEL_NETPLAY_TCP_UDP_PORT, PARSE_ONLY_UINT, true}, + {MENU_ENUM_LABEL_NETPLAY_MAX_CONNECTIONS, PARSE_ONLY_UINT, true}, {MENU_ENUM_LABEL_NETPLAY_PUBLIC_ANNOUNCE, PARSE_ONLY_BOOL, true }, {MENU_ENUM_LABEL_NETPLAY_USE_MITM_SERVER, PARSE_ONLY_BOOL, true }, {MENU_ENUM_LABEL_NETPLAY_MITM_SERVER, PARSE_ONLY_STRING, false}, diff --git a/menu/menu_setting.c b/menu/menu_setting.c index b67fe31ad3..6432af54ac 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -13651,6 +13651,7 @@ static bool setting_append_list( (*list)[list_info->index - 1].offset_by = 1; menu_settings_list_current_add_range(list, list_info, 1, 65536, 1, true, true); + CONFIG_UINT( list, list_info, &settings->uints.video_stream_quality, @@ -14564,6 +14565,23 @@ static bool setting_append_list( general_read_handler, SD_FLAG_NONE); +#ifdef HAVE_NETWORKING + CONFIG_BOOL( + list, list_info, + &settings->bools.notification_show_netplay_extra, + MENU_ENUM_LABEL_NOTIFICATION_SHOW_NETPLAY_EXTRA, + MENU_ENUM_LABEL_VALUE_NOTIFICATION_SHOW_NETPLAY_EXTRA, + DEFAULT_NOTIFICATION_SHOW_NETPLAY_EXTRA, + 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); +#endif + END_SUB_GROUP(list, list_info, parent_group); END_GROUP(list, list_info, parent_group); break; @@ -19031,6 +19049,21 @@ static bool setting_append_list( 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, + &settings->uints.netplay_max_connections, + MENU_ENUM_LABEL_NETPLAY_MAX_CONNECTIONS, + MENU_ENUM_LABEL_VALUE_NETPLAY_MAX_CONNECTIONS, + netplay_max_connections, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + (*list)[list_info->index - 1].ui_type = ST_UI_TYPE_UINT_SPINBOX; + (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint; + menu_settings_list_current_add_range(list, list_info, 1, 31, 1, true, true); + CONFIG_STRING( list, list_info, settings->paths.netplay_password, diff --git a/msg_hash.h b/msg_hash.h index 34e4e45aa6..848a1e0d9e 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -175,6 +175,10 @@ enum msg_hash_enums MSG_SETTING_DISK_IN_TRAY, MSG_FAILED_TO_SET_DISK, MSG_FAILED_TO_SET_INITIAL_DISK, + MSG_FAILED_TO_CONNECT_TO_CLIENT, + MSG_FAILED_TO_CONNECT_TO_HOST, + MSG_NETPLAY_HOST_FULL, + MSG_FAILED_TO_RECEIVE_HEADER_FROM_HOST, MSG_NETPLAY_FAILED, MSG_UNKNOWN_NETPLAY_COMMAND_RECEIVED, MSG_CONNECTING_TO_NETPLAY_HOST, @@ -1897,6 +1901,7 @@ enum msg_hash_enums MENU_LABEL(NETPLAY_INPUT_LATENCY_FRAMES_RANGE), MENU_LABEL(NETPLAY_SPECTATOR_MODE_ENABLE), MENU_LABEL(NETPLAY_TCP_UDP_PORT), + MENU_LABEL(NETPLAY_MAX_CONNECTIONS), MENU_LABEL(NETPLAY_NAT_TRAVERSAL), MENU_LABEL(NETPLAY_REQUEST_DEVICE_I), MENU_ENUM_LABEL_NETPLAY_REQUEST_DEVICE_1, @@ -2860,6 +2865,7 @@ enum msg_hash_enums MENU_ENUM_LABEL_VALUE_NOTIFICATION_SHOW_SCREENSHOT_FLASH_FAST, MENU_LABEL(NOTIFICATION_SHOW_REFRESH_RATE), + MENU_LABEL(NOTIFICATION_SHOW_NETPLAY_EXTRA), MENU_LABEL(SELECT_FILE), MENU_LABEL(SELECT_FROM_PLAYLIST), diff --git a/network/netplay/netplay_frontend.c b/network/netplay/netplay_frontend.c index de7a458e01..15b5da29f2 100644 --- a/network/netplay/netplay_frontend.c +++ b/network/netplay/netplay_frontend.c @@ -89,6 +89,7 @@ else if (recvd < 0) #define NETPLAY_MAGIC 0x52414E50 /* RANP */ +#define FULL_MAGIC 0x46554C4C /* FULL */ #define POKE_MAGIC 0x504F4B45 /* POKE */ /* @@ -807,6 +808,34 @@ static uint32_t simple_rand_uint32(void) parts[2]); } +/* + * netplay_init_socket_buffer + * + * Initialize a new socket buffer. + */ +static bool netplay_init_socket_buffer( + struct socket_buffer *sbuf, size_t size) +{ + sbuf->data = (unsigned char*)malloc(size); + if (!sbuf->data) + return false; + sbuf->bufsz = size; + sbuf->start = sbuf->read = sbuf->end = 0; + return true; +} + +/** + * netplay_deinit_socket_buffer + * + * Free a socket buffer. + */ +static void netplay_deinit_socket_buffer(struct socket_buffer *sbuf) +{ + if (sbuf->data) + free(sbuf->data); +} + + /** * netplay_handshake_init_send * @@ -878,37 +907,6 @@ static void handshake_password(void *ignore, const char *line) } #endif -/** - * netplay_deinit_socket_buffer - * - * Free a socket buffer. - */ -static void netplay_deinit_socket_buffer(struct socket_buffer *sbuf) -{ - if (sbuf->data) - free(sbuf->data); -} - - -static bool netplay_poke(netplay_t *netplay, struct netplay_connection *connection, uint32_t netplay_magic) -{ - if (!netplay || !netplay->is_server) - return false; - if (!connection || !connection->active) - return false; - if (netplay_magic != POKE_MAGIC) - return false; - - socket_close(connection->fd); - - connection->active = false; - - netplay_deinit_socket_buffer(&connection->send_packet_buffer); - netplay_deinit_socket_buffer(&connection->recv_packet_buffer); - - return true; -} - /** * netplay_handshake_init * @@ -928,19 +926,43 @@ bool netplay_handshake_init(netplay_t *netplay, uint32_t compression = 0; struct compression_transcoder *ctrans = NULL; const char *dmsg = NULL; + settings_t *settings = config_get_ptr(); + bool extra_notifications = settings->bools.notification_show_netplay_extra; memset(header, 0, sizeof(header)); - RECV(header, sizeof(uint32_t)) + RECV(header, sizeof(header[0])) { - dmsg = msg_hash_to_str(MSG_FAILED_TO_RECEIVE_HEADER_FROM_CLIENT); + dmsg = msg_hash_to_str( + netplay->is_server + ? MSG_FAILED_TO_CONNECT_TO_CLIENT + : MSG_FAILED_TO_CONNECT_TO_HOST); goto error; } netplay_magic = ntohl(header[0]); - if (netplay_poke(netplay, connection, netplay_magic)) - return true; + if (netplay->is_server) + { + /* Poking the server for information? Just disconnect */ + if (netplay_magic == POKE_MAGIC) + { + socket_close(connection->fd); + connection->active = false; + netplay_deinit_socket_buffer(&connection->send_packet_buffer); + netplay_deinit_socket_buffer(&connection->recv_packet_buffer); + return true; + } + } + else + { + if (netplay_magic == FULL_MAGIC) + { + dmsg = msg_hash_to_str(MSG_NETPLAY_HOST_FULL); + goto error; + } + } + if (netplay_magic != NETPLAY_MAGIC) { @@ -948,9 +970,13 @@ bool netplay_handshake_init(netplay_t *netplay, goto error; } - RECV(header + 1, sizeof(header) - sizeof(uint32_t)) + RECV(header + 1, sizeof(header) - sizeof(header[0])) { - dmsg = msg_hash_to_str(MSG_FAILED_TO_RECEIVE_HEADER_FROM_CLIENT); + dmsg = msg_hash_to_str( + netplay->is_server + ? MSG_FAILED_TO_RECEIVE_HEADER_FROM_CLIENT + : MSG_FAILED_TO_RECEIVE_HEADER_FROM_HOST); + goto error; } @@ -961,14 +987,6 @@ bool netplay_handshake_init(netplay_t *netplay, goto error; } - if (ntohl(header[5]) != netplay_impl_magic()) - { - /* We allow the connection but warn that this could cause issues. */ - dmsg = msg_hash_to_str(MSG_NETPLAY_DIFFERENT_VERSIONS); - RARCH_WARN("%s\n", dmsg); - runloop_msg_queue_push(dmsg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); - } - /* We only care about platform magic if our core is quirky */ local_pmagic = netplay_platform_magic(); remote_pmagic = ntohl(header[1]); @@ -976,18 +994,28 @@ bool netplay_handshake_init(netplay_t *netplay, if ((netplay->quirks & NETPLAY_QUIRK_ENDIAN_DEPENDENT) && netplay_endian_mismatch(local_pmagic, remote_pmagic)) { - RARCH_ERR("Endianness mismatch with an endian-sensitive core.\n"); dmsg = msg_hash_to_str(MSG_NETPLAY_ENDIAN_DEPENDENT); goto error; } if ((netplay->quirks & NETPLAY_QUIRK_PLATFORM_DEPENDENT) && (local_pmagic != remote_pmagic)) { - RARCH_ERR("Platform mismatch with a platform-sensitive core.\n"); dmsg = msg_hash_to_str(MSG_NETPLAY_PLATFORM_DEPENDENT); goto error; } + if (ntohl(header[5]) != netplay_impl_magic()) + { + /* We allow the connection but warn that this could cause issues. */ + dmsg = msg_hash_to_str(MSG_NETPLAY_DIFFERENT_VERSIONS); + RARCH_WARN("%s\n", dmsg); + if (extra_notifications) + runloop_msg_queue_push(dmsg, 1, 180, false, NULL, + MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + dmsg = NULL; + } + + /* Check what compression is supported */ compression = ntohl(header[2]); compression &= NETPLAY_COMPRESSION_SUPPORTED; @@ -1020,15 +1048,11 @@ bool netplay_handshake_init(netplay_t *netplay, /* Allocate our compression stream */ if (!ctrans->compression_stream) - { ctrans->compression_stream = ctrans->compression_backend->stream_new(); + if (!ctrans->decompression_stream) ctrans->decompression_stream = ctrans->decompression_backend->stream_new(); - } if (!ctrans->compression_stream || !ctrans->decompression_stream) - { - RARCH_ERR("Failed to allocate compression transcoder!\n"); return false; - } /* If a password is demanded, ask for it */ if (!netplay->is_server && (connection->salt = ntohl(header[3]))) @@ -1070,7 +1094,12 @@ error: if (dmsg) { RARCH_ERR("%s\n", dmsg); - runloop_msg_queue_push(dmsg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + /* These notifications are useful to the client in figuring out what caused its premature disconnection, + but they are quite useless (annoying) to the server. + Let them be optional if server. */ + if (!netplay->is_server || extra_notifications) + runloop_msg_queue_push(dmsg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + } return false; } @@ -1079,6 +1108,8 @@ static void netplay_handshake_ready(netplay_t *netplay, struct netplay_connection *connection) { char msg[512]; + settings_t *settings = config_get_ptr(); + bool extra_notifications = settings->bools.notification_show_netplay_extra; msg[0] = '\0'; if (netplay->is_server) @@ -1104,7 +1135,13 @@ static void netplay_handshake_ready(netplay_t *netplay, } RARCH_LOG("%s\n", msg); - runloop_msg_queue_push(msg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + /* Useful notification to the client in figuring out if a connection was successfully made before an error, + but not as useful to the server. + Let it be optional if server. */ + if (!netplay->is_server || extra_notifications) + runloop_msg_queue_push(msg, 1, 180, false, NULL, + MESSAGE_QUEUE_ICON_DEFAULT, + MESSAGE_QUEUE_CATEGORY_INFO); /* Unstall if we were waiting for this */ if (netplay->stall == NETPLAY_STALL_NO_CONNECTION) @@ -1315,9 +1352,9 @@ static bool netplay_handshake_pre_nick(netplay_t *netplay, { struct nick_buf_s nick_buf; ssize_t recvd; - char msg[512]; - - msg[0] = '\0'; + const char *dmsg = NULL; + settings_t *settings = config_get_ptr(); + bool extra_notifications = settings->bools.notification_show_netplay_extra; RECV(&nick_buf, sizeof(nick_buf)) {} @@ -1326,15 +1363,18 @@ static bool netplay_handshake_pre_nick(netplay_t *netplay, ntohl(nick_buf.cmd[0]) != NETPLAY_CMD_NICK || ntohl(nick_buf.cmd[1]) != sizeof(nick_buf.nick)) { - if (netplay->is_server) - strlcpy(msg, msg_hash_to_str(MSG_FAILED_TO_GET_NICKNAME_FROM_CLIENT), - sizeof(msg)); - else - strlcpy(msg, - msg_hash_to_str(MSG_FAILED_TO_RECEIVE_NICKNAME_FROM_HOST), - sizeof(msg)); - RARCH_ERR("%s\n", msg); - runloop_msg_queue_push(msg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + dmsg = msg_hash_to_str( + netplay->is_server + ? MSG_FAILED_TO_GET_NICKNAME_FROM_CLIENT + : MSG_FAILED_TO_RECEIVE_NICKNAME_FROM_HOST); + RARCH_ERR("%s\n", dmsg); + /* Useful to the client in figuring out + what caused its premature disconnection, + but not as useful to the server. + Let it be optional if server. */ + if (!netplay->is_server || extra_notifications) + runloop_msg_queue_push(dmsg, 1, 180, false, NULL, + MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); return false; } @@ -1344,8 +1384,6 @@ static bool netplay_handshake_pre_nick(netplay_t *netplay, if (netplay->is_server) { - settings_t *settings = config_get_ptr(); - /* There's a password, so just put them in PRE_PASSWORD mode */ if ( settings->paths.netplay_password[0] || settings->paths.netplay_spectate_password[0]) @@ -1380,11 +1418,12 @@ static bool netplay_handshake_pre_password(netplay_t *netplay, char password[8+NETPLAY_PASS_LEN]; /* 8 for salt */ char hash[NETPLAY_PASS_HASH_LEN+1]; /* + NULL terminator */ ssize_t recvd; - char msg[512]; bool correct = false; settings_t *settings = config_get_ptr(); - msg[0] = '\0'; + /* Only the server should call this */ + if (!netplay->is_server) + return false; RECV(&password_buf, sizeof(password_buf)) {} @@ -1393,15 +1432,7 @@ static bool netplay_handshake_pre_password(netplay_t *netplay, ntohl(password_buf.cmd[0]) != NETPLAY_CMD_PASSWORD || ntohl(password_buf.cmd[1]) != sizeof(password_buf.password)) { - if (netplay->is_server) - strlcpy(msg, msg_hash_to_str(MSG_FAILED_TO_GET_NICKNAME_FROM_CLIENT), - sizeof(msg)); - else - strlcpy(msg, - msg_hash_to_str(MSG_FAILED_TO_RECEIVE_NICKNAME_FROM_HOST), - sizeof(msg)); - RARCH_ERR("%s\n", msg); - runloop_msg_queue_push(msg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + RARCH_ERR("Failed to receive netplay password.\n"); return false; } @@ -1435,7 +1466,10 @@ static bool netplay_handshake_pre_password(netplay_t *netplay, /* Just disconnect if it was wrong */ if (!correct) + { + RARCH_WARN("A client tried to connect with the wrong password.\n"); return false; + } /* Otherwise, exchange info */ if (!netplay_handshake_info(netplay, connection)) @@ -1462,8 +1496,20 @@ static bool netplay_handshake_pre_info(netplay_t *netplay, uint32_t content_crc = 0; const char *dmsg = NULL; struct retro_system_info *system = &runloop_state_get_ptr()->system.info; + settings_t *settings = config_get_ptr(); + bool extra_notifications = settings->bools.notification_show_netplay_extra; - RECV(&info_buf, sizeof(info_buf.cmd)) {} + RECV(&info_buf, sizeof(info_buf.cmd)) + { + if (!netplay->is_server) + { + dmsg = msg_hash_to_str(MSG_NETPLAY_INCORRECT_PASSWORD); + RARCH_ERR("%s\n", dmsg); + runloop_msg_queue_push(dmsg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + return false; + } + + } if (recvd < 0 || ntohl(info_buf.cmd[0]) != NETPLAY_CMD_INFO) @@ -1473,9 +1519,10 @@ static bool netplay_handshake_pre_info(netplay_t *netplay, } cmd_size = ntohl(info_buf.cmd[1]); - if (cmd_size != sizeof(info_buf) - 2*sizeof(uint32_t)) + if (cmd_size != sizeof(info_buf) - sizeof(info_buf.cmd)) { - /* Either the host doesn't have anything loaded, or this is just screwy */ + /* Either the host doesn't have anything loaded, + or this is just screwy */ if (cmd_size != 0) { /* Huh? */ @@ -1493,10 +1540,7 @@ static bool netplay_handshake_pre_info(netplay_t *netplay, } RECV(&info_buf.content_crc, cmd_size) - { - RARCH_ERR("Failed to receive netplay info payload.\n"); return false; - } /* Check the core info */ if (system) @@ -1507,8 +1551,15 @@ static bool netplay_handshake_pre_info(netplay_t *netplay, /* Wrong core! */ dmsg = msg_hash_to_str(MSG_NETPLAY_DIFFERENT_CORES); RARCH_ERR("%s\n", dmsg); - runloop_msg_queue_push(dmsg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); - /* FIXME: Should still send INFO, so the other side knows what's what */ + /* Useful to the client in figuring out + what caused its premature disconnection, + but not as useful to the server. + Let it be optional if server. */ + if (!netplay->is_server || extra_notifications) + runloop_msg_queue_push(dmsg, 1, 180, false, NULL, + MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + /* FIXME: Should still send INFO, so the + other side knows what's what */ return false; } if (strncmp(info_buf.core_version, @@ -1516,7 +1567,9 @@ static bool netplay_handshake_pre_info(netplay_t *netplay, { dmsg = msg_hash_to_str(MSG_NETPLAY_DIFFERENT_CORE_VERSIONS); RARCH_WARN("%s\n", dmsg); - runloop_msg_queue_push(dmsg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + if (extra_notifications) + runloop_msg_queue_push(dmsg, 1, 180, false, NULL, + MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); } } @@ -1529,7 +1582,10 @@ static bool netplay_handshake_pre_info(netplay_t *netplay, { dmsg = msg_hash_to_str(MSG_CONTENT_CRC32S_DIFFER); RARCH_WARN("%s\n", dmsg); - runloop_msg_queue_push(dmsg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + if (extra_notifications) + runloop_msg_queue_push(dmsg, 1, 180, false, NULL, + MESSAGE_QUEUE_ICON_DEFAULT, + MESSAGE_QUEUE_CATEGORY_INFO); } } @@ -1570,21 +1626,19 @@ static bool netplay_handshake_pre_sync(netplay_t *netplay, char new_nick[NETPLAY_NICK_LEN]; retro_ctx_memory_info_t mem_info; - RECV(cmd, sizeof(cmd)) - { - const char *msg = msg_hash_to_str(MSG_NETPLAY_INCORRECT_PASSWORD); - RARCH_ERR("%s\n", msg); - runloop_msg_queue_push(msg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + /* Only the client should call this */ + if (netplay->is_server) return false; - } + + RECV(cmd, sizeof(cmd)) {} /* Only expecting a sync command */ - if (ntohl(cmd[0]) != NETPLAY_CMD_SYNC || + if (recvd < 0 || + ntohl(cmd[0]) != NETPLAY_CMD_SYNC || ntohl(cmd[1]) < (2+2*MAX_INPUT_DEVICES)*sizeof(uint32_t) + (MAX_INPUT_DEVICES)*sizeof(uint8_t) + NETPLAY_NICK_LEN) { - RARCH_ERR("%s\n", - msg_hash_to_str(MSG_FAILED_TO_RECEIVE_SRAM_DATA_FROM_HOST)); + RARCH_ERR("Failed to receive netplay sync.\n"); return false; } @@ -2039,22 +2093,6 @@ static size_t buf_remaining(struct socket_buffer *sbuf) return sbuf->bufsz - buf_used(sbuf) - 1; } -/** - * netplay_init_socket_buffer - * - * Initialize a new socket buffer. - */ -static bool netplay_init_socket_buffer( - struct socket_buffer *sbuf, size_t size) -{ - sbuf->data = (unsigned char*)malloc(size); - if (!sbuf->data) - return false; - sbuf->bufsz = size; - sbuf->start = sbuf->read = sbuf->end = 0; - return true; -} - /** * netplay_resize_socket_buffer * @@ -2100,13 +2138,6 @@ static bool netplay_resize_socket_buffer( return true; } -#if 0 -static void netplay_clear_socket_buffer(struct socket_buffer *sbuf) -{ - sbuf->start = sbuf->read = sbuf->end = 0; -} -#endif - /** * netplay_send * @@ -2356,6 +2387,46 @@ void netplay_recv_flush(struct socket_buffer *sbuf) sbuf->start = sbuf->read; } +static bool netplay_full(netplay_t *netplay, int sockfd) +{ + size_t i; + settings_t *settings = config_get_ptr(); + unsigned max_connections = settings->uints.netplay_max_connections; + unsigned total = 0; + + if (max_connections) + { + for (i = 0; i < netplay->connections_size; i++) + if (netplay->connections[i].active) total++; + + if (total >= max_connections) + { + /* We send a header to let the client + know we are full */ + uint32_t header[6]; + + /* The only parameter that we need to set is + netplay magic; + we set the protocol version parameter + too for backwards compatibility */ + memset(header, 0, sizeof(header)); + header[0] = htonl(FULL_MAGIC); + header[4] = htonl(NETPLAY_PROTOCOL_VERSION); + + /* The kernel might close the socket before + sending our data. + This is fine; the header is just a warning + for the client. */ + socket_send_all_nonblocking(sockfd, header, + sizeof(header), false); + + return true; + } + } + + return false; +} + /** * netplay_cmd_crc * @@ -3020,10 +3091,7 @@ bool netplay_sync_pre_frame(netplay_t *netplay) (struct sockaddr*)&their_addr, &addr_size); if (new_fd < 0) - { - RARCH_ERR("%s\n", msg_hash_to_str(MSG_NETPLAY_FAILED)); goto process; - } /* Set the socket nonblocking */ if (!socket_nonblock(new_fd)) @@ -3035,15 +3103,8 @@ bool netplay_sync_pre_frame(netplay_t *netplay) #if defined(IPPROTO_TCP) && defined(TCP_NODELAY) { - int flag = 1; - if (setsockopt(new_fd, IPPROTO_TCP, TCP_NODELAY, -#ifdef _WIN32 - (const char*) -#else - (const void*) -#endif - &flag, - sizeof(int)) < 0) + int on = 1; + if (setsockopt(new_fd, IPPROTO_TCP, TCP_NODELAY, (const char*) &on, sizeof(on)) < 0) RARCH_WARN("Could not set netplay TCP socket to nodelay. Expect jitter.\n"); } #endif @@ -3054,6 +3115,13 @@ bool netplay_sync_pre_frame(netplay_t *netplay) RARCH_WARN("Cannot set Netplay port to close-on-exec. It may fail to reopen if the client disconnects.\n"); #endif + if (netplay_full(netplay, new_fd)) + { + /* Not accepting any more connections. */ + socket_close(new_fd); + goto process; + } + /* Allocate a connection */ for (connection_num = 0; connection_num < netplay->connections_size; connection_num++) if (!netplay->connections[connection_num].active && @@ -3506,20 +3574,33 @@ void netplay_hangup(netplay_t *netplay, char msg[512]; const char *dmsg; size_t i; + bool was_playing = false; + settings_t *settings = config_get_ptr(); +#if 0 + /* TODO/FIXME - doesn't exist yet */ + bool extra_notifications = settings->bools.notification_show_netplay_extra; +#else + bool extra_notifications = false; +#endif if (!netplay) return; if (!connection->active) return; - msg[0] = msg[sizeof(msg)-1] = '\0'; - dmsg = msg; + was_playing = + connection->mode == NETPLAY_CONNECTION_PLAYING + || connection->mode == NETPLAY_CONNECTION_SLAVE; /* Report this disconnection */ if (netplay->is_server) { if (connection->nick[0]) - snprintf(msg, sizeof(msg)-1, msg_hash_to_str(MSG_NETPLAY_SERVER_NAMED_HANGUP), connection->nick); + { + snprintf(msg, sizeof(msg), + msg_hash_to_str(MSG_NETPLAY_SERVER_NAMED_HANGUP), connection->nick); + dmsg = msg; + } else dmsg = msg_hash_to_str(MSG_NETPLAY_SERVER_HANGUP); } @@ -3536,8 +3617,13 @@ void netplay_hangup(netplay_t *netplay, #endif netplay->is_connected = false; } - RARCH_LOG("[netplay] %s\n", dmsg); - runloop_msg_queue_push(dmsg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + RARCH_LOG("%s\n", dmsg); + /* This notification is really only important to the server if the client was playing. + * Let it be optional if server and the client wasn't playing. */ + if (!netplay->is_server || was_playing || extra_notifications) + runloop_msg_queue_push(dmsg, 1, 180, false, NULL, + MESSAGE_QUEUE_ICON_DEFAULT, + MESSAGE_QUEUE_CATEGORY_INFO); socket_close(connection->fd); connection->active = false; @@ -3550,9 +3636,8 @@ void netplay_hangup(netplay_t *netplay, netplay->connected_players &= (1L<self_client_num); for (i = 0; i < MAX_CLIENTS; i++) { - if (i == netplay->self_client_num) - continue; - netplay->client_devices[i] = 0; + if (i != netplay->self_client_num) + netplay->client_devices[i] = 0; } for (i = 0; i < MAX_INPUT_DEVICES; i++) netplay->device_clients[i] &= (1L<self_client_num); @@ -3561,16 +3646,17 @@ void netplay_hangup(netplay_t *netplay, } else { - uint32_t client_num = (uint32_t)(connection - netplay->connections + 1); - /* Mark the player for removal */ - if (connection->mode == NETPLAY_CONNECTION_PLAYING || - connection->mode == NETPLAY_CONNECTION_SLAVE) + if (was_playing) { - /* This special mode keeps the connection object alive long enough to - * send the disconnection message at the correct time */ - connection->mode = NETPLAY_CONNECTION_DELAYED_DISCONNECT; - connection->delay_frame = netplay->read_frame_count[client_num]; + uint32_t client_num = (uint32_t) + (connection - netplay->connections + 1); + + /* This special mode keeps the connection object + alive long enough to send the disconnection + message at the correct time */ + connection->mode = NETPLAY_CONNECTION_DELAYED_DISCONNECT; + connection->delay_frame = netplay->read_frame_count[client_num]; /* Mark them as not playing anymore */ netplay->connected_players &= ~(1L< device_str) + pdevice_str -= 2; /* Then we make the final string */ if (nick) - snprintf(msg, sizeof(msg) - 1, + snprintf(msg, sizeof(msg), msg_hash_to_str( - MSG_NETPLAY_S_HAS_JOINED_WITH_INPUT_DEVICES_S), - NETPLAY_NICK_LEN, nick, sizeof(device_str), - device_str); - else - snprintf(msg, sizeof(msg) - 1, - msg_hash_to_str( - MSG_NETPLAY_YOU_HAVE_JOINED_WITH_INPUT_DEVICES_S), + MSG_NETPLAY_S_HAS_JOINED_WITH_INPUT_DEVICES_S), + NETPLAY_NICK_LEN, nick, sizeof(device_str), device_str); + else + snprintf(msg, sizeof(msg), + msg_hash_to_str( + MSG_NETPLAY_YOU_HAVE_JOINED_WITH_INPUT_DEVICES_S), + sizeof(device_str), + device_str); } + dmsg = msg; break; } default: /* wrong usage */ - break; + return; } - if (msg[0]) - { - RARCH_LOG("[netplay] %s\n", msg); - runloop_msg_queue_push(msg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); - } + RARCH_LOG("%s\n", dmsg); + runloop_msg_queue_push(dmsg, 1, 180, false, NULL, + MESSAGE_QUEUE_ICON_DEFAULT, + MESSAGE_QUEUE_CATEGORY_INFO); } /** @@ -4058,8 +4149,10 @@ static void announce_play_spectate(netplay_t *netplay, * * Handle a play or spectate request */ -static void handle_play_spectate(netplay_t *netplay, uint32_t client_num, - struct netplay_connection *connection, uint32_t cmd, uint32_t cmd_size, +static void handle_play_spectate(netplay_t *netplay, + uint32_t client_num, + struct netplay_connection *connection, + uint32_t cmd, uint32_t cmd_size, uint32_t *in_payload) { /* @@ -4376,12 +4469,18 @@ static bool netplay_get_cmd(netplay_t *netplay, client_num = (uint32_t)(connection - netplay->connections + 1); } - if (client_num > MAX_CLIENTS) + if (client_num >= MAX_CLIENTS) { RARCH_ERR("NETPLAY_CMD_INPUT received data for an unsupported client.\n"); return netplay_cmd_nak(netplay, connection); } + if (!(netplay->connected_players & (1<client_devices[client_num]; input_size = netplay_expected_input_size(netplay, devices); @@ -4392,12 +4491,6 @@ static bool netplay_get_cmd(netplay_t *netplay, return netplay_cmd_nak(netplay, connection); } - if (client_num >= MAX_CLIENTS || !(netplay->connected_players & (1<mode == NETPLAY_CONNECTION_PLAYING) { @@ -4408,7 +4501,7 @@ static bool netplay_get_cmd(netplay_t *netplay, for (; input_size; input_size--) { RECV(&buf, sizeof(uint32_t)) - return netplay_cmd_nak(netplay, connection); + return false; } break; } @@ -4493,12 +4586,16 @@ static bool netplay_get_cmd(netplay_t *netplay, return netplay_cmd_nak(netplay, connection); } - RECV(&frame, sizeof(frame)) + if (cmd_size != sizeof(frame)) { - RARCH_ERR("Failed to receive NETPLAY_CMD_NOINPUT payload.\n"); + RARCH_ERR("NETPLAY_CMD_NOINPUT received" + " an unexpected payload size.\n"); return netplay_cmd_nak(netplay, connection); } + RECV(&frame, sizeof(frame)) + return false; + frame = ntohl(frame); /* We already had this, so ignore the new transmission */ @@ -4536,10 +4633,11 @@ static bool netplay_get_cmd(netplay_t *netplay, return netplay_cmd_nak(netplay, connection); } - if (connection->mode != NETPLAY_CONNECTION_PLAYING && - connection->mode != NETPLAY_CONNECTION_SLAVE) + if ( connection->mode != NETPLAY_CONNECTION_PLAYING + && connection->mode != NETPLAY_CONNECTION_SLAVE) { /* They were confused */ + RARCH_ERR("NETPLAY_CMD_SPECTATE from client not currently playing.\n"); return netplay_cmd_nak(netplay, connection); } @@ -4552,18 +4650,7 @@ static bool netplay_get_cmd(netplay_t *netplay, case NETPLAY_CMD_PLAY: { uint32_t client_num; - uint32_t payload[1]; - - if (cmd_size != sizeof(uint32_t)) - { - RARCH_ERR("Incorrect NETPLAY_CMD_PLAY payload size.\n"); - return netplay_cmd_nak(netplay, connection); - } - RECV(payload, sizeof(uint32_t)) - { - RARCH_ERR("Failed to receive NETPLAY_CMD_PLAY payload.\n"); - return netplay_cmd_nak(netplay, connection); - } + uint32_t payload; if (!netplay->is_server) { @@ -4571,19 +4658,30 @@ static bool netplay_get_cmd(netplay_t *netplay, return netplay_cmd_nak(netplay, connection); } + if (cmd_size != sizeof(payload)) + { + RARCH_ERR("Incorrect NETPLAY_CMD_PLAY payload size.\n"); + return netplay_cmd_nak(netplay, connection); + } + + RECV(&payload, sizeof(payload)) + return false; + if (connection->delay_frame) { - /* Can't switch modes while a mode switch is already in progress. */ - payload[0] = htonl(NETPLAY_CMD_MODE_REFUSED_REASON_TOO_FAST); - netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE_REFUSED, payload, sizeof(uint32_t)); + /* Can't switch modes while a mode switch + is already in progress. */ + payload = htonl(NETPLAY_CMD_MODE_REFUSED_REASON_TOO_FAST); + netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE_REFUSED, &payload, sizeof(payload)); break; } if (!connection->can_play) { /* Not allowed to play */ - payload[0] = htonl(NETPLAY_CMD_MODE_REFUSED_REASON_UNPRIVILEGED); - netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE_REFUSED, payload, sizeof(uint32_t)); + payload = htonl(NETPLAY_CMD_MODE_REFUSED_REASON_UNPRIVILEGED); + netplay_send_raw_cmd(netplay, connection, + NETPLAY_CMD_MODE_REFUSED, &payload, sizeof(payload)); break; } @@ -4591,11 +4689,14 @@ static bool netplay_get_cmd(netplay_t *netplay, if ( connection->mode == NETPLAY_CONNECTION_PLAYING || connection->mode == NETPLAY_CONNECTION_SLAVE) + { + RARCH_ERR("NETPLAY_CMD_PLAY from client already playing.\n"); return netplay_cmd_nak(netplay, connection); + } client_num = (unsigned)(connection - netplay->connections + 1); - handle_play_spectate(netplay, client_num, connection, cmd, cmd_size, payload); + handle_play_spectate(netplay, client_num, connection, cmd, cmd_size, &payload); break; } @@ -4631,10 +4732,7 @@ static bool netplay_get_cmd(netplay_t *netplay, } RECV(payload, sizeof(payload)) - { - RARCH_ERR("NETPLAY_CMD_MODE failed to receive payload.\n"); - return netplay_cmd_nak(netplay, connection); - } + return false; frame = ntohl(payload[0]); @@ -4642,7 +4740,7 @@ static bool netplay_get_cmd(netplay_t *netplay, if (frame < netplay->self_frame_count) netplay->force_rewind = true; - mode = ntohl(payload[1]); + mode = ntohl(payload[1]); client_num = mode & 0xFFFF; if (client_num >= MAX_CLIENTS) { @@ -4849,17 +4947,15 @@ static bool netplay_get_cmd(netplay_t *netplay, return netplay_cmd_nak(netplay, connection); } - if (cmd_size != sizeof(uint32_t)) + if (cmd_size != sizeof(reason)) { RARCH_ERR("Received invalid payload size for NETPLAY_CMD_MODE_REFUSED.\n"); return netplay_cmd_nak(netplay, connection); } RECV(&reason, sizeof(reason)) - { - RARCH_ERR("Failed to receive NETPLAY_CMD_MODE_REFUSED payload.\n"); - return netplay_cmd_nak(netplay, connection); - } + return false; + reason = ntohl(reason); switch (reason) @@ -4880,13 +4976,11 @@ static bool netplay_get_cmd(netplay_t *netplay, dmsg = msg_hash_to_str(MSG_NETPLAY_CANNOT_PLAY); } - if (dmsg) - { - RARCH_LOG("[netplay] %s\n", dmsg); - runloop_msg_queue_push(dmsg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); - } - break; + RARCH_LOG("%s\n", dmsg); + runloop_msg_queue_push(dmsg, 1, 180, false, NULL, + MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); } + break; case NETPLAY_CMD_DISCONNECT: netplay_hangup(netplay, connection); @@ -4905,10 +4999,7 @@ static bool netplay_get_cmd(netplay_t *netplay, } RECV(buffer, sizeof(buffer)) - { - RARCH_ERR("NETPLAY_CMD_CRC failed to receive payload.\n"); - return netplay_cmd_nak(netplay, connection); - } + return false; buffer[0] = ntohl(buffer[0]); buffer[1] = ntohl(buffer[1]); @@ -5018,17 +5109,15 @@ static bool netplay_get_cmd(netplay_t *netplay, /* Check the payload size */ if ((cmd == NETPLAY_CMD_LOAD_SAVESTATE && (cmd_size < 2*sizeof(uint32_t) || cmd_size > netplay->zbuffer_size + 2*sizeof(uint32_t))) || - (cmd == NETPLAY_CMD_RESET && cmd_size != sizeof(uint32_t))) + (cmd == NETPLAY_CMD_RESET && cmd_size != sizeof(frame))) { RARCH_ERR("CMD_LOAD_SAVESTATE received an unexpected payload size.\n"); return netplay_cmd_nak(netplay, connection); } RECV(&frame, sizeof(frame)) - { - RARCH_ERR("CMD_LOAD_SAVESTATE failed to receive savestate frame.\n"); - return netplay_cmd_nak(netplay, connection); - } + return false; + frame = ntohl(frame); if (netplay->is_server) @@ -5058,10 +5147,8 @@ static bool netplay_get_cmd(netplay_t *netplay, if (cmd == NETPLAY_CMD_LOAD_SAVESTATE) { RECV(&isize, sizeof(isize)) - { - RARCH_ERR("CMD_LOAD_SAVESTATE failed to receive inflated size.\n"); - return netplay_cmd_nak(netplay, connection); - } + return false; + isize = ntohl(isize); if (isize != netplay->state_size) @@ -5071,10 +5158,7 @@ static bool netplay_get_cmd(netplay_t *netplay, } RECV(netplay->zbuffer, cmd_size - 2*sizeof(uint32_t)) - { - RARCH_ERR("CMD_LOAD_SAVESTATE failed to receive savestate.\n"); - return netplay_cmd_nak(netplay, connection); - } + return false; /* And decompress it */ switch (connection->compression_supported) @@ -5150,7 +5234,6 @@ static bool netplay_get_cmd(netplay_t *netplay, case NETPLAY_CMD_PAUSE: { char msg[512], nick[NETPLAY_NICK_LEN]; - msg[sizeof(msg)-1] = '\0'; /* Read in the paused nick */ if (cmd_size != sizeof(nick)) @@ -5158,11 +5241,10 @@ static bool netplay_get_cmd(netplay_t *netplay, RARCH_ERR("NETPLAY_CMD_PAUSE received invalid payload size.\n"); return netplay_cmd_nak(netplay, connection); } + RECV(nick, sizeof(nick)) - { - RARCH_ERR("Failed to receive paused nickname.\n"); - return netplay_cmd_nak(netplay, connection); - } + return false; + nick[sizeof(nick)-1] = '\0'; /* We outright ignore pausing from spectators and slaves */ @@ -5174,7 +5256,9 @@ static bool netplay_get_cmd(netplay_t *netplay, if (netplay->is_server) { /* Inform peers */ - snprintf(msg, sizeof(msg)-1, msg_hash_to_str(MSG_NETPLAY_PEER_PAUSED), connection->nick); + snprintf(msg, sizeof(msg), + msg_hash_to_str(MSG_NETPLAY_PEER_PAUSED), + connection->nick); netplay_send_raw_cmd_all(netplay, connection, NETPLAY_CMD_PAUSE, connection->nick, NETPLAY_NICK_LEN); @@ -5183,10 +5267,9 @@ static bool netplay_get_cmd(netplay_t *netplay, netplay_send_flush_all(netplay, connection); } else - { - snprintf(msg, sizeof(msg)-1, msg_hash_to_str(MSG_NETPLAY_PEER_PAUSED), nick); - } - RARCH_LOG("[netplay] %s\n", msg); + snprintf(msg, sizeof(msg), + msg_hash_to_str(MSG_NETPLAY_PEER_PAUSED), nick); + RARCH_LOG("%s\n", msg); runloop_msg_queue_push(msg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); break; } @@ -5199,21 +5282,6 @@ static bool netplay_get_cmd(netplay_t *netplay, { uint32_t frames; - if (cmd_size != sizeof(uint32_t)) - { - RARCH_ERR("NETPLAY_CMD_STALL with incorrect payload size.\n"); - return netplay_cmd_nak(netplay, connection); - } - - RECV(&frames, sizeof(frames)) - { - RARCH_ERR("Failed to receive NETPLAY_CMD_STALL payload.\n"); - return netplay_cmd_nak(netplay, connection); - } - frames = ntohl(frames); - if (frames > NETPLAY_MAX_REQ_STALL_TIME) - frames = NETPLAY_MAX_REQ_STALL_TIME; - if (netplay->is_server) { /* Only servers can request a stall! */ @@ -5221,6 +5289,20 @@ static bool netplay_get_cmd(netplay_t *netplay, return netplay_cmd_nak(netplay, connection); } + if (cmd_size != sizeof(frames)) + { + RARCH_ERR("NETPLAY_CMD_STALL with incorrect payload size.\n"); + return netplay_cmd_nak(netplay, connection); + } + + RECV(&frames, sizeof(frames)) + return false; + + frames = ntohl(frames); + + if (frames > NETPLAY_MAX_REQ_STALL_TIME) + frames = NETPLAY_MAX_REQ_STALL_TIME; + /* We can only stall for one reason at a time */ if (!netplay->stall) { @@ -5232,7 +5314,8 @@ static bool netplay_get_cmd(netplay_t *netplay, } default: - RARCH_ERR("%s.\n", msg_hash_to_str(MSG_UNKNOWN_NETPLAY_COMMAND_RECEIVED)); + RARCH_ERR("%s\n", + msg_hash_to_str(MSG_UNKNOWN_NETPLAY_COMMAND_RECEIVED)); return netplay_cmd_nak(netplay, connection); } @@ -5313,10 +5396,14 @@ int netplay_poll_net_input(netplay_t *netplay, bool block) if (socket_select(max_fd, &fds, NULL, NULL, &tv) < 0) return -1; - RARCH_LOG("[netplay] Network is stalling at frame %u, count %u of %d ...\n", - netplay->run_frame_count, netplay->timeout_cnt, MAX_RETRIES); + RARCH_LOG( + "Network is stalling at frame %u, count %u of %d ...\n", + netplay->run_frame_count, + netplay->timeout_cnt, + MAX_RETRIES); - if (netplay->timeout_cnt >= MAX_RETRIES && !netplay->remote_paused) + if ( netplay->timeout_cnt >= MAX_RETRIES + && !netplay->remote_paused) return -1; } } @@ -5402,40 +5489,51 @@ void netplay_handle_slaves(netplay_t *netplay) void netplay_announce_nat_traversal(netplay_t *netplay) { #ifndef HAVE_SOCKET_LEGACY - char msg[4200], host[PATH_MAX_LENGTH], port[6]; - + char msg[512], host[256], port[6]; + const char *dmsg = NULL; + if (netplay->nat_traversal_state.have_inet4) { - if (getnameinfo((const struct sockaddr *) &netplay->nat_traversal_state.ext_inet4_addr, - sizeof(struct sockaddr_in), - host, PATH_MAX_LENGTH, port, 6, NI_NUMERICHOST|NI_NUMERICSERV) != 0) - return; + if (!getnameinfo( + (const struct sockaddr *) + &netplay->nat_traversal_state.ext_inet4_addr, + sizeof(netplay->nat_traversal_state.ext_inet4_addr), + host, sizeof(host), port, sizeof(port), + NI_NUMERICHOST|NI_NUMERICSERV)) + { + snprintf(msg, sizeof(msg), "%s: %s:%s", + msg_hash_to_str(MSG_PUBLIC_ADDRESS), + host, port); + dmsg = msg; + } + } #ifdef HAVE_INET6 else if (netplay->nat_traversal_state.have_inet6) { - if (getnameinfo((const struct sockaddr *) &netplay->nat_traversal_state.ext_inet6_addr, - sizeof(struct sockaddr_in6), - host, PATH_MAX_LENGTH, port, 6, NI_NUMERICHOST|NI_NUMERICSERV) != 0) - return; - + if (!getnameinfo( + (const struct sockaddr *) + &netplay->nat_traversal_state.ext_inet6_addr, + sizeof(netplay->nat_traversal_state.ext_inet6_addr), + host, sizeof(host), port, sizeof(port), + NI_NUMERICHOST|NI_NUMERICSERV)) + { + snprintf(msg, sizeof(msg), "%s: %s|%s", + msg_hash_to_str(MSG_PUBLIC_ADDRESS), + host, port); + dmsg = msg; + } } #endif else - { - snprintf(msg, sizeof(msg), "%s\n", - msg_hash_to_str(MSG_UPNP_FAILED)); - runloop_msg_queue_push(msg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); - RARCH_LOG("[netplay] %s\n", msg); - return; - } + dmsg = msg_hash_to_str(MSG_UPNP_FAILED); - snprintf(msg, sizeof(msg), "%s: %s:%s\n", - msg_hash_to_str(MSG_PUBLIC_ADDRESS), - host, port); - runloop_msg_queue_push(msg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); - RARCH_LOG("[netplay] %s\n", msg); + if (dmsg) + { + RARCH_LOG("%s\n", dmsg); + runloop_msg_queue_push(dmsg, 1, 180, false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + } #endif } @@ -5455,26 +5553,20 @@ static int init_tcp_connection(const struct addrinfo *res, bool server, struct sockaddr *other_addr, socklen_t addr_size) { - bool ret = true; - int fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + char host[256], port[6]; + char msg[512]; + const char *dmsg = NULL; + int fd = socket(res->ai_family, + res->ai_socktype, res->ai_protocol); if (fd < 0) - { - ret = false; - goto end; - } + return -1; #if defined(IPPROTO_TCP) && defined(TCP_NODELAY) { - int flag = 1; + int on = 1; if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, -#ifdef _WIN32 - (const char*) -#else - (const void*) -#endif - &flag, - sizeof(int)) < 0) + (const char*) &on, sizeof(on)) < 0) RARCH_WARN("Could not set netplay TCP socket to nodelay. Expect jitter.\n"); } #endif @@ -5487,83 +5579,125 @@ static int init_tcp_connection(const struct addrinfo *res, if (server) { - if (socket_connect(fd, (void*)res, false) < 0) + if (!socket_connect(fd, (void*)res, false)) + return fd; + + if (!getnameinfo(res->ai_addr, res->ai_addrlen, + host, sizeof(host), port, sizeof(port), + NI_NUMERICHOST|NI_NUMERICSERV)) { - ret = false; - goto end; + snprintf(msg, sizeof(msg), + "Failed to connect to host %s on port %s.", + host, port); + dmsg = msg; } } else { #if defined(HAVE_INET6) && defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY) /* Make sure we accept connections on both IPv6 and IPv4 */ - int on = 0; if (res->ai_family == AF_INET6) { - if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&on, sizeof(on)) < 0) + int on = 0; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, + (const char*)&on, sizeof(on)) < 0) RARCH_WARN("Failed to listen on both IPv6 and IPv4\n"); } #endif - if ( !socket_bind(fd, (void*)res) || - listen(fd, 1024) < 0) + if (socket_bind(fd, (void*)res)) { - ret = false; - goto end; + if (!listen(fd, 1024)) + return fd; + } + else + { + if (!getnameinfo(res->ai_addr, res->ai_addrlen, + NULL, 0, port, sizeof(port), NI_NUMERICSERV)) + { + snprintf(msg, sizeof(msg), "Failed to bind port %s.", port); + dmsg = msg; + } } } -end: - if (!ret && fd >= 0) - { - socket_close(fd); - fd = -1; - } + socket_close(fd); - return fd; + if (dmsg) + RARCH_ERR("%s\n", dmsg); + + return -1; } static bool init_tcp_socket(netplay_t *netplay, void *direct_host, const char *server, uint16_t port) { - char port_buf[16]; - bool ret = false; - const struct addrinfo *tmp_info = NULL; - struct addrinfo *res = NULL; - struct addrinfo hints = {0}; - - port_buf[0] = '\0'; + struct addrinfo *res; + const struct addrinfo *tmp_info; + struct sockaddr_storage sad; + struct addrinfo hints = {0}; + int fd = -1; if (!direct_host) { #ifdef HAVE_INET6 - /* Default to hosting on IPv6 and IPv4 */ + char port_buf[6]; + snprintf(port_buf, sizeof(port_buf), "%hu", port); + if (!server) + { + hints.ai_flags = AI_PASSIVE; +#ifdef HAVE_INET6 + /* Default to hosting on IPv6 and IPv4 */ hints.ai_family = AF_INET6; +#else + hints.ai_family = AF_INET; +#endif + } #endif hints.ai_socktype = SOCK_STREAM; - if (!server) - hints.ai_flags = AI_PASSIVE; - snprintf(port_buf, sizeof(port_buf), "%hu", (unsigned short)port); - if (getaddrinfo_retro(server, port_buf, &hints, &res) != 0) + if (getaddrinfo_retro(server, port_buf, &hints, &res)) { -#ifdef HAVE_INET6 - try_wildcard: if (!server) { - /* Didn't work with IPv6, try wildcard */ - hints.ai_family = 0; - if (getaddrinfo_retro(server, port_buf, &hints, &res) != 0) +#ifdef HAVE_INET6 +try_ipv4: + /* Didn't work with IPv6, try IPv4 */ + hints.ai_family = AF_INET; + if (getaddrinfo_retro(server, port_buf, &hints, &res)) +#endif + { + RARCH_ERR("Failed to set a hosting address.\n"); + return false; + } } else -#endif - return false; + { + char msg[512]; + snprintf(msg, sizeof(msg), + "Failed to resolve host: %s\n", server); + RARCH_ERR(msg); + return false; + } } if (!res) return false; + /* If we're serving on IPv6, make sure we accept all connections, including + * IPv4 */ +#ifdef HAVE_INET6 + if (!server && res->ai_family == AF_INET6) + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) res->ai_addr; +#if defined(_MSC_VER) && _MSC_VER <= 1200 + IN6ADDR_SETANY(sin6); +#else + sin6->sin6_addr = in6addr_any; +#endif + } +#endif } else { @@ -5575,68 +5709,47 @@ static bool init_tcp_socket(netplay_t *netplay, void *direct_host, hints.ai_addrlen = host->addrlen; hints.ai_addr = &host->addr; res = &hints; - } - /* If we're serving on IPv6, make sure we accept all connections, including - * IPv4 */ -#ifdef HAVE_INET6 - if (!direct_host && !server && res->ai_family == AF_INET6) - { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) res->ai_addr; -#if defined(_MSC_VER) && _MSC_VER <= 1200 - IN6ADDR_SETANY(sin6); -#else - sin6->sin6_addr = in6addr_any; -#endif - } -#endif - /* If "localhost" is used, it is important to check every possible * address for IPv4/IPv6. */ tmp_info = res; - while (tmp_info) + do { - struct sockaddr_storage sad = {0}; - int fd = init_tcp_connection( - tmp_info, - direct_host || server, - (struct sockaddr*)&sad, - sizeof(sad)); + fd = init_tcp_connection( + tmp_info, direct_host || server, + (struct sockaddr *) + memset(&sad, 0, sizeof(sad)), sizeof(sad)); if (fd >= 0) - { - ret = true; - if (direct_host || server) - { - netplay->connections[0].active = true; - netplay->connections[0].fd = fd; - netplay->connections[0].addr = sad; - } - else - { - netplay->listen_fd = fd; - } break; - } + } while ((tmp_info = tmp_info->ai_next)); - tmp_info = tmp_info->ai_next; - } - - if (res && !direct_host) + if (!direct_host) freeaddrinfo_retro(res); + res = NULL; - if (!ret) + if (fd < 0) { #ifdef HAVE_INET6 - if (!direct_host && (hints.ai_family == AF_INET6)) - goto try_wildcard; + if (!direct_host && !server && hints.ai_family == AF_INET6) + goto try_ipv4; #endif RARCH_ERR("Failed to set up netplay sockets.\n"); + return false; } - return ret; + if (direct_host || server) + { + netplay->connections[0].active = true; + netplay->connections[0].fd = fd; + netplay->connections[0].addr = sad; + } + else + netplay->listen_fd = fd; + + return true; } static bool init_socket(netplay_t *netplay, void *direct_host, @@ -5982,15 +6095,16 @@ void netplay_free(netplay_t *netplay) free(netplay->zbuffer); if (netplay->compress_nil.compression_stream) - { - netplay->compress_nil.compression_backend->stream_free(netplay->compress_nil.compression_stream); - netplay->compress_nil.decompression_backend->stream_free(netplay->compress_nil.decompression_stream); - } + netplay->compress_nil.compression_backend->stream_free( + netplay->compress_nil.compression_stream); + if (netplay->compress_nil.decompression_stream) + netplay->compress_nil.decompression_backend->stream_free( + netplay->compress_nil.decompression_stream); + if (netplay->compress_zlib.compression_stream) - { netplay->compress_zlib.compression_backend->stream_free(netplay->compress_zlib.compression_stream); + if (netplay->compress_zlib.decompression_stream) netplay->compress_zlib.decompression_backend->stream_free(netplay->compress_zlib.decompression_stream); - } if (netplay->addr) freeaddrinfo_retro(netplay->addr); diff --git a/ui/drivers/qt/qt_options.cpp b/ui/drivers/qt/qt_options.cpp index 3f0a5da757..e6d17a28c8 100644 --- a/ui/drivers/qt/qt_options.cpp +++ b/ui/drivers/qt/qt_options.cpp @@ -416,6 +416,7 @@ QWidget *NetplayPage::widget() serverForm->add(MENU_ENUM_LABEL_NETPLAY_IP_ADDRESS); serverForm->add(MENU_ENUM_LABEL_NETPLAY_TCP_UDP_PORT); + serverForm->add(MENU_ENUM_LABEL_NETPLAY_MAX_CONNECTIONS); serverForm->add(MENU_ENUM_LABEL_NETPLAY_PASSWORD); serverForm->add(MENU_ENUM_LABEL_NETPLAY_SPECTATE_PASSWORD); serverForm->add(MENU_ENUM_LABEL_NETPLAY_NAT_TRAVERSAL); @@ -593,6 +594,7 @@ QWidget *NotificationsPage::widget() notificationsGroup->add(MENU_ENUM_LABEL_NOTIFICATION_SHOW_SCREENSHOT_DURATION); notificationsGroup->add(MENU_ENUM_LABEL_NOTIFICATION_SHOW_SCREENSHOT_FLASH); notificationsGroup->add(MENU_ENUM_LABEL_NOTIFICATION_SHOW_REFRESH_RATE); + notificationsGroup->add(MENU_ENUM_LABEL_NOTIFICATION_SHOW_NETPLAY_EXTRA); layout->addWidget(notificationsGroup);