diff --git a/Makefile.common b/Makefile.common index 25704171f1..ce065527c2 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1549,6 +1549,7 @@ ifeq ($(HAVE_NETWORKING), 1) network/netplay/netplay_handshake.o \ network/netplay/netplay_init.o \ network/netplay/netplay_io.o \ + network/netplay/netplay_keyboard.o \ network/netplay/netplay_sync.o \ network/netplay/netplay_discovery.o \ network/netplay/netplay_buf.o \ diff --git a/command.c b/command.c index 57bf6d8226..ec0fcfbf02 100644 --- a/command.c +++ b/command.c @@ -159,7 +159,6 @@ static const struct cmd_map map[] = { { "SCREENSHOT", RARCH_SCREENSHOT }, { "MUTE", RARCH_MUTE }, { "OSK", RARCH_OSK }, - { "NETPLAY_FLIP", RARCH_NETPLAY_FLIP }, { "NETPLAY_GAME_WATCH", RARCH_NETPLAY_GAME_WATCH }, { "SLOWMOTION", RARCH_SLOWMOTION }, { "VOLUME_UP", RARCH_VOLUME_UP }, @@ -2559,9 +2558,6 @@ TODO: Add a setting for these tweaks */ #endif } break; - case CMD_EVENT_NETPLAY_FLIP_PLAYERS: - netplay_driver_ctl(RARCH_NETPLAY_CTL_FLIP_PLAYERS, NULL); - break; case CMD_EVENT_NETPLAY_GAME_WATCH: netplay_driver_ctl(RARCH_NETPLAY_CTL_GAME_WATCH, NULL); break; @@ -2572,7 +2568,6 @@ TODO: Add a setting for these tweaks */ case CMD_EVENT_NETPLAY_INIT: case CMD_EVENT_NETPLAY_INIT_DIRECT: case CMD_EVENT_NETPLAY_INIT_DIRECT_DEFERRED: - case CMD_EVENT_NETPLAY_FLIP_PLAYERS: case CMD_EVENT_NETPLAY_GAME_WATCH: return false; #endif diff --git a/command.h b/command.h index dd5099f9bc..86efe07bb0 100644 --- a/command.h +++ b/command.h @@ -179,8 +179,6 @@ enum event_command CMD_EVENT_NETPLAY_INIT_DIRECT_DEFERRED, /* Deinitializes netplay system. */ CMD_EVENT_NETPLAY_DEINIT, - /* Flip netplay players. */ - CMD_EVENT_NETPLAY_FLIP_PLAYERS, /* Switch between netplay gaming and watching. */ CMD_EVENT_NETPLAY_GAME_WATCH, /* Initializes BSV movie. */ diff --git a/config.def.h b/config.def.h index d807ad9133..f2686f5463 100644 --- a/config.def.h +++ b/config.def.h @@ -27,6 +27,10 @@ #include "config.h" #endif +#ifdef HAVE_NETWORKING +#include "network/netplay/netplay.h" +#endif + #if defined(HW_RVL) #define MAX_GAMMA_SETTING 30 #elif defined(GEKKO) @@ -544,6 +548,10 @@ static const int netplay_check_frames = 600; static const bool netplay_use_mitm_server = false; +static const unsigned netplay_share_digital = RARCH_NETPLAY_SHARE_DIGITAL_NO_PREFERENCE; + +static const unsigned netplay_share_analog = RARCH_NETPLAY_SHARE_ANALOG_NO_PREFERENCE; + /* On save state load, block SRAM from being overwritten. * This could potentially lead to buggy games. */ static const bool block_sram_overwrite = false; diff --git a/config.def.keybinds.h b/config.def.keybinds.h index b7f0a32e86..6da85fd5bb 100644 --- a/config.def.keybinds.h +++ b/config.def.keybinds.h @@ -85,7 +85,6 @@ static const struct retro_keybind retro_keybinds_1[] = { { true, RARCH_SCREENSHOT, MENU_ENUM_LABEL_VALUE_INPUT_META_SCREENSHOT, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_MUTE, MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_OSK, MENU_ENUM_LABEL_VALUE_INPUT_META_OSK, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE }, - { true, RARCH_NETPLAY_FLIP, MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_FLIP, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_NETPLAY_GAME_WATCH, MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_GAME_WATCH, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_SLOWMOTION, MENU_ENUM_LABEL_VALUE_INPUT_META_SLOWMOTION, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_ENABLE_HOTKEY, MENU_ENUM_LABEL_VALUE_INPUT_META_ENABLE_HOTKEY, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE }, @@ -160,7 +159,6 @@ static const struct retro_keybind retro_keybinds_1[] = { { true, RARCH_SCREENSHOT, MENU_ENUM_LABEL_VALUE_INPUT_META_SCREENSHOT, RETROK_F8, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_MUTE, MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE, RETROK_F9, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_OSK, MENU_ENUM_LABEL_VALUE_INPUT_META_OSK, RETROK_F12, NO_BTN, NO_BTN, 0, AXIS_NONE }, - { true, RARCH_NETPLAY_FLIP, MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_FLIP, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_NETPLAY_GAME_WATCH, MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_GAME_WATCH, RETROK_i, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_SLOWMOTION, MENU_ENUM_LABEL_VALUE_INPUT_META_SLOWMOTION, RETROK_e, NO_BTN, NO_BTN, 0, AXIS_NONE }, { true, RARCH_ENABLE_HOTKEY, MENU_ENUM_LABEL_VALUE_INPUT_META_ENABLE_HOTKEY, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE }, diff --git a/configuration.c b/configuration.c index ae6c58d8ad..a7ede9d695 100644 --- a/configuration.c +++ b/configuration.c @@ -1158,7 +1158,9 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings, { struct config_bool_setting *tmp = (struct config_bool_setting*)malloc((*size + 1) * sizeof(struct config_bool_setting)); unsigned count = 0; + unsigned user; global_t *global = global_get_ptr(); + char cfg[64] = {0}; SETTING_BOOL("automatically_add_content_to_playlist", &settings->bools.automatically_add_content_to_playlist, true, automatically_add_content_to_playlist, false); SETTING_BOOL("ui_companion_start_on_boot", &settings->bools.ui_companion_start_on_boot, true, ui_companion_start_on_boot, false); @@ -1173,8 +1175,12 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings, SETTING_BOOL("netplay_allow_slaves", &settings->bools.netplay_allow_slaves, true, netplay_allow_slaves, false); SETTING_BOOL("netplay_require_slaves", &settings->bools.netplay_require_slaves, true, netplay_require_slaves, false); SETTING_BOOL("netplay_stateless_mode", &settings->bools.netplay_stateless_mode, true, netplay_stateless_mode, false); - SETTING_BOOL("netplay_client_swap_input", &settings->bools.netplay_swap_input, true, netplay_client_swap_input, false); SETTING_BOOL("netplay_use_mitm_server", &settings->bools.netplay_use_mitm_server, true, netplay_use_mitm_server, false); + for (user = 0; user < MAX_USERS; user++) + { + snprintf(cfg, sizeof(cfg)-1, "netplay_request_device_p%u", user + 1); + SETTING_BOOL(strdup(cfg), &settings->bools.netplay_request_devices[user], true, false, false); + } #endif SETTING_BOOL("input_descriptor_label_show", &settings->bools.input_descriptor_label_show, true, input_descriptor_label_show, false); SETTING_BOOL("input_descriptor_hide_unbound", &settings->bools.input_descriptor_hide_unbound, true, input_descriptor_hide_unbound, false); @@ -1468,6 +1474,8 @@ static struct config_uint_setting *populate_settings_uint(settings_t *settings, SETTING_UINT("netplay_ip_port", &settings->uints.netplay_port, true, RARCH_DEFAULT_PORT, false); 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); + SETTING_UINT("netplay_share_digital", &settings->uints.netplay_share_digital, true, netplay_share_digital, false); + SETTING_UINT("netplay_share_analog", &settings->uints.netplay_share_analog, true, netplay_share_analog, false); #endif #ifdef HAVE_LANGEXTRA SETTING_UINT("user_language", msg_hash_get_uint(MSG_HASH_USER_LANGUAGE), true, RETRO_LANGUAGE_ENGLISH, false); diff --git a/configuration.h b/configuration.h index c7697167cd..656ceb4758 100644 --- a/configuration.h +++ b/configuration.h @@ -174,9 +174,9 @@ typedef struct settings bool netplay_allow_slaves; bool netplay_require_slaves; bool netplay_stateless_mode; - bool netplay_swap_input; bool netplay_nat_traversal; bool netplay_use_mitm_server; + bool netplay_request_devices[MAX_USERS]; /* Network */ bool network_buildbot_auto_extract_archive; @@ -315,6 +315,8 @@ typedef struct settings unsigned netplay_port; unsigned netplay_input_latency_frames_min; unsigned netplay_input_latency_frames_range; + unsigned netplay_share_digital; + unsigned netplay_share_analog; unsigned bundle_assets_extract_version_current; unsigned bundle_assets_extract_last_version; unsigned content_history_size; diff --git a/griffin/griffin.c b/griffin/griffin.c index c0607d7a36..00006d6f1c 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -1057,6 +1057,7 @@ NETPLAY #include "../network/netplay/netplay_handshake.c" #include "../network/netplay/netplay_init.c" #include "../network/netplay/netplay_io.c" +#include "../network/netplay/netplay_keyboard.c" #include "../network/netplay/netplay_sync.c" #include "../network/netplay/netplay_discovery.c" #include "../network/netplay/netplay_buf.c" diff --git a/input/input_defines.h b/input/input_defines.h index 3255a85180..892c2d9f87 100644 --- a/input/input_defines.h +++ b/input/input_defines.h @@ -91,7 +91,6 @@ enum RARCH_SCREENSHOT, RARCH_MUTE, RARCH_OSK, - RARCH_NETPLAY_FLIP, RARCH_NETPLAY_GAME_WATCH, RARCH_SLOWMOTION, RARCH_ENABLE_HOTKEY, diff --git a/input/input_driver.c b/input/input_driver.c index 033933ad4a..e5e31b7fbe 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -317,7 +317,6 @@ const struct input_bind_map input_config_bind_map[RARCH_BIND_LIST_END_NULL] = { DECLARE_META_BIND(2, screenshot, RARCH_SCREENSHOT, MENU_ENUM_LABEL_VALUE_INPUT_META_SCREENSHOT), DECLARE_META_BIND(2, audio_mute, RARCH_MUTE, MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE), DECLARE_META_BIND(2, osk_toggle, RARCH_OSK, MENU_ENUM_LABEL_VALUE_INPUT_META_OSK), - DECLARE_META_BIND(2, netplay_flip_players_1_2, RARCH_NETPLAY_FLIP, MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_FLIP), DECLARE_META_BIND(2, netplay_game_watch, RARCH_NETPLAY_GAME_WATCH, MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_GAME_WATCH), DECLARE_META_BIND(2, slowmotion, RARCH_SLOWMOTION, MENU_ENUM_LABEL_VALUE_INPUT_META_SLOWMOTION), DECLARE_META_BIND(2, enable_hotkey, RARCH_ENABLE_HOTKEY, MENU_ENUM_LABEL_VALUE_INPUT_META_ENABLE_HOTKEY), diff --git a/intl/msg_hash_chs.c b/intl/msg_hash_chs.c index 1c1cd4e428..7831fbcc7d 100644 --- a/intl/msg_hash_chs.c +++ b/intl/msg_hash_chs.c @@ -92,10 +92,6 @@ int menu_hash_get_help_chs_enum(enum msg_hash_enums msg, char *s, size_t len) snprintf(s, len, "显示/隐藏屏显键盘。"); break; - case RARCH_NETPLAY_FLIP: - snprintf(s, len, - "Netplay flip users."); - break; case RARCH_SLOWMOTION: snprintf(s, len, "按住并以慢动作运行。"); @@ -1641,11 +1637,6 @@ int menu_hash_get_help_chs_enum(enum msg_hash_enums msg, char *s, size_t len) "The username of the person running RetroArch. \n" "This will be used for playing online games."); break; - case MENU_ENUM_LABEL_NETPLAY_CLIENT_SWAP_INPUT: - snprintf(s, len, - "When being client over netplay, use \n" - "keybinds for player 1."); - break; case MENU_ENUM_LABEL_NETPLAY_TCP_UDP_PORT: snprintf(s, len, "The port of the host IP address. \n" @@ -1748,10 +1739,6 @@ int menu_hash_get_help_chs_enum(enum msg_hash_enums msg, char *s, size_t len) snprintf(s, len, "Saves state."); break; - case MENU_ENUM_LABEL_NETPLAY_FLIP_PLAYERS: - snprintf(s, len, - "Netplay flip users."); - break; case MENU_ENUM_LABEL_CHEAT_INDEX_PLUS: snprintf(s, len, "Increment cheat index.\n"); diff --git a/intl/msg_hash_chs.h b/intl/msg_hash_chs.h index 15611c9c26..e6a0cdf061 100644 --- a/intl/msg_hash_chs.h +++ b/intl/msg_hash_chs.h @@ -34,10 +34,6 @@ MSG_HASH( MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN, "未提供参数也没有内建菜单,显示帮助..." ) -MSG_HASH( - MSG_NETPLAY_USERS_HAS_FLIPPED, - "联机游戏用户已被踢出" - ) MSG_HASH( MSG_SETTING_DISK_IN_TRAY, "Setting disk in tray" @@ -54,10 +50,6 @@ MSG_HASH( MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N, "你已作为玩家 %d 加入" ) -MSG_HASH( - MSG_NETPLAY_IMPLEMENTATIONS_DIFFER, - "实现有差异。确保正在使用的RetroArch和核心是同版本的。" - ) MSG_HASH( MSG_NETPLAY_ENDIAN_DEPENDENT, "This core does not support inter-architecture netplay between these systems" @@ -824,8 +816,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MOVIE_RECORD_TOGGLE, "视频录制开关") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE, "静音开关") -MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_FLIP, - "联机游戏踢出用户") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_GAME_WATCH, "联机游戏切换 游戏/围观 模式") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_OSK, @@ -1016,8 +1006,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY, "在线游戏") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CHECK_FRAMES, "在线游戏检查帧数") -MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CLIENT_SWAP_INPUT, - "在线玩家P2使用C1") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DELAY_FRAMES, "在线游戏延迟帧数") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DISCONNECT, @@ -2775,8 +2763,6 @@ MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVEFILES_ENABLE, MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVESTATES_ENABLE, "Sort save states in folders named after the core used." ) -MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_CLIENT_SWAP_INPUT, - "When being client over netplay, use keybinds for Player 1.") MSG_HASH(MENU_ENUM_SUBLABEL_CORE_UPDATER_BUILDBOT_URL, "URL to core updater directory on the Libretro buildbot.") MSG_HASH(MENU_ENUM_SUBLABEL_BUILDBOT_ASSETS_URL, diff --git a/intl/msg_hash_cht.c b/intl/msg_hash_cht.c index cac0a4a1c0..ba696a59a2 100644 --- a/intl/msg_hash_cht.c +++ b/intl/msg_hash_cht.c @@ -92,10 +92,6 @@ int menu_hash_get_help_cht_enum(enum msg_hash_enums msg, char *s, size_t len) snprintf(s, len, "顯示/隱藏營幕鍵盤。"); break; - case RARCH_NETPLAY_FLIP: - snprintf(s, len, - "Netplay flip users."); - break; case RARCH_SLOWMOTION: snprintf(s, len, "按住並以慢動作運行。"); @@ -1638,11 +1634,6 @@ int menu_hash_get_help_cht_enum(enum msg_hash_enums msg, char *s, size_t len) "The username of the person running RetroArch. \n" "This will be used for playing online games."); break; - case MENU_ENUM_LABEL_NETPLAY_CLIENT_SWAP_INPUT: - snprintf(s, len, - "When being client over netplay, use \n" - "keybinds for player 1."); - break; case MENU_ENUM_LABEL_NETPLAY_TCP_UDP_PORT: snprintf(s, len, "The port of the host IP address. \n" @@ -1745,10 +1736,6 @@ int menu_hash_get_help_cht_enum(enum msg_hash_enums msg, char *s, size_t len) snprintf(s, len, "儲存即時存檔."); break; - case MENU_ENUM_LABEL_NETPLAY_FLIP_PLAYERS: - snprintf(s, len, - "踢掉連線遊戲的使用者."); - break; case MENU_ENUM_LABEL_CHEAT_INDEX_PLUS: snprintf(s, len, "Increment cheat index.\n"); diff --git a/intl/msg_hash_cht.h b/intl/msg_hash_cht.h index 9b3fe87236..2644f450a8 100644 --- a/intl/msg_hash_cht.h +++ b/intl/msg_hash_cht.h @@ -34,10 +34,6 @@ MSG_HASH( MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN, "未提供參數也沒有內建選單,顯示幫助..." ) -MSG_HASH( - MSG_NETPLAY_USERS_HAS_FLIPPED, - "連線遊戲用戶已被踢出" - ) MSG_HASH( MSG_SETTING_DISK_IN_TRAY, "設定光碟機裡光碟" @@ -54,10 +50,6 @@ MSG_HASH( MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N, "你已作為玩家 %d 加入" ) -MSG_HASH( - MSG_NETPLAY_IMPLEMENTATIONS_DIFFER, - "執行時錯誤發生。請確保雙方的所使用的RetroArch跟核心是同版本的。" - ) MSG_HASH( MSG_NETPLAY_ENDIAN_DEPENDENT, "This core does not support inter-architecture netplay between these systems" @@ -824,8 +816,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MOVIE_RECORD_TOGGLE, "視訊錄製開關") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE, "靜音開關") -MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_FLIP, - "連線遊戲踢出用戶") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_GAME_WATCH, "連線遊戲切換 遊戲/圍觀 模式") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_OSK, @@ -1016,8 +1006,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY, "連線遊戲") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CHECK_FRAMES, "連線遊戲檢查幀數") -MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CLIENT_SWAP_INPUT, - "連線玩家P2使用C1") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DELAY_FRAMES, "連線遊戲延遲幀數") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DISCONNECT, @@ -2769,8 +2757,6 @@ MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVEFILES_ENABLE, MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVESTATES_ENABLE, "Sort save states in folders named after the core used." ) -MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_CLIENT_SWAP_INPUT, - "When being client over netplay, use keybinds for Player 1.") MSG_HASH(MENU_ENUM_SUBLABEL_CORE_UPDATER_BUILDBOT_URL, "URL to core updater directory on the Libretro buildbot.") MSG_HASH(MENU_ENUM_SUBLABEL_BUILDBOT_ASSETS_URL, diff --git a/intl/msg_hash_de.c b/intl/msg_hash_de.c index 7b2237c38f..ececc198a5 100644 --- a/intl/msg_hash_de.c +++ b/intl/msg_hash_de.c @@ -87,10 +87,6 @@ int menu_hash_get_help_de_enum(enum msg_hash_enums msg, char *s, size_t len) snprintf(s, len, "Bildschirmtastatur ein-/ausschalten."); break; - case RARCH_NETPLAY_FLIP: - snprintf(s, len, - "Netplay-Spieler tauschen."); - break; case RARCH_NETPLAY_GAME_WATCH: snprintf(s, len, "Im Netplay zwischen Spiel- und Beobachter-Modus wechseln."); @@ -1772,11 +1768,6 @@ int menu_hash_get_help_de_enum(enum msg_hash_enums msg, char *s, size_t len) "Der Benutzername der Person, die RetroArch verwendet. \n" "Wird in Online-Spielen verwendet."); break; - case MENU_ENUM_LABEL_NETPLAY_CLIENT_SWAP_INPUT: - snprintf(s, len, - "Verwendet Tastenbelegung für Spieler 1, \n" - "wenn du Teilnehmer an einem Netplay-Spiel bist."); - break; case MENU_ENUM_LABEL_NETPLAY_TCP_UDP_PORT: snprintf(s, len, "Der Port der Host-IP-Adresse. \n" @@ -1892,10 +1883,6 @@ int menu_hash_get_help_de_enum(enum msg_hash_enums msg, char *s, size_t len) snprintf(s, len, "Speichert Save-State."); break; - case MENU_ENUM_LABEL_NETPLAY_FLIP_PLAYERS: - snprintf(s, len, - "Netplay-Benutzer vertauschen."); - break; case MENU_ENUM_LABEL_CHEAT_INDEX_PLUS: snprintf(s, len, "Cheat-Index erhöhen.\n"); diff --git a/intl/msg_hash_de.h b/intl/msg_hash_de.h index fcef9ae95a..3140125ee4 100644 --- a/intl/msg_hash_de.h +++ b/intl/msg_hash_de.h @@ -34,10 +34,6 @@ MSG_HASH( MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN, "Es wurden keine Argumente übergeben, außerdem ist kein Menü vorhanden - zeige Hilfe an..." ) -MSG_HASH( - MSG_NETPLAY_USERS_HAS_FLIPPED, - "Netplay-Benutzer wurden vertauscht" - ) MSG_HASH( MSG_SETTING_DISK_IN_TRAY, "Lege Datenträger in Laufwerksschublade ein" @@ -54,10 +50,6 @@ MSG_HASH( MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N, "Du bist als Spieler %d beigetreten" ) -MSG_HASH( - MSG_NETPLAY_IMPLEMENTATIONS_DIFFER, - "Unterschiedliche Implementierungen. Stelle sicher, dass Du die gleiche Version von RetroArch und dem Core verwendest." - ) MSG_HASH( MSG_NETPLAY_ENDIAN_DEPENDENT, "Dieser Core unterstützt kein Netplay zwischen diesen Systemen" @@ -831,8 +823,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MOVIE_RECORD_TOGGLE, "Videoaufzeichnung starten/beenden") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE, "Audio stumm-/lautschalten") -MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_FLIP, - "Netplay-Benutzer vertauschen") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_GAME_WATCH, "Zwischen Spieler- und Beobachter-Modus wechseln") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_OSK, @@ -1033,8 +1023,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_MIN, "Eingabeverzögerung") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_RANGE, "Erlaubte Eingabeverzögerung") -MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CLIENT_SWAP_INPUT, - "Netplay-Spieler 2 verwendet Client 1") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DELAY_FRAMES, "Netplay-Verzögerung") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DISCONNECT, @@ -2855,8 +2843,6 @@ MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVEFILES_ENABLE, MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVESTATES_ENABLE, "Speichere Spielstände in Ordnern ab, die nach dem verwendeten Core benannt sind." ) -MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_CLIENT_SWAP_INPUT, - "Verwende die Tastenbelegung für Spieler 1, wenn Du ein Client im Netplay bist.") MSG_HASH(MENU_ENUM_SUBLABEL_CORE_UPDATER_BUILDBOT_URL, "URL zum Core-Verzeichnis auf dem libretro-Buildbot.") MSG_HASH(MENU_ENUM_SUBLABEL_BUILDBOT_ASSETS_URL, diff --git a/intl/msg_hash_eo.h b/intl/msg_hash_eo.h index 7c53ff8d87..4052dcfe41 100644 --- a/intl/msg_hash_eo.h +++ b/intl/msg_hash_eo.h @@ -30,10 +30,6 @@ MSG_HASH( MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN, "No arguments supplied and no menu builtin, displaying help..." ) -MSG_HASH( - MSG_NETPLAY_USERS_HAS_FLIPPED, - "Netplay users has flipped" - ) MSG_HASH( MSG_SETTING_DISK_IN_TRAY, "Setting disk in tray" @@ -738,8 +734,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MOVIE_RECORD_TOGGLE, "Movie record toggle") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE, "Audio mute toggle") -MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_FLIP, - "Netplay flip users") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_OSK, "On-screen keyboard toggle") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_OVERLAY_NEXT, @@ -928,8 +922,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY, "Netplay") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CHECK_FRAMES, "Netplay Check Frames") -MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CLIENT_SWAP_INPUT, - "Netplay P2 Uses C1") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DELAY_FRAMES, "Netplay Delay Frames") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DISCONNECT, @@ -2636,8 +2628,6 @@ MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVEFILES_ENABLE, MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVESTATES_ENABLE, "Sort save states in folders named after the core used." ) -MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_CLIENT_SWAP_INPUT, - "When being client over netplay, use keybinds for Player 1.") MSG_HASH(MENU_ENUM_SUBLABEL_CORE_UPDATER_BUILDBOT_URL, "URL to core updater directory on the Libretro buildbot.") MSG_HASH(MENU_ENUM_SUBLABEL_BUILDBOT_ASSETS_URL, diff --git a/intl/msg_hash_es.c b/intl/msg_hash_es.c index 30556c2f57..3d97751946 100644 --- a/intl/msg_hash_es.c +++ b/intl/msg_hash_es.c @@ -1084,10 +1084,6 @@ int menu_hash_get_help_es_enum(enum msg_hash_enums msg, char *s, size_t len) snprintf(s, len, "Guarda rápidamente la partida."); break; - case MENU_ENUM_LABEL_NETPLAY_FLIP_PLAYERS: - snprintf(s, len, - "Cambia los usuarios en red."); - break; case MENU_ENUM_LABEL_CHEAT_INDEX_PLUS: snprintf(s, len, "Aumenta el índice de trucos.\n"); diff --git a/intl/msg_hash_es.h b/intl/msg_hash_es.h index e2aef1f59a..49bd2f839e 100644 --- a/intl/msg_hash_es.h +++ b/intl/msg_hash_es.h @@ -34,10 +34,6 @@ MSG_HASH( MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN, "No se pasaron argumentos y no hay menú integrado, Mostrando ayuda.." ) -MSG_HASH( - MSG_NETPLAY_USERS_HAS_FLIPPED, - "Usuarios de juego en red se fueron" - ) MSG_HASH( MSG_SETTING_DISK_IN_TRAY, "Poniendo disco en bandeja" @@ -54,10 +50,6 @@ MSG_HASH( MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N, "Unido como jugador %d" ) -MSG_HASH( - MSG_NETPLAY_IMPLEMENTATIONS_DIFFER, - "Las implementaciones difieren. Asegurate de usar la misma versión de Retroarch y del núcleo" - ) MSG_HASH( MSG_NETPLAY_ENDIAN_DEPENDENT, "Este núcleo no soporta juego en red entre diferentes arquitecturas de sistemas" @@ -1182,10 +1174,6 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE, "Silenciar audio" ) -MSG_HASH( - MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_FLIP, - "Juego en red: intercambiar usuarios" - ) MSG_HASH( MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_GAME_WATCH, "Juego en red: cambiar modo juego/espectador" @@ -1586,10 +1574,6 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_RANGE, "Rango de latencia en frames" ) -MSG_HASH( - MENU_ENUM_LABEL_VALUE_NETPLAY_CLIENT_SWAP_INPUT, - "Juego en red: P2 Usa Control 1" - ) MSG_HASH( MENU_ENUM_LABEL_VALUE_NETPLAY_DELAY_FRAMES, "Juego en red: retrasar frames" @@ -4846,10 +4830,6 @@ MSG_HASH( MENU_ENUM_SUBLABEL_SORT_SAVESTATES_ENABLE, "Ordenar guardados rápidos en carpetas nombradas por núcleo" ) -MSG_HASH( - MENU_ENUM_SUBLABEL_NETPLAY_CLIENT_SWAP_INPUT, - "Al ser cliente de juego en red, usar controles de jugador 1" - ) MSG_HASH( MENU_ENUM_SUBLABEL_CORE_UPDATER_BUILDBOT_URL, "URL de la carpeta del actualizador de núcleos en el buildbot Libretro" diff --git a/intl/msg_hash_fr.h b/intl/msg_hash_fr.h index 2adb722d69..96ac3c3425 100644 --- a/intl/msg_hash_fr.h +++ b/intl/msg_hash_fr.h @@ -34,10 +34,6 @@ MSG_HASH( MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN, "Aucun paramètre fourni et pas de menu intégré, affichage de l'aide..." ) -MSG_HASH( - MSG_NETPLAY_USERS_HAS_FLIPPED, - "Utilisateurs réseau intervertis" - ) MSG_HASH( MSG_SETTING_DISK_IN_TRAY, "Disque inséré dans le lecteur" @@ -54,10 +50,6 @@ MSG_HASH( MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N, "Vous avez rejoint le jeu en tant que joueur %d" ) -MSG_HASH( - MSG_NETPLAY_IMPLEMENTATIONS_DIFFER, - "Les implémentations diffèrent. Assurez vous d'utiliser les mêmes versions de RetroArch et du cœur." - ) MSG_HASH( MSG_NETPLAY_ENDIAN_DEPENDENT, "Ce cœur ne supporte pas le netplay inter-architectures entre ces systèmes" @@ -825,8 +817,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MOVIE_RECORD_TOGGLE, "Activer l'enregistrement") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE, "Sourdine") -MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_FLIP, - "Intervertir les joueurs en réseau") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_GAME_WATCH, "Changer de mode joueur/spectateur") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_OSK, @@ -1021,8 +1011,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_MIN, "Latence d'entrées minimale") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_RANGE, "Intervalle de latence d'entées") -MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CLIENT_SWAP_INPUT, - "Joueur #2 contrôle manette #1") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DELAY_FRAMES, "Netplay Delay Frames") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DISCONNECT, @@ -2807,8 +2795,6 @@ MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVEFILES_ENABLE, MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVESTATES_ENABLE, "Sort save states in folders named after the core used." ) -MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_CLIENT_SWAP_INPUT, - "When being client over netplay, use keybinds for Player 1.") MSG_HASH(MENU_ENUM_SUBLABEL_CORE_UPDATER_BUILDBOT_URL, "URL to core updater directory on the Libretro buildbot.") MSG_HASH(MENU_ENUM_SUBLABEL_BUILDBOT_ASSETS_URL, diff --git a/intl/msg_hash_it.c b/intl/msg_hash_it.c index 3c7855a1fe..d8dc11aa19 100644 --- a/intl/msg_hash_it.c +++ b/intl/msg_hash_it.c @@ -968,14 +968,6 @@ int menu_hash_get_help_it_enum(enum msg_hash_enums msg, char *s, size_t len) snprintf(s, len, "Saves state."); break; - case MENU_ENUM_LABEL_NETPLAY_FLIP_PLAYERS: - snprintf(s, len, - "Netplay flip users."); - break; - case MENU_ENUM_LABEL_CHEAT_INDEX_PLUS: - snprintf(s, len, - "Increment cheat index.\n"); - break; case MENU_ENUM_LABEL_CHEAT_INDEX_MINUS: snprintf(s, len, "Decrement cheat index.\n"); diff --git a/intl/msg_hash_it.h b/intl/msg_hash_it.h index 544312f035..0f25e1386f 100644 --- a/intl/msg_hash_it.h +++ b/intl/msg_hash_it.h @@ -34,10 +34,6 @@ MSG_HASH( MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN, "Nessun argomento fornito e nessun menu incorporato, visualizzazione della guida..." ) -MSG_HASH( - MSG_NETPLAY_USERS_HAS_FLIPPED, - "Gli utenti del Netplay hanno flippato" - ) MSG_HASH( MSG_SETTING_DISK_IN_TRAY, "impostazioni del disco nel cassetto" @@ -54,10 +50,6 @@ MSG_HASH( MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N, "Sei entrato come giocatore %d" ) -MSG_HASH( - MSG_NETPLAY_IMPLEMENTATIONS_DIFFER, - "Le implementazioni differiscono. Assicurati di utilizzare le stesse versioni di RetroArch e del core." - ) MSG_HASH( MSG_NETPLAY_ENDIAN_DEPENDENT, "Questo core non supporta la rete di inter-architettura tra questi sistemi" @@ -831,8 +823,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MOVIE_RECORD_TOGGLE, "Pulsante registratore Film") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE, "Pulsante per disattivare l'audio ") -MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_FLIP, - "Netplay flip users") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_GAME_WATCH, "Pulsante Netplay per la modalità giocatore/spettatore") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_OSK, @@ -1033,8 +1023,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_MIN, "Input della latenza del Frames") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_RANGE, "Input Latency Frames Range") -MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CLIENT_SWAP_INPUT, - "Netplay P2 Utilizza C1") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DELAY_FRAMES, "Netplay Delay Frames") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DISCONNECT, @@ -2853,8 +2841,6 @@ MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVEFILES_ENABLE, MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVESTATES_ENABLE, "Ordina i salva stati in cartelle dopo che un core viene utilizzato." ) -MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_CLIENT_SWAP_INPUT, - "Quando sei un client su netplay, utilizza i tasti di comando per il giocatore 1.") MSG_HASH(MENU_ENUM_SUBLABEL_CORE_UPDATER_BUILDBOT_URL, "URL alla directory di aggiornamento del core nel buildbot di Libretro.") MSG_HASH(MENU_ENUM_SUBLABEL_BUILDBOT_ASSETS_URL, diff --git a/intl/msg_hash_ja.c b/intl/msg_hash_ja.c index c9b6f552b7..fcef9422c0 100644 --- a/intl/msg_hash_ja.c +++ b/intl/msg_hash_ja.c @@ -93,10 +93,6 @@ int menu_hash_get_help_jp_enum(enum msg_hash_enums msg, char *s, size_t len) snprintf(s, len, "Toggles onscreen keyboard."); break; - case RARCH_NETPLAY_FLIP: - snprintf(s, len, - "Netplay flip users."); - break; case RARCH_SLOWMOTION: snprintf(s, len, "Hold for slowmotion."); @@ -1670,11 +1666,6 @@ int menu_hash_get_help_jp_enum(enum msg_hash_enums msg, char *s, size_t len) "The username of the person running RetroArch. \n" "This will be used for playing online games."); break; - case MENU_ENUM_LABEL_NETPLAY_CLIENT_SWAP_INPUT: - snprintf(s, len, - "When being client over netplay, use \n" - "keybinds for player 1."); - break; case MENU_ENUM_LABEL_NETPLAY_TCP_UDP_PORT: snprintf(s, len, "The port of the host IP address. \n" @@ -1777,10 +1768,6 @@ int menu_hash_get_help_jp_enum(enum msg_hash_enums msg, char *s, size_t len) snprintf(s, len, "Saves state."); break; - case MENU_ENUM_LABEL_NETPLAY_FLIP_PLAYERS: - snprintf(s, len, - "Netplay flip users."); - break; case MENU_ENUM_LABEL_CHEAT_INDEX_PLUS: snprintf(s, len, "Increment cheat index.\n"); diff --git a/intl/msg_hash_ja.h b/intl/msg_hash_ja.h index 1596afceb2..098b2dab58 100644 --- a/intl/msg_hash_ja.h +++ b/intl/msg_hash_ja.h @@ -39,10 +39,6 @@ MSG_HASH( MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN, "No arguments supplied and no menu builtin, displaying help..." ) -MSG_HASH( - MSG_NETPLAY_USERS_HAS_FLIPPED, - "Netplay users has flipped" - ) MSG_HASH( MSG_SETTING_DISK_IN_TRAY, "ディスクがトレイに入りました。" @@ -59,10 +55,6 @@ MSG_HASH( MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N, "「プレイヤー%d」で接続しました" ) -MSG_HASH( - MSG_NETPLAY_IMPLEMENTATIONS_DIFFER, - "実装が異なります。相手のRetroArchとコアのバージョンが同じに確認してください。" - ) MSG_HASH( MSG_NETPLAY_ENDIAN_DEPENDENT, "This core does not support inter-architecture netplay between these systems" @@ -861,8 +853,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MOVIE_RECORD_TOGGLE, "録画") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE, "消音") -MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_FLIP, - "ネットプレイのユーザ交換") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_GAME_WATCH, "ネットプレイの観覧を切り替え") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_OSK, @@ -1063,8 +1053,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_MIN, "入力遅延フレーム数") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_RANGE, "入力遅延フレーム範囲") -MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CLIENT_SWAP_INPUT, - "ネットプレイのP2がC1を使用") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DELAY_FRAMES, "ネットプレイの延期フレーム") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DISCONNECT, @@ -2841,8 +2829,6 @@ MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVEFILES_ENABLE, MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVESTATES_ENABLE, "Sort save states in folders named after the core used." ) -MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_CLIENT_SWAP_INPUT, - "When being client over netplay, use keybinds for Player 1.") MSG_HASH(MENU_ENUM_SUBLABEL_CORE_UPDATER_BUILDBOT_URL, "URL to core updater directory on the Libretro buildbot.") MSG_HASH(MENU_ENUM_SUBLABEL_BUILDBOT_ASSETS_URL, diff --git a/intl/msg_hash_ko.c b/intl/msg_hash_ko.c index 836052fad4..eda672f460 100644 --- a/intl/msg_hash_ko.c +++ b/intl/msg_hash_ko.c @@ -91,10 +91,6 @@ int menu_hash_get_help_ko_enum(enum msg_hash_enums msg, char *s, size_t len) { snprintf(s, len, "온스크린 키보드 전환."); break; - case RARCH_NETPLAY_FLIP: - snprintf(s, len, - "넷플레이 사용자 넘김."); - break; case RARCH_NETPLAY_GAME_WATCH: snprintf(s, len, "넷플레이 플레이/관전 모드 전환."); @@ -1729,11 +1725,6 @@ int menu_hash_get_help_ko_enum(enum msg_hash_enums msg, char *s, size_t len) { "The username of the person running RetroArch. \n" "This will be used for playing online games."); break; - case MENU_ENUM_LABEL_NETPLAY_CLIENT_SWAP_INPUT: - snprintf(s, len, - "When being client over netplay, use \n" - "keybinds for player 1."); - break; case MENU_ENUM_LABEL_NETPLAY_TCP_UDP_PORT: snprintf(s, len, "The port of the host IP address. \n" @@ -1849,10 +1840,6 @@ int menu_hash_get_help_ko_enum(enum msg_hash_enums msg, char *s, size_t len) { snprintf(s, len, "Saves state."); break; - case MENU_ENUM_LABEL_NETPLAY_FLIP_PLAYERS: - snprintf(s, len, - "Netplay flip users."); - break; case MENU_ENUM_LABEL_NETPLAY_GAME_WATCH: snprintf(s, len, "Netplay toggle play/spectate mode."); diff --git a/intl/msg_hash_ko.h b/intl/msg_hash_ko.h index 669e5042a3..26db263322 100644 --- a/intl/msg_hash_ko.h +++ b/intl/msg_hash_ko.h @@ -34,10 +34,6 @@ MSG_HASH( MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN, "제공되는 인수 및 메뉴, 도움말이 없습니다..." ) -MSG_HASH( - MSG_NETPLAY_USERS_HAS_FLIPPED, - "넷플레이 사용자가 변경되었습니다" - ) MSG_HASH( MSG_SETTING_DISK_IN_TRAY, "디스크 트레이 설정" @@ -54,10 +50,6 @@ MSG_HASH( MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N, "Y%d 플레이어로 입장했습니다" ) -MSG_HASH( - MSG_NETPLAY_IMPLEMENTATIONS_DIFFER, - "구현 방법에 차이가 있습니다. 동일한 버전의 RetroArch와 코어인지 확인하십시오." - ) MSG_HASH( MSG_NETPLAY_ENDIAN_DEPENDENT, "현재 코어는 해당 시스템 사이에서 구조 간 넷플레이를 지원하지 않습니다" @@ -811,8 +803,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MOVIE_RECORD_TOGGLE, "동영상 기록 시작/중지") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE, "음소거 켜기/끄기") -MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_FLIP, - "넷플레이 사용자 전환") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_GAME_WATCH, "넷플레이 플레이/관전 전환") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_OSK, @@ -1007,8 +997,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_MIN, "입력 대기 프레임") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_RANGE, "입력 대기 프레임 범위") -MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CLIENT_SWAP_INPUT, - "넷플레이 P2 사용 C1") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DELAY_FRAMES, "넷플레이 지연 프레임") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DISCONNECT, @@ -2771,8 +2759,6 @@ MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVEFILES_ENABLE, MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVESTATES_ENABLE, "코어 이름 폴더별로 저장된 상태저장파일 정렬." ) -MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_CLIENT_SWAP_INPUT, - "클라이어트로 넷플레이 접속시 사용자 1 키설정 사용.") MSG_HASH(MENU_ENUM_SUBLABEL_CORE_UPDATER_BUILDBOT_URL, "Libretro 빌드봇 상의 코어 업데이터 디렉토리 URL.") MSG_HASH(MENU_ENUM_SUBLABEL_BUILDBOT_ASSETS_URL, diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index 27124e3489..fe05843ca6 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -625,8 +625,12 @@ MSG_HASH(MENU_ENUM_LABEL_NETPLAY, "netplay") MSG_HASH(MENU_ENUM_LABEL_NETPLAY_CHECK_FRAMES, "netplay_check_frames") -MSG_HASH(MENU_ENUM_LABEL_NETPLAY_CLIENT_SWAP_INPUT, - "netplay_client_swap_input") +MSG_HASH(MENU_ENUM_LABEL_NETPLAY_REQUEST_DEVICE_I, + "netplay_request_device_%u") +MSG_HASH(MENU_ENUM_LABEL_NETPLAY_SHARE_ANALOG, + "netplay_share_analog") +MSG_HASH(MENU_ENUM_LABEL_NETPLAY_SHARE_DIGITAL, + "netplay_share_digital") MSG_HASH(MENU_ENUM_LABEL_NETPLAY_INPUT_LATENCY_FRAMES_MIN, "netplay_input_latency_frames_min") MSG_HASH(MENU_ENUM_LABEL_NETPLAY_INPUT_LATENCY_FRAMES_RANGE, diff --git a/intl/msg_hash_nl.h b/intl/msg_hash_nl.h index 9a825cd3f1..d5729cb759 100644 --- a/intl/msg_hash_nl.h +++ b/intl/msg_hash_nl.h @@ -30,10 +30,6 @@ MSG_HASH( MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN, "No arguments supplied and no menu builtin, displaying help..." ) -MSG_HASH( - MSG_NETPLAY_USERS_HAS_FLIPPED, - "Netplay users has flipped" - ) MSG_HASH( MSG_SETTING_DISK_IN_TRAY, "Setting disk in tray" @@ -738,8 +734,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MOVIE_RECORD_TOGGLE, "Movie record toggle") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE, "Audio mute toggle") -MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_FLIP, - "Netplay flip users") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_OSK, "On-screen keyboard toggle") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_OVERLAY_NEXT, @@ -926,8 +920,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY, "Netplay") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CHECK_FRAMES, "Netplay Check Frames") -MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CLIENT_SWAP_INPUT, - "Netplay P2 Uses C1") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DELAY_FRAMES, "Netplay Vertraging Frames") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DISCONNECT, @@ -2640,8 +2632,6 @@ MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVEFILES_ENABLE, MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVESTATES_ENABLE, "Sort save states in folders named after the core used." ) -MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_CLIENT_SWAP_INPUT, - "When being client over netplay, use keybinds for Player 1.") MSG_HASH(MENU_ENUM_SUBLABEL_CORE_UPDATER_BUILDBOT_URL, "URL to core updater directory on the Libretro buildbot.") MSG_HASH(MENU_ENUM_SUBLABEL_BUILDBOT_ASSETS_URL, diff --git a/intl/msg_hash_pl.h b/intl/msg_hash_pl.h index 7342c499c3..8a64d8cc08 100644 --- a/intl/msg_hash_pl.h +++ b/intl/msg_hash_pl.h @@ -34,10 +34,6 @@ MSG_HASH( MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN, "Brak argumentów i brak wbudowanego menu, wyświetlanie pomocy..." ) -MSG_HASH( - MSG_NETPLAY_USERS_HAS_FLIPPED, - "Użytkownicy Netplay odwrócili się" - ) MSG_HASH( MSG_SETTING_DISK_IN_TRAY, "Ustawianie dysku w zasobniku" @@ -54,10 +50,6 @@ MSG_HASH( MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N, "Dołączyłeś jako gracz %d" ) -MSG_HASH( - MSG_NETPLAY_IMPLEMENTATIONS_DIFFER, - "Implementacja różni się. Upewnij się, że używasz dokładnie tych samych wersji RetroArch i rdzenia." - ) MSG_HASH( MSG_NETPLAY_ENDIAN_DEPENDENT, "Ten rdzeń nie obsługuje net-play między architekturami w tych systemach" @@ -843,8 +835,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MOVIE_RECORD_TOGGLE, "Przełącznik nagrywania filmu") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE, "Przełącznik wyciszania dźwięku") -MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_FLIP, - "Netplay przerzuca użytkowników") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_GAME_WATCH, "Netplay przełącza tryb play / spectate") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_OSK, @@ -1045,8 +1035,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_MIN, "Wejściowe klatki opóźnień") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_RANGE, "Zakres latencji wejściowych klatek") -MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CLIENT_SWAP_INPUT, - "Netplay P2 Używa C1") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DELAY_FRAMES, "Netplay opóźnij klatki") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DISCONNECT, @@ -2867,8 +2855,6 @@ MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVEFILES_ENABLE, MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVESTATES_ENABLE, "Sortuj stany zachowywania w folderach nazwanych po używanym rdzeniu." ) -MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_CLIENT_SWAP_INPUT, - "When being client over netplay, use keybinds for Player 1.") MSG_HASH(MENU_ENUM_SUBLABEL_CORE_UPDATER_BUILDBOT_URL, "Adres URL do głównego katalogu Updater na kompilatorze Libretro.") MSG_HASH(MENU_ENUM_SUBLABEL_BUILDBOT_ASSETS_URL, diff --git a/intl/msg_hash_pt_br.c b/intl/msg_hash_pt_br.c index b2ddf5dcd0..5b0d02eaa1 100644 --- a/intl/msg_hash_pt_br.c +++ b/intl/msg_hash_pt_br.c @@ -93,10 +93,6 @@ int menu_hash_get_help_pt_br_enum(enum msg_hash_enums msg, char *s, size_t len) snprintf(s, len, "Alternar o teclado virtual."); break; - case RARCH_NETPLAY_FLIP: - snprintf(s, len, - "Inverter usuários do Netplay."); - break; case RARCH_NETPLAY_GAME_WATCH: snprintf(s, len, "Alternar modo jogador/espectador do Netplay."); @@ -1828,11 +1824,6 @@ int menu_hash_get_help_pt_br_enum(enum msg_hash_enums msg, char *s, size_t len) "o RetroArch. \n" "Será utilizado para jogos online."); break; - case MENU_ENUM_LABEL_NETPLAY_CLIENT_SWAP_INPUT: - snprintf(s, len, - "Ao ser o cliente de Netplay, use os \n" - "vínculos de teclas do jogador 1."); - break; case MENU_ENUM_LABEL_NETPLAY_TCP_UDP_PORT: snprintf(s, len, "A porta do endereço de IP do hospedeiro. \n" @@ -1952,10 +1943,6 @@ int menu_hash_get_help_pt_br_enum(enum msg_hash_enums msg, char *s, size_t len) snprintf(s, len, "Salvar Estado de Jogo."); break; - case MENU_ENUM_LABEL_NETPLAY_FLIP_PLAYERS: - snprintf(s, len, - "Inverter usuários do Netplay."); - break; case MENU_ENUM_LABEL_NETPLAY_GAME_WATCH: snprintf(s, len, "Alternar modo jogador/espectador do Netplay."); diff --git a/intl/msg_hash_pt_br.h b/intl/msg_hash_pt_br.h index 1356946958..a37db1a1e6 100644 --- a/intl/msg_hash_pt_br.h +++ b/intl/msg_hash_pt_br.h @@ -25,9 +25,6 @@ MSG_HASH(MSG_PUBLIC_ADDRESS, MSG_HASH(MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN, "Nenhum argumento fornecido e nenhum menu interno, exibindo ajuda..." ) -MSG_HASH(MSG_NETPLAY_USERS_HAS_FLIPPED, - "Usuários do Netplay foram invertidos" - ) MSG_HASH(MSG_SETTING_DISK_IN_TRAY, "Definindo disco na bandeja" ) @@ -40,9 +37,6 @@ MSG_HASH(MSG_NETPLAY_YOU_HAVE_LEFT_THE_GAME, MSG_HASH(MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N, "Você se juntou como jogador %d" ) -MSG_HASH(MSG_NETPLAY_IMPLEMENTATIONS_DIFFER, - "As implementações são diferentes. Certifique-se de que está usando exatamente as mesmas versões do RetroArch e do núcleo." - ) MSG_HASH(MSG_NETPLAY_ENDIAN_DEPENDENT, "Este núcleo não suporta Netplay inter-arquitetura entre estes sistemas" ) @@ -857,9 +851,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MOVIE_RECORD_TOGGLE, MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE, "Alternar áudio mudo" ) -MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_FLIP, - "Inverter usuários do Netplay" - ) MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_GAME_WATCH, "Alternar modo jogador/espectador do Netplay" ) @@ -1160,9 +1151,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_MIN, MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_RANGE, "Faixa de Quadros de Latência de Entrada" ) -MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CLIENT_SWAP_INPUT, - "J2 do Netplay Usa C1" - ) MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DELAY_FRAMES, "Atraso de Quadros do Netplay" ) @@ -3581,9 +3569,6 @@ MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVEFILES_ENABLE, MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVESTATES_ENABLE, "Ordenar os Estados de Jogo em pastas com o nome do núcleo utilizado." ) -MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_CLIENT_SWAP_INPUT, - "Ao se tornar cliente do Netplay, use os vínculos de teclas para o Jogador 1." - ) MSG_HASH(MENU_ENUM_SUBLABEL_CORE_UPDATER_BUILDBOT_URL, "URL para o diretório de atualização de núcleos no buildbot do Libreto." ) diff --git a/intl/msg_hash_pt_pt.c b/intl/msg_hash_pt_pt.c index 69b5720b4f..bdff63fadd 100644 --- a/intl/msg_hash_pt_pt.c +++ b/intl/msg_hash_pt_pt.c @@ -870,10 +870,6 @@ int menu_hash_get_help_pt_pt_enum(enum msg_hash_enums msg, char *s, size_t len) snprintf(s, len, "Salva Savestates."); break; - case MENU_ENUM_LABEL_NETPLAY_FLIP_PLAYERS: - snprintf(s, len, - "Netplay inverte usuários."); - break; case MENU_ENUM_LABEL_CHEAT_INDEX_PLUS: snprintf(s, len, "Incrementa o índice de cheats.\n"); diff --git a/intl/msg_hash_pt_pt.h b/intl/msg_hash_pt_pt.h index a1b320918c..c71f653067 100644 --- a/intl/msg_hash_pt_pt.h +++ b/intl/msg_hash_pt_pt.h @@ -34,10 +34,6 @@ MSG_HASH( MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN, "Não foi fornecido qualquer argumento e nenhum menu está contido, mostrando ajuda..." ) -MSG_HASH( - MSG_NETPLAY_USERS_HAS_FLIPPED, - "Utilizadores do Netplay devolveram" - ) MSG_HASH( MSG_SETTING_DISK_IN_TRAY, "Definindo disco na bandeja" @@ -54,10 +50,6 @@ MSG_HASH( MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N, "Você juntou-se como jogador %d" ) -MSG_HASH( - MSG_NETPLAY_IMPLEMENTATIONS_DIFFER, - "As implementações são diferentes. Certifique-se de que está a versão do RetroArch e do núcleo são as mesmas." - ) MSG_HASH( MSG_NETPLAY_ENDIAN_DEPENDENT, "Este núcleo não suporta inter-arquitetura de Netplay entre estes sistemas" @@ -811,8 +803,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MOVIE_RECORD_TOGGLE, "Alternar gravação de filme") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE, "Alternar áudio mudo") -MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_FLIP, - "Giro de usuários do Netplay") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_GAME_WATCH, "Alternar modo jogar/olhar no Netplay") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_OSK, @@ -1005,8 +995,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_MIN, "Entrada de Latência de Quadros") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_RANGE, "Alcance da Entrada de latência de Quadros") -MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CLIENT_SWAP_INPUT, - "J2 do Netplay Usa C1") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DELAY_FRAMES, "Atraso de Quadros do Netplay") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DISCONNECT, @@ -2749,8 +2737,6 @@ MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVEFILES_ENABLE, MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVESTATES_ENABLE, "Ordenar salvamento de estados em pastas com o nome do Core usado." ) -MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_CLIENT_SWAP_INPUT, - "Ao se tornar cliente do netplay, use uma lista de teclas para o Jogador 1.") MSG_HASH(MENU_ENUM_SUBLABEL_CORE_UPDATER_BUILDBOT_URL, "URL para o diretório de atualização do Core no buildbot do Libreto.") MSG_HASH(MENU_ENUM_SUBLABEL_BUILDBOT_ASSETS_URL, diff --git a/intl/msg_hash_ru.h b/intl/msg_hash_ru.h index 5729476995..cc391e57ef 100644 --- a/intl/msg_hash_ru.h +++ b/intl/msg_hash_ru.h @@ -39,10 +39,6 @@ MSG_HASH( MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN, "Нет аргументов и встроенного меню, отображается справка..." ) -MSG_HASH( - MSG_NETPLAY_USERS_HAS_FLIPPED, - "Пользователи Netplay отключились" - ) MSG_HASH( MSG_SETTING_DISK_IN_TRAY, "Установка диска в привод" @@ -59,10 +55,6 @@ MSG_HASH( MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N, "Вы присоединились под именем %d" ) -MSG_HASH( - MSG_NETPLAY_IMPLEMENTATIONS_DIFFER, - "Реализации различаются. Убедитесь, что вы используете одинаковые версии RetroArch и выбранного ядра." - ) MSG_HASH( MSG_NETPLAY_ENDIAN_DEPENDENT, "Выбранное ядро не поддерживает архитектуру netplay между этими системами" @@ -834,8 +826,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MOVIE_RECORD_TOGGLE, "Переключатель записи видео") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE, "Отключения звука") -MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_FLIP, - "Отключить пользователей Netplay") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_GAME_WATCH, "Переключить режим игры/наблюдателя Netplay") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_OSK, @@ -1030,8 +1020,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_MIN, "Кадры задержки ввода") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_RANGE, "Диапазон кадров задержки ввода") -MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CLIENT_SWAP_INPUT, - "Netplay P2 использует C1") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DELAY_FRAMES, "Задержка кадров Netplay") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DISCONNECT, @@ -2822,8 +2810,6 @@ MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVEFILES_ENABLE, MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVESTATES_ENABLE, "Сортировать файлы сохранений в каталогах, названные после использования ядра." ) -MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_CLIENT_SWAP_INPUT, - "Будучи клиентом netplay, использовать привязки клавиш для Игрока 1.") MSG_HASH(MENU_ENUM_SUBLABEL_CORE_UPDATER_BUILDBOT_URL, "URL каталога обновлений ядра на билдботе Libretro.") MSG_HASH(MENU_ENUM_SUBLABEL_BUILDBOT_ASSETS_URL, diff --git a/intl/msg_hash_us.c b/intl/msg_hash_us.c index ff2c544bb9..b26150d298 100644 --- a/intl/msg_hash_us.c +++ b/intl/msg_hash_us.c @@ -95,10 +95,6 @@ int menu_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len) snprintf(s, len, "Toggles onscreen keyboard."); break; - case RARCH_NETPLAY_FLIP: - snprintf(s, len, - "Netplay flip users."); - break; case RARCH_NETPLAY_GAME_WATCH: snprintf(s, len, "Netplay toggle play/spectate mode."); @@ -1753,11 +1749,6 @@ int menu_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len) "The username of the person running RetroArch. \n" "This will be used for playing online games."); break; - case MENU_ENUM_LABEL_NETPLAY_CLIENT_SWAP_INPUT: - snprintf(s, len, - "When being client over netplay, use \n" - "keybinds for player 1."); - break; case MENU_ENUM_LABEL_NETPLAY_TCP_UDP_PORT: snprintf(s, len, "The port of the host IP address. \n" @@ -1873,10 +1864,6 @@ int menu_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len) snprintf(s, len, "Saves state."); break; - case MENU_ENUM_LABEL_NETPLAY_FLIP_PLAYERS: - snprintf(s, len, - "Netplay flip users."); - break; case MENU_ENUM_LABEL_NETPLAY_GAME_WATCH: snprintf(s, len, "Netplay toggle play/spectate mode."); diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index fe4d25dcf7..aa83a5183c 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -34,10 +34,6 @@ MSG_HASH( MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN, "No arguments supplied and no menu builtin, displaying help..." ) -MSG_HASH( - MSG_NETPLAY_USERS_HAS_FLIPPED, - "Netplay users have flipped" - ) MSG_HASH( MSG_SETTING_DISK_IN_TRAY, "Setting disk in tray" @@ -52,11 +48,43 @@ MSG_HASH( ) MSG_HASH( MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N, - "You have joined as player %d" + "You have joined as player %u" ) MSG_HASH( - MSG_NETPLAY_IMPLEMENTATIONS_DIFFER, - "Implementations differ. Make sure you're using the exact same versions of RetroArch and the core." + MSG_NETPLAY_YOU_HAVE_JOINED_WITH_INPUT_DEVICES_S, + "You have joined with input devices %.*s" + ) +MSG_HASH( + MSG_NETPLAY_PLAYER_S_LEFT, + "Player %.*s has left the game" + ) +MSG_HASH( + MSG_NETPLAY_S_HAS_JOINED_AS_PLAYER_N, + "%2$.*1$s has joined as player %3$u" + ) +MSG_HASH( + MSG_NETPLAY_S_HAS_JOINED_WITH_INPUT_DEVICES_S, + "%2$.*1$s has joined with input devices %4$.*3$s" + ) +MSG_HASH( + MSG_NETPLAY_NOT_RETROARCH, + "A netplay connection attempt failed because the peer is not running RetroArch, or is running an old version of RetroArch." + ) +MSG_HASH( + MSG_NETPLAY_OUT_OF_DATE, + "The netplay peer is running an old version of RetroArch. Cannot connect." + ) +MSG_HASH( + MSG_NETPLAY_DIFFERENT_VERSIONS, + "WARNING: A netplay peer is running a different version of RetroArch. If problems occur, use the same version." + ) +MSG_HASH( + MSG_NETPLAY_DIFFERENT_CORES, + "A netplay peer is running a different core. Cannot connect." + ) +MSG_HASH( + MSG_NETPLAY_DIFFERENT_CORE_VERSIONS, + "WARNING: A netlpay peer is running a different version of the core. If problems occur, use the same version." ) MSG_HASH( MSG_NETPLAY_ENDIAN_DEPENDENT, @@ -94,6 +122,10 @@ MSG_HASH( MSG_NETPLAY_CANNOT_PLAY_NO_SLOTS, "There are no free player slots" ) +MSG_HASH( + MSG_NETPLAY_CANNOT_PLAY_NOT_AVAILABLE, + "The input devices requested are not available" + ) MSG_HASH( MSG_NETPLAY_CANNOT_PLAY, "Cannot switch to play mode" @@ -843,8 +875,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MOVIE_RECORD_TOGGLE, "Movie record toggle") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE, "Audio mute toggle") -MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_FLIP, - "Netplay flip users") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_GAME_WATCH, "Netplay toggle play/spectate mode") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_OSK, @@ -1045,8 +1075,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_MIN, "Input Latency Frames") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_RANGE, "Input Latency Frames Range") -MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CLIENT_SWAP_INPUT, - "Netplay P2 Uses C1") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DELAY_FRAMES, "Netplay Delay Frames") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DISCONNECT, @@ -1071,10 +1099,30 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_PASSWORD, "Server Password") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_PUBLIC_ANNOUNCE, "Publicly Announce Netplay") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_REQUEST_DEVICE_I, + "Request Device %u") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_REQUIRE_SLAVES, "Disallow Non-Slave-Mode Clients") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SETTINGS, "Netplay settings") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_ANALOG, + "Analog Input Sharing") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_ANALOG_MAX, + "Max") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_ANALOG_AVERAGE, + "Average") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL, + "Digital Input Sharing") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL_OR, + "Share") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL_XOR, + "Grapple") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL_VOTE, + "Vote") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_NONE, + "None") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_NO_PREFERENCE, + "No preference") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_START_AS_SPECTATOR, "Netplay Spectator Mode") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_STATELESS_MODE, @@ -2875,8 +2923,8 @@ MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVEFILES_ENABLE, MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVESTATES_ENABLE, "Sort save states in folders named after the core used." ) -MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_CLIENT_SWAP_INPUT, - "When being client over netplay, use keybinds for Player 1.") +MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_REQUEST_DEVICE_I, + "Request to play with the given input device.") MSG_HASH(MENU_ENUM_SUBLABEL_CORE_UPDATER_BUILDBOT_URL, "URL to core updater directory on the Libretro buildbot.") MSG_HASH(MENU_ENUM_SUBLABEL_BUILDBOT_ASSETS_URL, diff --git a/intl/msg_hash_vn.c b/intl/msg_hash_vn.c index 946ce5a783..e7f6e46416 100644 --- a/intl/msg_hash_vn.c +++ b/intl/msg_hash_vn.c @@ -93,10 +93,6 @@ int menu_hash_get_help_vn_enum(enum msg_hash_enums msg, char *s, size_t len) snprintf(s, len, "Bật/tắt bàn phím trên màn hình."); break; - case RARCH_NETPLAY_FLIP: - snprintf(s, len, - "Netplay flip users."); - break; case RARCH_SLOWMOTION: snprintf(s, len, "Nhấn để xem chậm."); @@ -1671,11 +1667,6 @@ int menu_hash_get_help_vn_enum(enum msg_hash_enums msg, char *s, size_t len) "The username of the person running RetroArch. \n" "This will be used for playing online games."); break; - case MENU_ENUM_LABEL_NETPLAY_CLIENT_SWAP_INPUT: - snprintf(s, len, - "When being client over netplay, use \n" - "keybinds for player 1."); - break; case MENU_ENUM_LABEL_NETPLAY_TCP_UDP_PORT: snprintf(s, len, "The port of the host IP address. \n" @@ -1778,10 +1769,6 @@ int menu_hash_get_help_vn_enum(enum msg_hash_enums msg, char *s, size_t len) snprintf(s, len, "Saves state."); break; - case MENU_ENUM_LABEL_NETPLAY_FLIP_PLAYERS: - snprintf(s, len, - "Netplay flip users."); - break; case MENU_ENUM_LABEL_CHEAT_INDEX_PLUS: snprintf(s, len, "Increment cheat index.\n"); diff --git a/intl/msg_hash_vn.h b/intl/msg_hash_vn.h index b2c1cc254a..77a36a6753 100644 --- a/intl/msg_hash_vn.h +++ b/intl/msg_hash_vn.h @@ -34,10 +34,6 @@ MSG_HASH( MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN, "No arguments supplied and no menu builtin, displaying help..." ) -MSG_HASH( - MSG_NETPLAY_USERS_HAS_FLIPPED, - "Người dùng Netplay đã flipped" - ) MSG_HASH( MSG_SETTING_DISK_IN_TRAY, "Setting disk in tray" @@ -54,10 +50,6 @@ MSG_HASH( MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N, "You have joined as player %d" ) -MSG_HASH( - MSG_NETPLAY_IMPLEMENTATIONS_DIFFER, - "Implementations differ. Make sure you're using the exact same versions of RetroArch and the core." - ) MSG_HASH( MSG_NETPLAY_ENDIAN_DEPENDENT, "This core does not support inter-architecture netplay between these systems" @@ -823,8 +815,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MOVIE_RECORD_TOGGLE, "Movie record toggle") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE, "Âm thanh mute toggle") -MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_FLIP, - "Netplay flip users") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_GAME_WATCH, "Netplay toggle play/spectate mode") MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_OSK, @@ -1019,8 +1009,6 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_MIN, "Input Latency Frames") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_INPUT_LATENCY_FRAMES_RANGE, "Input Latency Frames Range") -MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_CLIENT_SWAP_INPUT, - "Netplay P2 Uses C1") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DELAY_FRAMES, "Netplay Delay Frames") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_DISCONNECT, @@ -2799,8 +2787,6 @@ MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVEFILES_ENABLE, MSG_HASH(MENU_ENUM_SUBLABEL_SORT_SAVESTATES_ENABLE, "Sort save states in folders named after the core used." ) -MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_CLIENT_SWAP_INPUT, - "When being client over netplay, use keybinds for Player 1.") MSG_HASH(MENU_ENUM_SUBLABEL_CORE_UPDATER_BUILDBOT_URL, "URL to core updater directory on the Libretro buildbot.") MSG_HASH(MENU_ENUM_SUBLABEL_BUILDBOT_ASSETS_URL, diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index 4dd4791873..a5dba20d1e 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -43,6 +43,10 @@ #include "../../verbosity.h" #include "../../wifi/wifi_driver.h" +#ifdef HAVE_NETWORKING +#include "../network/netplay/netplay.h" +#endif + #ifndef BIND_ACTION_GET_VALUE #define BIND_ACTION_GET_VALUE(cbs, name) \ cbs->action_get_value = name; \ @@ -1714,6 +1718,84 @@ static void menu_action_setting_disp_set_label_setting_path(file_list_t* list, strlcpy(s2, path, len2); } +static void menu_action_setting_disp_set_label_netplay_share_digital(file_list_t* list, + unsigned *w, unsigned type, unsigned i, + const char *label, + char *s, size_t len, + const char *entry_label, + const char *path, + char *s2, size_t len2) +{ + settings_t *settings = config_get_ptr(); + const char *src; + + if (!settings) + return; + + strlcpy(s2, path, len2); + *w = 19; + switch (settings->uints.netplay_share_digital) + { + case RARCH_NETPLAY_SHARE_DIGITAL_NO_PREFERENCE: + src = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_NO_PREFERENCE); + break; + + case RARCH_NETPLAY_SHARE_DIGITAL_OR: + src = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL_OR); + break; + + case RARCH_NETPLAY_SHARE_DIGITAL_XOR: + src = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL_XOR); + break; + + case RARCH_NETPLAY_SHARE_DIGITAL_VOTE: + src = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL_VOTE); + break; + + default: + src = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_NONE); + break; + } + strlcpy(s, src, len); +} + +static void menu_action_setting_disp_set_label_netplay_share_analog(file_list_t* list, + unsigned *w, unsigned type, unsigned i, + const char *label, + char *s, size_t len, + const char *entry_label, + const char *path, + char *s2, size_t len2) +{ + settings_t *settings = config_get_ptr(); + const char *src; + + if (!settings) + return; + + strlcpy(s2, path, len2); + *w = 19; + switch (settings->uints.netplay_share_analog) + { + case RARCH_NETPLAY_SHARE_ANALOG_NO_PREFERENCE: + src = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_NO_PREFERENCE); + break; + + case RARCH_NETPLAY_SHARE_ANALOG_MAX: + src = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_ANALOG_MAX); + break; + + case RARCH_NETPLAY_SHARE_ANALOG_AVERAGE: + src = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_ANALOG_AVERAGE); + break; + + default: + src = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_NONE); + break; + } + strlcpy(s, src, len); +} + static int menu_cbs_init_bind_get_string_representation_compare_label( menu_file_list_cbs_t *cbs) { @@ -1817,6 +1899,14 @@ static int menu_cbs_init_bind_get_string_representation_compare_label( BIND_ACTION_GET_VALUE(cbs, menu_action_setting_disp_set_label_menu_input_keyboard_gamepad_mapping_type); break; + case MENU_ENUM_LABEL_NETPLAY_SHARE_DIGITAL: + BIND_ACTION_GET_VALUE(cbs, + menu_action_setting_disp_set_label_netplay_share_digital); + break; + case MENU_ENUM_LABEL_NETPLAY_SHARE_ANALOG: + BIND_ACTION_GET_VALUE(cbs, + menu_action_setting_disp_set_label_netplay_share_analog); + break; case MENU_ENUM_LABEL_CONTENT_COLLECTION_LIST: case MENU_ENUM_LABEL_LOAD_CONTENT_HISTORY: case MENU_ENUM_LABEL_DOWNLOADED_FILE_DETECT_CORE_LIST: diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 734da03336..2bd0baac54 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -229,7 +229,6 @@ default_sublabel_macro(action_bind_sublabel_scan_directory, MENU_ default_sublabel_macro(action_bind_sublabel_video_swap_interval, MENU_ENUM_SUBLABEL_VIDEO_SWAP_INTERVAL) default_sublabel_macro(action_bind_sublabel_sort_savefiles_enable, MENU_ENUM_SUBLABEL_SORT_SAVEFILES_ENABLE) default_sublabel_macro(action_bind_sublabel_sort_savestates_enable, MENU_ENUM_SUBLABEL_SORT_SAVESTATES_ENABLE) -default_sublabel_macro(action_bind_sublabel_netplay_client_swap_input, MENU_ENUM_SUBLABEL_NETPLAY_CLIENT_SWAP_INPUT) default_sublabel_macro(action_bind_sublabel_core_updater_buildbot_url, MENU_ENUM_SUBLABEL_CORE_UPDATER_BUILDBOT_URL) default_sublabel_macro(action_bind_sublabel_input_overlay_show_physical_inputs, MENU_ENUM_SUBLABEL_INPUT_OVERLAY_SHOW_PHYSICAL_INPUTS) default_sublabel_macro(action_bind_sublabel_input_overlay_show_physical_inputs_port, MENU_ENUM_SUBLABEL_INPUT_OVERLAY_SHOW_PHYSICAL_INPUTS_PORT) @@ -886,9 +885,6 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_BUILDBOT_ASSETS_URL: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_core_updater_buildbot_assets_url); break; - case MENU_ENUM_LABEL_NETPLAY_CLIENT_SWAP_INPUT: - BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_netplay_client_swap_input); - break; case MENU_ENUM_LABEL_SORT_SAVEFILES_ENABLE: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_sort_savefiles_enable); break; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index ace9d542f4..caf1ae040b 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -5544,9 +5544,20 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) PARSE_ONLY_BOOL, false) != -1) count++; if (menu_displaylist_parse_settings_enum(menu, info, - MENU_ENUM_LABEL_NETPLAY_CLIENT_SWAP_INPUT, - PARSE_ONLY_BOOL, false) != -1) + MENU_ENUM_LABEL_NETPLAY_SHARE_DIGITAL, + PARSE_ONLY_UINT, false) != -1) count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_NETPLAY_SHARE_ANALOG, + PARSE_ONLY_UINT, false) != -1) + count++; + for (user = 0; user < MAX_USERS; user++) + { + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_NETPLAY_REQUEST_DEVICE_1 + user, + PARSE_ONLY_BOOL, false) != -1) + count++; + } if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETWORK_CMD_ENABLE, PARSE_ONLY_BOOL, false) != -1) diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 358dcaee59..04eb1b6b23 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -86,6 +86,10 @@ #include "../tasks/tasks_internal.h" +#ifdef HAVE_NETWORKING +#include "../network/netplay/netplay.h" +#endif + enum settings_list_type { SETTINGS_LIST_NONE = 0, @@ -6698,9 +6702,10 @@ static bool setting_append_list( { #if defined(HAVE_NETWORKING) -#if defined(HAVE_NETWORK_CMD) unsigned user; -#endif + char dev_req_label[64]; + char dev_req_value[64]; + CONFIG_BOOL( list, list_info, &settings->bools.netplay_public_announce, @@ -6908,21 +6913,55 @@ static bool setting_append_list( SD_FLAG_NONE); settings_data_list_current_add_flags(list, list_info, SD_FLAG_ADVANCED); - CONFIG_BOOL( + CONFIG_UINT( list, list_info, - &settings->bools.netplay_swap_input, - MENU_ENUM_LABEL_NETPLAY_CLIENT_SWAP_INPUT, - MENU_ENUM_LABEL_VALUE_NETPLAY_CLIENT_SWAP_INPUT, - netplay_client_swap_input, - MENU_ENUM_LABEL_VALUE_OFF, - MENU_ENUM_LABEL_VALUE_ON, + &settings->uints.netplay_share_digital, + MENU_ENUM_LABEL_NETPLAY_SHARE_DIGITAL, + MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL, + 0, &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); + general_read_handler); + menu_settings_list_current_add_range(list, list_info, 0, RARCH_NETPLAY_SHARE_DIGITAL_LAST-1, 1, true, true); + + CONFIG_UINT( + list, list_info, + &settings->uints.netplay_share_analog, + MENU_ENUM_LABEL_NETPLAY_SHARE_ANALOG, + MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_ANALOG, + 0, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + menu_settings_list_current_add_range(list, list_info, 0, RARCH_NETPLAY_SHARE_ANALOG_LAST-1, 1, true, true); + + for (user = 0; user < MAX_USERS; user++) + { + snprintf(dev_req_label, sizeof(dev_req_label), + msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_REQUEST_DEVICE_I), user + 1); + snprintf(dev_req_value, sizeof(dev_req_value), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_REQUEST_DEVICE_I), user + 1); + CONFIG_BOOL_ALT( + list, list_info, + &settings->bools.netplay_request_devices[user], + strdup(dev_req_label), + strdup(dev_req_value), + false, + MENU_ENUM_LABEL_VALUE_NO, + MENU_ENUM_LABEL_VALUE_YES, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_ADVANCED); + settings_data_list_current_add_free_flags(list, list_info, SD_FREE_FLAG_NAME | SD_FREE_FLAG_SHORT); + menu_settings_list_current_add_enum_idx(list, list_info, (enum msg_hash_enums)(MENU_ENUM_LABEL_NETPLAY_REQUEST_DEVICE_1 + user)); + } END_SUB_GROUP(list, list_info, parent_group); diff --git a/msg_hash.h b/msg_hash.h index 72a5abf080..b753c62328 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -153,7 +153,6 @@ enum msg_hash_enums MSG_SETTING_DISK_IN_TRAY, MSG_FAILED_TO_SET_DISK, MSG_NETPLAY_FAILED, - MSG_NETPLAY_USERS_HAS_FLIPPED, MSG_UNKNOWN_NETPLAY_COMMAND_RECEIVED, MSG_CONNECTING_TO_NETPLAY_HOST, MSG_NETPLAY_LAN_SCAN_COMPLETE, @@ -161,7 +160,15 @@ enum msg_hash_enums MSG_WAITING_FOR_CLIENT, MSG_NETPLAY_YOU_HAVE_LEFT_THE_GAME, MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N, - MSG_NETPLAY_IMPLEMENTATIONS_DIFFER, + MSG_NETPLAY_YOU_HAVE_JOINED_WITH_INPUT_DEVICES_S, + MSG_NETPLAY_PLAYER_S_LEFT, + MSG_NETPLAY_S_HAS_JOINED_AS_PLAYER_N, + MSG_NETPLAY_S_HAS_JOINED_WITH_INPUT_DEVICES_S, + MSG_NETPLAY_NOT_RETROARCH, + MSG_NETPLAY_OUT_OF_DATE, + MSG_NETPLAY_DIFFERENT_VERSIONS, + MSG_NETPLAY_DIFFERENT_CORES, + MSG_NETPLAY_DIFFERENT_CORE_VERSIONS, MSG_NETPLAY_ENDIAN_DEPENDENT, MSG_NETPLAY_PLATFORM_DEPENDENT, MSG_NETPLAY_ENTER_PASSWORD, @@ -171,6 +178,7 @@ enum msg_hash_enums MSG_NETPLAY_CLIENT_HANGUP, MSG_NETPLAY_CANNOT_PLAY_UNPRIVILEGED, MSG_NETPLAY_CANNOT_PLAY_NO_SLOTS, + MSG_NETPLAY_CANNOT_PLAY_NOT_AVAILABLE, MSG_NETPLAY_CANNOT_PLAY, MSG_NETPLAY_PEER_PAUSED, MSG_NETPLAY_CHANGED_NICK, @@ -605,7 +613,6 @@ enum msg_hash_enums MENU_ENUM_LABEL_VALUE_INPUT_META_SCREENSHOT, MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE, MENU_ENUM_LABEL_VALUE_INPUT_META_OSK, - MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_FLIP, MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_GAME_WATCH, MENU_ENUM_LABEL_VALUE_INPUT_META_SLOWMOTION, MENU_ENUM_LABEL_VALUE_INPUT_META_ENABLE_HOTKEY, @@ -1103,7 +1110,6 @@ enum msg_hash_enums MENU_LABEL(UNDO_LOAD_STATE), MENU_LABEL(UNDO_SAVE_STATE), - MENU_LABEL(NETPLAY_FLIP_PLAYERS), MENU_LABEL(NETPLAY_GAME_WATCH), MENU_LABEL(CHEAT_INDEX_MINUS), MENU_LABEL(CHEAT_INDEX_PLUS), @@ -1156,7 +1162,6 @@ enum msg_hash_enums MENU_LABEL(SSH_ENABLE), MENU_LABEL(SAMBA_ENABLE), MENU_LABEL(BLUETOOTH_ENABLE), - MENU_LABEL(NETPLAY_CLIENT_SWAP_INPUT), MENU_LABEL(NETPLAY_DELAY_FRAMES), MENU_LABEL(NETPLAY_PUBLIC_ANNOUNCE), MENU_LABEL(NETPLAY_START_AS_SPECTATOR), @@ -1169,6 +1174,19 @@ enum msg_hash_enums MENU_LABEL(NETPLAY_SPECTATOR_MODE_ENABLE), MENU_LABEL(NETPLAY_TCP_UDP_PORT), MENU_LABEL(NETPLAY_NAT_TRAVERSAL), + MENU_LABEL(NETPLAY_REQUEST_DEVICE_I), + MENU_ENUM_LABEL_NETPLAY_REQUEST_DEVICE_1, + MENU_ENUM_LABEL_NETPLAY_REQUEST_DEVICE_LAST = MENU_ENUM_LABEL_NETPLAY_REQUEST_DEVICE_1 + MAX_USERS, + MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_NONE, + MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_NO_PREFERENCE, + MENU_LABEL(NETPLAY_SHARE_DIGITAL), + MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL_OR, + MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL_XOR, + MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_DIGITAL_VOTE, + MENU_LABEL(NETPLAY_SHARE_ANALOG), + MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_ANALOG_MAX, + MENU_ENUM_LABEL_VALUE_NETPLAY_SHARE_ANALOG_AVERAGE, + MENU_LABEL(SORT_SAVEFILES_ENABLE), MENU_LABEL(SORT_SAVESTATES_ENABLE), MENU_LABEL(SAVEFILES_IN_CONTENT_DIR_ENABLE), diff --git a/network/netplay/README b/network/netplay/README index 095854627e..2d3aa8a41d 100644 --- a/network/netplay/README +++ b/network/netplay/README @@ -106,6 +106,10 @@ early (i.e., it only forwards data on the frame it's reached), it must also inform all clients of its own current frame even if it has no input. The NOINPUT command is provided for that purpose. +Each client has a client number, and the server is always client number 0. +Client numbers are currently limited to 0-31, as they're used in 32-bit +bitmaps. + The handshake procedure (this part is done by both server and client): 1) Send connection header 2) Receive and verify connection header @@ -155,18 +159,16 @@ Command: INPUT Payload: { frame number: uint32 - is server data: 1 bit - player: 31 bits - joypad input: uint32 - analog 1 input: uint32 - analog 2 input: uint32 + client number: uint32 + input data: variable } Description: Input state for each frame. Netplay must send an INPUT command for every - frame in order to function at all. Client's player value is ignored. Server - indicates which frames are its own input data because INPUT is a - synchronization point: No synchronization events from the given frame may - arrive after the server's input for the frame. + frame in order to function at all. When sent from the server (client 0), + INPUT is a synchronization point: No synchronization events from the given + frame may arrive after the server's input for the frame. The actual size of + the input data is variable, but must match the expected size for each of + the devices the given client controls. Command: NOINPUT Payload: @@ -213,9 +215,10 @@ Payload: { frame number: uint32 paused?: 1 bit - connected players: 31 bits - flip frame: uint32 + client number: 31 bits controller devices: uint32[16] + share modes: uint8[16] + controller-client mapping: uint32[16] client nick: char[32] sram: variable } @@ -236,23 +239,31 @@ Description: Command: PLAY Payload: { - reserved: 31 bits as slave?: 1 bit + reserved: 7 bits + preferred share mode: 8 bits + requested device(s): 16 bits } Description: Request to enter player mode. The client must wait for a MODE command before sending input. Server may refuse or force slave connections, so the - request is not necessarily honored. Payload may be elided if zero. + request is not necessarily honored. If no devices are explicitly requested, + the server may choose how to assign; the default is to assign the first + unassigned device, and share the first device if all devices are assigned + and the first device is shareable. Command: MODE Payload: { frame number: uint32 - reserved: 13 bits - slave: 1 bit - playing: 1 bit you: 1 bit - player number: uint16 + playing: 1 bit + slave: 1 bit + reserved: 13 bits + client number: uint16 + device bitmap: uint32 + share modes: uint8[16] + nick: char[32] } Description: Inform of a connection mode change (possibly of the receiving client). Only @@ -346,3 +357,43 @@ Unused Command: CFG_ACK Unused + + +Input types + +Each input device uses a number of words fixed by the type of device. When +buttons are listed, they are listed from lowest bit to highest bit, i.e., +reverse order. When insufficient buttons are present to represent to represent +a full 32-bit word, the remainder are reserved and unused. + +JOYPAD + { + B, Y, Select, Start, Up, Down, Left, Right, A, X, L, R, L2, R2, L3, R3 + } + +MOUSE + { + unused, unused, Left, Right, Wheel up, Wheel down, Middle, Horizontal + wheel up, Horizontal wheel down + Y: int16 + X: int16 + } + +KEYBOARD + (5 words, see netplay_keys.h) + +LIGHTGUN + { + unused, unused, Trigger, Cursor, Turbo, Pause, Start + Y: int16 + X: int16 + } + +ANALOG + { + buttons: uint32, as JOYPAD + Left analog Y: int16 + Left analog X: int16 + Right analog Y: int16 + Right analog X: int16 + } diff --git a/network/netplay/netplay.h b/network/netplay/netplay.h index b680298584..712f66dc27 100644 --- a/network/netplay/netplay.h +++ b/network/netplay/netplay.h @@ -32,7 +32,6 @@ typedef struct netplay netplay_t; enum rarch_netplay_ctl_state { RARCH_NETPLAY_CTL_NONE = 0, - RARCH_NETPLAY_CTL_FLIP_PLAYERS, RARCH_NETPLAY_CTL_GAME_WATCH, RARCH_NETPLAY_CTL_POST_FRAME, RARCH_NETPLAY_CTL_PRE_FRAME, @@ -53,6 +52,27 @@ enum rarch_netplay_ctl_state RARCH_NETPLAY_CTL_DESYNC_POP }; +/* Preferences for sharing digital devices */ +enum rarch_netplay_share_digital_preference +{ + RARCH_NETPLAY_SHARE_DIGITAL_NO_SHARING, + RARCH_NETPLAY_SHARE_DIGITAL_NO_PREFERENCE, + RARCH_NETPLAY_SHARE_DIGITAL_OR, + RARCH_NETPLAY_SHARE_DIGITAL_XOR, + RARCH_NETPLAY_SHARE_DIGITAL_VOTE, + RARCH_NETPLAY_SHARE_DIGITAL_LAST +}; + +/* Preferences for sharing analog devices */ +enum rarch_netplay_share_analog_preference +{ + RARCH_NETPLAY_SHARE_ANALOG_NO_SHARING, + RARCH_NETPLAY_SHARE_ANALOG_NO_PREFERENCE, + RARCH_NETPLAY_SHARE_ANALOG_MAX, + RARCH_NETPLAY_SHARE_ANALOG_AVERAGE, + RARCH_NETPLAY_SHARE_ANALOG_LAST +}; + int16_t input_state_net(unsigned port, unsigned device, unsigned idx, unsigned id); diff --git a/network/netplay/netplay_delta.c b/network/netplay/netplay_delta.c index eabeb70f5f..edfd225427 100644 --- a/network/netplay/netplay_delta.c +++ b/network/netplay/netplay_delta.c @@ -23,6 +23,15 @@ #include "netplay_private.h" +static void clear_input(netplay_input_state_t istate) +{ + while (istate) + { + istate->used = false; + istate = istate->next; + } +} + /** * netplay_delta_frame_ready * @@ -35,7 +44,7 @@ bool netplay_delta_frame_ready(netplay_t *netplay, struct delta_frame *delta, uint32_t frame) { - void *remember_state; + size_t i; if (delta->used) { if (delta->frame == frame) return true; @@ -45,11 +54,18 @@ bool netplay_delta_frame_ready(netplay_t *netplay, struct delta_frame *delta, return false; } } - remember_state = delta->state; - memset(delta, 0, sizeof(struct delta_frame)); delta->used = true; delta->frame = frame; - delta->state = remember_state; + delta->crc = 0; + for (i = 0; i < MAX_INPUT_DEVICES; i++) + { + clear_input(delta->resolved_input[i]); + clear_input(delta->real_input[i]); + clear_input(delta->simlated_input[i]); + } + delta->have_local = false; + for (i = 0; i < MAX_CLIENTS; i++) + delta->have_real[i] = false; return true; } @@ -64,3 +80,110 @@ uint32_t netplay_delta_frame_crc(netplay_t *netplay, struct delta_frame *delta) return 0; return encoding_crc32(0L, (const unsigned char*)delta->state, netplay->state_size); } + +/* + * Free an input state list + */ +static void free_input_state(netplay_input_state_t *list) +{ + netplay_input_state_t cur, next; + cur = *list; + while (cur) + { + next = cur->next; + free(cur); + cur = next; + } + *list = NULL; +} + +/** + * netplay_delta_frame_free + * + * Free a delta frame's dependencies + */ +void netplay_delta_frame_free(struct delta_frame *delta) +{ + uint32_t i; + + if (delta->state) + { + free(delta->state); + delta->state = NULL; + } + + for (i = 0; i < MAX_INPUT_DEVICES; i++) + { + free_input_state(&delta->resolved_input[i]); + free_input_state(&delta->real_input[i]); + free_input_state(&delta->simlated_input[i]); + } +} + +/** + * netplay_input_state_for + * + * Get an input state for a particular client + */ +netplay_input_state_t netplay_input_state_for(netplay_input_state_t *list, + uint32_t client_num, size_t size, bool must_create, bool must_not_create) +{ + netplay_input_state_t ret; + while (*list) + { + ret = *list; + if (!ret->used && !must_not_create && ret->size == size) + { + ret->client_num = client_num; + ret->used = true; + memset(ret->data, 0, size*sizeof(uint32_t)); + return ret; + } + else if (ret->used && ret->client_num == client_num) + { + if (!must_create && ret->size == size) + return ret; + return NULL; + } + list = &(ret->next); + } + + if (must_not_create) + return NULL; + + /* Couldn't find a slot, allocate a fresh one */ + ret = calloc(1, sizeof(struct netplay_input_state) + (size-1) * sizeof(uint32_t)); + if (!ret) + return NULL; + *list = ret; + ret->client_num = client_num; + ret->used = true; + ret->size = size; + return ret; +} + +/** + * netplay_expected_input_size + * + * Size in words for a given set of devices. + */ +uint32_t netplay_expected_input_size(netplay_t *netplay, uint32_t devices) +{ + uint32_t ret = 0, device; + for (device = 0; device < MAX_INPUT_DEVICES; device++) + { + if (!(devices & (1<config_devices[device]&RETRO_DEVICE_MASK) + { + /* These are all essentially magic numbers, but each device has a + * fixed size, documented in network/netplay/README */ + case RETRO_DEVICE_JOYPAD: ret += 1; break; + case RETRO_DEVICE_MOUSE: ret += 2; break; + case RETRO_DEVICE_KEYBOARD: ret += 5; break; + case RETRO_DEVICE_LIGHTGUN: ret += 2; break; + case RETRO_DEVICE_ANALOG: ret += 3; break; + default: break; /* Unsupported */ + } + } + return ret; +} diff --git a/network/netplay/netplay_frontend.c b/network/netplay/netplay_frontend.c index c5d404dcba..ae8430c22c 100644 --- a/network/netplay/netplay_frontend.c +++ b/network/netplay/netplay_frontend.c @@ -71,7 +71,7 @@ static bool netplay_is_alive(void) { if (!netplay_data) return false; - return (netplay_data->is_server && !!netplay_data->connected_players) || + return (netplay_data->is_server) || (!netplay_data->is_server && netplay_data->self_mode >= NETPLAY_CONNECTION_CONNECTED); } @@ -115,8 +115,9 @@ static bool netplay_can_poll(netplay_t *netplay) */ static bool get_self_input_state(netplay_t *netplay) { - uint32_t state[WORDS_PER_INPUT] = {0, 0, 0}; struct delta_frame *ptr = &netplay->buffer[netplay->self_ptr]; + netplay_input_state_t istate = NULL; + uint32_t devices, used_devices = 0, devi, dev_type, local_device; size_t i; if (!netplay_delta_frame_ready(netplay, ptr, netplay->self_frame_count)) @@ -128,37 +129,110 @@ static bool get_self_input_state(netplay_t *netplay) return true; } - if (!input_driver_is_libretro_input_blocked() && netplay->self_frame_count > 0) + devices = netplay->self_devices; + used_devices = 0; + for (devi = 0; devi < MAX_INPUT_DEVICES; devi++) { + if (!(devices & (1<config_devices[devi]&RETRO_DEVICE_MASK; + for (local_device = 0; local_device < MAX_INPUT_DEVICES; local_device++) + { + if (used_devices & (1<config_devices[local_device]&RETRO_DEVICE_MASK) == dev_type) break; + } + if (local_device == MAX_INPUT_DEVICES) + local_device = 0; + used_devices |= (1<real_input[devi], + /* If we're a slave, we write our own input to MAX_CLIENTS to keep it separate */ + (netplay->self_mode==NETPLAY_CONNECTION_SLAVE)?MAX_CLIENTS:netplay->self_client_num, + netplay_expected_input_size(netplay, 1 << devi), + true, false); + if (!istate) + continue; /* FIXME: More severe? */ + /* First frame we always give zero input since relying on * input from first frame screws up when we use -F 0. */ - retro_input_state_t cb = netplay->cbs.state_cb; - for (i = 0; i < RARCH_FIRST_CUSTOM_BIND; i++) + if (!input_driver_is_libretro_input_blocked() && netplay->self_frame_count > 0) { - int16_t tmp = cb(0, - RETRO_DEVICE_JOYPAD, 0, (unsigned)i); - state[0] |= tmp ? 1 << i : 0; - } + uint32_t *state = istate->data; + retro_input_state_t cb = netplay->cbs.state_cb; + unsigned dtype = netplay->config_devices[devi]&RETRO_DEVICE_MASK; - for (i = 0; i < 2; i++) - { - int16_t tmp_x = cb(0, - RETRO_DEVICE_ANALOG, (unsigned)i, 0); - int16_t tmp_y = cb(0, - RETRO_DEVICE_ANALOG, (unsigned)i, 1); - state[1 + i] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16); + switch (dtype) + { + case RETRO_DEVICE_ANALOG: + for (i = 0; i < 2; i++) + { + int16_t tmp_x = cb(local_device, + RETRO_DEVICE_ANALOG, (unsigned)i, 0); + int16_t tmp_y = cb(local_device, + RETRO_DEVICE_ANALOG, (unsigned)i, 1); + state[1 + i] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16); + } + /* no break */ + + case RETRO_DEVICE_JOYPAD: + for (i = 0; i <= RETRO_DEVICE_ID_JOYPAD_R3; i++) + { + int16_t tmp = cb(local_device, + RETRO_DEVICE_JOYPAD, 0, (unsigned)i); + state[0] |= tmp ? 1 << i : 0; + } + break; + + case RETRO_DEVICE_MOUSE: + case RETRO_DEVICE_LIGHTGUN: + { + int16_t tmp_x = cb(local_device, dtype, 0, 0); + int16_t tmp_y = cb(local_device, dtype, 0, 1); + state[1] = (uint16_t)tmp_x | (((uint16_t)tmp_y) << 16); + for (i = 2; + i <= ((dtype == RETRO_DEVICE_MOUSE) ? + RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN : + RETRO_DEVICE_ID_LIGHTGUN_START); + i++) + { + int16_t tmp = cb(local_device, dtype, 0, + (unsigned) i); + state[0] |= tmp ? 1 << i : 0; + } + break; + } + + case RETRO_DEVICE_KEYBOARD: + { + unsigned key, word = 0, bit = 1; + for (key = 1; key < NETPLAY_KEY_LAST; key++) + { + state[word] |= + cb(local_device, RETRO_DEVICE_KEYBOARD, 0, netplay_key_ntoh(key)) ? + (1U << bit) : 0; + bit++; + if (bit >= 32) + { + bit = 0; + word++; + if (word >= istate->size) + break; + } + } + break; + } + } } } - memcpy(ptr->self_state, state, sizeof(state)); ptr->have_local = true; - - /* If we're playing, copy it in as real input */ if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING) { - memcpy(ptr->real_input_state[netplay->self_player], state, - sizeof(state)); - ptr->have_real[netplay->self_player] = true; + ptr->have_real[netplay->self_client_num] = true; + netplay->read_ptr[netplay->self_client_num] = NEXT_PTR(netplay->self_ptr); + netplay->read_frame_count[netplay->self_client_num] = netplay->self_frame_count + 1; } /* And send this input to our peers */ @@ -203,7 +277,7 @@ bool init_netplay_deferred(const char* server, unsigned port) static bool netplay_poll(void) { int res; - uint32_t player; + uint32_t client; size_t i; netplay_data->can_poll = false; @@ -219,7 +293,7 @@ static bool netplay_poll(void) * frame */ netplay_update_unread_ptr(netplay_data); if (netplay_data->stateless_mode && - netplay_data->connected_players && + (netplay_data->connected_players>1) && netplay_data->unread_frame_count <= netplay_data->run_frame_count) res = netplay_poll_net_input(netplay_data, true); else @@ -227,8 +301,8 @@ static bool netplay_poll(void) if (res == -1) goto catastrophe; - /* Simulate the input if we don't have real input */ - netplay_simulate_input(netplay_data, netplay_data->run_ptr, false); + /* Resolve and/or simulate the input if we don't have real input */ + netplay_resolve_input(netplay_data, netplay_data->run_ptr, false); /* Handle any slaves */ if (netplay_data->is_server && netplay_data->connected_slaves) @@ -369,21 +443,19 @@ static bool netplay_poll(void) /* Figure out who to blame */ if (netplay_data->is_server) { - for (player = 0; player < MAX_USERS; player++) + for (client = 1; client < MAX_CLIENTS; client++) { - if (!(netplay_data->connected_players & (1<read_frame_count[player] > netplay_data->unread_frame_count) continue; - for (i = 0; i < netplay_data->connections_size; i++) + struct netplay_connection *connection; + if (!(netplay_data->connected_players & (1<read_frame_count[client] > netplay_data->unread_frame_count) + continue; + connection = &netplay_data->connections[client-1]; + if (connection->active && + connection->mode == NETPLAY_CONNECTION_PLAYING) { - struct netplay_connection *connection = &netplay_data->connections[i]; - if (connection->active && - connection->mode == NETPLAY_CONNECTION_PLAYING && - connection->player == player) - { - connection->stall = NETPLAY_STALL_RUNNING_FAST; - connection->stall_time = netplay_data->stall_time; - break; - } + connection->stall = NETPLAY_STALL_RUNNING_FAST; + connection->stall_time = netplay_data->stall_time; } } } @@ -480,32 +552,37 @@ static int16_t netplay_input_state(netplay_t *netplay, { size_t ptr = netplay->is_replay ? netplay->replay_ptr : netplay->run_ptr; + struct delta_frame *delta; + netplay_input_state_t istate; const uint32_t *curr_input_state = NULL; - if (port <= 1) - { - /* Possibly flip the port */ - if (netplay_flip_port(netplay)) - port ^= 1; - } - else if (port >= MAX_USERS) - { + if (port >= MAX_INPUT_DEVICES) return 0; + + /* If the port doesn't seem to correspond to the device, "correct" it. This + * is common with devices that typically only have one instance, such as + * keyboards, mice and lightguns. */ + if (device != RETRO_DEVICE_JOYPAD && + (netplay->config_devices[port]&RETRO_DEVICE_MASK) != device) + { + for (port = 0; port < MAX_INPUT_DEVICES; port++) + { + if ((netplay->config_devices[port]&RETRO_DEVICE_MASK) == device) + break; + } + if (port == MAX_INPUT_DEVICES) + return 0; } - if (port > netplay->player_max) - netplay->player_max = port; + delta = &netplay->buffer[ptr]; + istate = delta->resolved_input[port]; + if (!istate || !istate->used) + return 0; - if (netplay->buffer[ptr].have_real[port]) - { - netplay->buffer[ptr].used_real[port] = true; - curr_input_state = netplay->buffer[ptr].real_input_state[port]; - } - else - { - curr_input_state = netplay->buffer[ptr].simulated_input_state[port]; - } + if (istate->size == 0) + return 0; + curr_input_state = istate->data; switch (device) { @@ -514,10 +591,36 @@ static int16_t netplay_input_state(netplay_t *netplay, case RETRO_DEVICE_ANALOG: { - uint32_t state = curr_input_state[1 + idx]; + uint32_t state; + if (istate->size != 3) + return 0; + state = curr_input_state[1 + idx]; return (int16_t)(uint16_t)(state >> (id * 16)); } + case RETRO_DEVICE_MOUSE: + case RETRO_DEVICE_LIGHTGUN: + { + if (istate->size != 2) + return 0; + if (id <= RETRO_DEVICE_ID_MOUSE_Y) + return (int16_t)(uint16_t)(curr_input_state[1] >> (id * 16)); + return ((1 << id) & curr_input_state[0]) ? 1 : 0; + } + + case RETRO_DEVICE_KEYBOARD: + { + unsigned key, word, bit; + key = netplay_key_hton(id); + if (key == NETPLAY_KEY_UNKNOWN) + return 0; + word = key/32; + bit = key%32; + if (word <= istate->size) + return ((1U<self_frame_count + 1; - uint32_t flip_frame_net = htonl(flip_frame); - 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_CONNECTED) - { - netplay_command(netplay, connection, NETPLAY_CMD_FLIP_PLAYERS, - &flip_frame_net, sizeof flip_frame_net, "flip users", - "Successfully flipped users.\n"); - } - } - - netplay->flip ^= true; - netplay->flip_frame = flip_frame; -} - -/** - * netplay_frontend_paused +* netplay_frontend_paused * @netplay : pointer to netplay object * @paused : true if frontend is paused * @@ -889,7 +963,7 @@ bool netplay_pre_frame(netplay_t *netplay) } if (sync_stalled || - ((!netplay->is_server || netplay->connected_players) && + ((!netplay->is_server || (netplay->connected_players>1)) && (netplay->stall || netplay->remote_paused))) { /* We may have received data even if we're stalled, so run post-frame @@ -942,19 +1016,20 @@ static void netplay_force_future(netplay_t *netplay) netplay->run_ptr = netplay->self_ptr; netplay->run_frame_count = netplay->self_frame_count; + /* We need to ignore any intervening data from the other side, * and never rewind past this */ netplay_update_unread_ptr(netplay); if (netplay->unread_frame_count < netplay->run_frame_count) { - uint32_t player; - for (player = 0; player < MAX_USERS; player++) + uint32_t client; + for (client = 0; client < MAX_CLIENTS; client++) { - if (!(netplay->connected_players & (1<read_frame_count[player] < netplay->run_frame_count) + if (!(netplay->connected_players & (1<read_frame_count[client] < netplay->run_frame_count) { - netplay->read_ptr[player] = netplay->run_ptr; - netplay->read_frame_count[player] = netplay->run_frame_count; + netplay->read_ptr[client] = netplay->run_ptr; + netplay->read_frame_count[client] = netplay->run_frame_count; } } if (netplay->server_frame_count < netplay->run_frame_count) @@ -1121,6 +1196,49 @@ static void netplay_core_reset(netplay_t *netplay) } } +/** + * netplay_settings_share_mode + * + * Get the preferred share mode + */ +uint8_t netplay_settings_share_mode(void) +{ + settings_t *settings = config_get_ptr(); + uint8_t share_mode = 0; + + if (settings->uints.netplay_share_digital + || settings->uints.netplay_share_analog) + { + switch (settings->uints.netplay_share_digital) + { + case RARCH_NETPLAY_SHARE_DIGITAL_OR: + share_mode |= NETPLAY_SHARE_DIGITAL_OR; + break; + case RARCH_NETPLAY_SHARE_DIGITAL_XOR: + share_mode |= NETPLAY_SHARE_DIGITAL_XOR; + break; + case RARCH_NETPLAY_SHARE_DIGITAL_VOTE: + share_mode |= NETPLAY_SHARE_DIGITAL_VOTE; + break; + default: + share_mode |= NETPLAY_SHARE_NO_PREFERENCE; + } + switch (settings->uints.netplay_share_analog) + { + case RARCH_NETPLAY_SHARE_ANALOG_MAX: + share_mode |= NETPLAY_SHARE_ANALOG_MAX; + break; + case RARCH_NETPLAY_SHARE_ANALOG_AVERAGE: + share_mode |= NETPLAY_SHARE_ANALOG_AVERAGE; + break; + default: + share_mode |= NETPLAY_SHARE_NO_PREFERENCE; + } + } + + return share_mode; +} + /** * netplay_toggle_play_spectate * @@ -1128,67 +1246,24 @@ static void netplay_core_reset(netplay_t *netplay) */ static void netplay_toggle_play_spectate(netplay_t *netplay) { - if (netplay->is_server) + size_t i; + enum rarch_netplay_connection_mode mode; + + if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING || + netplay->self_mode == NETPLAY_CONNECTION_SLAVE) { - /* FIXME: Duplication */ - uint32_t payload[2]; - char msg[512]; - const char *dmsg = NULL; - payload[0] = htonl(netplay->self_frame_count); - if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING || - netplay->self_mode == NETPLAY_CONNECTION_SLAVE) - { - /* Mark us as no longer playing */ - payload[1] = htonl(netplay->self_player); - netplay->self_mode = NETPLAY_CONNECTION_SPECTATING; - - dmsg = msg_hash_to_str(MSG_NETPLAY_YOU_HAVE_LEFT_THE_GAME); - - } - else if (netplay->self_mode == NETPLAY_CONNECTION_SPECTATING) - { - uint32_t player; - - /* Take a player number */ - for (player = 0; player < MAX_USERS; player++) - if (!(netplay->connected_players & (1<self_mode = NETPLAY_CONNECTION_PLAYING; - netplay->self_player = player; - - dmsg = msg; - msg[sizeof(msg)-1] = '\0'; - snprintf(msg, sizeof(msg)-1, msg_hash_to_str(MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N), player+1); - } - - RARCH_LOG("[netplay] %s\n", dmsg); - runloop_msg_queue_push(dmsg, 1, 180, false); - - netplay_send_raw_cmd_all(netplay, NULL, NETPLAY_CMD_MODE, payload, sizeof(payload)); - + /* Switch to spectator mode immediately */ + netplay->self_mode = NETPLAY_CONNECTION_SPECTATING; + mode = NETPLAY_CONNECTION_SPECTATING; } - else + else if (netplay->self_mode == NETPLAY_CONNECTION_SPECTATING) { - uint32_t cmd; - - if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING || - netplay->self_mode == NETPLAY_CONNECTION_SLAVE) - { - /* Switch to spectator mode immediately */ - netplay->self_mode = NETPLAY_CONNECTION_SPECTATING; - cmd = NETPLAY_CMD_SPECTATE; - } - else if (netplay->self_mode == NETPLAY_CONNECTION_SPECTATING) - { - /* Switch only after getting permission */ - cmd = NETPLAY_CMD_PLAY; - } - else return; - - netplay_send_raw_cmd_all(netplay, NULL, cmd, NULL, 0); + /* Switch only after getting permission */ + mode = NETPLAY_CONNECTION_PLAYING; } + else return; + + netplay_cmd_mode(netplay, mode); } /** @@ -1300,7 +1375,7 @@ bool init_netplay(void *direct_host, const char *server, unsigned port) if (netplay_data) { if (netplay_data->is_server && !settings->bools.netplay_start_as_spectator) - netplay_data->self_mode = NETPLAY_CONNECTION_PLAYING; + netplay_toggle_play_spectate(netplay_data); return true; } @@ -1388,10 +1463,6 @@ bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data) case RARCH_NETPLAY_CTL_PRE_FRAME: ret = netplay_pre_frame(netplay_data); goto done; - case RARCH_NETPLAY_CTL_FLIP_PLAYERS: - if (netplay_data->is_server) - netplay_flip_users(netplay_data); - break; case RARCH_NETPLAY_CTL_GAME_WATCH: netplay_toggle_play_spectate(netplay_data); break; diff --git a/network/netplay/netplay_handshake.c b/network/netplay/netplay_handshake.c index cdbc2f6f42..916583b237 100644 --- a/network/netplay/netplay_handshake.c +++ b/network/netplay/netplay_handshake.c @@ -35,12 +35,13 @@ #include "../../content.h" #include "../../retroarch.h" #include "../../version.h" -#include "../../input/input_driver.h" #ifdef HAVE_MENU #include "../../menu/widgets/menu_input_dialog.h" #endif +const uint32_t netplay_magic = 0x52414E50; /* RANP */ + /* TODO/FIXME - replace netplay_log_connection with calls * to inet_ntop_compat and move runloop message queue pushing * outside */ @@ -203,13 +204,15 @@ static uint32_t simple_rand_uint32(void) bool netplay_handshake_init_send(netplay_t *netplay, struct netplay_connection *connection) { - uint32_t header[4]; + uint32_t header[6]; settings_t *settings = config_get_ptr(); - header[0] = htonl(netplay_impl_magic()); + header[0] = htonl(netplay_magic); header[1] = htonl(netplay_platform_magic()); header[2] = htonl(NETPLAY_COMPRESSION_SUPPORTED); header[3] = 0; + header[4] = htonl(NETPLAY_PROTOCOL_VERSION); + header[5] = htonl(netplay_impl_magic()); if (netplay->is_server && (settings->paths.netplay_password[0] || @@ -303,30 +306,49 @@ bool netplay_handshake_init(netplay_t *netplay, { ssize_t recvd; struct nick_buf_s nick_buf; - uint32_t header[4]; + uint32_t header[6]; uint32_t local_pmagic = 0; uint32_t remote_pmagic = 0; + uint32_t remote_version = 0; uint32_t compression = 0; struct compression_transcoder *ctrans = NULL; const char *dmsg = NULL; - header[0] = 0; - header[1] = 0; - header[2] = 0; - header[3] = 0; + memset(header, 0, sizeof(header)); - RECV(header, sizeof(header)) + RECV(header, sizeof(uint32_t)) { dmsg = msg_hash_to_str(MSG_FAILED_TO_RECEIVE_HEADER_FROM_CLIENT); goto error; } - if (netplay_impl_magic() != ntohl(header[0])) + if (ntohl(header[0]) != netplay_magic) { - dmsg = msg_hash_to_str(MSG_NETPLAY_IMPLEMENTATIONS_DIFFER); + dmsg = msg_hash_to_str(MSG_NETPLAY_NOT_RETROARCH); goto error; } + RECV(header + 1, sizeof(header) - sizeof(uint32_t)) + { + dmsg = msg_hash_to_str(MSG_FAILED_TO_RECEIVE_HEADER_FROM_CLIENT); + goto error; + } + + remote_version = ntohl(header[4]); + if (remote_version < NETPLAY_PROTOCOL_VERSION) + { + dmsg = msg_hash_to_str(MSG_NETPLAY_OUT_OF_DATE); + 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); + } + /* We only care about platform magic if our core is quirky */ local_pmagic = netplay_platform_magic(); remote_pmagic = ntohl(header[1]); @@ -529,10 +551,10 @@ bool netplay_handshake_sync(netplay_t *netplay, /* If we're the server, now we send sync info */ size_t i; int matchct; - uint32_t cmd[5]; + uint32_t cmd[4]; retro_ctx_memory_info_t mem_info; + uint32_t client_num = 0; uint32_t device = 0; - uint32_t connected_players = 0; size_t nicklen, nickmangle = 0; bool nick_matched = false; @@ -543,30 +565,51 @@ bool netplay_handshake_sync(netplay_t *netplay, /* Send basic sync info */ cmd[0] = htonl(NETPLAY_CMD_SYNC); - cmd[1] = htonl(3*sizeof(uint32_t) + MAX_USERS*sizeof(uint32_t) + - NETPLAY_NICK_LEN + mem_info.size); + cmd[1] = htonl(2*sizeof(uint32_t) + /* Controller devices */ + + MAX_INPUT_DEVICES*sizeof(uint32_t) + + /* Share modes */ + + MAX_INPUT_DEVICES*sizeof(uint8_t) + + /* Device-client mapping */ + + MAX_INPUT_DEVICES*sizeof(uint32_t) + + /* Client nick */ + + NETPLAY_NICK_LEN + + /* And finally, sram */ + + mem_info.size); cmd[2] = htonl(netplay->self_frame_count); - connected_players = netplay->connected_players; - if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING) - connected_players |= 1<self_player; + client_num = connection - netplay->connections + 1; if (netplay->local_paused || netplay->remote_paused) - connected_players |= NETPLAY_CMD_SYNC_BIT_PAUSED; - cmd[3] = htonl(connected_players); - if (netplay->flip) - cmd[4] = htonl(netplay->flip_frame); - else - cmd[4] = htonl(0); + client_num |= NETPLAY_CMD_SYNC_BIT_PAUSED; + cmd[3] = htonl(client_num); if (!netplay_send(&connection->send_packet_buffer, connection->fd, cmd, sizeof(cmd))) return false; /* Now send the device info */ - for (i = 0; i < MAX_USERS; i++) + for (i = 0; i < MAX_INPUT_DEVICES; i++) { - device = htonl(input_config_get_device((unsigned)i)); + device = htonl(netplay->config_devices[i]); if (!netplay_send(&connection->send_packet_buffer, connection->fd, - &device, sizeof(device))) + &device, sizeof(device))) + return false; + } + + /* Then the share mode */ + if (!netplay_send(&connection->send_packet_buffer, connection->fd, + netplay->device_share_modes, sizeof(netplay->device_share_modes))) + return false; + + /* Then the device-client mapping */ + for (i = 0; i < MAX_INPUT_DEVICES; i++) + { + device = htonl(netplay->device_clients[i]); + if (!netplay_send(&connection->send_packet_buffer, connection->fd, + &device, sizeof(device))) return false; } @@ -835,12 +878,21 @@ bool netplay_handshake_pre_info(netplay_t *netplay, if (core_info) { if (strncmp(info_buf.core_name, - core_info->info.library_name, sizeof(info_buf.core_name)) || - strncmp(info_buf.core_version, + core_info->info.library_name, sizeof(info_buf.core_name))) + { + /* Wrong core! */ + dmsg = msg_hash_to_str(MSG_NETPLAY_DIFFERENT_CORES); + RARCH_ERR("%s\n", dmsg); + runloop_msg_queue_push(dmsg, 1, 180, false); + /* FIXME: Should still send INFO, so the other side knows what's what */ + return false; + } + if (strncmp(info_buf.core_version, core_info->info.library_version, sizeof(info_buf.core_version))) { - dmsg = msg_hash_to_str(MSG_NETPLAY_IMPLEMENTATIONS_DIFFER); - goto error; + dmsg = msg_hash_to_str(MSG_NETPLAY_DIFFERENT_CORE_VERSIONS); + RARCH_WARN("%s\n", dmsg); + runloop_msg_queue_push(dmsg, 1, 180, false); } } @@ -852,7 +904,8 @@ bool netplay_handshake_pre_info(netplay_t *netplay, if (ntohl(info_buf.content_crc) != content_crc) { dmsg = msg_hash_to_str(MSG_CONTENT_CRC32S_DIFFER); - goto error; + RARCH_WARN("%s\n", dmsg); + runloop_msg_queue_push(dmsg, 1, 180, false); } } @@ -873,24 +926,8 @@ bool netplay_handshake_pre_info(netplay_t *netplay, *had_input = true; netplay_recv_flush(&connection->recv_packet_buffer); return true; - -error: - if (dmsg) - { - RARCH_ERR("%s\n", dmsg); - runloop_msg_queue_push(dmsg, 1, 180, false); - } - - if (!netplay->is_server) - { - /* Counter-intuitively, we still want to send our info. This is simply so - * that the server knows why we disconnected. */ - if (!netplay_handshake_info(netplay, connection)) - return false; - } - - return false; } + /** * netplay_handshake_pre_sync * @@ -901,10 +938,10 @@ bool netplay_handshake_pre_sync(netplay_t *netplay, struct netplay_connection *connection, bool *had_input) { uint32_t cmd[2]; - uint32_t new_frame_count, connected_players, flip_frame; + uint32_t new_frame_count, client_num, flip_frame; uint32_t device; uint32_t local_sram_size, remote_sram_size; - size_t i; + size_t i, j; ssize_t recvd; retro_ctx_controller_info_t pad; char new_nick[NETPLAY_NICK_LEN]; @@ -921,7 +958,7 @@ bool netplay_handshake_pre_sync(netplay_t *netplay, /* Only expecting a sync command */ if (ntohl(cmd[0]) != NETPLAY_CMD_SYNC || - ntohl(cmd[1]) < 3*sizeof(uint32_t) + MAX_USERS*sizeof(uint32_t) + + ntohl(cmd[1]) < (2+2*MAX_INPUT_DEVICES)*sizeof(uint32_t) + (MAX_INPUT_DEVICES)*sizeof(uint8_t) + NETPLAY_NICK_LEN) { RARCH_ERR("%s\n", @@ -934,30 +971,23 @@ bool netplay_handshake_pre_sync(netplay_t *netplay, return false; new_frame_count = ntohl(new_frame_count); - /* Get the connected players and pause mode */ - RECV(&connected_players, sizeof(connected_players)) + /* Get our client number and pause mode */ + RECV(&client_num, sizeof(client_num)) return false; - connected_players = ntohl(connected_players); - if (connected_players & NETPLAY_CMD_SYNC_BIT_PAUSED) + client_num = ntohl(client_num); + if (client_num & NETPLAY_CMD_SYNC_BIT_PAUSED) { netplay->remote_paused = true; - connected_players ^= NETPLAY_CMD_SYNC_BIT_PAUSED; + client_num ^= NETPLAY_CMD_SYNC_BIT_PAUSED; } - netplay->connected_players = connected_players; - - /* And the flip state */ - RECV(&flip_frame, sizeof(flip_frame)) - return false; - - flip_frame = ntohl(flip_frame); - netplay->flip = !!flip_frame; - netplay->flip_frame = flip_frame; + netplay->self_client_num = client_num; /* Set our frame counters as requested */ netplay->self_frame_count = netplay->run_frame_count = netplay->other_frame_count = netplay->unread_frame_count = netplay->server_frame_count = new_frame_count; + /* And clear out the framebuffer */ for (i = 0; i < netplay->buffer_size; i++) { struct delta_frame *ptr = &netplay->buffer[i]; @@ -977,27 +1007,55 @@ bool netplay_handshake_pre_sync(netplay_t *netplay, } } - for (i = 0; i < MAX_USERS; i++) + for (i = 0; i < MAX_CLIENTS; i++) { - if (connected_players & (1<read_ptr[i] = netplay->self_ptr; - netplay->read_frame_count[i] = netplay->self_frame_count; - } + netplay->read_ptr[i] = netplay->self_ptr; + netplay->read_frame_count[i] = netplay->self_frame_count; } - /* Get and set each pad */ - for (i = 0; i < MAX_USERS; i++) + /* Get and set each input device */ + for (i = 0; i < MAX_INPUT_DEVICES; i++) { RECV(&device, sizeof(device)) return false; pad.port = (unsigned)i; pad.device = ntohl(device); + netplay->config_devices[i] = pad.device; + if ((pad.device&RETRO_DEVICE_MASK) == RETRO_DEVICE_KEYBOARD) + { + netplay->have_updown_device = true; + netplay_key_hton_init(); + } core_set_controller_port_device(&pad); } + /* Get the share modes */ + RECV(netplay->device_share_modes, sizeof(netplay->device_share_modes)) + return false; + + /* Get the client-controller mapping */ + netplay->connected_players = + netplay->connected_slaves = + netplay->self_devices = 0; + for (i = 0; i < MAX_CLIENTS; i++) + netplay->client_devices[i] = 0; + for (i = 0; i < MAX_INPUT_DEVICES; i++) + { + RECV(&device, sizeof(device)) + return false; + device = ntohl(device); + + netplay->device_clients[i] = device; + netplay->connected_players |= device; + for (j = 0; j < MAX_CLIENTS; j++) + { + if (device & (1<client_devices[j] |= 1<bools.netplay_start_as_spectator) - return netplay_cmd_mode(netplay, connection, NETPLAY_CONNECTION_PLAYING); + return netplay_cmd_mode(netplay, NETPLAY_CONNECTION_PLAYING); return true; } diff --git a/network/netplay/netplay_init.c b/network/netplay/netplay_init.c index 0ec1c4ebe1..4822b31614 100644 --- a/network/netplay/netplay_init.c +++ b/network/netplay/netplay_init.c @@ -32,6 +32,7 @@ #include "../../autosave.h" #include "../../retroarch.h" +#include "../../input/input_driver.h" #if defined(AF_INET6) && !defined(HAVE_SOCKET_LEGACY) #define HAVE_INET6 1 @@ -183,12 +184,8 @@ static bool init_tcp_socket(netplay_t *netplay, void *direct_host, while (tmp_info) { - struct sockaddr_storage sad; - int fd; - - memset(&sad, 0, sizeof(sad)); - - fd = init_tcp_connection( + struct sockaddr_storage sad = {0}; + int fd = init_tcp_connection( tmp_info, direct_host || server, (struct sockaddr*)&sad, @@ -243,7 +240,7 @@ static bool netplay_init_socket_buffers(netplay_t *netplay) * frames of input data, plus the headers for each of them */ size_t i; size_t packet_buffer_size = netplay->zbuffer_size + - NETPLAY_MAX_STALL_FRAMES * WORDS_PER_FRAME + (NETPLAY_MAX_STALL_FRAMES+1)*3; + NETPLAY_MAX_STALL_FRAMES * 16; netplay->packet_buffer_size = packet_buffer_size; for (i = 0; i < netplay->connections_size; i++) @@ -430,8 +427,6 @@ netplay_t *netplay_new(void *direct_host, const char *server, uint16_t port, netplay->listen_fd = -1; netplay->tcp_port = port; netplay->cbs = *cb; - netplay->connected_players = 0; - netplay->player_max = 1; netplay->is_server = (direct_host == NULL && server == NULL); netplay->is_connected = false;; netplay->nat_traversal = netplay->is_server ? nat_traversal : false; @@ -472,8 +467,26 @@ netplay_t *netplay_new(void *direct_host, const char *server, uint16_t port, return NULL; } - if (!netplay->is_server) + if (netplay->is_server) { + /* Clients get device info from the server */ + unsigned i; + for (i = 0; i < MAX_INPUT_DEVICES; i++) + { + uint32_t dtype = input_config_get_device(i); + netplay->config_devices[i] = dtype; + if ((dtype&RETRO_DEVICE_MASK) == RETRO_DEVICE_KEYBOARD) + { + netplay->have_updown_device = true; + netplay_key_hton_init(); + } + if (dtype != RETRO_DEVICE_NONE && !netplay_expected_input_size(netplay, 1<connections[0]); netplay->connections[0].mode = NETPLAY_CONNECTION_INIT; @@ -539,8 +552,7 @@ void netplay_free(netplay_t *netplay) if (netplay->buffer) { for (i = 0; i < netplay->buffer_size; i++) - if (netplay->buffer[i].state) - free(netplay->buffer[i].state); + netplay_delta_frame_free(&netplay->buffer[i]); free(netplay->buffer); } diff --git a/network/netplay/netplay_io.c b/network/netplay/netplay_io.c index 536c7a742d..6fa5b0f5e4 100644 --- a/network/netplay/netplay_io.c +++ b/network/netplay/netplay_io.c @@ -28,6 +28,10 @@ #include "../../retroarch.h" #include "../../tasks/tasks_internal.h" +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 *payload); + #if 0 #define DEBUG_NETPLAY_STEPS 1 @@ -35,7 +39,7 @@ static void print_state(netplay_t *netplay) { char msg[512]; size_t cur = 0; - uint32_t player; + uint32_t client; #define APPEND(out) cur += snprintf out #define M msg + cur, sizeof(msg) - cur @@ -43,12 +47,11 @@ static void print_state(netplay_t *netplay) APPEND((M, "NETPLAY: S:%u U:%u O:%u", netplay->self_frame_count, netplay->unread_frame_count, netplay->other_frame_count)); if (!netplay->is_server) APPEND((M, " H:%u", netplay->server_frame_count)); - for (player = 0; player < MAX_USERS; player++) + for (client = 0; client < MAX_USERS; client++) { - if ((netplay->connected_players & (1<read_frame_count[player])); + if ((netplay->connected_players & (1<read_frame_count[client])); } - APPEND((M, "\n")); msg[sizeof(msg)-1] = '\0'; RARCH_LOG("%s\n", msg); @@ -91,6 +94,7 @@ void netplay_hangup(netplay_t *netplay, struct netplay_connection *connection) { char msg[512]; const char *dmsg; + size_t i; if (!netplay) return; @@ -124,12 +128,22 @@ void netplay_hangup(netplay_t *netplay, struct netplay_connection *connection) if (!netplay->is_server) { netplay->self_mode = NETPLAY_CONNECTION_NONE; - netplay->connected_players = 0; + 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; + } + for (i = 0; i < MAX_INPUT_DEVICES; i++) + netplay->device_clients[i] &= (1L<self_client_num); netplay->stall = NETPLAY_STALL_NONE; } else { + uint32_t client_num = connection - netplay->connections + 1; + /* Mark the player for removal */ if (connection->mode == NETPLAY_CONNECTION_PLAYING || connection->mode == NETPLAY_CONNECTION_SLAVE) @@ -137,11 +151,14 @@ void netplay_hangup(netplay_t *netplay, struct netplay_connection *connection) /* This special mode keeps the connection object alive long enough to * send the disconnection message at the correct time */ connection->mode = NETPLAY_CONNECTION_DELAYED_DISCONNECT; - connection->delay_frame = netplay->read_frame_count[connection->player]; + connection->delay_frame = netplay->read_frame_count[client_num]; /* Mark them as not playing anymore */ - netplay->connected_players &= ~(1<player); - netplay->connected_slaves &= ~(1<player); + netplay->connected_players &= ~(1L<connected_slaves &= ~(1L<client_devices[client_num] = 0; + for (i = 0; i < MAX_INPUT_DEVICES; i++) + netplay->device_clients[i] &= ~(1L<connections_size; i++) { + uint32_t client_num = i+1; connection = &netplay->connections[i]; if ((connection->active || connection->mode == NETPLAY_CONNECTION_DELAYED_DISCONNECT) && connection->delay_frame && connection->delay_frame <= netplay->self_frame_count) { /* Something was delayed! Prepare the MODE command */ - uint32_t payload[2]; + uint32_t payload[15] = {0}; payload[0] = htonl(connection->delay_frame); - payload[1] = htonl(connection->player); + payload[1] = htonl(client_num); + payload[2] = htonl(0); + memcpy(payload + 3, netplay->device_share_modes, sizeof(netplay->device_share_modes)); + strncpy((char *) (payload + 7), connection->nick, NETPLAY_NICK_LEN); /* Remove the connection entirely if relevant */ if (connection->mode == NETPLAY_CONNECTION_DELAYED_DISCONNECT) @@ -189,24 +210,48 @@ void netplay_delayed_state_change(netplay_t *netplay) } /* Send the specified input data */ -static bool send_input_frame(netplay_t *netplay, - struct netplay_connection *only, struct netplay_connection *except, - uint32_t frame, uint32_t player, uint32_t *state) +static bool send_input_frame(netplay_t *netplay, struct delta_frame *dframe, + struct netplay_connection *only, struct netplay_connection *except, + uint32_t client_num, bool slave) { - uint32_t buffer[2 + WORDS_PER_FRAME]; - size_t i; +#define BUFSZ 16 /* FIXME: Arbitrary restriction */ + uint32_t buffer[BUFSZ], devices, device; + size_t bufused, i; + /* Set up the basic buffer */ + bufused = 4; buffer[0] = htonl(NETPLAY_CMD_INPUT); - buffer[1] = htonl(WORDS_PER_FRAME * sizeof(uint32_t)); - buffer[2] = htonl(frame); - buffer[3] = htonl(player); - buffer[4] = htonl(state[0]); - buffer[5] = htonl(state[1]); - buffer[6] = htonl(state[2]); + buffer[2] = htonl(dframe->frame); + buffer[3] = htonl(client_num); + + /* Add the device data */ + devices = netplay->client_devices[client_num]; + for (device = 0; device < MAX_INPUT_DEVICES; device++) + { + netplay_input_state_t istate; + if (!(devices & (1<real_input[device]; + while (istate && (!istate->used || istate->client_num != (slave?MAX_CLIENTS:client_num))) + istate = istate->next; + if (!istate) + continue; + if (bufused + istate->size >= BUFSZ) + continue; /* FIXME: More severe? */ + for (i = 0; i < istate->size; i++) + buffer[bufused+i] = htonl(istate->data[i]); + bufused += istate->size; + } + buffer[1] = htonl((bufused-2) * sizeof(uint32_t)); + +#ifdef DEBUG_NETPLAY_STEPS + RARCH_LOG("Sending input for client %u\n", (unsigned) client_num); + print_state(netplay); +#endif if (only) { - if (!netplay_send(&only->send_packet_buffer, only->fd, buffer, sizeof(buffer))) + if (!netplay_send(&only->send_packet_buffer, only->fd, buffer, bufused*sizeof(uint32_t))) { netplay_hangup(netplay, only); return false; @@ -221,16 +266,17 @@ static bool send_input_frame(netplay_t *netplay, if (connection->active && connection->mode >= NETPLAY_CONNECTION_CONNECTED && (connection->mode != NETPLAY_CONNECTION_PLAYING || - connection->player != player)) + i+1 != client_num)) { if (!netplay_send(&connection->send_packet_buffer, connection->fd, - buffer, sizeof(buffer))) + buffer, bufused*sizeof(uint32_t))) netplay_hangup(netplay, connection); } } } return true; +#undef BUFSZ } /** @@ -244,23 +290,25 @@ bool netplay_send_cur_input(netplay_t *netplay, struct netplay_connection *connection) { struct delta_frame *dframe = &netplay->buffer[netplay->self_ptr]; - uint32_t player; + uint32_t from_client, to_client; + size_t i; + netplay_input_state_t istate; if (netplay->is_server) { - /* Send the other players' input data */ - for (player = 0; player < MAX_USERS; player++) + to_client = connection - netplay->connections + 1; + + /* Send the other players' input data (FIXME: This involves an + * unacceptable amount of recalculating) */ + for (from_client = 1; from_client < MAX_CLIENTS; from_client++) { - if (connection->mode == NETPLAY_CONNECTION_PLAYING && - connection->player == player) + if (from_client == to_client) continue; - if ((netplay->connected_players & (1<connected_players & (1<have_real[player]) + if (dframe->have_real[from_client]) { - if (!send_input_frame(netplay, connection, NULL, - netplay->self_frame_count, player, - dframe->real_input_state[player])) + if (!send_input_frame(netplay, dframe, connection, NULL, from_client, false)) return false; } } @@ -278,13 +326,12 @@ bool netplay_send_cur_input(netplay_t *netplay, } /* Send our own data */ - if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING || - netplay->self_mode == NETPLAY_CONNECTION_SLAVE) + if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING + || netplay->self_mode == NETPLAY_CONNECTION_SLAVE) { - if (!send_input_frame(netplay, connection, NULL, - netplay->self_frame_count, - (netplay->is_server ? NETPLAY_CMD_INPUT_BIT_SERVER : 0) | netplay->self_player, - dframe->self_state)) + if (!send_input_frame(netplay, dframe, connection, NULL, + netplay->self_client_num, + netplay->self_mode == NETPLAY_CONNECTION_SLAVE)) return false; } @@ -419,14 +466,21 @@ bool netplay_cmd_request_savestate(netplay_t *netplay) /** * netplay_cmd_mode * - * Send a mode request command to either play or spectate. + * Send a mode change request. As a server, the request is to ourself, and so + * honored instantly. */ bool netplay_cmd_mode(netplay_t *netplay, - struct netplay_connection *connection, enum rarch_netplay_connection_mode mode) { - uint32_t cmd; - uint32_t payloadBuf, *payload = NULL; + uint32_t cmd, device; + uint32_t payloadBuf = 0, *payload = NULL; + uint8_t share_mode; + settings_t *settings = config_get_ptr(); + struct netplay_connection *connection = NULL; + + if (!netplay->is_server) + connection = &netplay->one_connection; + switch (mode) { case NETPLAY_CONNECTION_SPECTATING: @@ -434,19 +488,42 @@ bool netplay_cmd_mode(netplay_t *netplay, break; case NETPLAY_CONNECTION_SLAVE: - payload = &payloadBuf; - payloadBuf = htonl(NETPLAY_CMD_PLAY_BIT_SLAVE); - /* Intentional fallthrough */ + payloadBuf = NETPLAY_CMD_PLAY_BIT_SLAVE; + /* no break */ case NETPLAY_CONNECTION_PLAYING: + payload = &payloadBuf; + + /* Add a share mode if requested */ + share_mode = netplay_settings_share_mode(); + payloadBuf |= ((uint32_t) share_mode) << 16; + + /* Request devices */ + for (device = 0; device < MAX_INPUT_DEVICES; device++) + { + if (settings->bools.netplay_request_devices[device]) + payloadBuf |= 1<is_server) + { + handle_play_spectate(netplay, 0, NULL, cmd, payload ? sizeof(uint32_t) : 0, payload); + return true; + + } + else + { + return netplay_send_raw_cmd(netplay, connection, cmd, payload, + payload ? sizeof(uint32_t) : 0); + } } /** @@ -462,6 +539,366 @@ bool netplay_cmd_stall(netplay_t *netplay, return netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_STALL, &frames, sizeof(frames)); } +/** + * announce_play_spectate + * + * Announce a play or spectate mode change + */ +static void announce_play_spectate(netplay_t *netplay, + const char *nick, + enum rarch_netplay_connection_mode mode, uint32_t devices) +{ + char msg[512]; + msg[0] = msg[sizeof(msg) - 1] = '\0'; + + switch (mode) + { + case NETPLAY_CONNECTION_SPECTATING: + if (nick) + { + snprintf_p(msg, sizeof(msg) - 1, + msg_hash_to_str(MSG_NETPLAY_PLAYER_S_LEFT), NETPLAY_NICK_LEN, + nick); + } + else + { + strlcpy(msg, msg_hash_to_str(MSG_NETPLAY_YOU_HAVE_LEFT_THE_GAME), sizeof(msg)); + } + break; + + case NETPLAY_CONNECTION_PLAYING: + case NETPLAY_CONNECTION_SLAVE: + { + uint32_t device; + uint32_t one_device = (uint32_t) -1; + char device_str[512]; + size_t device_str_len; + + for (device = 0; device < MAX_INPUT_DEVICES; device++) + { + if (!(devices & (1<delay_frame = netplay->read_frame_count[client_num]; + + /* Mark them as not playing anymore */ + if (connection) + { + connection->mode = NETPLAY_CONNECTION_SPECTATING; + } + else + { + netplay->self_devices = 0; + netplay->self_mode = NETPLAY_CONNECTION_SPECTATING; + } + netplay->connected_players &= ~(1 << client_num); + netplay->connected_slaves &= ~(1 << client_num); + netplay->client_devices[client_num] = 0; + for (i = 0; i < MAX_INPUT_DEVICES; i++) + netplay->device_clients[client_num] &= ~(1 << client_num); + + /* Tell someone */ + payload[0] = htonl(netplay->read_frame_count[client_num]); + payload[2] = htonl(0); + memcpy(payload + 3, netplay->device_share_modes, sizeof(netplay->device_share_modes)); + if (connection) + { + /* Only tell the player. The others will be told at delay_frame */ + payload[1] = htonl(NETPLAY_CMD_MODE_BIT_YOU | client_num); + strncpy((char *) (payload + 7), connection->nick, NETPLAY_NICK_LEN); + netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload)); + + } + else + { + /* It was the server, so tell everyone else */ + payload[1] = htonl(0); + strncpy((char *) (payload + 7), netplay->nick, NETPLAY_NICK_LEN); + netplay_send_raw_cmd_all(netplay, NULL, NETPLAY_CMD_MODE, payload, sizeof(payload)); + + } + + /* Announce it */ + announce_play_spectate(netplay, connection ? connection->nick : NULL, + NETPLAY_CONNECTION_SPECTATING, 0); + break; + } + + case NETPLAY_CMD_PLAY: + { + uint32_t mode, devices = 0, device; + uint8_t share_mode; + bool slave = false; + settings_t *settings = config_get_ptr(); + + if (cmd_size != sizeof(uint32_t) || !in_payload) + return; + mode = ntohl(in_payload[0]); + + /* Check the requested mode */ + slave = (mode&NETPLAY_CMD_PLAY_BIT_SLAVE)?true:false; + share_mode = (mode>>16)&0xFF; + + /* And the requested devices */ + devices = mode&0xFFFF; + + /* Check if their slave mode request corresponds with what we allow */ + if (connection) + { + if (settings->bools.netplay_require_slaves) + slave = true; + else if (!settings->bools.netplay_allow_slaves) + slave = false; + } + else + slave = false; + + /* Fix our share mode */ + if (share_mode) + { + if ((share_mode & NETPLAY_SHARE_DIGITAL_BITS) == 0) + share_mode |= NETPLAY_SHARE_DIGITAL_OR; + if ((share_mode & NETPLAY_SHARE_ANALOG_BITS) == 0) + share_mode |= NETPLAY_SHARE_ANALOG_MAX; + share_mode &= ~NETPLAY_SHARE_NO_PREFERENCE; + } + + /* They start at the next frame, but we start immediately */ + if (connection) + { + netplay->read_ptr[client_num] = NEXT_PTR(netplay->self_ptr); + netplay->read_frame_count[client_num] = netplay->self_frame_count + 1; + } + else + { + netplay->read_ptr[client_num] = netplay->self_ptr; + netplay->read_frame_count[client_num] = netplay->self_frame_count; + } + payload[0] = htonl(netplay->read_frame_count[client_num]); + + if (devices) + { + /* Make sure the devices are available and/or shareable */ + for (device = 0; device < MAX_INPUT_DEVICES; device++) + { + if (!(devices & (1<device_clients[device]) continue; + if (netplay->device_share_modes[device] && share_mode) continue; + + /* Device already taken and unshareable */ + payload[0] = htonl(NETPLAY_CMD_MODE_REFUSED_REASON_NOT_AVAILABLE); + /* FIXME: Refusal message for the server */ + if (connection) + netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE_REFUSED, payload, sizeof(uint32_t)); + devices = 0; + break; + } + if (devices == 0) + break; + + /* Set the share mode on any new devices */ + for (device = 0; device < MAX_INPUT_DEVICES; device++) + { + if (!(devices & (1<device_clients[device]) + netplay->device_share_modes[device] = share_mode; + } + + } + else + { + /* Find an available device */ + for (device = 0; device < MAX_INPUT_DEVICES; device++) + { + if (netplay->config_devices[device] == RETRO_DEVICE_NONE) + { + device = MAX_INPUT_DEVICES; + break; + } + if (!netplay->device_clients[device]) + break; + } + if (device >= MAX_INPUT_DEVICES && share_mode) + { + /* No device was totally free, maybe one is shareable? */ + for (device = 0; device < MAX_INPUT_DEVICES; device++) + { + if (netplay->device_clients[device] && netplay->device_share_modes[device]) + { + share_mode = netplay->device_share_modes[device]; + break; + } + } + } + if (device >= MAX_INPUT_DEVICES) + { + /* No slots free! */ + payload[0] = htonl(NETPLAY_CMD_MODE_REFUSED_REASON_NO_SLOTS); + /* FIXME: Message for the server */ + if (connection) + netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE_REFUSED, payload, sizeof(uint32_t)); + break; + } + devices = 1<device_share_modes[device] = share_mode; + + } + + payload[2] = htonl(devices); + + /* Mark them as playing */ + if (connection) + { + connection->mode = + slave ? NETPLAY_CONNECTION_SLAVE : NETPLAY_CONNECTION_PLAYING; + } + else + { + netplay->self_devices = devices; + netplay->self_mode = NETPLAY_CONNECTION_PLAYING; + } + netplay->connected_players |= 1 << client_num; + if (slave) + netplay->connected_slaves |= 1 << client_num; + netplay->client_devices[client_num] = devices; + for (device = 0; device < MAX_INPUT_DEVICES; device++) + { + if (!(devices & (1<device_clients[device] |= 1 << client_num; + } + + /* Tell everyone */ + payload[1] = htonl( + NETPLAY_CMD_MODE_BIT_PLAYING + | (slave ? NETPLAY_CMD_MODE_BIT_SLAVE : 0) | client_num); + memcpy(payload + 3, netplay->device_share_modes, sizeof(netplay->device_share_modes)); + if (connection) + strncpy((char *) (payload + 7), connection->nick, NETPLAY_NICK_LEN); + else + strncpy((char *) (payload + 7), netplay->nick, NETPLAY_NICK_LEN); + netplay_send_raw_cmd_all(netplay, connection, NETPLAY_CMD_MODE, + payload, sizeof(payload)); + + /* Tell the player */ + if (connection) + { + payload[1] = htonl(NETPLAY_CMD_MODE_BIT_PLAYING | + ((connection->mode == NETPLAY_CONNECTION_SLAVE)? + NETPLAY_CMD_MODE_BIT_SLAVE:0) | + NETPLAY_CMD_MODE_BIT_YOU | + client_num); + netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload)); + } + + /* Announce it */ + announce_play_spectate(netplay, connection ? connection->nick : NULL, + NETPLAY_CONNECTION_PLAYING, devices); + break; + } + } +} + #undef RECV #define RECV(buf, sz) \ recvd = netplay_recv(&connection->recv_packet_buffer, connection->fd, (buf), \ @@ -492,6 +929,11 @@ static bool netplay_get_cmd(netplay_t *netplay, cmd_size = ntohl(cmd_size); +#ifdef DEBUG_NETPLAY_STEPS + RARCH_LOG("Received netplay command %X (%u) from %u\n", cmd, cmd_size, + (unsigned) (connection - netplay->connections)); +#endif + netplay->timeout_cnt = 0; switch (cmd) @@ -506,43 +948,53 @@ static bool netplay_get_cmd(netplay_t *netplay, case NETPLAY_CMD_INPUT: { - uint32_t buffer[WORDS_PER_FRAME]; - uint32_t player; + uint32_t frame_num, client_num, input_size, devices, device; unsigned i; struct delta_frame *dframe; - if (cmd_size != WORDS_PER_FRAME * sizeof(uint32_t)) + if (cmd_size < 2*sizeof(uint32_t)) { - RARCH_ERR("NETPLAY_CMD_INPUT received an unexpected payload size.\n"); + RARCH_ERR("NETPLAY_CMD_INPUT too short, no frame/client number."); return netplay_cmd_nak(netplay, connection); } - RECV(buffer, sizeof(buffer)) - { - RARCH_ERR("Failed to receive NETPLAY_CMD_INPUT input.\n"); - return netplay_cmd_nak(netplay, connection); - } - - for (i = 0; i < WORDS_PER_FRAME; i++) - buffer[i] = ntohl(buffer[i]); + RECV(&frame_num, sizeof(frame_num)) + return false; + RECV(&client_num, sizeof(client_num)) + return false; + frame_num = ntohl(frame_num); + client_num = ntohl(client_num); + client_num &= 0xFFFF; if (netplay->is_server) { - /* Ignore the claimed player #, must be this client */ + /* Ignore the claimed client #, must be this client */ if (connection->mode != NETPLAY_CONNECTION_PLAYING && connection->mode != NETPLAY_CONNECTION_SLAVE) { RARCH_ERR("Netplay input from non-participating player.\n"); return netplay_cmd_nak(netplay, connection); } - player = connection->player; - } - else - { - player = buffer[1] & ~NETPLAY_CMD_INPUT_BIT_SERVER; + client_num = connection - netplay->connections + 1; } - if (player >= MAX_USERS || !(netplay->connected_players & (1< MAX_CLIENTS) + { + RARCH_ERR("NETPLAY_CMD_INPUT received data for an unsupported client.\n"); + return netplay_cmd_nak(netplay, connection); + } + + /* Figure out how much input is expected */ + devices = netplay->client_devices[client_num]; + input_size = netplay_expected_input_size(netplay, devices); + + if (cmd_size != (2+input_size) * sizeof(uint32_t)) + { + RARCH_ERR("NETPLAY_CMD_INPUT received an unexpected payload size.\n"); + return netplay_cmd_nak(netplay, connection); + } + + if (client_num >= MAX_CLIENTS || !(netplay->connected_players & (1<mode == NETPLAY_CONNECTION_PLAYING) { - if (buffer[0] < netplay->read_frame_count[player]) + if (frame_num < netplay->read_frame_count[client_num]) { + uint32_t buf; /* We already had this, so ignore the new transmission */ + for (; input_size; input_size--) + { + RECV(&buf, sizeof(uint32_t)) + return netplay_cmd_nak(netplay, connection); + } break; } - else if (buffer[0] > netplay->read_frame_count[player]) + else if (frame_num > netplay->read_frame_count[client_num]) { /* Out of order = out of luck */ RARCH_ERR("Netplay input out of order.\n"); @@ -565,42 +1023,63 @@ static bool netplay_get_cmd(netplay_t *netplay, } /* The data's good! */ - dframe = &netplay->buffer[netplay->read_ptr[player]]; - if (!netplay_delta_frame_ready(netplay, dframe, netplay->read_frame_count[player])) + dframe = &netplay->buffer[netplay->read_ptr[client_num]]; + if (!netplay_delta_frame_ready(netplay, dframe, netplay->read_frame_count[client_num])) { /* Hopefully we'll be ready after another round of input */ goto shrt; } - memcpy(dframe->real_input_state[player], buffer + 2, - WORDS_PER_INPUT*sizeof(uint32_t)); - dframe->have_real[player] = true; + + /* Copy in the input */ + for (device = 0; device < MAX_INPUT_DEVICES; device++) + { + netplay_input_state_t istate; + uint32_t dsize, di; + if (!(devices & (1<real_input[device], + client_num, dsize, + false /* Must be false because of slave-mode clients */, + false); + if (!istate) + { + /* Catastrophe! */ + return netplay_cmd_nak(netplay, connection); + } + RECV(istate->data, dsize*sizeof(uint32_t)) + return false; + for (di = 0; di < dsize; di++) + istate->data[di] = ntohl(istate->data[di]); + } + dframe->have_real[client_num] = true; /* 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) { - netplay->read_ptr[player] = NEXT_PTR(netplay->read_ptr[player]); - netplay->read_frame_count[player]++; + netplay->read_ptr[client_num] = NEXT_PTR(netplay->read_ptr[client_num]); + netplay->read_frame_count[client_num]++; if (netplay->is_server) { - /* Forward it on if it's past data*/ + /* 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]); + send_input_frame(netplay, dframe, NULL, connection, client_num, false); } } /* If this was server data, advance our server pointer too */ - if (!netplay->is_server && (buffer[1] & NETPLAY_CMD_INPUT_BIT_SERVER)) + if (!netplay->is_server && client_num == 0) { - netplay->server_ptr = netplay->read_ptr[player]; - netplay->server_frame_count = netplay->read_frame_count[player]; + netplay->server_ptr = netplay->read_ptr[0]; + netplay->server_frame_count = netplay->read_frame_count[0]; } #ifdef DEBUG_NETPLAY_STEPS - RARCH_LOG("Received input from %u\n", player); + RARCH_LOG("Received input from %u\n", client_num); print_state(netplay); #endif break; @@ -638,54 +1117,16 @@ static bool netplay_get_cmd(netplay_t *netplay, netplay->server_ptr = NEXT_PTR(netplay->server_ptr); netplay->server_frame_count++; +#ifdef DEBUG_NETPLAY_STEPS + RARCH_LOG("Received server noinput\n"); + print_state(netplay); +#endif break; } - case NETPLAY_CMD_FLIP_PLAYERS: - if (cmd_size != sizeof(uint32_t)) - { - RARCH_ERR("CMD_FLIP_PLAYERS received an unexpected command size.\n"); - return netplay_cmd_nak(netplay, connection); - } - - RECV(&flip_frame, sizeof(flip_frame)) - { - RARCH_ERR("Failed to receive CMD_FLIP_PLAYERS argument.\n"); - return netplay_cmd_nak(netplay, connection); - } - - if (netplay->is_server) - { - RARCH_ERR("NETPLAY_CMD_FLIP_PLAYERS from a client.\n"); - return netplay_cmd_nak(netplay, connection); - } - - flip_frame = ntohl(flip_frame); - - if (flip_frame < netplay->server_frame_count) - { - RARCH_ERR("Host asked us to flip users in the past. Not possible ...\n"); - return netplay_cmd_nak(netplay, connection); - } - - netplay->flip ^= true; - netplay->flip_frame = flip_frame; - - /* Force a rewind to assure the flip happens: This just prevents us - * from skipping other past the flip because our prediction was - * correct */ - if (flip_frame < netplay->self_frame_count) - netplay->force_rewind = true; - - RARCH_LOG("%s.\n", msg_hash_to_str(MSG_NETPLAY_USERS_HAS_FLIPPED)); - runloop_msg_queue_push( - msg_hash_to_str(MSG_NETPLAY_USERS_HAS_FLIPPED), 1, 180, false); - - break; - case NETPLAY_CMD_SPECTATE: { - uint32_t payload[2]; + uint32_t client_num; if (!netplay->is_server) { @@ -693,67 +1134,41 @@ 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 (cmd_size != 0) { - /* The frame we haven't received is their end frame */ - connection->delay_frame = netplay->read_frame_count[connection->player]; - - /* Mark them as not playing anymore */ - connection->mode = NETPLAY_CONNECTION_SPECTATING; - netplay->connected_players &= ~(1<player); - netplay->connected_slaves &= ~(1<player); - - /* Announce it */ - msg[sizeof(msg)-1] = '\0'; - snprintf(msg, sizeof(msg)-1, "Player %d has left", connection->player+1); - RARCH_LOG("%s\n", msg); - runloop_msg_queue_push(msg, 1, 180, false); - } - else - { - payload[0] = htonl(0); + RARCH_ERR("Unexpected payload in NETPLAY_CMD_SPECTATE.\n"); + return netplay_cmd_nak(netplay, connection); } - /* Tell the player even if they were confused */ - payload[1] = htonl(NETPLAY_CMD_MODE_BIT_YOU | connection->player); - netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload)); + if (connection->mode != NETPLAY_CONNECTION_PLAYING && + connection->mode != NETPLAY_CONNECTION_SLAVE) + { + /* They were confused */ + return netplay_cmd_nak(netplay, connection); + } + + client_num = connection - netplay->connections + 1; + + handle_play_spectate(netplay, client_num, connection, cmd, 0, NULL); break; } case NETPLAY_CMD_PLAY: { - uint32_t payload[2]; - uint32_t player = 0; - bool slave = false; + uint32_t client_num; + uint32_t payload[1]; settings_t *settings = config_get_ptr(); - /* Check if they requested slave mode */ - if (cmd_size == sizeof(uint32_t)) + 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"); + 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); } - - /* Check if their slave mode request corresponds with what we allow */ - if (settings->bools.netplay_require_slaves) - slave = true; - else if (!settings->bools.netplay_allow_slaves) - slave = false; - - payload[0] = htonl(netplay->self_frame_count + 1); if (!netplay->is_server) { @@ -777,67 +1192,26 @@ static bool netplay_get_cmd(netplay_t *netplay, break; } - /* Find an available player slot */ - for (player = 0; player <= netplay->player_max; player++) + if (connection->mode == NETPLAY_CONNECTION_PLAYING + || connection->mode == NETPLAY_CONNECTION_SLAVE) { - if (!(netplay->self_mode == NETPLAY_CONNECTION_PLAYING && - netplay->self_player == player) && - !(netplay->connected_players & (1< netplay->player_max) - { - /* No slots free! */ - payload[0] = htonl(NETPLAY_CMD_MODE_REFUSED_REASON_NO_SLOTS); - netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE_REFUSED, payload, sizeof(uint32_t)); - break; + /* They were obviously confused */ + return netplay_cmd_nak(netplay, connection); } - if (connection->mode != NETPLAY_CONNECTION_PLAYING && - connection->mode != NETPLAY_CONNECTION_SLAVE) - { - /* Mark them as playing */ - connection->mode = slave ? NETPLAY_CONNECTION_SLAVE : - NETPLAY_CONNECTION_PLAYING; - connection->player = player; - netplay->connected_players |= 1<connected_slaves |= 1<connections + 1; - /* Tell everyone */ - payload[1] = htonl(NETPLAY_CMD_MODE_BIT_PLAYING | - (slave?NETPLAY_CMD_MODE_BIT_SLAVE:0) | - connection->player); - netplay_send_raw_cmd_all(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload)); - - /* Announce it */ - msg[sizeof(msg)-1] = '\0'; - snprintf(msg, sizeof(msg)-1, "Player %d has joined", player+1); - RARCH_LOG("%s\n", msg); - runloop_msg_queue_push(msg, 1, 180, false); - - } - - /* Tell the player even if they were confused */ - payload[1] = htonl(NETPLAY_CMD_MODE_BIT_PLAYING | - ((connection->mode == NETPLAY_CONNECTION_SLAVE)? - NETPLAY_CMD_MODE_BIT_SLAVE:0) | - NETPLAY_CMD_MODE_BIT_YOU | - connection->player); - netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_MODE, payload, sizeof(payload)); - - /* And expect their data */ - netplay->read_ptr[player] = NEXT_PTR(netplay->self_ptr); - netplay->read_frame_count[player] = netplay->self_frame_count + 1; + handle_play_spectate(netplay, client_num, connection, cmd, cmd_size, payload); break; } case NETPLAY_CMD_MODE: { - uint32_t payload[2]; - uint32_t frame, mode, player; + uint32_t payload[15]; + uint32_t frame, mode, client_num, devices, device; size_t ptr; struct delta_frame *dframe; + const char *nick; #define START(which) \ do { \ @@ -850,8 +1224,13 @@ static bool netplay_get_cmd(netplay_t *netplay, dframe = &netplay->buffer[ptr]; \ } while(0) - if (cmd_size != sizeof(payload) || - netplay->is_server) + if (netplay->is_server) + { + RARCH_ERR("NETPLAY_CMD_MODE from client.\n"); + return netplay_cmd_nak(netplay, connection); + } + + if (cmd_size != sizeof(payload)) { RARCH_ERR("Invalid payload size for NETPLAY_CMD_MODE.\n"); return netplay_cmd_nak(netplay, connection); @@ -870,13 +1249,17 @@ static bool netplay_get_cmd(netplay_t *netplay, netplay->force_rewind = true; mode = ntohl(payload[1]); - player = mode & 0xFFFF; - if (player >= MAX_USERS) + client_num = mode & 0xFFFF; + if (client_num >= MAX_CLIENTS) { RARCH_ERR("Received NETPLAY_CMD_MODE for a higher player number than we support.\n"); return netplay_cmd_nak(netplay, connection); } + devices = ntohl(payload[2]); + memcpy(netplay->device_share_modes, payload + 3, sizeof(netplay->device_share_modes)); + nick = (const char *) (payload + 7); + if (mode & NETPLAY_CMD_MODE_BIT_YOU) { /* A change to me! */ @@ -897,31 +1280,42 @@ static bool netplay_get_cmd(netplay_t *netplay, /* 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<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->connected_players |= (1<client_devices[client_num] = devices; + for (device = 0; device < MAX_INPUT_DEVICES; device++) + if (devices & (1<device_clients[device] |= (1<self_devices = devices; + + netplay->read_ptr[client_num] = netplay->server_ptr; + netplay->read_frame_count[client_num] = netplay->server_frame_count; /* Fix up current frame info */ - if (frame <= netplay->self_frame_count) + if (!(mode & NETPLAY_CMD_MODE_BIT_SLAVE) && frame <= netplay->self_frame_count) { /* It wanted past frames, better send 'em! */ START(netplay->server_ptr); while (dframe->used && dframe->frame <= netplay->self_frame_count) { - memcpy(dframe->real_input_state[player], dframe->self_state, sizeof(dframe->self_state)); - dframe->have_real[player] = true; - send_input_frame(netplay, connection, NULL, dframe->frame, player, dframe->self_state); + for (device = 0; device < MAX_INPUT_DEVICES; device++) + { + uint32_t dsize; + netplay_input_state_t istate; + if (!(devices & (1<real_input[device], client_num, dsize, + false, false); + if (!istate) continue; + memset(istate->data, 0, dsize*sizeof(uint32_t)); + } + dframe->have_local = true; + dframe->have_real[client_num] = true; + send_input_frame(netplay, dframe, connection, NULL, client_num, false); if (dframe->frame == netplay->self_frame_count) break; NEXT(); } @@ -930,6 +1324,7 @@ static bool netplay_get_cmd(netplay_t *netplay, else { uint32_t frame_count; + netplay_input_state_t istate; /* It wants future frames, make sure we don't capture or send intermediate ones */ START(netplay->self_ptr); @@ -946,8 +1341,6 @@ static bool netplay_get_cmd(netplay_t *netplay, } } - memset(dframe->self_state, 0, sizeof(dframe->self_state)); - memset(dframe->real_input_state[player], 0, sizeof(dframe->self_state)); dframe->have_local = true; /* Go on to the next delta frame */ @@ -961,13 +1354,10 @@ static bool netplay_get_cmd(netplay_t *netplay, } /* Announce it */ - msg[sizeof(msg)-1] = '\0'; - snprintf(msg, sizeof(msg)-1, "You have joined as player %d", player+1); - RARCH_LOG("%s\n", msg); - runloop_msg_queue_push(msg, 1, 180, false); + announce_play_spectate(netplay, NULL, NETPLAY_CONNECTION_PLAYING, devices); #ifdef DEBUG_NETPLAY_STEPS - RARCH_LOG("Received mode change self->%u\n", player); + RARCH_LOG("Received mode change self->%X\n", devices); print_state(netplay); #endif @@ -982,15 +1372,16 @@ static bool netplay_get_cmd(netplay_t *netplay, } /* Unmark ourself, in case we were in slave mode */ - netplay->connected_players &= ~(1<connected_players &= ~(1<client_devices[client_num] = 0; + for (device = 0; device < MAX_INPUT_DEVICES; device++) + netplay->device_clients[device] &= ~(1<spectating\n", netplay->self_player); + RARCH_LOG("Received mode change self->spectating\n"); print_state(netplay); #endif @@ -1008,35 +1399,36 @@ static bool netplay_get_cmd(netplay_t *netplay, return netplay_cmd_nak(netplay, connection); } - netplay->connected_players |= (1<connected_players |= (1<client_devices[client_num] = devices; + for (device = 0; device < MAX_INPUT_DEVICES; device++) + if (devices & (1<device_clients[device] |= (1<read_ptr[player] = netplay->server_ptr; - netplay->read_frame_count[player] = netplay->server_frame_count; + netplay->read_ptr[client_num] = netplay->server_ptr; + netplay->read_frame_count[client_num] = netplay->server_frame_count; /* Announce it */ - msg[sizeof(msg)-1] = '\0'; - snprintf(msg, sizeof(msg)-1, "Player %d has joined", player+1); - RARCH_LOG("%s\n", msg); - runloop_msg_queue_push(msg, 1, 180, false); + announce_play_spectate(netplay, nick, NETPLAY_CONNECTION_PLAYING, devices); #ifdef DEBUG_NETPLAY_STEPS - RARCH_LOG("Received mode change spectator->%u\n", player); + RARCH_LOG("Received mode change %u->%u\n", client_num, devices); print_state(netplay); #endif } else { - netplay->connected_players &= ~(1<connected_players &= ~(1<client_devices[client_num] = 0; + for (device = 0; device < MAX_INPUT_DEVICES; device++) + netplay->device_clients[device] &= ~(1<spectator\n", player); + RARCH_LOG("Received mode change %u->spectator\n", client_num); print_state(netplay); #endif @@ -1084,6 +1476,10 @@ static bool netplay_get_cmd(netplay_t *netplay, dmsg = msg_hash_to_str(MSG_NETPLAY_CANNOT_PLAY_NO_SLOTS); break; + case NETPLAY_CMD_MODE_REFUSED_REASON_NOT_AVAILABLE: + dmsg = msg_hash_to_str(MSG_NETPLAY_CANNOT_PLAY_NOT_AVAILABLE); + break; + default: dmsg = msg_hash_to_str(MSG_NETPLAY_CANNOT_PLAY); } @@ -1176,9 +1572,13 @@ static bool netplay_get_cmd(netplay_t *netplay, uint32_t frame; uint32_t isize; uint32_t rd, wn; - uint32_t player; + uint32_t client, client_num; + uint32_t load_frame_count; + size_t load_ptr; struct compression_transcoder *ctrans; + client_num = connection - netplay->connections + 1; + /* Make sure we're ready for it */ if (netplay->quirks & NETPLAY_QUIRK_INITIALIZATION) { @@ -1239,14 +1639,24 @@ static bool netplay_get_cmd(netplay_t *netplay, } frame = ntohl(frame); - if ((netplay->is_server && frame != netplay->read_frame_count[connection->player]) || - (!netplay->is_server && frame != netplay->server_frame_count)) + if (netplay->is_server) + { + load_ptr = netplay->read_ptr[client_num]; + load_frame_count = netplay->read_frame_count[client_num]; + } + else + { + load_ptr = netplay->server_ptr; + load_frame_count = netplay->server_frame_count; + } + + if (frame != load_frame_count) { RARCH_ERR("CMD_LOAD_SAVESTATE loading a state out of order!\n"); return netplay_cmd_nak(netplay, connection); } - if (!netplay_delta_frame_ready(netplay, &netplay->buffer[netplay->read_ptr[connection->player]], frame)) + if (!netplay_delta_frame_ready(netplay, &netplay->buffer[load_ptr], load_frame_count)) { /* Hopefully it will be after another round of input */ goto shrt; @@ -1286,7 +1696,7 @@ static bool netplay_get_cmd(netplay_t *netplay, ctrans->decompression_backend->set_in(ctrans->decompression_stream, netplay->zbuffer, cmd_size - 2*sizeof(uint32_t)); ctrans->decompression_backend->set_out(ctrans->decompression_stream, - (uint8_t*)netplay->buffer[netplay->read_ptr[connection->player]].state, + (uint8_t*)netplay->buffer[load_ptr].state, (unsigned)netplay->state_size); ctrans->decompression_backend->trans(ctrans->decompression_stream, true, &rd, &wn, NULL); @@ -1302,7 +1712,7 @@ static bool netplay_get_cmd(netplay_t *netplay, } /* Skip ahead if it's past where we are */ - if (frame > netplay->run_frame_count || + if (load_frame_count > netplay->run_frame_count || cmd == NETPLAY_CMD_RESET) { /* This is squirrely: We need to assure that when we advance the @@ -1310,8 +1720,8 @@ static bool netplay_get_cmd(netplay_t *netplay, * load into. If we refer directly to read_ptr, then we'll end * up never reading the input for read_frame_count itself, which * will make the other side unhappy. */ - netplay->run_ptr = PREV_PTR(netplay->read_ptr[connection->player]); - netplay->run_frame_count = frame - 1; + netplay->run_ptr = PREV_PTR(load_ptr); + netplay->run_frame_count = load_frame_count - 1; if (frame > netplay->self_frame_count) { netplay->self_ptr = netplay->run_ptr; @@ -1320,23 +1730,23 @@ static bool netplay_get_cmd(netplay_t *netplay, } /* Don't expect earlier data from other clients */ - for (player = 0; player < MAX_USERS; player++) + for (client = 0; client < MAX_CLIENTS; client++) { - if (!(netplay->connected_players & (1< netplay->read_frame_count[player]) + if (!(netplay->connected_players & (1< netplay->read_frame_count[client]) { - netplay->read_ptr[player] = netplay->read_ptr[connection->player]; - netplay->read_frame_count[player] = frame; + netplay->read_ptr[client] = load_ptr; + netplay->read_frame_count[client] = load_frame_count; } } /* Make sure our states are correct */ netplay->savestate_request_outstanding = false; - netplay->other_ptr = netplay->read_ptr[connection->player]; - netplay->other_frame_count = frame; + netplay->other_ptr = load_ptr; + netplay->other_frame_count = load_frame_count; #ifdef DEBUG_NETPLAY_STEPS - RARCH_LOG("Loading state at %u\n", frame); + RARCH_LOG("Loading state at %u\n", load_frame_count); print_state(netplay); #endif @@ -1528,7 +1938,7 @@ int netplay_poll_net_input(netplay_t *netplay, bool block) */ void netplay_handle_slaves(netplay_t *netplay) { - struct delta_frame *frame = &netplay->buffer[netplay->self_ptr]; + struct delta_frame *oframe, *frame = &netplay->buffer[netplay->self_ptr]; size_t i; for (i = 0; i < netplay->connections_size; i++) { @@ -1536,52 +1946,60 @@ void netplay_handle_slaves(netplay_t *netplay) if (connection->active && connection->mode == NETPLAY_CONNECTION_SLAVE) { - int player = connection->player; + uint32_t client_num = i + 1; + uint32_t devices, device; /* 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) + if (netplay->read_frame_count[client_num] > netplay->self_frame_count) continue; /* Alright, we have to send something. Do we need to generate it first? */ - if (!frame->have_real[player]) + if (!frame->have_real[client_num]) { + devices = netplay->client_devices[client_num]; + /* 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; + oframe = &netplay->buffer[PREV_PTR(netplay->self_ptr)]; + for (device = 0; device < MAX_INPUT_DEVICES; device++) + { + netplay_input_state_t istate_out, istate_in; + if (!(devices & (1<real_input[device]; + while (istate_in && istate_in->client_num != client_num) + istate_in = istate_in->next; + if (!istate_in) + { + /* Start with blank input */ + netplay_input_state_for(&frame->real_input[device], + client_num, + netplay_expected_input_size(netplay, 1 << device), true, + false); + + } + else + { + /* Copy the previous input */ + istate_out = netplay_input_state_for(&frame->real_input[device], + client_num, istate_in->size, true, false); + memcpy(istate_out->data, istate_in->data, + istate_in->size * sizeof(uint32_t)); + } + } + frame->have_real[client_num] = true; } /* Send it along */ - send_input_frame(netplay, NULL, NULL, netplay->self_frame_count, - player, frame->real_input_state[player]); + send_input_frame(netplay, frame, NULL, NULL, client_num, false); /* 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->read_ptr[client_num] = NEXT_PTR(netplay->self_ptr); + netplay->read_frame_count[client_num] = netplay->self_frame_count + 1; } } } -/** - * netplay_flip_port - * - * Should we flip ports 0 and 1? - */ -bool netplay_flip_port(netplay_t *netplay) -{ - size_t frame = netplay->self_frame_count; - - if (netplay->flip_frame == 0) - return false; - - if (netplay->is_replay) - frame = netplay->replay_frame_count; - - return netplay->flip ^ (frame < netplay->flip_frame); -} - /** * netplay_announce_nat_traversal * diff --git a/network/netplay/netplay_keyboard.c b/network/netplay/netplay_keyboard.c new file mode 100644 index 0000000000..38deffc1a9 --- /dev/null +++ b/network/netplay/netplay_keyboard.c @@ -0,0 +1,53 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2017 - Gregor Richards + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include + +#include "netplay_private.h" + +/* The mapping of keys from netplay (network) to libretro (host) */ +const uint16_t netplay_key_ntoh_mapping[] = { + (uint16_t) RETROK_UNKNOWN, +#define K(k) (uint16_t) RETROK_ ## k, +#define KL(k,l) (uint16_t) l, +#include "netplay_keys.h" +#undef KL +#undef K + 0 +}; + +static bool mapping_defined = false; +static uint16_t mapping[RETROK_LAST]; + +/* The mapping of keys from libretro (host) to netplay (network) */ +uint32_t netplay_key_hton(unsigned key) +{ + if (key >= RETROK_LAST) + return NETPLAY_KEY_UNKNOWN; + return mapping[key]; +} + +/* Because the hton keymapping has to be generated, call this before using + * netplay_key_hton */ +void netplay_key_hton_init(void) +{ + if (!mapping_defined) + { + uint16_t i; + for (i = 0; i < NETPLAY_KEY_LAST; i++) + mapping[netplay_key_ntoh(i)] = i; + mapping_defined = true; + } +} diff --git a/network/netplay/netplay_keys.h b/network/netplay/netplay_keys.h new file mode 100644 index 0000000000..0184497a1b --- /dev/null +++ b/network/netplay/netplay_keys.h @@ -0,0 +1,169 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2016-2017 - Gregor Richards + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +/* This is an X-include file which defines the mapping of keycodes used by + * libretro to keycodes used by RetroArch Netplay. The keycodes are different + * because the keycodes supported by libretro are in discontiguous blocks, + * which would create gaps in the input data, requiring more space and more + * network bandwidth to represent them. + * + * If you want to add a new keycode, make sure you add it to the END. The order + * that the keys appear in this file defines their indices in the netplay + * protocol, so adding a key in the middle will break backwards compatibility. + * If you want to clean up the order and thereby break backwards compatibility, + * make sure you bump the protocol version in netplay_private.h. + */ + +K(BACKSPACE) +K(TAB) +KL(LINEFEED, 10) +K(CLEAR) +K(RETURN) +K(PAUSE) +K(ESCAPE) +K(SPACE) +K(EXCLAIM) +K(QUOTEDBL) +K(HASH) +K(DOLLAR) +K(AMPERSAND) +K(QUOTE) +K(LEFTPAREN) +K(RIGHTPAREN) +K(ASTERISK) +K(PLUS) +K(COMMA) +K(MINUS) +K(PERIOD) +K(SLASH) +K(0) +K(1) +K(2) +K(3) +K(4) +K(5) +K(6) +K(7) +K(8) +K(9) +K(COLON) +K(SEMICOLON) +K(LESS) +K(EQUALS) +K(GREATER) +K(QUESTION) +K(AT) +K(LEFTBRACKET) +K(BACKSLASH) +K(RIGHTBRACKET) +K(CARET) +K(UNDERSCORE) +K(BACKQUOTE) +K(a) +K(b) +K(c) +K(d) +K(e) +K(f) +K(g) +K(h) +K(i) +K(j) +K(k) +K(l) +K(m) +K(n) +K(o) +K(p) +K(q) +K(r) +K(s) +K(t) +K(u) +K(v) +K(w) +K(x) +K(y) +K(z) +K(DELETE) + +K(KP0) +K(KP1) +K(KP2) +K(KP3) +K(KP4) +K(KP5) +K(KP6) +K(KP7) +K(KP8) +K(KP9) +K(KP_PERIOD) +K(KP_DIVIDE) +K(KP_MULTIPLY) +K(KP_MINUS) +K(KP_PLUS) +K(KP_ENTER) +K(KP_EQUALS) + +K(UP) +K(DOWN) +K(RIGHT) +K(LEFT) +K(INSERT) +K(HOME) +K(END) +K(PAGEUP) +K(PAGEDOWN) + +K(F1) +K(F2) +K(F3) +K(F4) +K(F5) +K(F6) +K(F7) +K(F8) +K(F9) +K(F10) +K(F11) +K(F12) +K(F13) +K(F14) +K(F15) + +K(NUMLOCK) +K(CAPSLOCK) +K(SCROLLOCK) +K(RSHIFT) +K(LSHIFT) +K(RCTRL) +K(LCTRL) +K(RALT) +K(LALT) +K(RMETA) +K(LMETA) +K(LSUPER) +K(RSUPER) +K(MODE) +K(COMPOSE) + +K(HELP) +K(PRINT) +K(SYSREQ) +K(BREAK) +K(MENU) +K(POWER) +K(EURO) +K(UNDO) diff --git a/network/netplay/netplay_private.h b/network/netplay/netplay_private.h index d473a65dff..b0f3b863cf 100644 --- a/network/netplay/netplay_private.h +++ b/network/netplay/netplay_private.h @@ -28,10 +28,7 @@ #include "../../msg_hash.h" #include "../../verbosity.h" -#define WORDS_PER_INPUT 3 /* Buttons, left stick, right stick */ -#define WORDS_PER_FRAME (WORDS_PER_INPUT+2) /* + frameno, playerno */ - -#define NETPLAY_PROTOCOL_VERSION 4 +#define NETPLAY_PROTOCOL_VERSION 5 #define RARCH_DEFAULT_PORT 55435 #define RARCH_DEFAULT_NICK "Anonymous" @@ -45,6 +42,15 @@ #define CATCH_UP_CHECK_TIME_USEC (500*1000) #define MAX_RETRIES 16 #define RETRY_MS 500 +#define MAX_INPUT_DEVICES 16 + +/* We allow only 32 clients to fit into a 32-bit bitmap */ +#define MAX_CLIENTS 32 +typedef uint32_t client_bitmap_t; + +/* Because the callback keyboard reverses some assumptions, when the keyboard + * callbacks are in use, we assign a pseudodevice for it */ +#define RETRO_DEVICE_NETPLAY_KEYBOARD RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_KEYBOARD, 65535) #define NETPLAY_MAX_STALL_FRAMES 60 #define NETPLAY_FRAME_RUN_TIME_WINDOW 120 @@ -89,6 +95,13 @@ #define NETPLAY_COMPRESSION_SUPPORTED 0 #endif +/* Positional snprintf */ +#ifdef _MSC_VER +#define snprintf_p _snprintf_p +#else /* sensible systems */ +#define snprintf_p snprintf +#endif + enum netplay_cmd { /* Basic commands */ @@ -162,9 +175,6 @@ enum netplay_cmd /* Misc. commands */ - /* Swap inputs between player 1 and player 2 */ - NETPLAY_CMD_FLIP_PLAYERS = 0x0060, - /* Sends multiple config requests over, * See enum netplay_cmd_cfg */ NETPLAY_CMD_CFG = 0x0061, @@ -175,12 +185,11 @@ enum netplay_cmd NETPLAY_CMD_CFG_ACK = 0x0062 }; -#define NETPLAY_CMD_INPUT_BIT_SERVER (1U<<31) #define NETPLAY_CMD_SYNC_BIT_PAUSED (1U<<31) -#define NETPLAY_CMD_PLAY_BIT_SLAVE (1U) -#define NETPLAY_CMD_MODE_BIT_SLAVE (1U<<18) -#define NETPLAY_CMD_MODE_BIT_PLAYING (1U<<17) -#define NETPLAY_CMD_MODE_BIT_YOU (1U<<16) +#define NETPLAY_CMD_PLAY_BIT_SLAVE (1U<<31) +#define NETPLAY_CMD_MODE_BIT_YOU (1U<<31) +#define NETPLAY_CMD_MODE_BIT_PLAYING (1U<<30) +#define NETPLAY_CMD_MODE_BIT_SLAVE (1U<<29) /* These are the reasons given for mode changes to be rejected */ enum netplay_cmd_mode_reasons @@ -195,9 +204,35 @@ enum netplay_cmd_mode_reasons NETPLAY_CMD_MODE_REFUSED_REASON_NO_SLOTS, /* You're changing modes too fast */ - NETPLAY_CMD_MODE_REFUSED_REASON_TOO_FAST + NETPLAY_CMD_MODE_REFUSED_REASON_TOO_FAST, + + /* You requested a particular port but it's not available */ + NETPLAY_CMD_MODE_REFUSED_REASON_NOT_AVAILABLE }; +/* Real preferences for sharing devices */ +enum rarch_netplay_share_preference +{ + /* Prefer not to share, shouldn't be set as a sharing mode for an shared device */ + NETPLAY_SHARE_NO_SHARING = 0x0, + + /* No preference. Only for requests. Set if sharing is requested but either + * digital or analog doesn't have a preference. */ + NETPLAY_SHARE_NO_PREFERENCE = 0x1, + + /* For digital devices */ + NETPLAY_SHARE_DIGITAL_BITS = 0x1C, + NETPLAY_SHARE_DIGITAL_OR = 0x4, + NETPLAY_SHARE_DIGITAL_XOR = 0x8, + NETPLAY_SHARE_DIGITAL_VOTE = 0xC, + + /* For analog devices */ + NETPLAY_SHARE_ANALOG_BITS = 0xE0, + NETPLAY_SHARE_ANALOG_MAX = 0x20, + NETPLAY_SHARE_ANALOG_AVERAGE = 0x40 +}; + +/* The current status of a connection */ enum rarch_netplay_connection_mode { NETPLAY_CONNECTION_NONE = 0, @@ -240,7 +275,24 @@ enum rarch_netplay_stall_reason NETPLAY_STALL_NO_CONNECTION }; -typedef uint32_t netplay_input_state_t[WORDS_PER_INPUT]; +/* Input state for a particular client-device pair */ +typedef struct netplay_input_state +{ + /* The next input state (forming a list) */ + struct netplay_input_state *next; + + /* Is this a buffer with real data? */ + bool used; + + /* Whose data is this? */ + uint32_t client_num; + + /* How many words of input data do we have? */ + uint32_t size; + + /* The input data itself (note: should expand beyond 1 by overallocating). */ + uint32_t data[1]; +} *netplay_input_state_t; struct delta_frame { @@ -253,20 +305,22 @@ struct delta_frame /* The CRC-32 of the serialized state if we've calculated it, else 0 */ uint32_t crc; - /* The real, simulated and local input. If we're playing, self_state is - * mirrored to the appropriate real_input_state player. */ - netplay_input_state_t real_input_state[MAX_USERS]; - netplay_input_state_t simulated_input_state[MAX_USERS]; - netplay_input_state_t self_state; + /* The resolved input, i.e., what's actually going to the core. One input + * per device. */ + netplay_input_state_t resolved_input[MAX_INPUT_DEVICES]; + + /* The real input */ + netplay_input_state_t real_input[MAX_INPUT_DEVICES]; + + /* The simulated input. is_real here means the simulation is done, i.e., + * it's a real simulation, not real input. */ + netplay_input_state_t simlated_input[MAX_INPUT_DEVICES]; /* Have we read local input? */ bool have_local; /* Have we read the real (remote) input? */ - bool have_real[MAX_USERS]; - - /* Is the current state as of self_frame_count using the real (remote) data? */ - bool used_real[MAX_USERS]; + bool have_real[MAX_CLIENTS]; }; struct socket_buffer @@ -309,9 +363,6 @@ struct netplay_connection * to wait for, or 0 if no delay is active. */ uint32_t delay_frame; - /* Player # of connected player */ - uint32_t player; - /* What compression does this peer support? */ uint32_t compression_supported; @@ -350,8 +401,8 @@ struct netplay /* TCP connection for listening (server only) */ int listen_fd; - /* Our player number */ - uint32_t self_player; + /* Our client number */ + uint32_t self_client_num; /* Our mode and status */ enum rarch_netplay_connection_mode self_mode; @@ -361,19 +412,39 @@ struct netplay size_t connections_size; struct netplay_connection one_connection; /* Client only */ - /* Bitmap of players with controllers (low bit is player 1) */ + /* Bitmap of clients with input devices */ uint32_t connected_players; - /* Bitmap of players playing in slave mode (should be a subset of + /* Bitmap of clients playing in slave mode (should be a subset of * connected_players) */ uint32_t connected_slaves; + /* For each client, the bitmap of devices they're connected to */ + uint32_t client_devices[MAX_CLIENTS]; + + /* For each device, the bitmap of clients connected */ + client_bitmap_t device_clients[MAX_INPUT_DEVICES]; + + /* The sharing mode for each device */ + uint8_t device_share_modes[MAX_INPUT_DEVICES]; + + /* Our own device bitmap */ + uint32_t self_devices; + /* Number of desync operations we're currently performing. If set, we don't * attempt to stay in sync. */ uint32_t desync; - /* Maximum player number */ - uint32_t player_max; + /* The device types for every connected device. We store them and ignore any + * menu changes, as netplay needs fixed devices. */ + uint32_t config_devices[MAX_INPUT_DEVICES]; + + /* Set to true if we have a device that most cores translate to "up/down" + * actions, typically a keyboard. We need to keep track of this because with + * such a device, we need to "fix" the input state to the frame BEFORE a + * state load, then perform the state load, and the up/down states will + * proceed as expected */ + bool have_updown_device; struct retro_callbacks cbs; @@ -418,9 +489,9 @@ struct netplay size_t unread_ptr; uint32_t unread_frame_count; - /* Pointer to the next frame to read from each player */ - size_t read_ptr[MAX_USERS]; - uint32_t read_frame_count[MAX_USERS]; + /* Pointer to the next frame to read from each client */ + size_t read_ptr[MAX_CLIENTS]; + uint32_t read_frame_count[MAX_CLIENTS]; /* Pointer to the next frame to read from the server (as it might not be a * player but still synchronizes) */ @@ -441,7 +512,7 @@ struct netplay bool can_poll; /* Force a rewind to other_frame_count/other_ptr. This is for synchronized - * events, such as player flipping or savestate loading. */ + * events, such as restarting or savestate loading. */ bool force_rewind; /* Force a reset */ @@ -456,21 +527,12 @@ struct netplay /* Have we requested a savestate as a sync point? */ bool savestate_request_outstanding; - /* A buffer for outgoing input packets. */ - uint32_t input_packet_buffer[2 + WORDS_PER_FRAME]; - /* Our local socket info */ struct addrinfo *addr; /* Counter for timeouts */ unsigned timeout_cnt; - /* User flipping - * Flipping state. If frame >= flip_frame, we apply the flip. - * If not, we apply the opposite, effectively creating a trigger point. */ - bool flip; - uint32_t flip_frame; - /* Netplay pausing */ bool local_paused; bool remote_paused; @@ -608,6 +670,28 @@ bool netplay_delta_frame_ready(netplay_t *netplay, struct delta_frame *delta, */ uint32_t netplay_delta_frame_crc(netplay_t *netplay, struct delta_frame *delta); +/** + * netplay_delta_frame_free + * + * Free a delta frame's dependencies + */ +void netplay_delta_frame_free(struct delta_frame *delta); + +/** + * netplay_input_state_for + * + * Get an input state for a particular client + */ +netplay_input_state_t netplay_input_state_for(netplay_input_state_t *list, + uint32_t client_num, size_t size, bool must_create, bool must_not_create); + +/** + * netplay_expected_input_size + * + * Size in words for a given set of devices. + */ +uint32_t netplay_expected_input_size(netplay_t *netplay, uint32_t devices); + /*************************************************************** * NETPLAY-DISCOVERY.C @@ -638,6 +722,13 @@ bool netplay_lan_ad_server(netplay_t *netplay); void netplay_load_savestate(netplay_t *netplay, retro_ctx_serialize_info_t *serial_info, bool save); +/** + * netplay_settings_share_mode + * + * Get the preferred share mode + */ +uint8_t netplay_settings_share_mode(void); + /** * input_poll_net * @@ -787,10 +878,10 @@ bool netplay_cmd_request_savestate(netplay_t *netplay); /** * netplay_cmd_mode * - * Send a mode request command to either play or spectate. + * Send a mode change request. As a server, the request is to ourself, and so + * honored instantly. */ bool netplay_cmd_mode(netplay_t *netplay, - struct netplay_connection *connection, enum rarch_netplay_connection_mode mode); /** @@ -816,13 +907,6 @@ int netplay_poll_net_input(netplay_t *netplay, bool block); */ void netplay_handle_slaves(netplay_t *netplay); -/** - * netplay_flip_port - * - * Should we flip ports 0 and 1? - */ -bool netplay_flip_port(netplay_t *netplay); - /** * netplay_announce_nat_traversal * @@ -838,6 +922,33 @@ void netplay_announce_nat_traversal(netplay_t *netplay); void netplay_init_nat_traversal(netplay_t *netplay); +/*************************************************************** + * NETPLAY-KEYBOARD.C + **************************************************************/ + +/* The keys supported by netplay */ +enum netplay_keys { + NETPLAY_KEY_UNKNOWN = 0, +#define K(k) NETPLAY_KEY_ ## k, +#define KL(k,l) K(k) +#include "netplay_keys.h" +#undef KL +#undef K + NETPLAY_KEY_LAST +}; + +/* The mapping of keys from netplay (network) to libretro (host) */ +extern const uint16_t netplay_key_ntoh_mapping[]; +#define netplay_key_ntoh(k) (netplay_key_ntoh_mapping[k]) + +/* The mapping of keys from libretro (host) to netplay (network) */ +uint32_t netplay_key_hton(unsigned key); + +/* Because the hton keymapping has to be generated, call this before using + * netplay_key_hton */ +void netplay_key_hton_init(void); + + /*************************************************************** * NETPLAY-SYNC.C **************************************************************/ @@ -851,15 +962,17 @@ void netplay_init_nat_traversal(netplay_t *netplay); void netplay_update_unread_ptr(netplay_t *netplay); /** - * netplay_simulate_input + * netplay_resolve_input * @netplay : pointer to netplay object - * @sim_ptr : frame index for which to simulate input + * @sim_ptr : frame pointer for which to resolve input * @resim : are we resimulating, or simulating this frame for the * first time? * * "Simulate" input by assuming it hasn't changed since the last read input. + * Returns true if the resolved input changed from the last time it was + * resolved. */ -void netplay_simulate_input(netplay_t *netplay, size_t sim_ptr, bool resim); +bool netplay_resolve_input(netplay_t *netplay, size_t sim_ptr, bool resim); /** * netplay_sync_pre_frame diff --git a/network/netplay/netplay_sync.c b/network/netplay/netplay_sync.c index 282690261c..a40404f646 100644 --- a/network/netplay/netplay_sync.c +++ b/network/netplay/netplay_sync.c @@ -39,7 +39,7 @@ */ void netplay_update_unread_ptr(netplay_t *netplay) { - if (netplay->is_server && !netplay->connected_players) + if (netplay->is_server && netplay->connected_players<=1) { /* Nothing at all to read! */ netplay->unread_ptr = netplay->self_ptr; @@ -50,16 +50,16 @@ void netplay_update_unread_ptr(netplay_t *netplay) { size_t new_unread_ptr = 0; uint32_t new_unread_frame_count = (uint32_t) -1; - uint32_t player; + uint32_t client; - for (player = 0; player < MAX_USERS; player++) + for (client = 0; client < MAX_CLIENTS; client++) { - if (!(netplay->connected_players & (1<connected_slaves & (1<read_frame_count[player] < new_unread_frame_count) + if (!(netplay->connected_players & (1<connected_slaves & (1<read_frame_count[client] < new_unread_frame_count) { - new_unread_ptr = netplay->read_ptr[player]; - new_unread_frame_count = netplay->read_frame_count[player]; + new_unread_ptr = netplay->read_ptr[client]; + new_unread_frame_count = netplay->read_frame_count[client]; } } @@ -82,63 +82,373 @@ void netplay_update_unread_ptr(netplay_t *netplay) } } +struct vote_count { + uint16_t votes[32]; +}; + /** - * netplay_simulate_input + * netplay_device_client_state * @netplay : pointer to netplay object - * @sim_ptr : frame index for which to simulate input + * @simframe : frame in which merging is being performed + * @device : device being merged + * @client : client to find state for + */ +netplay_input_state_t netplay_device_client_state(netplay_t *netplay, + struct delta_frame *simframe, uint32_t device, uint32_t client) +{ + uint32_t dsize = netplay_expected_input_size(netplay, 1 << device); + netplay_input_state_t simstate = + netplay_input_state_for( + &simframe->real_input[device], client, + dsize, false, true); + if (!simstate) + { + if (netplay->read_frame_count[client] > simframe->frame) + return NULL; + simstate = netplay_input_state_for(&simframe->simlated_input[device], + client, dsize, false, true); + } + return simstate; +} + +/** + * netplay_merge_digital + * @netplay : pointer to netplay object + * @resstate : state being resolved + * @simframe : frame in which merging is being performed + * @device : device being merged + * @clients : bitmap of clients being merged + * @digital : bitmap of digital bits + */ +static void netplay_merge_digital(netplay_t *netplay, + netplay_input_state_t resstate, struct delta_frame *simframe, + uint32_t device, uint32_t clients, const uint32_t *digital) +{ + netplay_input_state_t simstate; + uint32_t word, bit, client; + uint8_t share_mode = netplay->device_share_modes[device] & NETPLAY_SHARE_DIGITAL_BITS; + + /* Make sure all real clients are accounted for */ + for (simstate = simframe->real_input[device]; simstate; simstate = simstate->next) + { + if (!simstate->used || simstate->size != resstate->size) continue; + clients |= 1<client_num; + } + + if (share_mode == NETPLAY_SHARE_DIGITAL_VOTE) + { + /* Vote mode requires counting all the bits */ + uint32_t client_count = 0; + + /* This just assumes we have no more than three words, will need to be adjusted for new devices */ + struct vote_count votes[3] = {0}; + for (client = 0; client < MAX_CLIENTS; client++) + { + if (!(clients & (1<size; word++) + { + if (!digital[word]) continue; + for (bit = 0; bit < 32; bit++) + { + if (!(digital[word] & (1<data[word] & (1<size; word++) + { + for (bit = 0; bit < 32; bit++) + { + if (votes[word].votes[bit] > client_count) + resstate->data[word] |= (1<size; word++) + { + uint32_t part; + if (!digital[word]) continue; + part = simstate->data[word]; + if (digital[word] == (uint32_t) -1) + { + /* Combine the whole word */ + switch (share_mode) + { + case NETPLAY_SHARE_DIGITAL_XOR: resstate->data[word] ^= part; break; + default: resstate->data[word] |= part; + } + + } + else /* !whole word */ + { + for (bit = 0; bit < 32; bit++) + { + if (!(digital[word] & (1<data[word] ^= part & (1<data[word] |= part & (1<device_share_modes[device] & NETPLAY_SHARE_ANALOG_BITS; + int32_t value = 0, new_value; + + /* Make sure all real clients are accounted for */ + for (simstate = simframe->real_input[device]; simstate; simstate = simstate->next) + { + if (!simstate->used || simstate->size != resstate->size) continue; + clients |= 1<client_num; + } + + for (client = 0; client < MAX_CLIENTS; client++) + { + if (!(clients & (1<data[word]>>bit) & 0xFFFF); + switch (share_mode) + { + case NETPLAY_SHARE_ANALOG_AVERAGE: + value += (int32_t) new_value; + break; + default: + if (abs(new_value) > abs(value) || + (abs(new_value) == abs(value) && new_value > value)) + value = new_value; + } + } + + if (share_mode == NETPLAY_SHARE_ANALOG_AVERAGE) + value /= client_count; + + resstate->data[word] |= ((uint32_t) (uint16_t) value) << bit; +} + +/** + * netplay_merge_analog + * @netplay : pointer to netplay object + * @resstate : state being resolved + * @simframe : frame in which merging is being performed + * @device : device being merged + * @clients : bitmap of clients being merged + * @dtype : device type + */ +static void netplay_merge_analog(netplay_t *netplay, + netplay_input_state_t resstate, struct delta_frame *simframe, + uint32_t device, uint32_t clients, unsigned dtype) +{ + /* Devices with no analog parts */ + if (dtype == RETRO_DEVICE_JOYPAD || dtype == RETRO_DEVICE_KEYBOARD) + return; + + /* All other devices have at least one analog word */ + merge_analog_part(netplay, resstate, simframe, device, clients, 1, 0); + merge_analog_part(netplay, resstate, simframe, device, clients, 1, 16); + + /* And the ANALOG device has two (two sticks) */ + if (dtype == RETRO_DEVICE_ANALOG) + { + merge_analog_part(netplay, resstate, simframe, device, clients, 2, 0); + merge_analog_part(netplay, resstate, simframe, device, clients, 2, 16); + } +} + +/** + * netplay_resolve_input + * @netplay : pointer to netplay object + * @sim_ptr : frame pointer for which to resolve input * @resim : are we resimulating, or simulating this frame for the * first time? * * "Simulate" input by assuming it hasn't changed since the last read input. + * Returns true if the resolved input changed from the last time it was + * resolved. */ -void netplay_simulate_input(netplay_t *netplay, size_t sim_ptr, bool resim) +bool netplay_resolve_input(netplay_t *netplay, size_t sim_ptr, bool resim) { - uint32_t player; size_t prev; struct delta_frame *simframe, *pframe; + netplay_input_state_t simstate, client_state = NULL, resstate, oldresstate, pstate; + uint32_t clients, client, client_count; + uint32_t device; + bool ret = false; simframe = &netplay->buffer[sim_ptr]; - for (player = 0; player < MAX_USERS; player++) + for (device = 0; device < MAX_INPUT_DEVICES; device++) { - if (!(netplay->connected_players & (1<have_real[player]) continue; + unsigned dtype = netplay->config_devices[device]&RETRO_DEVICE_MASK; + uint32_t dsize = netplay_expected_input_size(netplay, 1 << device); + clients = netplay->device_clients[device]; + client_count = 0; - prev = PREV_PTR(netplay->read_ptr[player]); - pframe = &netplay->buffer[prev]; - - if (resim) + /* Make sure all real clients are accounted for */ + for (simstate = simframe->real_input[device]; simstate; simstate = simstate->next) { - /* In resimulation mode, we only copy the buttons. The reason for this - * is nonobvious: - * - * If we resimulated nothing, then the /duration/ with which any input - * was pressed would be approximately correct, since the original - * simulation came in as the input came in, but the /number of times/ - * the input was pressed would be wrong, as there would be an - * advancing wavefront of real data overtaking the simulated data - * (which is really just real data offset by some frames). - * - * That's acceptable for arrows in most situations, since the amount - * you move is tied to the duration, but unacceptable for buttons, - * which will seem to jerkily be pressed numerous times with those - * wavefronts. - */ - const uint32_t keep = (1U<simulated_input_state[player][0] & keep; - sim_state |= pframe->real_input_state[player][0] & ~keep; - simframe->simulated_input_state[player][0] = sim_state; + if (!simstate->used || simstate->size != dsize) continue; + clients |= 1<client_num; + } + + for (client = 0; client < MAX_CLIENTS; client++) + { + if (!(clients & (1<real_input[device], client, dsize, false, true); + if (!simstate) + { + /* Don't already have this input, so must simulate if we're supposed to have it at all */ + if (netplay->read_frame_count[client] > simframe->frame) + continue; + simstate = netplay_input_state_for(&simframe->simlated_input[device], client, dsize, false, false); + if (!simstate) + continue; + + prev = PREV_PTR(netplay->read_ptr[client]); + pframe = &netplay->buffer[prev]; + pstate = netplay_input_state_for(&pframe->real_input[device], client, dsize, false, true); + if (!pstate) + continue; + + if (resim && (dtype == RETRO_DEVICE_JOYPAD || dtype == RETRO_DEVICE_ANALOG)) + { + /* In resimulation mode, we only copy the buttons. The reason for this + * is nonobvious: + * + * If we resimulated nothing, then the /duration/ with which any input + * was pressed would be approximately correct, since the original + * simulation came in as the input came in, but the /number of times/ + * the input was pressed would be wrong, as there would be an + * advancing wavefront of real data overtaking the simulated data + * (which is really just real data offset by some frames). + * + * That's acceptable for arrows in most situations, since the amount + * you move is tied to the duration, but unacceptable for buttons, + * which will seem to jerkily be pressed numerous times with those + * wavefronts. + */ + const uint32_t keep = + (1U<data[0] &= keep; + simstate->data[0] |= pstate->data[0] & ~keep; + } + else + { + memcpy(simstate->data, pstate->data, + dsize * sizeof(uint32_t)); + } + } + + client_state = simstate; + client_count++; + } + + /* The frontend always uses the first resolved input, so make sure it's right */ + while (simframe->resolved_input[device] + && (simframe->resolved_input[device]->size != dsize + || simframe->resolved_input[device]->client_num != 0)) + { + /* The default resolved input is of the wrong size! */ + netplay_input_state_t nextistate = simframe->resolved_input[device]->next; + free(simframe->resolved_input[device]); + simframe->resolved_input[device] = nextistate; + } + + /* Now we copy the state, whether real or simulated, out into the resolved state */ + resstate = netplay_input_state_for(&simframe->resolved_input[device], 0, + dsize, false, false); + if (!resstate) + continue; + + if (client_count == 1) + { + /* Trivial in the common 1-client case */ + if (memcmp(resstate->data, client_state->data, dsize * sizeof(uint32_t))) + ret = true; + memcpy(resstate->data, client_state->data, dsize * sizeof(uint32_t)); + + } + else if (client_count == 0) + { + uint32_t word; + for (word = 0; word < dsize; word++) + { + if (resstate->data[word]) + ret = true; + resstate->data[word] = 0; + } + } else { - memcpy(simframe->simulated_input_state[player], - pframe->real_input_state[player], - WORDS_PER_INPUT * sizeof(uint32_t)); + /* Merge them */ + /* Most devices have all the digital parts in the first word. */ + static const uint32_t digital_common[3] = {-1, 0, 0}; + static const uint32_t digital_keyboard[5] = {-1, -1, -1, -1, -1}; + const uint32_t *digital; + if (dtype == RETRO_DEVICE_KEYBOARD) + digital = digital_keyboard; + else + digital = digital_common; + oldresstate = netplay_input_state_for(&simframe->resolved_input[device], 1, dsize, false, false); + if (!oldresstate) + continue; + memcpy(oldresstate->data, resstate->data, dsize * sizeof(uint32_t)); + memset(resstate->data, 0, dsize * sizeof(uint32_t)); + + netplay_merge_digital(netplay, resstate, simframe, device, clients, digital); + netplay_merge_analog(netplay, resstate, simframe, device, clients, dtype); + + if (memcmp(resstate->data, oldresstate->data, dsize * sizeof(uint32_t))) + ret = true; + } } + + return ret; } static void netplay_handle_frame_hash(netplay_t *netplay, struct delta_frame *delta) @@ -391,7 +701,7 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled) } /* Only relevant if we're connected and not in a desynching operation */ - if ((netplay->is_server && !netplay->connected_players) || + if ((netplay->is_server && (netplay->connected_players<=1)) || (netplay->self_mode < NETPLAY_CONNECTION_CONNECTED) || (netplay->desync)) { @@ -414,43 +724,72 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled) netplay->force_reset = false; } + netplay->replay_ptr = netplay->other_ptr; + netplay->replay_frame_count = netplay->other_frame_count; + #ifndef DEBUG_NONDETERMINISTIC_CORES if (!netplay->force_rewind) { + bool cont = true; + /* Skip ahead if we predicted correctly. * Skip until our simulation failed. */ while (netplay->other_frame_count < netplay->unread_frame_count && netplay->other_frame_count < netplay->run_frame_count) { struct delta_frame *ptr = &netplay->buffer[netplay->other_ptr]; - size_t i; - for (i = 0; i < MAX_USERS; i++) + /* If resolving the input changes it, we used bad input */ + if (netplay_resolve_input(netplay, netplay->other_ptr, true)) { - if (memcmp(ptr->simulated_input_state[i], ptr->real_input_state[i], - sizeof(ptr->real_input_state[i])) != 0 - && !ptr->used_real[i]) - break; + cont = false; + break; } - if (i != MAX_USERS) break; + netplay_handle_frame_hash(netplay, ptr); netplay->other_ptr = NEXT_PTR(netplay->other_ptr); netplay->other_frame_count++; } + netplay->replay_ptr = netplay->other_ptr; + netplay->replay_frame_count = netplay->other_frame_count; + + if (cont) + { + while (netplay->replay_frame_count < netplay->run_frame_count) + { + struct delta_frame *ptr = &netplay->buffer[netplay->replay_ptr]; + if (netplay_resolve_input(netplay, netplay->replay_ptr, true)) + break; + netplay->replay_ptr = NEXT_PTR(netplay->replay_ptr); + netplay->replay_frame_count++; + } + } } #endif /* Now replay the real input if we've gotten ahead of it */ if (netplay->force_rewind || - (netplay->other_frame_count < netplay->unread_frame_count && - netplay->other_frame_count < netplay->run_frame_count)) + (netplay->replay_frame_count < netplay->unread_frame_count && + netplay->replay_frame_count < netplay->run_frame_count)) { retro_ctx_serialize_info_t serial_info; /* Replay frames. */ netplay->is_replay = true; - netplay->replay_ptr = netplay->other_ptr; - netplay->replay_frame_count = netplay->other_frame_count; + + /* If we have a keyboard device, we replay the previous frame's input + * just to assert that the keydown/keyup events work if the core + * translates them in that way */ + if (netplay->have_updown_device) + { + netplay->replay_ptr = PREV_PTR(netplay->replay_ptr); + netplay->replay_frame_count--; + autosave_lock(); + core_run(); + autosave_unlock(); + netplay->replay_ptr = NEXT_PTR(netplay->replay_ptr); + netplay->replay_frame_count++; + } if (netplay->quirks & NETPLAY_QUIRK_INITIALIZATION) /* Make sure we're initialized before we start loading things */ @@ -483,7 +822,7 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled) netplay_handle_frame_hash(netplay, ptr); /* Re-simulate this frame's input */ - netplay_simulate_input(netplay, netplay->replay_ptr, true); + netplay_resolve_input(netplay, netplay->replay_ptr, true); autosave_lock(); core_run(); @@ -536,16 +875,16 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled) if (netplay->is_server) { - uint32_t player; + uint32_t client; lo_frame_count = hi_frame_count = netplay->unread_frame_count; /* Look for players that are ahead of us */ - for (player = 0; player < MAX_USERS; player++) + for (client = 0; client < MAX_CLIENTS; client++) { - if (!(netplay->connected_players & (1<read_frame_count[player] > hi_frame_count) - hi_frame_count = netplay->read_frame_count[player]; + if (!(netplay->connected_players & (1<read_frame_count[client] > hi_frame_count) + hi_frame_count = netplay->read_frame_count[client]; } } else @@ -611,14 +950,14 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled) for (i = 0; i < netplay->connections_size; i++) { struct netplay_connection *connection = &netplay->connections[i]; - int player; + uint32_t client_num; if (!connection->active || connection->mode != NETPLAY_CONNECTION_PLAYING) continue; - player = connection->player; + client_num = i + 1; /* Are they ahead? */ - if (netplay->self_frame_count + 3 < netplay->read_frame_count[player]) + if (netplay->self_frame_count + 3 < netplay->read_frame_count[client_num]) { /* Tell them to stall */ if (connection->stall_frame + NETPLAY_MAX_REQ_STALL_FREQUENCY < @@ -626,7 +965,7 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled) { connection->stall_frame = netplay->self_frame_count; netplay_cmd_stall(netplay, connection, - netplay->read_frame_count[player] - + netplay->read_frame_count[client_num] - netplay->self_frame_count + 1); } } diff --git a/retroarch.c b/retroarch.c index 59a23c07a3..7dff2df3cd 100644 --- a/retroarch.c +++ b/retroarch.c @@ -2692,20 +2692,13 @@ static enum runloop_state runloop_check_state( #ifdef HAVE_NETWORKING /* Check Netplay */ { - static bool old_netplay_flip = false; static bool old_netplay_watch = false; - bool netplay_flip = BIT256_GET( - current_input, RARCH_NETPLAY_FLIP); bool netplay_watch = BIT256_GET( current_input, RARCH_NETPLAY_GAME_WATCH); - if (netplay_flip && !old_netplay_flip) - netplay_driver_ctl(RARCH_NETPLAY_CTL_FLIP_PLAYERS, NULL); - if (netplay_watch && !old_netplay_watch) netplay_driver_ctl(RARCH_NETPLAY_CTL_GAME_WATCH, NULL); - old_netplay_flip = netplay_flip; old_netplay_watch = netplay_watch; } #endif diff --git a/tools/ranetplayer/ranetplayer.c b/tools/ranetplayer/ranetplayer.c index 60360b70d2..11743cf079 100644 --- a/tools/ranetplayer/ranetplayer.c +++ b/tools/ranetplayer/ranetplayer.c @@ -120,7 +120,6 @@ uint32_t frame_offset_cmd(bool ntoh) case NETPLAY_CMD_CRC: case NETPLAY_CMD_LOAD_SAVESTATE: case NETPLAY_CMD_RESET: - case NETPLAY_CMD_FLIP_PLAYERS: frame = ntohl(payload[0]); if (ntoh) frame -= frame_offset; @@ -285,7 +284,7 @@ int main(int argc, char **argv) } /* Expect the header */ - if (!socket_receive_all_blocking(sock, payload, 4*sizeof(uint32_t))) + if (!socket_receive_all_blocking(sock, payload, 6*sizeof(uint32_t))) { fprintf(stderr, "Failed to receive connection header.\n"); return 1; @@ -299,7 +298,7 @@ int main(int argc, char **argv) } /* Echo the connection header back */ - socket_send_all_blocking(sock, payload, 4*sizeof(uint32_t), true); + socket_send_all_blocking(sock, payload, 6*sizeof(uint32_t), true); /* Send a nickname */ cmd = NETPLAY_CMD_NICK; @@ -340,7 +339,8 @@ int main(int argc, char **argv) if (playing) { cmd = NETPLAY_CMD_PLAY; - cmd_size = 0; + cmd_size = sizeof(uint32_t); + payload[0] = htonl(1); SEND(); } @@ -397,7 +397,7 @@ int main(int argc, char **argv) /* Only sync based on server time */ if (cmd == NETPLAY_CMD_INPUT && (cmd_size < 2*sizeof(uint32_t) || - !(ntohl(payload[1]) & NETPLAY_CMD_INPUT_BIT_SERVER))) + (ntohl(payload[1]) != 0))) { break; }