From 7e798d04ba16fb449df36f44f6808df20030ced9 Mon Sep 17 00:00:00 2001 From: Cthulhu-throwaway <96153783+Cthulhu-throwaway@users.noreply.github.com> Date: Fri, 17 Jun 2022 17:38:56 -0300 Subject: [PATCH] (Netplay) Some refactoring and fixes --- menu/cbs/menu_cbs_ok.c | 217 ++-- menu/cbs/menu_cbs_sublabel.c | 116 +- menu/menu_displaylist.c | 69 +- network/discord.c | 96 +- network/netplay/netplay.h | 218 +--- network/netplay/netplay_frontend.c | 1799 +++++++++++++++------------- network/netplay/netplay_private.h | 444 +++---- 7 files changed, 1352 insertions(+), 1607 deletions(-) diff --git a/menu/cbs/menu_cbs_ok.c b/menu/cbs/menu_cbs_ok.c index 7c6a31745d..54e9c441e4 100644 --- a/menu/cbs/menu_cbs_ok.c +++ b/menu/cbs/menu_cbs_ok.c @@ -5959,113 +5959,109 @@ static int action_ok_wifi_disconnect(const char *path, } #endif -static int action_ok_netplay_connect_room(const char *path, - const char *label, unsigned type, size_t idx, size_t entry_idx) +static int action_ok_netplay_connect_room(const char *path, const char *label, + unsigned type, size_t idx, size_t entry_idx) { - char tmp_hostname[4115]; + char hostname[512]; + struct netplay_room *room; net_driver_state_t *net_st = networking_state_get_ptr(); unsigned room_index = type - MENU_SETTINGS_NETPLAY_ROOMS_START; if (room_index >= (unsigned)net_st->room_count) return menu_cbs_exit(); - tmp_hostname[0] = '\0'; + room = &net_st->room_list[room_index]; if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL)) generic_action_ok_command(CMD_EVENT_NETPLAY_DEINIT); + netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_CLIENT, NULL); - if (net_st->room_list[room_index].host_method == NETPLAY_HOST_METHOD_MITM) - snprintf(tmp_hostname, - sizeof(tmp_hostname), - "%s|%d|%s", - net_st->room_list[room_index].mitm_address, - net_st->room_list[room_index].mitm_port, - net_st->room_list[room_index].mitm_session); + if (room->host_method == NETPLAY_HOST_METHOD_MITM) + snprintf(hostname, sizeof(hostname), "%s|%d|%s", + room->mitm_address, room->mitm_port, room->mitm_session); else - snprintf(tmp_hostname, - sizeof(tmp_hostname), - "%s|%d", - net_st->room_list[room_index].address, - net_st->room_list[room_index].port); + snprintf(hostname, sizeof(hostname), "%s|%d", room->address, room->port); #if 0 - RARCH_LOG("[lobby] connecting to: %s with game: %s/%08x\n", - tmp_hostname, - net_st->room_list[room_index].gamename, - net_st->room_list[room_index].gamecrc); + RARCH_LOG("[Lobby] Connecting to: %s with game: %s/%08x\n", + hostname, room->gamename, room->gamecrc); #endif - task_push_netplay_crc_scan( - net_st->room_list[room_index].gamecrc, - net_st->room_list[room_index].gamename, - tmp_hostname, - net_st->room_list[room_index].corename, - net_st->room_list[room_index].subsystem_name); + task_push_netplay_crc_scan(room->gamecrc, room->gamename, hostname, + room->corename, room->subsystem_name); return 0; } -static void netplay_refresh_rooms_cb(retro_task_t *task, - void *task_data, void *user_data, const char *error) +static void netplay_refresh_rooms_cb(retro_task_t *task, void *task_data, + void *user_data, const char *error) { - char *new_data = NULL; + char *room_data = NULL; const char *path = NULL; const char *label = NULL; unsigned menu_type = 0; enum msg_hash_enums enum_idx = MSG_UNKNOWN; - net_driver_state_t *net_st = networking_state_get_ptr(); - http_transfer_data_t *data = (http_transfer_data_t*)task_data; bool refresh = false; + http_transfer_data_t *data = (http_transfer_data_t*)task_data; + net_driver_state_t *net_st = networking_state_get_ptr(); + + free(net_st->room_list); + net_st->room_list = NULL; + net_st->room_count = 0; menu_entries_get_last_stack(&path, &label, &menu_type, &enum_idx, NULL); /* Don't push the results if we left the netplay menu */ - if (!string_is_equal(label, - msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_TAB)) && - !string_is_equal(label, - msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY))) + if (!string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_TAB)) && + !string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY))) return; if (error) { - RARCH_ERR("%s: %s\n", msg_hash_to_str(MSG_DOWNLOAD_FAILED), - error); - return; + RARCH_ERR("%s: %s\n", msg_hash_to_str(MSG_DOWNLOAD_FAILED), error); + goto done; } if (!data || !data->data || !data->len || data->status != 200) { RARCH_ERR("%s\n", msg_hash_to_str(MSG_DOWNLOAD_FAILED)); - return; + goto done; } - new_data = (char*)realloc(data->data, data->len + 1); - if (!new_data) - return; - data->data = new_data; - data->data[data->len] = '\0'; + room_data = (char*)malloc(data->len + 1); + if (!room_data) + goto done; + memcpy(room_data, data->data, data->len); + room_data[data->len] = '\0'; - if (net_st->room_list) - free(net_st->room_list); - - net_st->room_list = NULL; - net_st->room_count = 0; - - if (!string_is_empty(data->data)) + if (!string_is_empty(room_data)) { - int i; + int room_count; - netplay_rooms_parse(data->data); + netplay_rooms_parse(room_data); - net_st->room_count = netplay_rooms_get_count(); - net_st->room_list = (struct netplay_room*)calloc(net_st->room_count, - sizeof(*net_st->room_list)); - - for (i = 0; i < net_st->room_count; i++) - memcpy(&net_st->room_list[i], netplay_room_get(i), + room_count = netplay_rooms_get_count(); + if (room_count > 0) + { + net_st->room_list = (struct netplay_room*)calloc(room_count, sizeof(*net_st->room_list)); + if (net_st->room_list) + { + int i; + + net_st->room_count = room_count; + for (i = 0; i < room_count; i++) + memcpy(&net_st->room_list[i], netplay_room_get(i), + sizeof(*net_st->room_list)); + } + } + + netplay_rooms_free(); } + free(room_data); + +done: menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh); menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL); } @@ -6073,92 +6069,81 @@ static void netplay_refresh_rooms_cb(retro_task_t *task, static int action_ok_push_netplay_refresh_rooms(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { -#ifndef NETPLAY_TEST_BUILD - const char *url = "http://lobby.libretro.com/list"; -#else - const char *url = "http://lobbytest.libretro.com/list"; -#endif - - task_push_http_transfer(url, true, NULL, netplay_refresh_rooms_cb, NULL); + task_push_http_transfer(FILE_PATH_LOBBY_LIBRETRO_URL "list", true, NULL, + netplay_refresh_rooms_cb, NULL); return 0; } #ifdef HAVE_NETPLAYDISCOVERY -static void netplay_refresh_lan_cb(retro_task_t *task, - void *task_data, void *user_data, const char *error) +static void netplay_refresh_lan_cb(retro_task_t *task, void *task_data, + void *user_data, const char *error) { + int i; const char *path = NULL; const char *label = NULL; unsigned menu_type = 0; enum msg_hash_enums enum_idx = MSG_UNKNOWN; - net_driver_state_t *net_st = networking_state_get_ptr(); struct netplay_host_list *hosts = NULL; bool refresh = false; + net_driver_state_t *net_st = networking_state_get_ptr(); + + free(net_st->room_list); + net_st->room_list = NULL; + net_st->room_count = 0; menu_entries_get_last_stack(&path, &label, &menu_type, &enum_idx, NULL); /* Don't push the results if we left the netplay menu */ - if (!string_is_equal(label, - msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_TAB)) && - !string_is_equal(label, - msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY))) - goto finished; + if (!string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_TAB)) && + !string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY))) + goto deinit; if (!netplay_discovery_driver_ctl( - RARCH_NETPLAY_DISCOVERY_CTL_LAN_GET_RESPONSES, &hosts) || - !hosts) - goto finished; + RARCH_NETPLAY_DISCOVERY_CTL_LAN_GET_RESPONSES, &hosts)) + goto done; + if (!hosts || !hosts->size) + goto done; - if (net_st->room_list) - free(net_st->room_list); + net_st->room_list = + (struct netplay_room*)calloc(hosts->size, sizeof(*net_st->room_list)); + if (!net_st->room_list) + goto done; + net_st->room_count = hosts->size; - net_st->room_list = NULL; - net_st->room_count = 0; - - if (hosts->size) + for (i = 0; i < net_st->room_count; i++) { - int i; + struct netplay_host *host = &hosts->hosts[i]; + struct netplay_room *room = &net_st->room_list[i]; - net_st->room_count = hosts->size; - net_st->room_list = (struct netplay_room*)calloc(net_st->room_count, - sizeof(*net_st->room_list)); + room->gamecrc = host->content_crc; + room->port = host->port; - for (i = 0; i < net_st->room_count; i++) - { - struct netplay_host *host = &hosts->hosts[i]; - struct netplay_room *room = &net_st->room_list[i]; + strlcpy(room->nickname, host->nick, sizeof(room->nickname)); + strlcpy(room->frontend, host->frontend, sizeof(room->frontend)); + strlcpy(room->corename, host->core, sizeof(room->corename)); + strlcpy(room->coreversion, host->core_version, + sizeof(room->coreversion)); + strlcpy(room->retroarch_version, host->retroarch_version, + sizeof(room->retroarch_version)); + strlcpy(room->gamename, host->content, sizeof(room->gamename)); + strlcpy(room->subsystem_name, host->subsystem_name, + sizeof(room->subsystem_name)); + strlcpy(room->address, host->address, sizeof(room->address)); - room->port = host->port; - room->gamecrc = host->content_crc; - strlcpy(room->retroarch_version, host->retroarch_version, - sizeof(room->retroarch_version)); - strlcpy(room->nickname, host->nick, - sizeof(room->nickname)); - strlcpy(room->subsystem_name, host->subsystem_name, - sizeof(room->subsystem_name)); - strlcpy(room->corename, host->core, - sizeof(room->corename)); - strlcpy(room->frontend, host->frontend, - sizeof(room->frontend)); - strlcpy(room->coreversion, host->core_version, - sizeof(room->coreversion)); - strlcpy(room->gamename, host->content, - sizeof(room->gamename)); - strlcpy(room->address, host->address, - sizeof(room->address)); - room->has_password = host->has_password; - room->has_spectate_password = host->has_spectate_password; - room->connectable = true; - room->is_retroarch = true; - room->lan = true; - } + room->has_password = host->has_password; + room->has_spectate_password = host->has_spectate_password; + + room->connectable = true; + room->is_retroarch = true; + room->lan = true; } +done: menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh); menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL); -finished: +deinit: deinit_netplay_discovery(); } diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index e8da9a383f..feef5154fb 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -1494,93 +1494,48 @@ static int action_bind_sublabel_cheat_desc( #endif #ifdef HAVE_NETWORKING -static int action_bind_sublabel_netplay_room( - file_list_t *list, +static int action_bind_sublabel_netplay_room(file_list_t *list, unsigned type, unsigned i, const char *label, const char *path, char *s, size_t len) { - uint32_t gamecrc = 0; - const char *ra_version = NULL; - const char *corename = NULL; - const char *gamename = NULL; - const char *core_ver = NULL; - const char *frontend = NULL; - const char *na = NULL; - const char *subsystem = NULL; + char buf[512]; + struct netplay_room *room; net_driver_state_t *net_st = networking_state_get_ptr(); unsigned room_index = type - MENU_SETTINGS_NETPLAY_ROOMS_START; if (room_index >= (unsigned)net_st->room_count) return menu_cbs_exit(); - ra_version = net_st->room_list[room_index].retroarch_version; - corename = net_st->room_list[room_index].corename; - gamename = net_st->room_list[room_index].gamename; - core_ver = net_st->room_list[room_index].coreversion; - gamecrc = net_st->room_list[room_index].gamecrc; - frontend = net_st->room_list[room_index].frontend; - subsystem = net_st->room_list[room_index].subsystem_name; - na = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE); + room = &net_st->room_list[room_index]; - if (string_is_empty(subsystem) || string_is_equal(subsystem, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE))) - { - snprintf(s, len, - "%s: %s (%s)\n%s: %s (%s)\nGame: %s (%08lx)", - msg_hash_to_str(MSG_PROGRAM), - string_is_empty(ra_version) ? na : ra_version, - string_is_empty(frontend) ? na : frontend, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CONTENT_INFO_CORE_NAME), - corename, core_ver, - !string_is_equal(gamename, na) ? gamename : na, - (unsigned long)gamecrc); - } + snprintf(s, len, + "%s: %s (%s)\n" + "%s: %s (%s)\n" + "%s: %s ", + msg_hash_to_str(MSG_PROGRAM), + !string_is_empty(room->retroarch_version) ? room->retroarch_version : + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE), + (!string_is_empty(room->frontend) && + !string_is_equal_case_insensitive(room->frontend, "N/A")) ? + room->frontend : + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CONTENT_INFO_CORE_NAME), + room->corename, room->coreversion, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CONTENT), + (!string_is_empty(room->gamename) && + !string_is_equal_case_insensitive(room->gamename, "N/A")) ? + room->gamename : + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE)); + + if (string_is_empty(room->subsystem_name) || + string_is_equal_case_insensitive(room->subsystem_name, "N/A")) + snprintf(buf, sizeof(buf), "(%08lX)", (unsigned long)room->gamecrc); else - { - if (strstr(gamename, "|")) - { - char buf[4096]; - unsigned i = 0; - struct string_list list = {0}; + snprintf(buf, sizeof(buf), "(%s)", room->subsystem_name); - string_list_initialize(&list); - string_split_noalloc(&list, gamename, "|"); + strlcat(s, buf, len); - buf[0] = '\0'; - - for (i = 0; i < list.size; i++) - { - strlcat(buf, " ", sizeof(buf)); - strlcat(buf, list.elems[i].data, sizeof(buf)); - /* Never terminate a UI string with a newline */ - if (i != list.size - 1) - strlcat(buf, "\n", sizeof(buf)); - } - snprintf(s, len, - "%s: %s (%s)\n%s: %s (%s)\nSubsystem: %s\nGames:\n%s", - msg_hash_to_str(MSG_PROGRAM), - string_is_empty(ra_version) ? na : ra_version, - string_is_empty(frontend) ? na : frontend, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CONTENT_INFO_CORE_NAME), - corename, core_ver, subsystem, - !string_is_equal(gamename, na) ? buf : na - ); - string_list_deinitialize(&list); - } - else - { - snprintf(s, len, - "%s: %s (%s)\n%s: %s (%s)\nSubsystem: %s\nGame: %s (%08lx)", - msg_hash_to_str(MSG_PROGRAM), - string_is_empty(ra_version) ? na : ra_version, - string_is_empty(frontend) ? na : frontend, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_CONTENT_INFO_CORE_NAME), - corename, core_ver, subsystem, - !string_is_equal(gamename, na) ? gamename : na, - (unsigned long)gamecrc); - } - } return 0; } @@ -1590,10 +1545,10 @@ static int action_bind_sublabel_netplay_kick_client(file_list_t *list, char *s, size_t len) { char buf[256]; - netplay_client_info_t *client = NULL; - const char *status = NULL; - size_t idx = list->list[i].entry_idx; - net_driver_state_t *net_st = networking_state_get_ptr(); + netplay_client_info_t *client; + const char *status = NULL; + size_t idx = list->list[i].entry_idx; + net_driver_state_t *net_st = networking_state_get_ptr(); if (idx >= net_st->client_info_count) return menu_cbs_exit(); @@ -1625,20 +1580,17 @@ static int action_bind_sublabel_netplay_kick_client(file_list_t *list, snprintf(buf, sizeof(buf), "%s: %s", msg_hash_to_str(MSG_NETPLAY_CHAT_SUPPORTED), msg_hash_to_str((client->protocol >= 6) ? - MENU_ENUM_LABEL_VALUE_YES : - MENU_ENUM_LABEL_VALUE_NO)); + MENU_ENUM_LABEL_VALUE_YES : MENU_ENUM_LABEL_VALUE_NO)); strlcat(s, buf, len); if (client->ping >= 0) { - snprintf(buf, sizeof(buf), "\nPing: %u", - (unsigned)client->ping); + snprintf(buf, sizeof(buf), "\nPing: %u", (unsigned)client->ping); strlcat(s, buf, len); } return 0; } - #endif static int action_bind_sublabel_playlist_entry( diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 1dae7f3231..b409e36c73 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -10386,9 +10386,16 @@ static unsigned menu_displaylist_build_shader_parameter( unsigned menu_displaylist_netplay_refresh_rooms(file_list_t *list) { int i; + char buf[256]; + char passworded[64]; + char country[8]; + const char *room_type; + struct netplay_room *room; unsigned count = 0; settings_t *settings = config_get_ptr(); net_driver_state_t *net_st = networking_state_get_ptr(); + bool show_only_connectable = settings->bools.netplay_show_only_connectable; + bool show_passworded = settings->bools.netplay_show_passworded; menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, list); @@ -10400,8 +10407,8 @@ unsigned menu_displaylist_netplay_refresh_rooms(file_list_t *list) count++; if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL) && - !netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_SERVER, NULL) && - netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_CONNECTED, NULL)) + !netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_SERVER, NULL) && + netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_CONNECTED, NULL)) { if (menu_entries_append_enum(list, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_DISCONNECT), @@ -10412,10 +10419,10 @@ unsigned menu_displaylist_netplay_refresh_rooms(file_list_t *list) } if (menu_entries_append_enum(list, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_ENABLE_CLIENT), - msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_ENABLE_CLIENT), - MENU_ENUM_LABEL_NETPLAY_ENABLE_CLIENT, - MENU_SETTING_ACTION, 0, 0)) + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NETPLAY_ENABLE_CLIENT), + msg_hash_to_str(MENU_ENUM_LABEL_NETPLAY_ENABLE_CLIENT), + MENU_ENUM_LABEL_NETPLAY_ENABLE_CLIENT, + MENU_SETTING_ACTION, 0, 0)) count++; if (menu_entries_append_enum(list, @@ -10450,11 +10457,7 @@ unsigned menu_displaylist_netplay_refresh_rooms(file_list_t *list) for (i = 0; i < net_st->room_count; i++) { - char buf[8192]; - char passworded[64]; - char country[8]; - const char *room_type; - struct netplay_room *room = &net_st->room_list[i]; + room = &net_st->room_list[i]; /* Get rid of any room that is not running RetroArch. */ if (!room->is_retroarch) @@ -10464,7 +10467,7 @@ unsigned menu_displaylist_netplay_refresh_rooms(file_list_t *list) if the user opt-in. */ if (!room->connectable) { - if (settings->bools.netplay_show_only_connectable) + if (show_only_connectable) continue; room_type = msg_hash_to_str(MSG_INTERNET_NOT_CONNECTABLE); @@ -10480,7 +10483,7 @@ unsigned menu_displaylist_netplay_refresh_rooms(file_list_t *list) if the user opt-in. */ if (room->has_password || room->has_spectate_password) { - if (!settings->bools.netplay_show_passworded) + if (!show_passworded) continue; snprintf(passworded, sizeof(passworded), "[%s] ", @@ -10490,46 +10493,20 @@ unsigned menu_displaylist_netplay_refresh_rooms(file_list_t *list) *passworded = '\0'; if (!room->lan && !string_is_empty(room->country)) - snprintf(country, sizeof(country), " (%s)", - room->country); + snprintf(country, sizeof(country), " (%s)", room->country); else *country = '\0'; snprintf(buf, sizeof(buf), "%s%s: %s%s", - passworded, room_type, - room->nickname, country); + passworded, room_type, room->nickname, country); - if (menu_entries_append_enum(list, - buf, + if (menu_entries_append_enum(list, buf, msg_hash_to_str(MENU_ENUM_LABEL_CONNECT_NETPLAY_ROOM), MENU_ENUM_LABEL_CONNECT_NETPLAY_ROOM, - (unsigned)(MENU_SETTINGS_NETPLAY_ROOMS_START + i), 0, 0)) + (unsigned)MENU_SETTINGS_NETPLAY_ROOMS_START + i, 0, 0)) count++; - - /* Uncomment this to debug mismatched room parameters*/ -#if 0 - RARCH_LOG("[Lobby]: Room Data: %d\n" - "Nickname: %s\n" - "Address: %s\n" - "Port: %d\n" - "Core: %s\n" - "Core Version: %s\n" - "Game: %s\n" - "Game CRC: %08x\n" - "Timestamp: %d\n", room_data->elems[j + 6].data, - room->nickname, - room->address, - room->port, - room->corename, - room->coreversion, - room->gamename, - room->gamecrc, - room->timestamp); -#endif } - netplay_rooms_free(); - return count; } @@ -10637,13 +10614,14 @@ static unsigned menu_displaylist_netplay_kick(file_list_t *list) if (netplay_driver_ctl(RARCH_NETPLAY_CTL_REFRESH_CLIENT_INFO, NULL)) { - char client_id[4]; size_t i; + char client_id[4]; + netplay_client_info_t *client; net_driver_state_t *net_st = networking_state_get_ptr(); for (i = 0; i < net_st->client_info_count; i++) { - netplay_client_info_t *client = &net_st->client_info[i]; + client = &net_st->client_info[i]; snprintf(client_id, sizeof(client_id), "%d", client->id); if (menu_entries_append_enum(list, client->name, client_id, @@ -10664,7 +10642,6 @@ static unsigned menu_displaylist_netplay_kick(file_list_t *list) return count; } - #endif bool menu_displaylist_has_subsystems(void) diff --git a/network/discord.c b/network/discord.c index c2f2669161..fde2d36c1a 100644 --- a/network/discord.c +++ b/network/discord.c @@ -53,7 +53,7 @@ extern "C" } #endif -static discord_state_t discord_state_st; /* int64_t alignment */ +static discord_state_t discord_state_st = {0}; /* int64_t alignment */ discord_state_t *discord_state_get_ptr(void) { @@ -153,99 +153,113 @@ static void handle_discord_error(int errcode, const char* message) { } -static void handle_discord_join_cb(retro_task_t *task, - void *task_data, void *user_data, const char *err) +static void handle_discord_join_cb(retro_task_t *task, void *task_data, + void *user_data, const char *error) { - char join_hostname[PATH_MAX_LENGTH]; - struct netplay_room *room = NULL; - http_transfer_data_t *data = (http_transfer_data_t*)task_data; - discord_state_t *discord_st = &discord_state_st; + char hostname[512]; + struct netplay_room *room; + char *room_data = NULL; + http_transfer_data_t *data = (http_transfer_data_t*)task_data; + discord_state_t *discord_st = &discord_state_st; - if (!data || err || !data->data || !data->len) - goto finish; + if (error) + goto done; + if (!data || !data->data || !data->len) + goto done; + if (data->status != 200) + goto done; - data->data = (char*)realloc(data->data, data->len + 1); - data->data[data->len] = '\0'; + room_data = (char*)malloc(data->len + 1); + if (!room_data) + goto done; + memcpy(room_data, data->data, data->len); + room_data[data->len] = '\0'; - netplay_rooms_parse(data->data); - room = netplay_room_get(0); + netplay_rooms_parse(room_data); + free(room_data); + room = netplay_room_get(0); if (room) { - if (room->host_method == NETPLAY_HOST_METHOD_MITM) - snprintf(join_hostname, sizeof(join_hostname), "%s|%d|%s", - room->mitm_address, room->mitm_port, room->mitm_session); - else - snprintf(join_hostname, sizeof(join_hostname), "%s|%d", - room->address, room->port); - if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL)) deinit_netplay(); + netplay_driver_ctl(RARCH_NETPLAY_CTL_ENABLE_CLIENT, NULL); - task_push_netplay_crc_scan(room->gamecrc, - room->gamename, join_hostname, room->corename, room->subsystem_name); + if (room->host_method == NETPLAY_HOST_METHOD_MITM) + snprintf(hostname, sizeof(hostname), "%s|%d|%s", + room->mitm_address, room->mitm_port, room->mitm_session); + else + snprintf(hostname, sizeof(hostname), "%s|%d", + room->address, room->port); + + task_push_netplay_crc_scan(room->gamecrc, room->gamename, hostname, + room->corename, room->subsystem_name); + discord_st->connecting = true; if (discord_st->ready) discord_update(PRESENCE_NETPLAY_CLIENT); } -finish: - if (user_data) - free(user_data); + netplay_rooms_free(); + +done: + free(user_data); } -static void handle_discord_join(const char* secret) +static void handle_discord_join(const char *secret) { - char url[2048]; - struct string_list *list = string_split(secret, "|"); + char url[512]; discord_state_t *discord_st = &discord_state_st; + int room_id = (int)strtol(secret, NULL, 10); - strlcpy(discord_st->peer_party_id, - list->elems[0].data, sizeof(discord_st->peer_party_id)); - snprintf(url, sizeof(url), FILE_PATH_LOBBY_LIBRETRO_URL "%s/", - discord_st->peer_party_id); + if (room_id) + { + snprintf(discord_st->peer_party_id, sizeof(discord_st->peer_party_id), + "%d", room_id); - task_push_http_transfer(url, true, NULL, handle_discord_join_cb, NULL); + strlcpy(url, FILE_PATH_LOBBY_LIBRETRO_URL, sizeof(url)); + strlcat(url, discord_st->peer_party_id, sizeof(url)); + + task_push_http_transfer(url, true, NULL, handle_discord_join_cb, NULL); + } } -static void handle_discord_spectate(const char* secret) +static void handle_discord_spectate(const char *secret) { } -#ifdef HAVE_MENU #if 0 +#ifdef HAVE_MENU static void handle_discord_join_response(void *ignore, const char *line) { /* TODO/FIXME: needs in-game widgets */ if (strstr(line, "yes")) Discord_Respond(user_id, DISCORD_REPLY_YES); -#ifdef HAVE_MENU menu_input_dialog_end(); retroarch_menu_running_finished(false); -#endif } #endif #endif -static void handle_discord_join_request(const DiscordUser* request) +static void handle_discord_join_request(const DiscordUser *request) { #ifdef HAVE_MENU #if 0 char buf[PATH_MAX_LENGTH]; - menu_input_ctx_line_t line; + menu_input_ctx_line_t line = {0}; #endif discord_state_t *discord_st = &discord_state_st; + discord_download_avatar(discord_st, request->userId, request->avatar); #if 0 /* TODO/FIXME: Needs in-game widgets */ retroarch_menu_running(); - memset(&line, 0, sizeof(line)); snprintf(buf, sizeof(buf), "%s %s?", - msg_hash_to_str(MSG_DISCORD_CONNECTION_REQUEST), request->username); + msg_hash_to_str(MSG_DISCORD_CONNECTION_REQUEST), request->username); line.label = buf; line.label_setting = "no_setting"; line.cb = handle_discord_join_response; diff --git a/network/netplay/netplay.h b/network/netplay/netplay.h index 46a0ad8897..a67cc97ae3 100644 --- a/network/netplay/netplay.h +++ b/network/netplay/netplay.h @@ -23,8 +23,6 @@ #include #include -#include -#include #ifdef HAVE_CONFIG_H #include "../../config.h" @@ -32,16 +30,14 @@ #include -#include "../../retroarch_types.h" - #include "../natt.h" -#include "netplay_protocol.h" - #define NETPLAY_NICK_LEN 32 #define NETPLAY_HOST_STR_LEN 32 #define NETPLAY_HOST_LONGSTR_LEN 256 +#define NETPLAY_MITM_SERVERS 5 + #define NETPLAY_CHAT_MAX_MESSAGES 5 #define NETPLAY_CHAT_MAX_SIZE 96 #define NETPLAY_CHAT_FRAME_TIME 900 @@ -132,10 +128,10 @@ enum netplay_host_method enum rarch_netplay_discovery_ctl_state { - RARCH_NETPLAY_DISCOVERY_CTL_NONE = 0, - RARCH_NETPLAY_DISCOVERY_CTL_LAN_SEND_QUERY, - RARCH_NETPLAY_DISCOVERY_CTL_LAN_GET_RESPONSES, - RARCH_NETPLAY_DISCOVERY_CTL_LAN_CLEAR_RESPONSES + RARCH_NETPLAY_DISCOVERY_CTL_NONE = 0, + RARCH_NETPLAY_DISCOVERY_CTL_LAN_SEND_QUERY, + RARCH_NETPLAY_DISCOVERY_CTL_LAN_GET_RESPONSES, + RARCH_NETPLAY_DISCOVERY_CTL_LAN_CLEAR_RESPONSES }; typedef struct netplay netplay_t; @@ -143,62 +139,38 @@ typedef struct netplay netplay_t; typedef struct netplay_client_info { uint32_t protocol; - int32_t ping; - int id; - enum rarch_netplay_connection_mode mode; - char name[NETPLAY_NICK_LEN]; + int32_t ping; + int id; + enum rarch_netplay_connection_mode mode; + char name[NETPLAY_NICK_LEN]; } netplay_client_info_t; -struct ad_packet -{ - uint32_t header; - int32_t content_crc; - int32_t port; - uint32_t has_password; - char nick[NETPLAY_NICK_LEN]; - char frontend[NETPLAY_HOST_STR_LEN]; - char core[NETPLAY_HOST_STR_LEN]; - char core_version[NETPLAY_HOST_STR_LEN]; - char retroarch_version[NETPLAY_HOST_STR_LEN]; - char content[NETPLAY_HOST_LONGSTR_LEN]; - char subsystem_name[NETPLAY_HOST_LONGSTR_LEN]; -}; - typedef struct mitm_server { const char *name; const char *description; } mitm_server_t; -static const mitm_server_t netplay_mitm_server_list[] = { - { "nyc", "New York City, USA" }, - { "madrid", "Madrid, Spain" }, - { "saopaulo", "Sao Paulo, Brazil" }, - { "singapore", "Singapore" }, - { "custom", "Custom" }, -}; - struct netplay_room { struct netplay_room *next; int id; + int gamecrc; int port; int mitm_port; - int gamecrc; - int timestamp; int host_method; - char country [3]; - char retroarch_version [33]; - char nickname [33]; - char subsystem_name [256]; - char corename [256]; - char frontend [256]; - char coreversion [256]; - char gamename [256]; - char address [256]; - char mitm_handle [33]; - char mitm_address [256]; - char mitm_session [33]; + char nickname[NETPLAY_NICK_LEN]; + char frontend[NETPLAY_HOST_STR_LEN]; + char corename[NETPLAY_HOST_STR_LEN]; + char coreversion[NETPLAY_HOST_STR_LEN]; + char retroarch_version[NETPLAY_HOST_STR_LEN]; + char gamename[NETPLAY_HOST_LONGSTR_LEN]; + char subsystem_name[NETPLAY_HOST_LONGSTR_LEN]; + char country[3]; + char address[NETPLAY_HOST_LONGSTR_LEN]; + char mitm_handle[NETPLAY_HOST_STR_LEN]; + char mitm_address[NETPLAY_HOST_LONGSTR_LEN]; + char mitm_session[NETPLAY_HOST_STR_LEN]; bool has_password; bool has_spectate_password; bool connectable; @@ -216,7 +188,7 @@ struct netplay_host { int content_crc; int port; - char address[NETPLAY_HOST_STR_LEN]; + char address[16]; char nick[NETPLAY_NICK_LEN]; char frontend[NETPLAY_HOST_STR_LEN]; char core[NETPLAY_HOST_STR_LEN]; @@ -231,27 +203,17 @@ struct netplay_host struct netplay_host_list { struct netplay_host *hosts; + size_t allocated; size_t size; }; -struct netplay_chat -{ - struct - { - uint32_t frames; - char nick[NETPLAY_NICK_LEN]; - char msg[NETPLAY_CHAT_MAX_SIZE]; - } messages[NETPLAY_CHAT_MAX_MESSAGES]; - uint32_t message_slots; -}; - struct netplay_chat_buffer { struct { uint8_t alpha; - char nick[NETPLAY_NICK_LEN]; - char msg[NETPLAY_CHAT_MAX_SIZE]; + char nick[NETPLAY_NICK_LEN]; + char msg[NETPLAY_CHAT_MAX_SIZE]; } messages[NETPLAY_CHAT_MAX_MESSAGES]; }; @@ -260,8 +222,6 @@ typedef struct /* NAT traversal info (if NAT traversal is used and serving) */ struct nat_traversal_data nat_traversal_request; #ifdef HAVE_NETPLAYDISCOVERY - /* Packet buffer for advertisement and responses */ - struct ad_packet ad_packet_buffer; /* List of discovered hosts */ struct netplay_host_list discovered_hosts; #endif @@ -272,21 +232,15 @@ typedef struct /* Used while Netplay is running */ netplay_t *data; netplay_client_info_t *client_info; - /* Chat messages */ - struct netplay_chat *chat; size_t client_info_count; #ifdef HAVE_NETPLAYDISCOVERY - size_t discovered_hosts_allocated; /* LAN discovery sockets */ int lan_ad_server_fd; int lan_ad_client_fd; #endif int room_count; - int reannounce; - int reping; int latest_ping; unsigned server_port_deferred; - uint16_t mapping[RETROK_LAST]; char server_address_deferred[256]; char server_session_deferred[32]; bool netplay_client_deferred; @@ -302,101 +256,16 @@ typedef struct net_driver_state_t *networking_state_get_ptr(void); -bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data); +bool netplay_decode_hostname(const char *hostname, + char *address, unsigned *port, char *session, size_t len); +bool netplay_is_lan_address(struct sockaddr_in *addr); +bool netplay_6to4(struct sockaddr_storage *addr); int netplay_rooms_parse(const char *buf); - -struct netplay_room* netplay_room_get(int index); - int netplay_rooms_get_count(void); - +struct netplay_room *netplay_room_get(int index); void netplay_rooms_free(void); -/** -* netplay_frontend_paused - * @netplay : pointer to netplay object - * @paused : true if frontend is paused - * - * Inform Netplay of the frontend's pause state (paused or otherwise) - */ -void netplay_frontend_paused(netplay_t *netplay, bool paused); - -/** - * netplay_toggle_play_spectate - * - * Toggle between play mode and spectate mode - */ -void netplay_toggle_play_spectate(netplay_t *netplay); - -/** - * netplay_input_chat - * - * Opens an input menu for sending netplay chat - */ -void netplay_input_chat(netplay_t *netplay); - -/** - * netplay_load_savestate - * @netplay : pointer to netplay object - * @serial_info : the savestate being loaded, NULL means - * "load it yourself" - * @save : Whether to save the provided serial_info - * into the frame buffer - * - * Inform Netplay of a savestate load and send it to the other side - **/ -void netplay_load_savestate(netplay_t *netplay, - retro_ctx_serialize_info_t *serial_info, bool save); - -/** - * netplay_core_reset - * @netplay : pointer to netplay object - * - * Indicate that the core has been reset to netplay peers - **/ -void netplay_core_reset(netplay_t *netplay); - -int16_t netplay_input_state(netplay_t *netplay, - unsigned port, unsigned device, - unsigned idx, unsigned id); - -/** - * netplay_poll: - * @netplay : pointer to netplay object - * - * Polls network to see if we have anything new. If our - * network buffer is full, we simply have to block - * for new input data. - * - * Returns: true (1) if successful, otherwise false (0). - **/ -bool netplay_poll( - bool block_libretro_input, - void *settings_data, - netplay_t *netplay); - -/** - * netplay_is_alive: - * @netplay : pointer to netplay object - * - * Checks if input port/index is controlled by netplay or not. - * - * Returns: true (1) if alive, otherwise false (0). - **/ -bool netplay_is_alive(netplay_t *netplay); - -/** - * netplay_should_skip: - * @netplay : pointer to netplay object - * - * If we're fast-forward replaying to resync, check if we - * should actually show frame. - * - * Returns: bool (1) if we should skip this frame, otherwise - * false (0). - **/ -bool netplay_should_skip(netplay_t *netplay); - /** * init_netplay * @server : server address to connect to (client only) @@ -410,33 +279,22 @@ bool netplay_should_skip(netplay_t *netplay); * Returns: true (1) if successful, otherwise false (0). **/ bool init_netplay(const char *server, unsigned port, const char *mitm_session); - -bool init_netplay_deferred(const char *server, unsigned port, const char *mitm_session); - +bool init_netplay_deferred(const char *server, unsigned port, + const char *mitm_session); void deinit_netplay(void); -void video_frame_net(const void *data, unsigned width, - unsigned height, size_t pitch); -void audio_sample_net(int16_t left, int16_t right); -size_t audio_sample_batch_net(const int16_t *data, size_t frames); -int16_t input_state_net(unsigned port, unsigned device, - unsigned idx, unsigned id); +bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data); #ifdef HAVE_NETPLAYDISCOVERY /** Initialize Netplay discovery */ bool init_netplay_discovery(void); - /** Deinitialize and free Netplay discovery */ void deinit_netplay_discovery(void); /** Discovery control */ -bool netplay_discovery_driver_ctl( - enum rarch_netplay_discovery_ctl_state state, void *data); +bool netplay_discovery_driver_ctl(enum rarch_netplay_discovery_ctl_state state, + void *data); #endif -bool netplay_decode_hostname(const char *hostname, - char *address, unsigned *port, char *session, size_t len); -bool netplay_is_lan_address(struct sockaddr_in *addr); -bool netplay_6to4(struct sockaddr_storage *addr); - +extern const mitm_server_t netplay_mitm_server_list[NETPLAY_MITM_SERVERS]; #endif diff --git a/network/netplay/netplay_frontend.c b/network/netplay/netplay_frontend.c index 3714ec3d65..c6a992f65d 100644 --- a/network/netplay/netplay_frontend.c +++ b/network/netplay/netplay_frontend.c @@ -20,17 +20,16 @@ #pragma comment(lib, "ws2_32") #endif -#include #include +#include #include -#include -#include - #ifdef HAVE_CONFIG_H #include "../../config.h" #endif +#include + #include #include #include @@ -115,6 +114,8 @@ } \ else +#define STRING_SAFE(str, sz) (str)[(sz) - 1] = '\0' + #define SET_PING(connection) \ ping = (int32_t)((cpu_features_get_time_usec() - (connection)->ping_timer) / 1000); \ if ((connection)->ping < 0 || ping < (connection)->ping) \ @@ -140,10 +141,9 @@ #define MITM_LINK_MAGIC 0x5241544C /* RATL */ #define MITM_PING_MAGIC 0x52415450 /* RATP */ -struct vote_count -{ - uint16_t votes[32]; -}; +#define ANNOUNCE_FRAMES 1200 +#define ANNOUNCE_FRAME_START 900 +#define PING_FRAMES 180 struct nick_buf_s { @@ -165,6 +165,21 @@ struct info_buf_s char core_version[NETPLAY_NICK_LEN]; }; +struct ad_packet +{ + uint32_t header; + int32_t content_crc; + int32_t port; + uint32_t has_password; + char nick[NETPLAY_NICK_LEN]; + char frontend[NETPLAY_HOST_STR_LEN]; + char core[NETPLAY_HOST_STR_LEN]; + char core_version[NETPLAY_HOST_STR_LEN]; + char retroarch_version[NETPLAY_HOST_STR_LEN]; + char content[NETPLAY_HOST_LONGSTR_LEN]; + char subsystem_name[NETPLAY_HOST_LONGSTR_LEN]; +}; + struct mode_payload { uint32_t frame; @@ -174,31 +189,20 @@ struct mode_payload char nick[NETPLAY_NICK_LEN]; }; -/* The keys supported by netplay */ -enum netplay_keys +struct vote_count { - 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 + uint16_t votes[32]; }; -/* The mapping of keys from netplay (network) to libretro (host) */ -static 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 +/* TODO/FIXME: description member should be replaced with localization enums */ +const mitm_server_t netplay_mitm_server_list[NETPLAY_MITM_SERVERS] = { + { "nyc", "New York City, USA" }, + { "madrid", "Madrid, Spain" }, + { "saopaulo", "Sao Paulo, Brazil" }, + { "singapore", "Singapore" }, + { "custom", "Custom" }, }; -#define NETPLAY_KEY_NTOH(k) (netplay_key_ntoh_mapping[k]) - static net_driver_state_t networking_driver_st = {0}; net_driver_state_t *networking_state_get_ptr(void) @@ -212,24 +216,24 @@ bool init_netplay_discovery(void) { struct addrinfo *addr = NULL; net_driver_state_t *net_st = &networking_driver_st; - int fd = socket_init( - (void **) &addr, 0, NULL, + int fd = socket_init((void**)&addr, 0, NULL, SOCKET_TYPE_DATAGRAM); - bool ret = fd >= 0; + bool ret = fd >= 0 && addr; if (ret) { #ifndef HAVE_SOCKET_LEGACY net_ifinfo_best("223.255.255.255", - &((struct sockaddr_in *) addr->ai_addr)->sin_addr, false); + &((struct sockaddr_in*)addr->ai_addr)->sin_addr, false); #endif #ifdef SO_BROADCAST /* Make it broadcastable */ { int broadcast = 1; + if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, - (const char *) &broadcast, sizeof(broadcast)) < 0) + (const char*)&broadcast, sizeof(broadcast)) < 0) RARCH_WARN("[Discovery] Failed to set netplay discovery port to broadcast.\n"); } #endif @@ -276,8 +280,7 @@ static bool netplay_lan_ad_client_query(void) bool ret = false; /* Get the broadcast address (IPv4 only for now) */ - snprintf(port, sizeof(port), "%hu", - (uint16_t) RARCH_DEFAULT_PORT); + snprintf(port, sizeof(port), "%hu", (unsigned short)RARCH_DEFAULT_PORT); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM; if (getaddrinfo_retro("255.255.255.255", port, &hints, &addr)) @@ -289,11 +292,11 @@ static bool netplay_lan_ad_client_query(void) header = htonl(DISCOVERY_QUERY_MAGIC); /* And send it off */ - if (sendto(net_st->lan_ad_client_fd, (char *) &header, sizeof(header), - 0, addr->ai_addr, addr->ai_addrlen) == sizeof(header)) + if (sendto(net_st->lan_ad_client_fd, (char*)&header, sizeof(header), 0, + addr->ai_addr, addr->ai_addrlen) == sizeof(header)) ret = true; else - RARCH_ERR("[Discovery] Failed to send netplay discovery query (error: %d).\n", errno); + RARCH_ERR("[Discovery] Failed to send netplay discovery query.\n"); freeaddrinfo_retro(addr); @@ -302,6 +305,10 @@ static bool netplay_lan_ad_client_query(void) static bool netplay_lan_ad_client_response(void) { + char address[16]; + struct ad_packet ad_packet_buffer; + struct netplay_host *host; + uint32_t has_password; net_driver_state_t *net_st = &networking_driver_st; /* Check for any ad queries */ @@ -317,105 +324,112 @@ static bool netplay_lan_ad_client_response(void) /* Somebody queried, so check that it's valid */ if (recvfrom(net_st->lan_ad_client_fd, - (char*)&net_st->ad_packet_buffer, sizeof(net_st->ad_packet_buffer), - 0, - (struct sockaddr*)&their_addr, &addr_size) == - sizeof(net_st->ad_packet_buffer)) - { - char address[256]; - struct netplay_host *host = NULL; - uint32_t has_password = 0; + (char*)&ad_packet_buffer, sizeof(ad_packet_buffer), 0, + (struct sockaddr*)&their_addr, &addr_size) != + sizeof(ad_packet_buffer)) + continue; - /* Make sure it's a valid response */ - if (ntohl(net_st->ad_packet_buffer.header) != - DISCOVERY_RESPONSE_MAGIC) - continue; + /* Make sure it's a valid response */ + if (ntohl(ad_packet_buffer.header) != DISCOVERY_RESPONSE_MAGIC) + continue; - /* And that we know how to handle it */ + /* And that we know how to handle it */ #ifdef HAVE_INET6 - if (their_addr.ss_family == AF_INET6) - { - /* Check for IPv4 tunneling */ - if (!netplay_6to4(&their_addr)) - continue; - } - else if (their_addr.ss_family != AF_INET) + if (their_addr.ss_family == AF_INET6) + { + /* Check for IPv4 tunneling */ + if (!netplay_6to4(&their_addr)) continue; + } + else if (their_addr.ss_family != AF_INET) + continue; #endif - if (!netplay_is_lan_address((struct sockaddr_in*)&their_addr)) - continue; + if (!netplay_is_lan_address((struct sockaddr_in*)&their_addr)) + continue; #ifndef HAVE_SOCKET_LEGACY - if (getnameinfo((struct sockaddr*)&their_addr, sizeof(their_addr), - address, sizeof(address), NULL, 0, - NI_NUMERICHOST)) - continue; + if (getnameinfo((struct sockaddr*)&their_addr, sizeof(their_addr), + address, sizeof(address), NULL, 0, NI_NUMERICHOST)) + continue; #else - /* We need to convert the address manually */ - { - uint8_t *addr8 = - (uint8_t*)&((struct sockaddr_in*)&their_addr)->sin_addr; - snprintf(address, sizeof(address), "%d.%d.%d.%d", - (int)addr8[0], (int)addr8[1], - (int)addr8[2], (int)addr8[3]); - } + /* We need to convert the address manually */ + { + uint8_t *addr8 = + (uint8_t*)&((struct sockaddr_in*)&their_addr)->sin_addr; + + snprintf(address, sizeof(address), "%d.%d.%d.%d", + (int)addr8[0], (int)addr8[1], (int)addr8[2], (int)addr8[3]); + } #endif - /* Allocate space for it */ - if (net_st->discovered_hosts.size >= net_st->discovered_hosts_allocated) + /* Allocate space for it */ + if (net_st->discovered_hosts.size >= net_st->discovered_hosts.allocated) + { + if (!net_st->discovered_hosts.size) { - size_t allocated = net_st->discovered_hosts_allocated; - struct netplay_host *new_hosts = NULL; - - if (allocated == 0) - allocated = 2; - else - allocated *= 2; - - if (net_st->discovered_hosts.hosts) - new_hosts = (struct netplay_host*)realloc( - net_st->discovered_hosts.hosts, - allocated * sizeof(struct netplay_host)); - else - /* Should be equivalent to realloc, - * but I don't trust screwy libcs */ - new_hosts = (struct netplay_host*)malloc( - allocated * sizeof(struct netplay_host)); + net_st->discovered_hosts.hosts = (struct netplay_host*) + malloc(sizeof(*net_st->discovered_hosts.hosts)); + if (!net_st->discovered_hosts.hosts) + return false; + net_st->discovered_hosts.allocated = 1; + } + else + { + size_t new_allocated = net_st->discovered_hosts.allocated + 4; + struct netplay_host *new_hosts = (struct netplay_host*)realloc( + net_st->discovered_hosts.hosts, + new_allocated * sizeof(*new_hosts)); if (!new_hosts) + { + free(net_st->discovered_hosts.hosts); + memset(&net_st->discovered_hosts, 0, + sizeof(net_st->discovered_hosts)); + return false; + } + net_st->discovered_hosts.allocated = new_allocated; net_st->discovered_hosts.hosts = new_hosts; - net_st->discovered_hosts_allocated = allocated; } - - /* Get our host structure */ - host = &net_st->discovered_hosts.hosts[net_st->discovered_hosts.size++]; - - /* Copy in the response */ - host->content_crc = (int)ntohl(net_st->ad_packet_buffer.content_crc); - host->port = (int)ntohl(net_st->ad_packet_buffer.port); - strlcpy(host->address, address, sizeof(host->address)); - strlcpy(host->nick, net_st->ad_packet_buffer.nick, - sizeof(host->nick)); - strlcpy(host->frontend, net_st->ad_packet_buffer.frontend, - sizeof(host->frontend)); - strlcpy(host->core, net_st->ad_packet_buffer.core, - sizeof(host->core)); - strlcpy(host->core_version, net_st->ad_packet_buffer.core_version, - sizeof(host->core_version)); - strlcpy(host->retroarch_version, - net_st->ad_packet_buffer.retroarch_version, - sizeof(host->retroarch_version)); - strlcpy(host->content, net_st->ad_packet_buffer.content, - sizeof(host->content)); - strlcpy(host->subsystem_name, net_st->ad_packet_buffer.subsystem_name, - sizeof(host->subsystem_name)); - has_password = ntohl(net_st->ad_packet_buffer.has_password); - host->has_password = (has_password & 1) ? true : false; - host->has_spectate_password = (has_password & 2) ? true : false; } + + /* Get our host structure */ + host = &net_st->discovered_hosts.hosts[net_st->discovered_hosts.size++]; + + STRING_SAFE(ad_packet_buffer.nick, sizeof(ad_packet_buffer.nick)); + STRING_SAFE(ad_packet_buffer.frontend, + sizeof(ad_packet_buffer.frontend)); + STRING_SAFE(ad_packet_buffer.core, sizeof(ad_packet_buffer.core)); + STRING_SAFE(ad_packet_buffer.core_version, + sizeof(ad_packet_buffer.core_version)); + STRING_SAFE(ad_packet_buffer.retroarch_version, + sizeof(ad_packet_buffer.retroarch_version)); + STRING_SAFE(ad_packet_buffer.content, sizeof(ad_packet_buffer.content)); + STRING_SAFE(ad_packet_buffer.subsystem_name, + sizeof(ad_packet_buffer.subsystem_name)); + + /* Copy in the response */ + host->content_crc = (int)ntohl(ad_packet_buffer.content_crc); + host->port = (int)ntohl(ad_packet_buffer.port); + + strlcpy(host->address, address, sizeof(host->address)); + strlcpy(host->nick, ad_packet_buffer.nick, sizeof(host->nick)); + strlcpy(host->frontend, ad_packet_buffer.frontend, + sizeof(host->frontend)); + strlcpy(host->core, ad_packet_buffer.core, sizeof(host->core)); + strlcpy(host->core_version, ad_packet_buffer.core_version, + sizeof(host->core_version)); + strlcpy(host->retroarch_version, ad_packet_buffer.retroarch_version, + sizeof(host->retroarch_version)); + strlcpy(host->content, ad_packet_buffer.content, sizeof(host->content)); + strlcpy(host->subsystem_name, ad_packet_buffer.subsystem_name, + sizeof(host->subsystem_name)); + + has_password = ntohl(ad_packet_buffer.has_password); + host->has_password = (has_password & 1) ? true : false; + host->has_spectate_password = (has_password & 2) ? true : false; } return true; @@ -438,7 +452,7 @@ bool netplay_discovery_driver_ctl( case RARCH_NETPLAY_DISCOVERY_CTL_LAN_GET_RESPONSES: if (!netplay_lan_ad_client_response()) return false; - *((struct netplay_host_list **) data) = &net_st->discovered_hosts; + *(struct netplay_host_list**)data = &net_st->discovered_hosts; break; case RARCH_NETPLAY_DISCOVERY_CTL_LAN_CLEAR_RESPONSES: @@ -457,11 +471,9 @@ static bool init_lan_ad_server_socket(void) { struct addrinfo *addr = NULL; net_driver_state_t *net_st = &networking_driver_st; - int fd = socket_init( - (void **) &addr, RARCH_DEFAULT_PORT, NULL, - SOCKET_TYPE_DATAGRAM); - bool ret = fd >= 0 && - socket_bind(fd, addr); + int fd = socket_init((void**)&addr, RARCH_DEFAULT_PORT, + NULL, SOCKET_TYPE_DATAGRAM); + bool ret = fd >= 0 && addr && socket_bind(fd, addr); if (ret) { @@ -474,7 +486,7 @@ static bool init_lan_ad_server_socket(void) net_st->lan_ad_server_fd = -1; - RARCH_ERR("[Discovery] Failed to initialize netplay advertisement socket.\n");; + RARCH_ERR("[Discovery] Failed to initialize netplay advertisement socket.\n"); } if (addr) @@ -525,13 +537,14 @@ static bool netplay_lan_ad_server(netplay_t *netplay) /* Somebody queried, so check that it's valid */ if (ret == sizeof(header)) { - const frontend_ctx_driver_t *frontend_drv; char frontend_architecture_tmp[24]; - uint32_t content_crc = 0; - uint32_t has_password = 0; - struct retro_system_info *system = &runloop_state_get_ptr()->system.info; - struct string_list *subsystem = path_get_subsystem_list(); - settings_t *settings = config_get_ptr(); + const frontend_ctx_driver_t *frontend_drv; + uint32_t has_password = 0; + struct ad_packet ad_packet_buffer = {0}; + struct retro_system_info *system = + &runloop_state_get_ptr()->system.info; + struct string_list *subsystem = path_get_subsystem_list(); + settings_t *settings = config_get_ptr(); /* Make sure it's a valid query */ if (ntohl(header) != DISCOVERY_QUERY_MAGIC) @@ -557,82 +570,75 @@ static bool netplay_lan_ad_server(netplay_t *netplay) RARCH_LOG("[Discovery] Query received on LAN interface.\n"); /* Now build our response */ - memset(&net_st->ad_packet_buffer, 0, sizeof(net_st->ad_packet_buffer)); + ad_packet_buffer.header = htonl(DISCOVERY_RESPONSE_MAGIC); - net_st->ad_packet_buffer.header = htonl(DISCOVERY_RESPONSE_MAGIC); + ad_packet_buffer.port = (int32_t)htonl(netplay->tcp_port); - net_st->ad_packet_buffer.port = (int32_t)htonl((uint32_t)netplay->tcp_port); + strlcpy(ad_packet_buffer.nick, netplay->nick, + sizeof(ad_packet_buffer.nick)); - strlcpy(net_st->ad_packet_buffer.nick, netplay->nick, - sizeof(net_st->ad_packet_buffer.nick)); - - frontend_drv = - (const frontend_ctx_driver_t*)frontend_driver_get_cpu_architecture_str( - frontend_architecture_tmp, sizeof(frontend_architecture_tmp)); + frontend_drv = (const frontend_ctx_driver_t*) + frontend_driver_get_cpu_architecture_str(frontend_architecture_tmp, + sizeof(frontend_architecture_tmp)); if (frontend_drv) - snprintf(net_st->ad_packet_buffer.frontend, - sizeof(net_st->ad_packet_buffer.frontend), + snprintf(ad_packet_buffer.frontend, sizeof(ad_packet_buffer.frontend), "%s %s", frontend_drv->ident, frontend_architecture_tmp); else - strlcpy(net_st->ad_packet_buffer.frontend, "N/A", - sizeof(net_st->ad_packet_buffer.frontend)); + strlcpy(ad_packet_buffer.frontend, "N/A", + sizeof(ad_packet_buffer.frontend)); - strlcpy(net_st->ad_packet_buffer.core, system->library_name, - sizeof(net_st->ad_packet_buffer.core)); - strlcpy(net_st->ad_packet_buffer.core_version, system->library_version, - sizeof(net_st->ad_packet_buffer.core_version)); + strlcpy(ad_packet_buffer.core, system->library_name, + sizeof(ad_packet_buffer.core)); + strlcpy(ad_packet_buffer.core_version, system->library_version, + sizeof(ad_packet_buffer.core_version)); - strlcpy(net_st->ad_packet_buffer.retroarch_version, PACKAGE_VERSION, - sizeof(net_st->ad_packet_buffer.retroarch_version)); + strlcpy(ad_packet_buffer.retroarch_version, PACKAGE_VERSION, + sizeof(ad_packet_buffer.retroarch_version)); if (subsystem && subsystem->size > 0) { unsigned i; - char buf[4096]; - buf[0] = '\0'; for (i = 0;;) { - strlcat(buf, path_basename(subsystem->elems[i++].data), - sizeof(buf)); - if (i >= subsystem->size) + strlcat(ad_packet_buffer.content, + path_basename(subsystem->elems[i].data), + sizeof(ad_packet_buffer.content)); + if (++i >= subsystem->size) break; - strlcat(buf, "|", sizeof(buf)); + strlcat(ad_packet_buffer.content, "|", + sizeof(ad_packet_buffer.content)); } - strlcpy(net_st->ad_packet_buffer.content, buf, - sizeof(net_st->ad_packet_buffer.content)); - strlcpy(net_st->ad_packet_buffer.subsystem_name, + strlcpy(ad_packet_buffer.subsystem_name, path_get(RARCH_PATH_SUBSYSTEM), - sizeof(net_st->ad_packet_buffer.subsystem_name)); + sizeof(ad_packet_buffer.subsystem_name)); - content_crc = 0; + ad_packet_buffer.content_crc = 0; } else { - const char *base = path_basename(path_get(RARCH_PATH_BASENAME)); + const char *basename = path_basename(path_get(RARCH_PATH_BASENAME)); - strlcpy(net_st->ad_packet_buffer.content, - !string_is_empty(base) ? base : "N/A", - sizeof(net_st->ad_packet_buffer.content)); - strlcpy(net_st->ad_packet_buffer.subsystem_name, "N/A", - sizeof(net_st->ad_packet_buffer.subsystem_name)); + strlcpy(ad_packet_buffer.content, + !string_is_empty(basename) ? basename : "N/A", + sizeof(ad_packet_buffer.content)); + strlcpy(ad_packet_buffer.subsystem_name, "N/A", + sizeof(ad_packet_buffer.subsystem_name)); - content_crc = content_get_crc(); + ad_packet_buffer.content_crc = (int32_t)htonl(content_get_crc()); } - net_st->ad_packet_buffer.content_crc = (int32_t)htonl(content_crc); - if (!string_is_empty(settings->paths.netplay_password)) has_password |= 1; if (!string_is_empty(settings->paths.netplay_spectate_password)) has_password |= 2; - net_st->ad_packet_buffer.has_password = htonl(has_password); + ad_packet_buffer.has_password = htonl(has_password); /* Send our response */ sendto(net_st->lan_ad_server_fd, - (char*)&net_st->ad_packet_buffer, sizeof(net_st->ad_packet_buffer), - 0, (struct sockaddr*)&their_addr, sizeof(their_addr)); + (char*)&ad_packet_buffer, sizeof(ad_packet_buffer), 0, + (struct sockaddr*)&their_addr, sizeof(their_addr)); } return true; @@ -711,14 +717,14 @@ static uint32_t simple_rand_uint32(unsigned long *simple_rand_next) * * Initialize a new socket buffer. */ -static bool netplay_init_socket_buffer( - struct socket_buffer *sbuf, size_t size) +static bool netplay_init_socket_buffer(struct socket_buffer *sbuf, size_t size) { sbuf->data = (unsigned char*)malloc(size); if (!sbuf->data) return false; sbuf->bufsz = size; sbuf->start = sbuf->read = sbuf->end = 0; + return true; } @@ -729,8 +735,8 @@ static bool netplay_init_socket_buffer( */ static void netplay_deinit_socket_buffer(struct socket_buffer *sbuf) { - if (sbuf->data) - free(sbuf->data); + free(sbuf->data); + sbuf->data = NULL; } @@ -739,7 +745,7 @@ static void netplay_deinit_socket_buffer(struct socket_buffer *sbuf) * * Initialize our handshake and send the first part of the handshake protocol. */ -bool netplay_handshake_init_send(netplay_t *netplay, +static bool netplay_handshake_init_send(netplay_t *netplay, struct netplay_connection *connection, uint32_t protocol) { uint32_t header[6]; @@ -1134,17 +1140,16 @@ static void netplay_handshake_ready(netplay_t *netplay, snprintf(msg, sizeof(msg), msg_hash_to_str(MSG_GOT_CONNECTION_FROM), connection->nick); - RARCH_LOG("[Netplay] %s %u\n", msg_hash_to_str(MSG_CONNECTION_SLOT), slot); + RARCH_LOG("[Netplay] %s %u\n", msg_hash_to_str(MSG_CONNECTION_SLOT), + slot); /* Send them the savestate */ if (!(netplay->quirks & - (NETPLAY_QUIRK_NO_SAVESTATES|NETPLAY_QUIRK_NO_TRANSMISSION))) + (NETPLAY_QUIRK_NO_SAVESTATES | NETPLAY_QUIRK_NO_TRANSMISSION))) netplay->force_send_savestate = true; } else { - netplay->is_connected = true; - snprintf(msg, sizeof(msg), "%s: \"%s\"", msg_hash_to_str(MSG_CONNECTED_TO), connection->nick); @@ -1160,7 +1165,7 @@ static void netplay_handshake_ready(netplay_t *netplay, /* Unstall if we were waiting for this */ if (netplay->stall == NETPLAY_STALL_NO_CONNECTION) - netplay->stall = NETPLAY_STALL_NONE; + netplay->stall = NETPLAY_STALL_NONE; } /** @@ -1431,6 +1436,7 @@ static bool netplay_handshake_pre_nick(netplay_t *netplay, SET_PING(connection) + STRING_SAFE(nick_buf.nick, sizeof(nick_buf.nick)); strlcpy(connection->nick, nick_buf.nick, sizeof(connection->nick)); @@ -1565,8 +1571,7 @@ static bool netplay_handshake_pre_info(netplay_t *netplay, } } - if (recvd < 0 || - ntohl(info_buf.cmd[0]) != NETPLAY_CMD_INFO) + if (recvd < 0 || ntohl(info_buf.cmd[0]) != NETPLAY_CMD_INFO) { RARCH_ERR("[Netplay] Failed to receive netplay info.\n"); return false; @@ -1605,6 +1610,7 @@ static bool netplay_handshake_pre_info(netplay_t *netplay, /* Check the core info */ if (system) { + STRING_SAFE(info_buf.core_name, sizeof(info_buf.core_name)); if (!string_is_equal_case_insensitive( info_buf.core_name, system->library_name)) { @@ -1616,6 +1622,7 @@ static bool netplay_handshake_pre_info(netplay_t *netplay, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); return false; } + STRING_SAFE(info_buf.core_version, sizeof(info_buf.core_version)); if (!string_is_equal_case_insensitive( info_buf.core_version, system->library_version)) { @@ -1779,10 +1786,7 @@ static bool netplay_handshake_pre_sync(netplay_t *netplay, netplay->config_devices[i] = device; if ((device & RETRO_DEVICE_MASK) == RETRO_DEVICE_KEYBOARD) - { netplay->have_updown_device = true; - netplay_key_hton_init(); - } pad.port = (unsigned)i; pad.device = device; @@ -1820,6 +1824,7 @@ static bool netplay_handshake_pre_sync(netplay_t *netplay, RECV(new_nick, sizeof(new_nick)) return false; + STRING_SAFE(new_nick, sizeof(new_nick)); if (!string_is_equal(new_nick, netplay->nick)) { char msg[512]; @@ -1927,8 +1932,8 @@ static bool netplay_handshake_pre_sync(netplay_t *netplay, * * Data receiver for all handshake states. */ -bool netplay_handshake(netplay_t *netplay, - struct netplay_connection *connection, bool *had_input) +static bool netplay_handshake(netplay_t *netplay, + struct netplay_connection *connection, bool *had_input) { bool ret = false; @@ -1961,29 +1966,33 @@ bool netplay_handshake(netplay_t *netplay, return ret; } -/* The mapping of keys from libretro (host) to netplay (network) */ -uint32_t netplay_key_hton(unsigned key) +/* Because the keymapping has to be generated, call this before using + * netplay_key_hton or netplay_key_ntoh */ +static void netplay_key_init(netplay_t *netplay) { - net_driver_state_t *net_st = &networking_driver_st; - if (key >= RETROK_LAST) - return NETPLAY_KEY_UNKNOWN; - return net_st->mapping[key]; +#define INIT_KEY(kn, kh) \ + netplay->mapping_hton[kh] = (uint16_t)kn; \ + netplay->mapping_ntoh[kn] = (uint16_t)kh; +#define K(k) INIT_KEY((NETPLAY_KEY_ ## k), (RETROK_ ## k)) +#define KL(k,l) INIT_KEY((NETPLAY_KEY_ ## k), l) +#include "netplay_keys.h" +#undef KL +#undef K +#undef INIT_KEY } -/* Because the hton keymapping has to be generated, call this before using - * netplay_key_hton */ -void netplay_key_hton_init(void) +/* The mapping of keys from libretro (host) to netplay (network) */ +static uint32_t netplay_key_hton(netplay_t *netplay, unsigned key) { - static bool mapping_defined = false; - net_driver_state_t *net_st = &networking_driver_st; + return (key < RETROK_LAST) ? netplay->mapping_hton[key] : + NETPLAY_KEY_UNKNOWN; +} - if (!mapping_defined) - { - uint16_t i; - for (i = 0; i < NETPLAY_KEY_LAST; i++) - net_st->mapping[NETPLAY_KEY_NTOH(i)] = i; - mapping_defined = true; - } +/* The mapping of keys from netplay (network) to libretro (host) */ +static uint32_t netplay_key_ntoh(netplay_t *netplay, unsigned key) +{ + return (key < NETPLAY_KEY_LAST) ? netplay->mapping_ntoh[key] : + RETROK_UNKNOWN; } static void clear_input(netplay_input_state_t istate) @@ -2026,7 +2035,7 @@ bool netplay_delta_frame_ready(netplay_t *netplay, struct delta_frame *delta, { clear_input(delta->resolved_input[i]); clear_input(delta->real_input[i]); - clear_input(delta->simlated_input[i]); + clear_input(delta->simulated_input[i]); } delta->have_local = false; for (i = 0; i < MAX_CLIENTS; i++) @@ -2081,7 +2090,7 @@ static void netplay_delta_frame_free(struct delta_frame *delta) { free_input_state(&delta->resolved_input[i]); free_input_state(&delta->real_input[i]); - free_input_state(&delta->simlated_input[i]); + free_input_state(&delta->simulated_input[i]); } } @@ -2090,7 +2099,7 @@ static void netplay_delta_frame_free(struct delta_frame *delta) * * Get an input state for a particular client */ -netplay_input_state_t netplay_input_state_for( +static 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) @@ -2137,7 +2146,8 @@ netplay_input_state_t netplay_input_state_for( * * Size in words for a given set of devices. */ -uint32_t netplay_expected_input_size(netplay_t *netplay, uint32_t devices) +static uint32_t netplay_expected_input_size(netplay_t *netplay, + uint32_t devices) { uint32_t ret = 0, device; @@ -2509,7 +2519,13 @@ static bool netplay_full(netplay_t *netplay, int sockfd) max_connections = MAX_CLIENTS - 1; for (i = 0; i < netplay->connections_size; i++) - if (netplay->connections[i].active) total++; + { + struct netplay_connection *connection = &netplay->connections[i]; + + if (connection->active || + connection->mode == NETPLAY_CONNECTION_DELAYED_DISCONNECT) + total++; + } if (total >= max_connections) { @@ -2602,7 +2618,7 @@ static bool netplay_cmd_stall(netplay_t *netplay, * Update the global unread_ptr and unread_frame_count to correspond to the * earliest unread frame count of any connected player */ -void netplay_update_unread_ptr(netplay_t *netplay) +static void netplay_update_unread_ptr(netplay_t *netplay) { if (netplay->is_server && netplay->connected_players<=1) { @@ -2672,7 +2688,7 @@ netplay_input_state_t netplay_device_client_state(netplay_t *netplay, { if (netplay->read_frame_count[client] > simframe->frame) return NULL; - simstate = netplay_input_state_for(&simframe->simlated_input[device], + simstate = netplay_input_state_for(&simframe->simulated_input[device], client, dsize, false, true); } return simstate; @@ -2905,7 +2921,8 @@ static void netplay_merge_analog(netplay_t *netplay, * Returns true if the resolved input changed from the last time it was * resolved. */ -bool netplay_resolve_input(netplay_t *netplay, size_t sim_ptr, bool resim) +static bool netplay_resolve_input(netplay_t *netplay, + size_t sim_ptr, bool resim) { size_t prev; uint32_t device; @@ -2945,7 +2962,9 @@ bool netplay_resolve_input(netplay_t *netplay, size_t sim_ptr, bool resim) * 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); + simstate = netplay_input_state_for( + &simframe->simulated_input[device], + client, dsize, false, false); if (!simstate) continue; @@ -3350,6 +3369,64 @@ critical_failure: return -1; } +/** + * allocate_connection + * @netplay : pointer to netplay object + * + * Allocates a new client connection. + * + * Returns: pointer to connection object. + */ +static struct netplay_connection *allocate_connection(netplay_t *netplay) +{ + size_t i; + struct netplay_connection *connection; + + /* Look for an existing non-used connection first. */ + for (i = 0; i < netplay->connections_size; i++) + { + connection = &netplay->connections[i]; + if (!connection->active && + connection->mode != NETPLAY_CONNECTION_DELAYED_DISCONNECT) + break; + } + if (i < netplay->connections_size) + memset(connection, 0, sizeof(*connection)); + else if (!netplay->connections_size) + { + netplay->connections = + (struct netplay_connection*)calloc(1, sizeof(*netplay->connections)); + if (!netplay->connections) + return NULL; + netplay->connections_size = 1; + + connection = &netplay->connections[0]; + } + else + { + size_t new_size; + struct netplay_connection *new_connections; + + if (netplay->connections_size >= (MAX_CLIENTS - 1)) + return NULL; + + new_size = netplay->connections_size + 3; + new_connections = (struct netplay_connection*)realloc( + netplay->connections, new_size * sizeof(*new_connections)); + if (!new_connections) + return NULL; + memset(new_connections + netplay->connections_size, 0, + (new_size - netplay->connections_size) * sizeof(*new_connections)); + + connection = &new_connections[netplay->connections_size]; + + netplay->connections_size = new_size; + netplay->connections = new_connections; + } + + return connection; +} + /** * netplay_sync_pre_frame * @netplay : pointer to netplay object @@ -3357,61 +3434,73 @@ critical_failure: * * Pre-frame for Netplay synchronization. */ -bool netplay_sync_pre_frame(netplay_t *netplay, bool *disconnect) +static bool netplay_sync_pre_frame(netplay_t *netplay, bool *disconnect) { - retro_ctx_serialize_info_t serial_info; - - if (netplay_delta_frame_ready(netplay, - &netplay->buffer[netplay->run_ptr], netplay->run_frame_count)) + if (netplay_delta_frame_ready(netplay, &netplay->buffer[netplay->run_ptr], + netplay->run_frame_count) && netplay->run_frame_count > 0) { - serial_info.data_const = NULL; - serial_info.data = netplay->buffer[netplay->run_ptr].state; - serial_info.size = netplay->state_size; + /* Don't serialize until it's safe */ + if (!(netplay->quirks & + (NETPLAY_QUIRK_INITIALIZATION | NETPLAY_QUIRK_NO_SAVESTATES))) + { + retro_ctx_serialize_info_t serial_info = {0}; - memset(serial_info.data, 0, serial_info.size); - if ((netplay->quirks & NETPLAY_QUIRK_INITIALIZATION) - || netplay->run_frame_count == 0) - { - /* Don't serialize until it's safe */ - } - else if (!(netplay->quirks & NETPLAY_QUIRK_NO_SAVESTATES) - && core_serialize(&serial_info)) - { - if (netplay->force_send_savestate && !netplay->stall - && !netplay->remote_paused) + serial_info.data = netplay->buffer[netplay->run_ptr].state; + serial_info.size = netplay->state_size; + memset(serial_info.data, 0, serial_info.size); + + if (core_serialize(&serial_info)) { - /* Bring our running frame and input frames into - * parity so we don't send old info. */ - if (netplay->run_ptr != netplay->self_ptr) + if (netplay->force_send_savestate && !netplay->stall && + !netplay->remote_paused) { - memcpy(netplay->buffer[netplay->self_ptr].state, - netplay->buffer[netplay->run_ptr].state, - netplay->state_size); - netplay->run_ptr = netplay->self_ptr; - netplay->run_frame_count = netplay->self_frame_count; - } + /* Bring our running frame and input frames into + * parity so we don't send old info. */ + if (netplay->run_ptr != netplay->self_ptr) + { + memcpy(netplay->buffer[netplay->self_ptr].state, + netplay->buffer[netplay->run_ptr].state, + netplay->state_size); + netplay->run_ptr = netplay->self_ptr; + netplay->run_frame_count = netplay->self_frame_count; + } - /* Send this along to the other side */ - serial_info.data_const = netplay->buffer[netplay->run_ptr].state; - netplay_load_savestate(netplay, &serial_info, false); - netplay->force_send_savestate = false; + /* Send this along to the other side */ + serial_info.data_const = + netplay->buffer[netplay->run_ptr].state; + + netplay_load_savestate(netplay, &serial_info, false); + + netplay->force_send_savestate = false; + } + } + else + { + /* If the core can't serialize properly, we must stall for the + * remote input on EVERY frame, because we can't recover */ + netplay->quirks |= NETPLAY_QUIRK_NO_SAVESTATES; + netplay->stateless_mode = true; } - } - else - { - /* If the core can't serialize properly, we must stall for the - * remote input on EVERY frame, because we can't recover */ - netplay->quirks |= NETPLAY_QUIRK_NO_SAVESTATES; - netplay->stateless_mode = true; } /* If we can't transmit savestates, we must stall * until the client is ready. */ - if (netplay->run_frame_count > 0 && - (netplay->quirks & (NETPLAY_QUIRK_NO_SAVESTATES|NETPLAY_QUIRK_NO_TRANSMISSION)) && - (netplay->connections_size == 0 || !netplay->connections[0].active || - netplay->connections[0].mode < NETPLAY_CONNECTION_CONNECTED)) - netplay->stall = NETPLAY_STALL_NO_CONNECTION; + if (netplay->quirks & + (NETPLAY_QUIRK_NO_SAVESTATES | NETPLAY_QUIRK_NO_TRANSMISSION)) + { + 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) + break; + } + if (i >= netplay->connections_size) + netplay->stall = NETPLAY_STALL_NO_CONNECTION; + } } if (netplay->is_server) @@ -3431,7 +3520,6 @@ bool netplay_sync_pre_frame(netplay_t *netplay, bool *disconnect) else if (new_fd >= 0) { struct netplay_connection *connection; - size_t connection_num; if (netplay_full(netplay, new_fd)) { @@ -3441,80 +3529,46 @@ bool netplay_sync_pre_frame(netplay_t *netplay, bool *disconnect) } /* Allocate a connection */ - for (connection_num = 0; connection_num < netplay->connections_size; connection_num++) - if (!netplay->connections[connection_num].active && - netplay->connections[connection_num].mode != NETPLAY_CONNECTION_DELAYED_DISCONNECT) - break; - if (connection_num == netplay->connections_size) + connection = allocate_connection(netplay); + if (!connection) { - if (connection_num == 0) - { - netplay->connections = (struct netplay_connection*) - malloc(sizeof(struct netplay_connection)); - - if (!netplay->connections) - { - socket_close(new_fd); - goto process; - } - netplay->connections_size = 1; - } - else - { - size_t new_connections_size = netplay->connections_size * 2; - struct netplay_connection - *new_connections = (struct netplay_connection*) - realloc(netplay->connections, - new_connections_size*sizeof(struct netplay_connection)); - - if (!new_connections) - { - socket_close(new_fd); - goto process; - } - - memset(new_connections + netplay->connections_size, 0, - netplay->connections_size * sizeof(struct netplay_connection)); - netplay->connections = new_connections; - netplay->connections_size = new_connections_size; - } + socket_close(new_fd); + goto process; } - connection = &netplay->connections[connection_num]; - - /* Set it up */ - memset(connection, 0, sizeof(*connection)); - connection->active = true; - connection->fd = new_fd; - connection->mode = NETPLAY_CONNECTION_INIT; if (!netplay_init_socket_buffer(&connection->send_packet_buffer, - netplay->packet_buffer_size) || - !netplay_init_socket_buffer(&connection->recv_packet_buffer, - netplay->packet_buffer_size)) + netplay->packet_buffer_size) || + !netplay_init_socket_buffer(&connection->recv_packet_buffer, + netplay->packet_buffer_size)) { netplay_deinit_socket_buffer(&connection->send_packet_buffer); netplay_deinit_socket_buffer(&connection->recv_packet_buffer); - connection->active = false; socket_close(new_fd); + goto process; } + + /* Set it up */ + connection->active = true; + connection->fd = new_fd; + connection->mode = NETPLAY_CONNECTION_INIT; } } process: - netplay->can_poll = true; - input_poll_net(); + input_poll_net(netplay); - return (netplay->stall != NETPLAY_STALL_NO_CONNECTION); + return netplay->stall != NETPLAY_STALL_NO_CONNECTION; } /** * netplay_sync_post_frame * @netplay : pointer to netplay object + * @stalled : true if we're currently stalled * * Post-frame for Netplay synchronization. * We check if we have new input and replay from recorded input. */ -void netplay_sync_post_frame(netplay_t *netplay, bool stalled) +static void netplay_sync_post_frame(netplay_t *netplay, bool stalled) { uint32_t lo_frame_count, hi_frame_count; @@ -3879,7 +3933,7 @@ static void remote_unpaused(netplay_t *netplay, * * Disconnects an active Netplay connection due to an error */ -void netplay_hangup(netplay_t *netplay, +static void netplay_hangup(netplay_t *netplay, struct netplay_connection *connection) { char msg[512]; @@ -3917,7 +3971,6 @@ void netplay_hangup(netplay_t *netplay, command_event(CMD_EVENT_PRESENCE_UPDATE, &userdata); } #endif - netplay->is_connected = false; } RARCH_LOG("[Netplay] %s\n", dmsg); @@ -3980,7 +4033,7 @@ void netplay_hangup(netplay_t *netplay, * Handle any pending state changes which are ready * as of the beginning of the current frame. */ -void netplay_delayed_state_change(netplay_t *netplay) +static void netplay_delayed_state_change(netplay_t *netplay) { size_t i; struct mode_payload payload; @@ -4736,7 +4789,7 @@ static bool chat_check(netplay_t *netplay) /* Do nothing if we are not playing. */ if (netplay->self_mode != NETPLAY_CONNECTION_PLAYING && - netplay->self_mode != NETPLAY_CONNECTION_SLAVE) + netplay->self_mode != NETPLAY_CONNECTION_SLAVE) return false; /* If we are the server, @@ -4744,14 +4797,18 @@ static bool chat_check(netplay_t *netplay) if (netplay->is_server) { size_t i; + for (i = 0; i < netplay->connections_size; i++) { - struct netplay_connection *conn = &netplay->connections[i]; - if (conn->active && - (conn->mode == NETPLAY_CONNECTION_PLAYING || - conn->mode == NETPLAY_CONNECTION_SLAVE)) + struct netplay_connection *connection = &netplay->connections[i]; + + if (!connection->active) + continue; + + if (connection->mode == NETPLAY_CONNECTION_PLAYING || + connection->mode == NETPLAY_CONNECTION_SLAVE) { - REQUIRE_PROTOCOL_VERSION(conn, 6) + REQUIRE_PROTOCOL_VERSION(connection, 6) return true; } } @@ -4770,83 +4827,64 @@ static bool chat_check(netplay_t *netplay) return false; } -static void relay_chat(netplay_t *netplay, - const char *nick, const char *msg) +static void relay_chat(netplay_t *netplay, const char *nick, const char *msg) { size_t i; - size_t msg_len; - char data[NETPLAY_NICK_LEN + NETPLAY_CHAT_MAX_SIZE - 1]; - - msg_len = strlen(msg); + char data[NETPLAY_NICK_LEN + NETPLAY_CHAT_MAX_SIZE]; + size_t msg_len = strlen(msg); + size_t data_len = NETPLAY_NICK_LEN + msg_len; memcpy(data, nick, NETPLAY_NICK_LEN); memcpy(data + NETPLAY_NICK_LEN, msg, msg_len); for (i = 0; i < netplay->connections_size; i++) { - struct netplay_connection *conn = &netplay->connections[i]; + struct netplay_connection *connection = &netplay->connections[i]; + + if (!connection->active) + continue; + /* Only playing clients can receive chat. Protocol 6+ is required. */ - if (conn->active && - (conn->mode == NETPLAY_CONNECTION_PLAYING || - conn->mode == NETPLAY_CONNECTION_SLAVE)) + if (connection->mode == NETPLAY_CONNECTION_PLAYING || + connection->mode == NETPLAY_CONNECTION_SLAVE) { - REQUIRE_PROTOCOL_VERSION(conn, 6) - netplay_send_raw_cmd(netplay, conn, - NETPLAY_CMD_PLAYER_CHAT, data, NETPLAY_NICK_LEN + msg_len); + REQUIRE_PROTOCOL_VERSION(connection, 6) + netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_PLAYER_CHAT, + data, data_len); } } /* We don't flush. Chat is not time essential. */ } -static void show_chat(const char *nick, const char *msg) +static void show_chat(netplay_t *netplay, const char *nick, const char *msg) { char formatted_chat[NETPLAY_CHAT_MAX_SIZE]; /* Truncate the message if necessary. */ - snprintf(formatted_chat, sizeof(formatted_chat), - "%s: %s", nick, msg); + snprintf(formatted_chat, sizeof(formatted_chat), "%s: %s", nick, msg); RARCH_LOG("[Netplay] %s\n", formatted_chat); #ifdef HAVE_GFX_WIDGETS if (gfx_widgets_ready()) { - uint32_t msg_slot; - net_driver_state_t *net_st = &networking_driver_st; - struct netplay_chat *chat = net_st->chat; + int i; + struct netplay_chat *chat = &netplay->chat; - /* Do we have a free slot for this message? - If not, get rid of the oldest message to make room for it. */ - if (!chat->message_slots) - { - int i; + /* Get rid of the oldest message, while moving the rest up. */ + for (i = ARRAY_SIZE(chat->messages) - 2; i >= 0; i--) + memcpy(&chat->messages[i+1], &chat->messages[i], + sizeof(*chat->messages)); - for (i = ARRAY_SIZE(chat->messages) - 2; i >= 0; i--) - { - memcpy(&chat->messages[i+1], &chat->messages[i], - sizeof(*chat->messages)); - } - - msg_slot = 0; - } - else - { - msg_slot = --chat->message_slots; - } - - chat->messages[msg_slot].frames = - NETPLAY_CHAT_FRAME_TIME; - strlcpy(chat->messages[msg_slot].nick, nick, - sizeof(chat->messages[msg_slot].nick)); - strlcpy(chat->messages[msg_slot].msg, msg, - sizeof(chat->messages[msg_slot].msg)); + chat->messages[0].frames = NETPLAY_CHAT_FRAME_TIME; + strlcpy(chat->messages[0].nick, nick, sizeof(chat->messages[0].nick)); + strlcpy(chat->messages[0].msg, msg, sizeof(chat->messages[0].msg)); } else #endif - runloop_msg_queue_push(formatted_chat, - 1, NETPLAY_CHAT_FRAME_TIME, false, NULL, - MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + runloop_msg_queue_push(formatted_chat, 1, NETPLAY_CHAT_FRAME_TIME, false, + NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); } #ifdef HAVE_MENU @@ -4867,15 +4905,13 @@ static void send_chat(void *userdata, const char *line) if (netplay->is_server) { relay_chat(netplay, netplay->nick, msg); - show_chat(netplay->nick, msg); + show_chat(netplay, netplay->nick, msg); } /* For clients, we just send it to the server. */ else - { netplay_send_raw_cmd(netplay, &netplay->connections[0], NETPLAY_CMD_PLAYER_CHAT, msg, strlen(msg)); /* We don't flush. Chat is not time essential. */ - } } menu_input_dialog_end(); @@ -4883,7 +4919,12 @@ static void send_chat(void *userdata, const char *line) } #endif -void netplay_input_chat(netplay_t *netplay) +/** + * netplay_input_chat + * + * Opens an input menu for sending netplay chat + */ +static void netplay_input_chat(netplay_t *netplay) { #ifdef HAVE_MENU if (chat_check(netplay)) @@ -4910,8 +4951,7 @@ static bool handle_chat(netplay_t *netplay, struct netplay_connection *connection, const char *nick, const char *msg) { - if (!connection->active || - string_is_empty(nick) || string_is_empty(msg)) + if (!connection->active || string_is_empty(nick) || string_is_empty(msg)) return false; REQUIRE_PROTOCOL_VERSION(connection, 6) @@ -4933,7 +4973,7 @@ static bool handle_chat(netplay_t *netplay, ignore it! */ if (netplay->self_mode == NETPLAY_CONNECTION_PLAYING || netplay->self_mode == NETPLAY_CONNECTION_SLAVE) - show_chat(nick, msg); + show_chat(netplay, nick, msg); return true; } @@ -4944,8 +4984,7 @@ static bool handle_chat(netplay_t *netplay, static void request_ping(netplay_t *netplay, struct netplay_connection *connection) { - if (!connection->active || - connection->mode < NETPLAY_CONNECTION_CONNECTED) + if (!connection->active || connection->mode < NETPLAY_CONNECTION_CONNECTED) return; /* Only protocol 6+ supports the ping command. */ @@ -4953,24 +4992,25 @@ static void request_ping(netplay_t *netplay, { connection->ping_timer = cpu_features_get_time_usec(); - netplay_send_raw_cmd(netplay, connection, - NETPLAY_CMD_PING_REQUEST, NULL, 0); - /* We need to get this sent asap. */ - netplay_send_flush(&connection->send_packet_buffer, - connection->fd, false); - - connection->ping_requested = true; + if (netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_PING_REQUEST, + NULL, 0)) + { + /* We need to get this sent asap. */ + if (netplay_send_flush(&connection->send_packet_buffer, + connection->fd, false)) + connection->ping_requested = true; + } } } static void answer_ping(netplay_t *netplay, struct netplay_connection *connection) { - netplay_send_raw_cmd(netplay, connection, - NETPLAY_CMD_PING_RESPONSE, NULL, 0); - /* We need to get this sent asap. */ - netplay_send_flush(&connection->send_packet_buffer, - connection->fd, false); + if (netplay_send_raw_cmd(netplay, connection, NETPLAY_CMD_PING_RESPONSE, + NULL, 0)) + /* We need to get this sent asap. */ + netplay_send_flush(&connection->send_packet_buffer, connection->fd, + false); } #undef RECV @@ -5346,6 +5386,7 @@ static bool netplay_get_cmd(netplay_t *netplay, devices = ntohl(payload.devices); memcpy(netplay->device_share_modes, payload.share_modes, sizeof(netplay->device_share_modes)); + STRING_SAFE(payload.nick, sizeof(payload.nick)); nick = payload.nick; /* We're changing past input, so must replay it */ @@ -5846,8 +5887,11 @@ static bool netplay_get_cmd(netplay_t *netplay, netplay_send_flush_all(netplay, connection); } else + { + STRING_SAFE(nick, sizeof(nick)); snprintf(msg, sizeof(msg), msg_hash_to_str(MSG_NETPLAY_PEER_PAUSED), nick); + } connection->paused = true; netplay->remote_paused = true; @@ -5921,7 +5965,7 @@ static bool netplay_get_cmd(netplay_t *netplay, /* If client, we receive both the nickname and the message from the server. */ if (cmd_size <= sizeof(nickname) || - cmd_size >= sizeof(nickname) + sizeof(message)) + cmd_size >= sizeof(nickname) + sizeof(message)) { RARCH_ERR("[Netplay] NETPLAY_CMD_PLAYER_CHAT with incorrect payload size.\n"); return netplay_cmd_nak(netplay, connection); @@ -5929,6 +5973,7 @@ static bool netplay_get_cmd(netplay_t *netplay, RECV(nickname, sizeof(nickname)) return false; + STRING_SAFE(nickname, sizeof(nickname)); cmd_size -= sizeof(nickname); } @@ -5936,8 +5981,7 @@ static bool netplay_get_cmd(netplay_t *netplay, return false; message[recvd] = '\0'; - if (!handle_chat(netplay, connection, - nickname, message)) + if (!handle_chat(netplay, connection, nickname, message)) { RARCH_ERR("[Netplay] NETPLAY_CMD_PLAYER_CHAT with invalid message or from an invalid peer.\n"); return netplay_cmd_nak(netplay, connection); @@ -6025,14 +6069,16 @@ static bool netplay_get_cmd(netplay_t *netplay, default: { unsigned char buf[1024]; + while (cmd_size) { RECV(buf, (cmd_size > sizeof(buf)) ? sizeof(buf) : cmd_size) return false; cmd_size -= recvd; } + RARCH_WARN("[Netplay] %s\n", - msg_hash_to_str(MSG_UNKNOWN_NETPLAY_COMMAND_RECEIVED)); + msg_hash_to_str(MSG_UNKNOWN_NETPLAY_COMMAND_RECEIVED)); } break; } @@ -6056,7 +6102,7 @@ shrt: * * Poll input from the network */ -int netplay_poll_net_input(netplay_t *netplay, bool block) +static int netplay_poll_net_input(netplay_t *netplay, bool block) { size_t i; bool had_input; @@ -6089,7 +6135,7 @@ int netplay_poll_net_input(netplay_t *netplay, bool block) if (!had_input) { #ifdef NETWORK_HAVE_POLL - struct pollfd fds[MAX_CLIENTS]; + struct pollfd fds[MAX_CLIENTS - 1]; unsigned nfds = 0; #else fd_set fds; @@ -6165,7 +6211,7 @@ int netplay_poll_net_input(netplay_t *netplay, bool block) * * Handle any slave connections */ -void netplay_handle_slaves(netplay_t *netplay) +static void netplay_handle_slaves(netplay_t *netplay) { struct delta_frame *oframe, *frame = &netplay->buffer[netplay->self_ptr]; size_t i; @@ -6274,14 +6320,15 @@ static void netplay_announce_nat_traversal(netplay_t *netplay, * * Initialize the NAT traversal library and try to open a port */ -void netplay_init_nat_traversal(netplay_t *netplay) +static void netplay_init_nat_traversal(netplay_t *netplay) { net_driver_state_t *net_st = &networking_driver_st; - task_push_netplay_nat_traversal(&net_st->nat_traversal_request, netplay->tcp_port); + task_push_netplay_nat_traversal(&net_st->nat_traversal_request, + netplay->tcp_port); } -void netplay_deinit_nat_traversal(void) +static void netplay_deinit_nat_traversal(void) { net_driver_state_t *net_st = &networking_driver_st; @@ -6582,14 +6629,13 @@ static bool netplay_init_socket_buffers(netplay_t *netplay) static bool netplay_init_serialization(netplay_t *netplay) { - unsigned i; - retro_ctx_size_info_t info; + size_t i; + retro_ctx_size_info_t info = {0}; if (netplay->state_size) return true; core_serialize_size(&info); - if (!info.size) return false; @@ -6597,21 +6643,23 @@ static bool netplay_init_serialization(netplay_t *netplay) for (i = 0; i < netplay->buffer_size; i++) { - netplay->buffer[i].state = calloc(netplay->state_size, 1); - + netplay->buffer[i].state = calloc(1, netplay->state_size); if (!netplay->buffer[i].state) { netplay->quirks |= NETPLAY_QUIRK_NO_SAVESTATES; + netplay->stateless_mode = true; + return false; } } netplay->zbuffer_size = netplay->state_size * 2; - netplay->zbuffer = (uint8_t *) calloc(netplay->zbuffer_size, 1); + netplay->zbuffer = (uint8_t*)calloc(1, netplay->zbuffer_size); if (!netplay->zbuffer) { netplay->quirks |= NETPLAY_QUIRK_NO_TRANSMISSION; netplay->zbuffer_size = 0; + return false; } @@ -6625,7 +6673,7 @@ static bool netplay_init_serialization(netplay_t *netplay) * * Returns true if serialization is now ready, false otherwise. */ -bool netplay_try_init_serialization(netplay_t *netplay) +static bool netplay_try_init_serialization(netplay_t *netplay) { retro_ctx_serialize_info_t serial_info; @@ -6709,6 +6757,73 @@ static bool netplay_init_buffers(netplay_t *netplay) return netplay_init_socket_buffers(netplay); } +/** + * netplay_free + * @netplay : pointer to netplay object + * + * Frees netplay data. + */ +static void netplay_free(netplay_t *netplay) +{ + size_t i; + + if (netplay->listen_fd >= 0) + socket_close(netplay->listen_fd); + + if (netplay->mitm_pending) + { + for (i = 0; i < NETPLAY_MITM_MAX_PENDING; i++) + { + int fd = netplay->mitm_pending->fds[i]; + + if (fd >= 0) + socket_close(fd); + } + + freeaddrinfo_retro(netplay->mitm_pending->base_addr); + free(netplay->mitm_pending); + } + + for (i = 0; i < netplay->connections_size; i++) + { + struct netplay_connection *connection = &netplay->connections[i]; + + if (connection->active) + { + socket_close(connection->fd); + netplay_deinit_socket_buffer(&connection->send_packet_buffer); + netplay_deinit_socket_buffer(&connection->recv_packet_buffer); + } + } + + free(netplay->connections); + + if (netplay->buffer) + { + for (i = 0; i < netplay->buffer_size; i++) + netplay_delta_frame_free(&netplay->buffer[i]); + + free(netplay->buffer); + } + + free(netplay->zbuffer); + + if (netplay->compress_nil.compression_stream) + netplay->compress_nil.compression_backend->stream_free( + netplay->compress_nil.compression_stream); + if (netplay->compress_nil.decompression_stream) + netplay->compress_nil.decompression_backend->stream_free( + netplay->compress_nil.decompression_stream); + if (netplay->compress_zlib.compression_stream) + netplay->compress_zlib.compression_backend->stream_free( + netplay->compress_zlib.compression_stream); + if (netplay->compress_zlib.decompression_stream) + netplay->compress_zlib.decompression_backend->stream_free( + netplay->compress_zlib.decompression_stream); + + free(netplay); +} + /** * netplay_new: * @server : IP address of server. @@ -6727,8 +6842,8 @@ static bool netplay_init_buffers(netplay_t *netplay) * * Returns: new netplay data. */ -netplay_t *netplay_new(const char *server, const char *mitm, uint16_t port, - const char *mitm_session, +static netplay_t *netplay_new(const char *server, const char *mitm, + uint16_t port, const char *mitm_session, bool stateless_mode, int check_frames, const struct retro_callbacks *cb, bool nat_traversal, const char *nick, uint64_t quirks) @@ -6750,8 +6865,6 @@ netplay_t *netplay_new(const char *server, const char *mitm, uint16_t port, netplay->crcs_valid = true; netplay->quirks = quirks; netplay->simple_rand_next = 1; - netplay->self_mode = netplay->is_server ? - NETPLAY_CONNECTION_SPECTATING : NETPLAY_CONNECTION_NONE; if (netplay->stateless_mode) netplay->quirks |= NETPLAY_QUIRK_NO_SAVESTATES; @@ -6768,11 +6881,17 @@ netplay_t *netplay_new(const char *server, const char *mitm, uint16_t port, netplay->input_latency_frames_max = netplay->input_latency_frames_min + settings->uints.netplay_input_latency_frames_range; + + netplay->self_mode = NETPLAY_CONNECTION_SPECTATING; } else { - netplay->connections = &netplay->one_connection; - netplay->connections_size = 1; + netplay->connections_size = 1; + netplay->connections = + (struct netplay_connection*)calloc(1, sizeof(*netplay->connections)); + if (!netplay->connections) + goto failure; + netplay->connections[0].fd = -1; if (!string_is_empty(mitm_session)) @@ -6805,27 +6924,12 @@ netplay_t *netplay_new(const char *server, const char *mitm, uint16_t port, !netplay_init_buffers(netplay)) goto failure; + netplay_key_init(netplay); + if (netplay->is_server) { unsigned i; - if (netplay->mitm_session_id.magic) - { - net_driver_state_t *net_st = &networking_driver_st; - struct netplay_room *host_room = &net_st->host_room; - int flen = 0; - char *buf = base64(netplay->mitm_session_id.unique, - sizeof(netplay->mitm_session_id.unique), &flen); - - if (!buf) - goto failure; - strlcpy(host_room->mitm_session, buf, - sizeof(host_room->mitm_session)); - free(buf); - } - else if (netplay->nat_traversal) - netplay_init_nat_traversal(netplay); - /* Clients get device info from the server */ for (i = 0; i < MAX_INPUT_DEVICES; i++) { @@ -6834,16 +6938,15 @@ netplay_t *netplay_new(const char *server, const char *mitm, uint16_t port, 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 << i)) RARCH_WARN("[Netplay] Netplay does not support input device %u\n", i + 1); } + + netplay->reannounce = ANNOUNCE_FRAME_START; } else { @@ -6853,6 +6956,8 @@ netplay_t *netplay_new(const char *server, const char *mitm, uint16_t port, netplay->connections[0].mode = NETPLAY_CONNECTION_INIT; netplay->self_mode = NETPLAY_CONNECTION_INIT; + + netplay->reping = -1; } return netplay; @@ -6863,75 +6968,6 @@ failure: return NULL; } -/** - * netplay_free - * @netplay : pointer to netplay object - * - * Frees netplay data/ - */ -void netplay_free(netplay_t *netplay) -{ - size_t i; - - if (netplay->listen_fd >= 0) - socket_close(netplay->listen_fd); - - if (netplay->mitm_pending) - { - for (i = 0; i < NETPLAY_MITM_MAX_PENDING; i++) - { - int fd = netplay->mitm_pending->fds[i]; - if (fd >= 0) - socket_close(fd); - } - - freeaddrinfo_retro(netplay->mitm_pending->base_addr); - free(netplay->mitm_pending); - } - - for (i = 0; i < netplay->connections_size; i++) - { - struct netplay_connection *connection = &netplay->connections[i]; - if (connection->active) - { - socket_close(connection->fd); - netplay_deinit_socket_buffer(&connection->send_packet_buffer); - netplay_deinit_socket_buffer(&connection->recv_packet_buffer); - } - } - - if (netplay->connections && netplay->connections != &netplay->one_connection) - free(netplay->connections); - - if (netplay->buffer) - { - for (i = 0; i < netplay->buffer_size; i++) - netplay_delta_frame_free(&netplay->buffer[i]); - - free(netplay->buffer); - } - - if (netplay->zbuffer) - free(netplay->zbuffer); - - if (netplay->compress_nil.compression_stream) - netplay->compress_nil.compression_backend->stream_free( - netplay->compress_nil.compression_stream); - if (netplay->compress_nil.decompression_stream) - netplay->compress_nil.decompression_backend->stream_free( - netplay->compress_nil.decompression_stream); - - if (netplay->compress_zlib.compression_stream) - netplay->compress_zlib.compression_backend->stream_free(netplay->compress_zlib.compression_stream); - if (netplay->compress_zlib.decompression_stream) - netplay->compress_zlib.decompression_backend->stream_free(netplay->compress_zlib.decompression_stream); - - if (netplay->addr) - freeaddrinfo_retro(netplay->addr); - - free(netplay); -} - /** * netplay_send_savestate * @netplay : pointer to netplay object @@ -6985,7 +7021,14 @@ static void netplay_send_savestate(netplay_t *netplay, } } -void netplay_frontend_paused(netplay_t *netplay, bool paused) +/** + * netplay_frontend_paused + * @netplay : pointer to netplay object + * @paused : true if frontend is paused + * + * Inform Netplay of the frontend's pause state (paused or otherwise) + */ +static void netplay_frontend_paused(netplay_t *netplay, bool paused) { size_t i; uint32_t paused_ct = 0; @@ -7074,8 +7117,13 @@ static void netplay_force_future(netplay_t *netplay) } } - -void netplay_core_reset(netplay_t *netplay) +/** + * netplay_core_reset + * @netplay : pointer to netplay object + * + * Indicate that the core has been reset to netplay peers + **/ +static void netplay_core_reset(netplay_t *netplay) { size_t i; uint32_t cmd[3]; @@ -7150,7 +7198,12 @@ void netplay_load_savestate(netplay_t *netplay, &netplay->compress_zlib); } -void netplay_toggle_play_spectate(netplay_t *netplay) +/** + * netplay_toggle_play_spectate + * + * Toggle between play mode and spectate mode + */ +static void netplay_toggle_play_spectate(netplay_t *netplay) { switch (netplay->self_mode) { @@ -7189,7 +7242,7 @@ void netplay_toggle_play_spectate(netplay_t *netplay) } } -int16_t netplay_input_state(netplay_t *netplay, +static int16_t netplay_input_state(netplay_t *netplay, unsigned port, unsigned device, unsigned idx, unsigned id) { @@ -7251,7 +7304,7 @@ int16_t netplay_input_state(netplay_t *netplay, break; case RETRO_DEVICE_KEYBOARD: { - unsigned key = netplay_key_hton(id); + unsigned key = netplay_key_hton(netplay, id); if (key != NETPLAY_KEY_UNKNOWN) { unsigned word = key / 32; @@ -7381,7 +7434,7 @@ static bool get_self_input_state( { state[word] |= cb(local_device, RETRO_DEVICE_KEYBOARD, 0, - NETPLAY_KEY_NTOH(key)) ? + netplay_key_ntoh(netplay, key)) ? (UINT32_C(1) << bit) : 0; bit++; if (bit >= 32) @@ -7421,11 +7474,17 @@ static bool get_self_input_state( return true; } - -bool netplay_poll( - bool block_libretro_input, - void *settings_data, - netplay_t *netplay) +/** + * netplay_poll: + * @netplay : pointer to netplay object + * + * Polls network to see if we have anything new. If our + * network buffer is full, we simply have to block + * for new input data. + * + * Returns: true (1) if successful, otherwise false (0). + **/ +static bool netplay_poll(netplay_t *netplay, bool block_libretro_input) { size_t i; int res; @@ -7657,19 +7716,37 @@ catastrophe: return false; } -bool netplay_is_alive(netplay_t *netplay) +/** + * netplay_is_alive: + * @netplay : pointer to netplay object + * + * Checks if input port/index is controlled by netplay or not. + * + * Returns: true (1) if alive, otherwise false (0). + **/ +static bool netplay_is_alive(netplay_t *netplay) { - return (netplay->is_server) || - (!netplay->is_server && - netplay->self_mode >= NETPLAY_CONNECTION_CONNECTED); + return netplay->is_server || + netplay->self_mode >= NETPLAY_CONNECTION_CONNECTED; } -bool netplay_should_skip(netplay_t *netplay) +/** + * netplay_should_skip: + * @netplay : pointer to netplay object + * + * If we're fast-forward replaying to resync, check if we + * should actually show frame. + * + * Returns: bool (1) if we should skip this frame, otherwise + * false (0). + **/ +static bool netplay_should_skip(netplay_t *netplay) { if (!netplay) return false; - return netplay->is_replay - && (netplay->self_mode >= NETPLAY_CONNECTION_CONNECTED); + + return netplay->is_replay && + netplay->self_mode >= NETPLAY_CONNECTION_CONNECTED; } bool init_netplay_deferred(const char *server, unsigned port, const char *mitm_session) @@ -7694,22 +7771,17 @@ bool init_netplay_deferred(const char *server, unsigned port, const char *mitm_s /** * input_poll_net + * @netplay : pointer to netplay object * * Poll the network if necessary. */ -void input_poll_net(void) +void input_poll_net(netplay_t *netplay) { - net_driver_state_t *net_st = &networking_driver_st; - netplay_t *netplay = net_st->data; - if (!netplay_should_skip(netplay) && netplay && netplay->can_poll) + if (!netplay_should_skip(netplay)) { - input_driver_state_t - *input_st = input_state_get_ptr(); - netplay->can_poll = false; - netplay_poll( - input_st->block_libretro_input, - config_get_ptr(), - netplay); + input_driver_state_t *input_st = input_state_get_ptr(); + + netplay_poll(netplay, input_st->block_libretro_input); } } @@ -7740,107 +7812,118 @@ size_t audio_sample_batch_net(const int16_t *data, size_t frames) return frames; } -static void netplay_announce_cb(retro_task_t *task, - void *task_data, void *user_data, const char *error) +static void netplay_announce_cb(retro_task_t *task, void *task_data, + void *user_data, const char *error) { - char *buf_start, *buf; + char *buf, *buf_data; size_t remaining; - net_driver_state_t *net_st = &networking_driver_st; - struct netplay_room *host_room = &net_st->host_room; http_transfer_data_t *data = (http_transfer_data_t*)task_data; + net_driver_state_t *net_st = &networking_driver_st; + struct netplay_room *host_room = &net_st->host_room; bool first = !host_room->id; - if (error) - return; - if (!data || !data->data || !data->len) - return; - if (data->status != 200) + /* Abort if netplay is not initialized. */ + if (!net_st->data) return; - buf_start = (char*)malloc(data->len); - if (!buf_start) + if (error || !data || !data->data || !data->len || data->status != 200) + { + RARCH_ERR("[Netplay] Failed to announce session to the lobby server."); return; - memcpy(buf_start, data->data, data->len); + } - buf = buf_start; - remaining = data->len; + buf = (char*)malloc(data->len); + if (!buf) + return; + memcpy(buf, data->data, data->len); + + buf_data = buf; + remaining = data->len; do { char *lnbreak, *delim; + char *key, *value; - lnbreak = (char*) memchr(buf, '\n', remaining); + lnbreak = (char*)memchr(buf_data, '\n', remaining); if (!lnbreak) break; *lnbreak++ = '\0'; - delim = (char*) strchr(buf, '='); + delim = (char*)strchr(buf_data, '='); if (delim) { - char *key, *value; - - *delim++ = '\0'; - - key = buf; - value = delim; + *delim = '\0'; + key = buf_data; + value = delim + 1; if (!string_is_empty(key) && !string_is_empty(value)) { if (string_is_equal(key, "id")) host_room->id = (int)strtol(value, NULL, 10); else if (string_is_equal(key, "username")) - strlcpy(host_room->nickname, value, sizeof(host_room->nickname)); + strlcpy(host_room->nickname, value, + sizeof(host_room->nickname)); + else if (string_is_equal(key, "core_name")) + strlcpy(host_room->corename, value, + sizeof(host_room->corename)); + else if (string_is_equal(key, "game_name")) + strlcpy(host_room->gamename, value, + sizeof(host_room->gamename)); + else if (string_is_equal(key, "game_crc")) + host_room->gamecrc = (int)strtoul(value, NULL, 16); + else if (string_is_equal(key, "core_version")) + strlcpy(host_room->coreversion, value, + sizeof(host_room->coreversion)); else if (string_is_equal(key, "ip")) strlcpy(host_room->address, value, sizeof(host_room->address)); else if (string_is_equal(key, "port")) host_room->port = (int)strtol(value, NULL, 10); - else if (string_is_equal(key, "core_name")) - strlcpy(host_room->corename, value, sizeof(host_room->corename)); - else if (string_is_equal(key, "frontend")) - strlcpy(host_room->frontend, value, sizeof(host_room->frontend)); - else if (string_is_equal(key, "core_version")) - strlcpy(host_room->coreversion, value, sizeof(host_room->coreversion)); - else if (string_is_equal(key, "game_name")) - strlcpy(host_room->gamename, value, sizeof(host_room->gamename)); - else if (string_is_equal(key, "game_crc")) - host_room->gamecrc = (int)strtoul(value, NULL, 16); else if (string_is_equal(key, "host_method")) host_room->host_method = (int)strtol(value, NULL, 10); else if (string_is_equal(key, "has_password")) - host_room->has_password = string_is_equal_noncase(value, "true") || + host_room->has_password = + string_is_equal_case_insensitive(value, "true") || string_is_equal(value, "1"); else if (string_is_equal(key, "has_spectate_password")) - host_room->has_spectate_password = string_is_equal_noncase(value, "true") || + host_room->has_spectate_password = + string_is_equal_case_insensitive(value, "true") || string_is_equal(value, "1"); else if (string_is_equal(key, "retroarch_version")) strlcpy(host_room->retroarch_version, value, sizeof(host_room->retroarch_version)); + else if (string_is_equal(key, "frontend")) + strlcpy(host_room->frontend, value, + sizeof(host_room->frontend)); + else if (string_is_equal(key, "subsystem_name")) + strlcpy(host_room->subsystem_name, value, + sizeof(host_room->subsystem_name)); else if (string_is_equal(key, "country")) strlcpy(host_room->country, value, sizeof(host_room->country)); else if (string_is_equal(key, "connectable")) - host_room->connectable = string_is_equal_noncase(value, "true") || + host_room->connectable = + string_is_equal_case_insensitive(value, "true") || string_is_equal(value, "1"); } } - remaining -= (size_t)lnbreak - (size_t)buf; - buf = lnbreak; + remaining -= (size_t)lnbreak - (size_t)buf_data; + buf_data = lnbreak; } while (remaining); - free(buf_start); + free(buf); /* Warn only on the first announce. */ if (!host_room->connectable && first) { - const char *dmsg = msg_hash_to_str(MSG_ROOM_NOT_CONNECTABLE); - - RARCH_WARN("[Netplay] %s\n", dmsg); - runloop_msg_queue_push(dmsg, 1, 180, false, NULL, - MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + RARCH_WARN("[Netplay] %s\n", msg_hash_to_str(MSG_ROOM_NOT_CONNECTABLE)); + runloop_msg_queue_push(msg_hash_to_str(MSG_ROOM_NOT_CONNECTABLE), 1, 180, + false, NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); } #ifdef HAVE_PRESENCE { presence_userdata_t userdata; + userdata.status = PRESENCE_NETPLAY_HOSTING; command_event(CMD_EVENT_PRESENCE_UPDATE, &userdata); } @@ -7849,11 +7932,9 @@ static void netplay_announce_cb(retro_task_t *task, static void netplay_announce(netplay_t *netplay) { - char buf[8192]; + char buf[4096]; + char frontend_architecture_tmp[24]; const frontend_ctx_driver_t *frontend_drv; - char frontend_architecture[PATH_MAX_LENGTH]; - char frontend_architecture_tmp[32]; - uint32_t content_crc; char *username = NULL; char *corename = NULL; char *coreversion = NULL; @@ -7861,45 +7942,54 @@ static void netplay_announce(netplay_t *netplay) char *subsystemname = NULL; char *frontend_ident = NULL; char *mitm_session = NULL; - const char *mitm_custom_addr = ""; + char *mitm_custom_addr = NULL; int mitm_custom_port = 0; int is_mitm = 0; - settings_t *settings = config_get_ptr(); + uint32_t content_crc = 0; net_driver_state_t *net_st = &networking_driver_st; struct netplay_room *host_room = &net_st->host_room; struct retro_system_info *system = &runloop_state_get_ptr()->system.info; struct string_list *subsystem = path_get_subsystem_list(); - const char *url = FILE_PATH_LOBBY_LIBRETRO_URL "add"; + settings_t *settings = config_get_ptr(); net_http_urlencode(&username, netplay->nick); - net_http_urlencode(&corename, system->library_name); - net_http_urlencode(&coreversion, system->library_version); + strlcpy(buf, system->library_name, sizeof(host_room->corename)); + net_http_urlencode(&corename, buf); + strlcpy(buf, system->library_version, sizeof(host_room->coreversion)); + net_http_urlencode(&coreversion, buf); if (subsystem && subsystem->size > 0) { unsigned i; + buf[0] = '\0'; for (i = 0;;) { - strlcat(buf, path_basename(subsystem->elems[i++].data), sizeof(buf)); - if (i >= subsystem->size) + strlcat(buf, path_basename(subsystem->elems[i].data), + sizeof(host_room->gamename)); + if (++i >= subsystem->size) break; - strlcat(buf, "|", sizeof(buf)); + strlcat(buf, "|", sizeof(host_room->gamename)); } net_http_urlencode(&gamename, buf); - net_http_urlencode(&subsystemname, path_get(RARCH_PATH_SUBSYSTEM)); - - content_crc = 0; + strlcpy(buf, path_get(RARCH_PATH_SUBSYSTEM), + sizeof(host_room->subsystem_name)); + net_http_urlencode(&subsystemname, buf); } else { - const char *base = path_basename(path_get(RARCH_PATH_BASENAME)); + const char *basename = path_basename(path_get(RARCH_PATH_BASENAME)); + + if (!string_is_empty(basename)) + { + strlcpy(buf, basename, sizeof(host_room->gamename)); + net_http_urlencode(&gamename, buf); + } + else + net_http_urlencode(&gamename, "N/A"); - net_http_urlencode(&gamename, - !string_is_empty(base) ? base : "N/A"); - /* TODO/FIXME - subsystem should be implemented later? */ net_http_urlencode(&subsystemname, "N/A"); content_crc = content_get_crc(); @@ -7907,42 +7997,40 @@ static void netplay_announce(netplay_t *netplay) frontend_drv = (const frontend_ctx_driver_t*)frontend_driver_get_cpu_architecture_str( - frontend_architecture_tmp, sizeof(frontend_architecture_tmp)); + frontend_architecture_tmp, sizeof(frontend_architecture_tmp)); if (frontend_drv) - snprintf(frontend_architecture, - sizeof(frontend_architecture), - "%s %s", - frontend_drv->ident, - frontend_architecture_tmp); + { + snprintf(buf, sizeof(host_room->frontend), "%s %s", + frontend_drv->ident, frontend_architecture_tmp); + net_http_urlencode(&frontend_ident, buf); + } else - strlcpy(frontend_architecture, - "N/A", - sizeof(frontend_architecture)); - net_http_urlencode(&frontend_ident, frontend_architecture); + net_http_urlencode(&frontend_ident, "N/A"); if (!string_is_empty(host_room->mitm_session)) { is_mitm = 1; net_http_urlencode(&mitm_session, host_room->mitm_session); - - if (string_is_equal(host_room->mitm_handle, "custom")) - { - mitm_custom_addr = host_room->mitm_address; - mitm_custom_port = host_room->mitm_port; - } } else - { net_http_urlencode(&mitm_session, ""); - } + if (is_mitm && string_is_equal(host_room->mitm_handle, "custom")) + { + net_http_urlencode(&mitm_custom_addr, host_room->mitm_address); + mitm_custom_port = host_room->mitm_port; + } + else + net_http_urlencode(&mitm_custom_addr, ""); + + /* Estimated to a maximum of 3062 bytes. */ snprintf(buf, sizeof(buf), "username=%s&" "core_name=%s&" "core_version=%s&" "game_name=%s&" "game_crc=%08lX&" - "port=%hu&" + "port=%d&" "mitm_server=%s&" "has_password=%d&" "has_spectate_password=%d&" @@ -7958,7 +8046,7 @@ static void netplay_announce(netplay_t *netplay) coreversion, gamename, (unsigned long)content_crc, - netplay->ext_tcp_port, + (int)netplay->ext_tcp_port, host_room->mitm_handle, !string_is_empty(settings->paths.netplay_password) ? 1 : 0, !string_is_empty(settings->paths.netplay_spectate_password) ? 1 : 0, @@ -7970,6 +8058,9 @@ static void netplay_announce(netplay_t *netplay) mitm_custom_addr, mitm_custom_port); + task_push_http_post_transfer(FILE_PATH_LOBBY_LIBRETRO_URL "add", buf, + true, NULL, netplay_announce_cb, NULL); + free(username); free(corename); free(coreversion); @@ -7977,51 +8068,47 @@ static void netplay_announce(netplay_t *netplay) free(subsystemname); free(frontend_ident); free(mitm_session); - - task_push_http_post_transfer(url, buf, true, NULL, - netplay_announce_cb, NULL); + free(mitm_custom_addr); } static void netplay_mitm_query_cb(retro_task_t *task, void *task_data, - void *user_data, const char *error) + void *user_data, const char *error) { - char *buf_start, *buf; + char *buf, *buf_data; size_t remaining; - net_driver_state_t *net_st = (net_driver_state_t*)&networking_driver_st; - struct netplay_room *host_room = (struct netplay_room*)&net_st->host_room; http_transfer_data_t *data = (http_transfer_data_t*)task_data; + net_driver_state_t *net_st = &networking_driver_st; + struct netplay_room *host_room = &net_st->host_room; - if (error) - return; - if (!data || !data->data || !data->len) - return; - if (data->status != 200) + if (error || !data || !data->data || !data->len || data->status != 200) + { + RARCH_ERR("[Netplay] Failed to query the lobby server for tunnel information."); return; + } - buf_start = (char*)malloc(data->len); - if (!buf_start) + buf = (char*)malloc(data->len); + if (!buf) return; - memcpy(buf_start, data->data, data->len); + memcpy(buf, data->data, data->len); - buf = buf_start; - remaining = data->len; + buf_data = buf; + remaining = data->len; do { - char *delim = NULL; - char *key = NULL; - char *value = NULL; - char *lnbreak = (char*) memchr(buf, '\n', remaining); + char *lnbreak, *delim; + char *key, *value; + + lnbreak = (char*)memchr(buf_data, '\n', remaining); if (!lnbreak) break; *lnbreak++ = '\0'; - delim = (char*)strchr(buf, '='); + delim = (char*)strchr(buf_data, '='); if (delim) { - *delim++ = '\0'; - - key = buf; - value = delim; + *delim = '\0'; + key = buf_data; + value = delim + 1; if (!string_is_empty(key) && !string_is_empty(value)) { @@ -8033,62 +8120,56 @@ static void netplay_mitm_query_cb(retro_task_t *task, void *task_data, } } - remaining -= (size_t)lnbreak - (size_t)buf; - buf = lnbreak; + remaining -= (size_t)lnbreak - (size_t)buf_data; + buf_data = lnbreak; } while (remaining); - free(buf_start); + free(buf); } -static bool netplay_mitm_query(const char *mitm_name) +static bool netplay_mitm_query(const char *handle) { net_driver_state_t *net_st = &networking_driver_st; struct netplay_room *host_room = &net_st->host_room; - if (string_is_empty(mitm_name)) + if (string_is_empty(handle)) return false; /* We don't need to query, if we are using a custom relay server. */ - if (string_is_equal(mitm_name, "custom")) + if (string_is_equal(handle, "custom")) { - char addr[256]; - char sess[sizeof(addr)]; + char addr[256]; unsigned port = 0; settings_t *settings = config_get_ptr(); - const char *custom_server = - settings->paths.netplay_custom_mitm_server; - - addr[0] = '\0'; - sess[0] = '\0'; - - netplay_decode_hostname(custom_server, - addr, &port, sess, sizeof(addr)); + const char *custom_server = settings->paths.netplay_custom_mitm_server; + addr[0] = '\0'; + if (!netplay_decode_hostname(custom_server, addr, &port, NULL, + sizeof(addr))) + return false; if (!port) - port = RARCH_DEFAULT_PORT; + port = RARCH_DEFAULT_PORT; - strlcpy(host_room->mitm_address, addr, - sizeof(host_room->mitm_address)); - host_room->mitm_port = port; + strlcpy(host_room->mitm_address, addr, sizeof(host_room->mitm_address)); + host_room->mitm_port = (int)port; } else { - char query[512]; - const char *url = FILE_PATH_LOBBY_LIBRETRO_URL "tunnel"; + char query[256]; - snprintf(query, sizeof(query), "%s?name=%s", url, mitm_name); + snprintf(query, sizeof(query), + FILE_PATH_LOBBY_LIBRETRO_URL "tunnel?name=%s", handle); - if (!task_push_http_transfer(query, - true, NULL, netplay_mitm_query_cb, NULL)) + if (!task_push_http_transfer(query, true, NULL, + netplay_mitm_query_cb, NULL)) return false; /* Make sure we've the tunnel address before continuing. */ task_queue_wait(NULL, NULL); } - return !string_is_empty(host_room->mitm_address) && - host_room->mitm_port; + return !string_is_empty(host_room->mitm_address) && host_room->mitm_port; } int16_t input_state_net(unsigned port, unsigned device, @@ -8140,36 +8221,13 @@ static void netplay_disconnect(netplay_t *netplay) * Pre-frame for Netplay. * Call this before running retro_run(). * - * Returns: true (1) if the frontend is cleared to emulate the frame, false (0) - * if we're stalled or paused + * Returns: true (1) if the frontend is cleared to emulate the frame, + * false (0) if we're stalled or paused **/ static bool netplay_pre_frame(netplay_t *netplay) { - bool netplay_force_disconnect = false; - bool sync_stalled = false; - settings_t *settings = config_get_ptr(); - net_driver_state_t *net_st = &networking_driver_st; - - retro_assert(netplay); - - if (netplay->is_server) - { - if (settings->bools.netplay_public_announce) - { - if (++net_st->reannounce % 1200 == 0) - netplay_announce(netplay); - } - /* Make sure that if announcement is turned on mid-game, it gets announced */ - else - { - net_st->reannounce = -1; - } - } - else if (netplay->is_connected) - { - if (++net_st->reping % 180 == 0) - request_ping(netplay, &netplay->connections[0]); - } + bool force_disconnect = false; + bool sync_stalled = false; /* FIXME: This is an ugly way to learn we're not paused anymore */ if (netplay->local_paused) @@ -8179,37 +8237,57 @@ static bool netplay_pre_frame(netplay_t *netplay) if (netplay->quirks & NETPLAY_QUIRK_INITIALIZATION) netplay_try_init_serialization(netplay); -#ifdef HAVE_NETPLAYDISCOVERY - if (netplay->is_server && !netplay->mitm_pending) + sync_stalled = !netplay_sync_pre_frame(netplay, &force_disconnect); + + if (force_disconnect) { - /* Advertise our server */ - if (net_st->lan_ad_server_fd >= 0 || - init_lan_ad_server_socket()) - netplay_lan_ad_server(netplay); + netplay_disconnect(netplay); + return true; } + + if (netplay->is_server) + { + settings_t *settings = config_get_ptr(); + +#ifdef HAVE_NETPLAYDISCOVERY + if (!netplay->mitm_pending) + { + net_driver_state_t *net_st = &networking_driver_st; + + /* Advertise our server */ + if (net_st->lan_ad_server_fd >= 0 || init_lan_ad_server_socket()) + netplay_lan_ad_server(netplay); + } #endif - sync_stalled = !netplay_sync_pre_frame(netplay, &netplay_force_disconnect); - - if (netplay_force_disconnect) + if (settings->bools.netplay_public_announce) + { + if (++netplay->reannounce % ANNOUNCE_FRAMES == 0) + netplay_announce(netplay); + } + /* Make sure that if announcement is turned on mid-game, + it gets announced */ + else + netplay->reannounce = -1; + } + else { - netplay_disconnect(netplay); - return true; + /* If we're disconnected, deinitialize */ + if (!netplay->connections[0].active) + { + netplay_disconnect(netplay); + return true; + } + + if (++netplay->reping % PING_FRAMES == 0) + request_ping(netplay, &netplay->connections[0]); } - /* If we're disconnected, deinitialize */ - if (!netplay->is_server && !netplay->connections[0].active) + if (sync_stalled || ((netplay->stall || netplay->remote_paused) && + (!netplay->is_server || netplay->connected_players > 1))) { - netplay_disconnect(netplay); - return true; - } - - if (sync_stalled || - ((!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 - * sync */ + /* We may have received data even if we're stalled, + * so run post-frame sync */ netplay_sync_post_frame(netplay, true); return false; } @@ -8248,41 +8326,42 @@ static void netplay_post_frame(netplay_t *netplay) void deinit_netplay(void) { - net_driver_state_t *net_st = &networking_driver_st; + net_driver_state_t *net_st = &networking_driver_st; + netplay_t *netplay = net_st->data; - if (net_st->data) + if (netplay) { - if (net_st->data->nat_traversal) + if (netplay->nat_traversal) netplay_deinit_nat_traversal(); - netplay_free(net_st->data); - net_st->data = NULL; - net_st->netplay_enabled = false; - net_st->netplay_is_client = false; + netplay_free(netplay); #ifdef HAVE_NETPLAYDISCOVERY deinit_lan_ad_server_socket(); #endif + + net_st->data = NULL; + net_st->netplay_enabled = false; + net_st->netplay_is_client = false; } free(net_st->client_info); net_st->client_info = NULL; net_st->client_info_count = 0; - free(net_st->chat); - net_st->chat = NULL; - core_unset_netplay_callbacks(); } bool init_netplay(const char *server, unsigned port, const char *mitm_session) { - struct retro_callbacks cbs = {0}; - uint64_t serialization_quirks = 0; - uint64_t quirks = 0; - settings_t *settings = config_get_ptr(); - net_driver_state_t *net_st = &networking_driver_st; - const char *mitm = NULL; + netplay_t *netplay; + struct retro_callbacks cbs = {0}; + uint64_t serialization_quirks = 0; + uint64_t quirks = 0; + settings_t *settings = config_get_ptr(); + net_driver_state_t *net_st = &networking_driver_st; + struct netplay_room *host_room = &net_st->host_room; + const char *mitm = NULL; if (!net_st->netplay_enabled) return false; @@ -8294,9 +8373,9 @@ bool init_netplay(const char *server, unsigned port, const char *mitm_session) serialization_quirks = core_serialization_quirks(); if (!core_info_current_supports_netplay() || - serialization_quirks & ~((uint64_t) NETPLAY_QUIRK_MAP_UNDERSTOOD) || - serialization_quirks & NETPLAY_QUIRK_MAP_NO_SAVESTATES || - serialization_quirks & NETPLAY_QUIRK_MAP_NO_TRANSMISSION) + (serialization_quirks & ~((uint64_t)NETPLAY_QUIRK_MAP_UNDERSTOOD)) || + (serialization_quirks & (NETPLAY_QUIRK_MAP_NO_SAVESTATES | + NETPLAY_QUIRK_MAP_NO_TRANSMISSION))) { RARCH_ERR("[Netplay] %s\n", msg_hash_to_str(MSG_NETPLAY_UNSUPPORTED)); runloop_msg_queue_push( @@ -8319,8 +8398,6 @@ bool init_netplay(const char *server, unsigned port, const char *mitm_session) if (!net_st->netplay_is_client) { - struct netplay_room *host_room = &net_st->host_room; - memset(host_room, 0, sizeof(*host_room)); host_room->connectable = true; @@ -8342,9 +8419,7 @@ bool init_netplay(const char *server, unsigned port, const char *mitm_session) port = host_room->mitm_port; } else - { RARCH_WARN("[Netplay] Failed to get tunnel information. Switching to direct mode.\n"); - } } if (!port) @@ -8362,40 +8437,47 @@ bool init_netplay(const char *server, unsigned port, const char *mitm_session) net_st->netplay_client_deferred = false; - net_st->chat = (struct netplay_chat*)calloc(1, sizeof(*net_st->chat)); - if (!net_st->chat) - goto failure; - net_st->chat->message_slots = ARRAY_SIZE(net_st->chat->messages); - - net_st->data = netplay_new( - server, mitm, port, mitm_session, - /*settings->bools.netplay_stateless_mode,*/ - false, - settings->ints.netplay_check_frames, - &cbs, - settings->bools.netplay_nat_traversal, + netplay = netplay_new( + server, mitm, port, mitm_session, + /*settings->bools.netplay_stateless_mode,*/ + false, + settings->ints.netplay_check_frames, + &cbs, + settings->bools.netplay_nat_traversal, #ifdef HAVE_DISCORD - discord_get_own_username() - ? discord_get_own_username() - : + !string_is_empty(discord_get_own_username()) ? discord_get_own_username() + : #endif - settings->paths.username, - quirks); - if (!net_st->data) + settings->paths.username, + quirks); + if (!netplay) goto failure; - net_st->reannounce = 900; - net_st->reping = -1; + net_st->data = netplay; - if (net_st->data->is_server) + if (netplay->is_server) { + if (mitm) + { + int flen = 0; + char *buf = base64(netplay->mitm_session_id.unique, + sizeof(netplay->mitm_session_id.unique), &flen); + + if (!buf) + goto failure; + strlcpy(host_room->mitm_session, buf, + sizeof(host_room->mitm_session)); + free(buf); + } + else if (netplay->nat_traversal) + netplay_init_nat_traversal(netplay); + runloop_msg_queue_push( - msg_hash_to_str(MSG_WAITING_FOR_CLIENT), - 0, 180, false, - NULL, MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); + msg_hash_to_str(MSG_WAITING_FOR_CLIENT), 0, 180, false, NULL, + MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); if (!settings->bools.netplay_start_as_spectator) - netplay_toggle_play_spectate(net_st->data); + netplay_toggle_play_spectate(netplay); } return true; @@ -8568,7 +8650,7 @@ bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data) if (!net_st->client_info) { net_st->client_info = (netplay_client_info_t*)calloc( - MAX_CLIENTS, sizeof(*net_st->client_info)); + MAX_CLIENTS - 1, sizeof(*net_st->client_info)); if (!net_st->client_info) { ret = false; @@ -8596,7 +8678,8 @@ bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data) break; case RARCH_NETPLAY_CTL_IS_CONNECTED: - ret = netplay && netplay->is_connected; + ret = netplay && !netplay->is_server && + (netplay->self_mode >= NETPLAY_CONNECTION_CONNECTED); break; case RARCH_NETPLAY_CTL_IS_SPECTATING: @@ -8723,32 +8806,43 @@ bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data) bool netplay_decode_hostname(const char *hostname, char *address, unsigned *port, char *session, size_t len) { - struct string_list *hostname_data = NULL; + struct string_list hostname_data; if (string_is_empty(hostname)) return false; - - hostname_data = string_split(hostname, "|"); - if (!hostname_data) + if (!string_list_initialize(&hostname_data)) return false; - - if (hostname_data->size >= 1 && - !string_is_empty(hostname_data->elems[0].data)) - strlcpy(address, hostname_data->elems[0].data, len); - - if (hostname_data->size >= 2 && - !string_is_empty(hostname_data->elems[1].data)) + if (!string_split_noalloc(&hostname_data, hostname, "|")) { - unsigned tmp_port = strtoul(hostname_data->elems[1].data, NULL, 10); - if (tmp_port && tmp_port <= 65535) - *port = tmp_port; + string_list_deinitialize(&hostname_data); + return false; } - if (hostname_data->size >= 3 && - !string_is_empty(hostname_data->elems[2].data)) - strlcpy(session, hostname_data->elems[2].data, len); + if (hostname_data.size >= 1 && + !string_is_empty(hostname_data.elems[0].data)) + { + if (address) + strlcpy(address, hostname_data.elems[0].data, len); + } + if (hostname_data.size >= 2 && + !string_is_empty(hostname_data.elems[1].data)) + { + if (port) + { + unsigned tmp_port = strtoul(hostname_data.elems[1].data, NULL, 10); - string_list_free(hostname_data); + if (tmp_port && tmp_port <= 65535) + *port = tmp_port; + } + } + if (hostname_data.size >= 3 && + !string_is_empty(hostname_data.elems[2].data)) + { + if (session) + strlcpy(session, hostname_data.elems[2].data, len); + } + + string_list_deinitialize(&hostname_data); return true; } @@ -8801,22 +8895,22 @@ bool netplay_6to4(struct sockaddr_storage *addr) #ifdef HAVE_GFX_WIDGETS static void gfx_widget_netplay_chat_iterate(void *user_data, - unsigned width, unsigned height, bool fullscreen, - const char *dir_assets, char *font_path, - bool is_threaded) + unsigned width, unsigned height, bool fullscreen, + const char *dir_assets, char *font_path, bool is_threaded) { size_t i; - net_driver_state_t *net_st = &networking_driver_st; - struct netplay_chat *chat = net_st->chat; + net_driver_state_t *net_st = &networking_driver_st; + netplay_t *netplay = net_st->data; struct netplay_chat_buffer *chat_buffer = &net_st->chat_buffer; - if (chat) + if (netplay) { - settings_t *settings = config_get_ptr(); + struct netplay_chat *chat = &netplay->chat; + settings_t *settings = config_get_ptr(); #ifdef HAVE_MENU - bool menu_open = menu_state_get_ptr()->alive; + bool menu_open = menu_state_get_ptr()->alive; #endif - bool fade_chat = settings->bools.netplay_fade_chat; + bool fade_chat = settings->bools.netplay_fade_chat; /* Move the messages to a thread-safe buffer before drawing them. */ @@ -8826,7 +8920,7 @@ static void gfx_widget_netplay_chat_iterate(void *user_data, uint8_t *alpha = &chat_buffer->messages[i].alpha; #ifdef HAVE_MENU - /* Don't show chat while in the menu */ + /* Don't show chat while in the menu. */ if (menu_open) { *alpha = 0; @@ -8841,12 +8935,10 @@ static void gfx_widget_netplay_chat_iterate(void *user_data, } else if (*frames) { - float alpha_percent = (float) *frames / - (float) NETPLAY_CHAT_FRAME_TIME; - - *alpha = (uint8_t) float_max( - alpha_percent * 255.0f, 1.0f); + float alpha_percent = (float)*frames / + (float)NETPLAY_CHAT_FRAME_TIME; + *alpha = (uint8_t)float_max(alpha_percent * 255.0f, 1.0f); (*frames)--; } else @@ -8864,7 +8956,7 @@ static void gfx_widget_netplay_chat_iterate(void *user_data, /* If we are not in netplay, do nothing. */ else { - for (i = 0; i < ARRAY_SIZE(chat->messages); i++) + for (i = 0; i < ARRAY_SIZE(chat_buffer->messages); i++) chat_buffer->messages[i].alpha = 0; } } @@ -8872,22 +8964,22 @@ static void gfx_widget_netplay_chat_iterate(void *user_data, static void gfx_widget_netplay_chat_frame(void *data, void *userdata) { size_t i; - video_frame_info_t *video_info = (video_frame_info_t*)data; - dispgfx_widget_t *p_dispwidget = (dispgfx_widget_t*)userdata; - net_driver_state_t *net_st = &networking_driver_st; - struct netplay_chat_buffer *chat_buffer = &net_st->chat_buffer; - int line_height = - p_dispwidget->gfx_widget_fonts.regular.line_height + - p_dispwidget->simple_widget_padding / 3.0f; - int height = - video_info->height - line_height; + char formatted_nick[NETPLAY_CHAT_MAX_SIZE]; + char formatted_msg[NETPLAY_CHAT_MAX_SIZE]; + int formatted_nick_len; + int formatted_nick_width; + video_frame_info_t *video_info = (video_frame_info_t*)data; + dispgfx_widget_t *p_dispwidget = (dispgfx_widget_t*)userdata; + net_driver_state_t *net_st = &networking_driver_st; + struct netplay_chat_buffer *chat_buffer = &net_st->chat_buffer; + gfx_widget_font_data_t *font = + &p_dispwidget->gfx_widget_fonts.regular; + int line_height = + font->line_height + p_dispwidget->simple_widget_padding / 3.0f; + int height = video_info->height - line_height; for (i = 0; i < ARRAY_SIZE(chat_buffer->messages); i++) { - char formatted_nick[NETPLAY_CHAT_MAX_SIZE]; - char formatted_msg[NETPLAY_CHAT_MAX_SIZE]; - int formatted_nick_len; - int formatted_nick_width; uint8_t alpha = chat_buffer->messages[i].alpha; const char *nick = chat_buffer->messages[i].nick; const char *msg = chat_buffer->messages[i].msg; @@ -8896,36 +8988,33 @@ static void gfx_widget_netplay_chat_frame(void *data, void *userdata) continue; /* Truncate the message, if necessary. */ - formatted_nick_len = snprintf(formatted_nick, - sizeof(formatted_nick), "%s: ", nick); - strlcpy(formatted_msg, msg, - sizeof(formatted_msg) - formatted_nick_len); + formatted_nick_len = snprintf(formatted_nick, sizeof(formatted_nick), + "%s: ", nick); + strlcpy(formatted_msg, msg, sizeof(formatted_msg) - formatted_nick_len); formatted_nick_width = font_driver_get_message_width( - p_dispwidget->gfx_widget_fonts.regular.font, - formatted_nick, formatted_nick_len, - 1.0f); + font->font, formatted_nick, formatted_nick_len, 1.0f); /* Draw the nickname first. */ gfx_widgets_draw_text( - &p_dispwidget->gfx_widget_fonts.regular, + font, formatted_nick, p_dispwidget->simple_widget_padding, height, video_info->width, video_info->height, - NETPLAY_CHAT_NICKNAME_COLOR | (unsigned) alpha, + (unsigned)alpha | NETPLAY_CHAT_NICKNAME_COLOR, TEXT_ALIGN_LEFT, true); /* Now draw the message. */ gfx_widgets_draw_text( - &p_dispwidget->gfx_widget_fonts.regular, + font, formatted_msg, p_dispwidget->simple_widget_padding + formatted_nick_width, height, video_info->width, video_info->height, - NETPLAY_CHAT_MESSAGE_COLOR | (unsigned) alpha, + (unsigned)alpha | NETPLAY_CHAT_MESSAGE_COLOR, TEXT_ALIGN_LEFT, true); @@ -8935,17 +9024,16 @@ static void gfx_widget_netplay_chat_frame(void *data, void *userdata) } static void gfx_widget_netplay_ping_iterate(void *user_data, - unsigned width, unsigned height, bool fullscreen, - const char *dir_assets, char *font_path, - bool is_threaded) + unsigned width, unsigned height, bool fullscreen, + const char *dir_assets, char *font_path, bool is_threaded) { - settings_t *settings = config_get_ptr(); - net_driver_state_t *net_st = &networking_driver_st; - netplay_t *netplay = net_st->data; + net_driver_state_t *net_st = &networking_driver_st; + netplay_t *netplay = net_st->data; + settings_t *settings = config_get_ptr(); #ifdef HAVE_MENU - bool menu_open = menu_state_get_ptr()->alive; + bool menu_open = menu_state_get_ptr()->alive; #endif - bool show_ping = settings->bools.netplay_ping_show; + bool show_ping = settings->bools.netplay_ping_show; if (!netplay || !show_ping) { @@ -8953,7 +9041,7 @@ static void gfx_widget_netplay_ping_iterate(void *user_data, return; } - /* Don't show the ping counter while in the menu */ + /* Don't show the ping counter while in the menu. */ #ifdef HAVE_MENU if (menu_open) { @@ -8962,7 +9050,8 @@ static void gfx_widget_netplay_ping_iterate(void *user_data, } #endif - if (!netplay->is_server && netplay->is_connected) + if (!netplay->is_server && + netplay->self_mode >= NETPLAY_CONNECTION_CONNECTED) net_st->latest_ping = netplay->connections[0].ping; else net_st->latest_ping = -1; @@ -8970,41 +9059,39 @@ static void gfx_widget_netplay_ping_iterate(void *user_data, static void gfx_widget_netplay_ping_frame(void *data, void *userdata) { - video_frame_info_t *video_info = (video_frame_info_t*)data; - dispgfx_widget_t *p_dispwidget = (dispgfx_widget_t*)userdata; - net_driver_state_t *net_st = (net_driver_state_t*)&networking_driver_st; - int ping = net_st->latest_ping; + net_driver_state_t *net_st = &networking_driver_st; + int ping = net_st->latest_ping; if (ping >= 0) { char ping_str[16]; int ping_len; int ping_width, total_width; - gfx_display_t *p_disp = (gfx_display_t*)video_info->disp_userdata; + video_frame_info_t *video_info = (video_frame_info_t*)data; + dispgfx_widget_t *p_dispwidget = (dispgfx_widget_t*)userdata; + gfx_display_t *p_disp = + (gfx_display_t*)video_info->disp_userdata; + gfx_widget_font_data_t *font = + &p_dispwidget->gfx_widget_fonts.regular; - /* Limit ping counter to 999. */ + /* Limit the ping counter to 999. */ if (ping > 999) ping = 999; - ping_len = snprintf(ping_str, sizeof(ping_str), - "PING: %d", ping); + ping_len = snprintf(ping_str, sizeof(ping_str), "PING: %d", ping); ping_width = font_driver_get_message_width( - p_dispwidget->gfx_widget_fonts.regular.font, - ping_str, ping_len, 1.0f); - total_width = ping_width + - p_dispwidget->simple_widget_padding * 2; + font->font, ping_str, ping_len, 1.0f); + total_width = ping_width + p_dispwidget->simple_widget_padding * 2; - gfx_display_set_alpha(p_dispwidget->backdrop_orig, - DEFAULT_BACKDROP); + gfx_display_set_alpha(p_dispwidget->backdrop_orig, DEFAULT_BACKDROP); gfx_display_draw_quad( p_disp, video_info->userdata, video_info->width, video_info->height, video_info->width - total_width, - video_info->height - - p_dispwidget->simple_widget_height, + video_info->height - p_dispwidget->simple_widget_height, total_width, p_dispwidget->simple_widget_height, video_info->width, @@ -9012,12 +9099,10 @@ static void gfx_widget_netplay_ping_frame(void *data, void *userdata) p_dispwidget->backdrop_orig, NULL); gfx_widgets_draw_text( - &p_dispwidget->gfx_widget_fonts.regular, + font, ping_str, - video_info->width - ping_width - - p_dispwidget->simple_widget_padding, - video_info->height - - p_dispwidget->gfx_widget_fonts.regular.line_centre_offset, + video_info->width - ping_width - p_dispwidget->simple_widget_padding, + video_info->height - font->line_centre_offset, video_info->width, video_info->height, 0xFFFFFFFF, diff --git a/network/netplay/netplay_private.h b/network/netplay/netplay_private.h index b4753c5f25..a16d7db0aa 100644 --- a/network/netplay/netplay_private.h +++ b/network/netplay/netplay_private.h @@ -20,25 +20,30 @@ #define __RARCH_NETPLAY_PRIVATE_H #include "netplay.h" +#include "netplay_protocol.h" + +#include +#include #include +#include "../../retroarch_types.h" + #define RARCH_DEFAULT_PORT 55435 #define RARCH_DEFAULT_NICK "Anonymous" #define NETPLAY_PASS_LEN 128 #define NETPLAY_PASS_HASH_LEN 64 /* length of a SHA-256 hash */ -#define MAX_SERVER_STALL_TIME_USEC (5*1000*1000) -#define MAX_CLIENT_STALL_TIME_USEC (10*1000*1000) -#define CATCH_UP_CHECK_TIME_USEC (500*1000) -#define MAX_RETRIES 16 -#define RETRY_MS 500 -#define MAX_INPUT_DEVICES 16 +#define MAX_SERVER_STALL_TIME_USEC (5*1000*1000) +#define MAX_CLIENT_STALL_TIME_USEC (10*1000*1000) +#define CATCH_UP_CHECK_TIME_USEC (500*1000) +#define MAX_RETRIES 16 +#define RETRY_MS 500 +#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; +#define MAX_CLIENTS 32 /* Because the callback keyboard reverses some assumptions, when the keyboard * callbacks are in use, we assign a pseudodevice for it */ @@ -63,8 +68,9 @@ typedef uint32_t client_bitmap_t; /* Mapping of serialization quirks to netplay quirks. */ #define NETPLAY_QUIRK_MAP_UNDERSTOOD (\ RETRO_SERIALIZATION_QUIRK_INCOMPLETE |\ - RETRO_SERIALIZATION_QUIRK_CORE_VARIABLE_SIZE |\ RETRO_SERIALIZATION_QUIRK_MUST_INITIALIZE |\ + RETRO_SERIALIZATION_QUIRK_CORE_VARIABLE_SIZE |\ + RETRO_SERIALIZATION_QUIRK_FRONT_VARIABLE_SIZE |\ RETRO_SERIALIZATION_QUIRK_SINGLE_SESSION |\ RETRO_SERIALIZATION_QUIRK_ENDIAN_DEPENDENT |\ RETRO_SERIALIZATION_QUIRK_PLATFORM_DEPENDENT \ @@ -89,6 +95,18 @@ typedef uint32_t client_bitmap_t; #define NETPLAY_COMPRESSION_SUPPORTED 0 #endif +/* 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 +}; + enum netplay_cmd { /* Basic commands */ @@ -194,11 +212,11 @@ enum netplay_cmd NETPLAY_CMD_SETTING_INPUT_LATENCY_FRAMES = 0x2001 }; -#define NETPLAY_CMD_SYNC_BIT_PAUSED (1U<<31) -#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) +#define NETPLAY_CMD_SYNC_BIT_PAUSED (1U<<31) +#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 @@ -224,22 +242,22 @@ 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, + NETPLAY_SHARE_NO_SHARING = 0x00, /* 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, + NETPLAY_SHARE_NO_PREFERENCE = 0x01, /* For digital devices */ - NETPLAY_SHARE_DIGITAL_BITS = 0x1C, - NETPLAY_SHARE_DIGITAL_OR = 0x4, - NETPLAY_SHARE_DIGITAL_XOR = 0x8, - NETPLAY_SHARE_DIGITAL_VOTE = 0xC, + NETPLAY_SHARE_DIGITAL_BITS = 0x1C, + NETPLAY_SHARE_DIGITAL_OR = 0x04, + NETPLAY_SHARE_DIGITAL_XOR = 0x08, + NETPLAY_SHARE_DIGITAL_VOTE = 0x0C, /* For analog devices */ - NETPLAY_SHARE_ANALOG_BITS = 0xE0, - NETPLAY_SHARE_ANALOG_MAX = 0x20, + NETPLAY_SHARE_ANALOG_BITS = 0xE0, + NETPLAY_SHARE_ANALOG_MAX = 0x20, NETPLAY_SHARE_ANALOG_AVERAGE = 0x40 }; @@ -300,7 +318,7 @@ struct delta_frame /* 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]; + netplay_input_state_t simulated_input[MAX_INPUT_DEVICES]; /* The serialized state of the core at this frame, before input */ void *state; @@ -339,14 +357,18 @@ struct netplay_connection /* Timer used to estimate a connection's latency */ retro_time_t ping_timer; - /* Address of peer */ - struct sockaddr_storage addr; - /* Buffers for sending and receiving data */ - struct socket_buffer send_packet_buffer, recv_packet_buffer; + struct socket_buffer send_packet_buffer; + struct socket_buffer recv_packet_buffer; - /* fd associated with this connection */ - int fd; + /* What compression does this peer support? */ + uint32_t compression_supported; + + /* Salt associated with password transaction */ + uint32_t salt; + + /* Which netplay protocol is this connection running? */ + uint32_t netplay_protocol; /* If the mode is a DELAYED_DISCONNECT or SPECTATOR, * the transmission of the mode change may have to @@ -355,43 +377,37 @@ struct netplay_connection * is active. */ uint32_t delay_frame; - /* What compression does this peer support? */ - uint32_t compression_supported; - /* For the server: When was the last time we requested * this client to stall? * For the client: How many frames of stall do we have left? */ uint32_t stall_frame; - /* Salt associated with password transaction */ - uint32_t salt; - - /* Which netplay protocol is this connection running? */ - uint32_t netplay_protocol; - /* What latency is this connection running on? * Network latency has limited precision as we estimate it * once every pre-frame. */ int32_t ping; - /* Is this connection stalling? */ - enum rarch_netplay_stall_reason stall; + /* fd associated with this connection */ + int fd; /* Mode of the connection */ enum rarch_netplay_connection_mode mode; + /* Is this connection stalling? */ + enum rarch_netplay_stall_reason stall; + /* Nickname of peer */ char nick[NETPLAY_NICK_LEN]; + /* Is this connection buffer in use? */ + bool active; + /* Is this player paused? */ bool paused; /* Is this connection allowed to play (server only)? */ bool can_play; - /* Is this connection buffer in use? */ - bool active; - /* Did we request a ping response? */ bool ping_requested; }; @@ -400,8 +416,8 @@ struct netplay_connection struct compression_transcoder { const struct trans_stream_backend *compression_backend; - void *compression_stream; const struct trans_stream_backend *decompression_backend; + void *compression_stream; void *decompression_stream; }; @@ -423,42 +439,52 @@ struct netplay_mitm_pending int fds[NETPLAY_MITM_MAX_PENDING]; }; +struct netplay_chat +{ + struct + { + uint32_t frames; + char nick[NETPLAY_NICK_LEN]; + char msg[NETPLAY_CHAT_MAX_SIZE]; + } messages[NETPLAY_CHAT_MAX_MESSAGES]; +}; + struct netplay { /* Quirks in the savestate implementation */ uint64_t quirks; + /* We stall if we're far enough ahead that we + * couldn't transparently rewind. + * To know if we could transparently rewind, + * we need to know how long running a frame takes. + * We record that every frame and get a running (window) average. */ + retro_time_t frame_run_time[NETPLAY_FRAME_RUN_TIME_WINDOW]; + retro_time_t frame_run_time_sum; + retro_time_t frame_run_time_avg; + /* When did we start falling behind? */ retro_time_t catch_up_time; /* How long have we been stalled? */ retro_time_t stall_time; - /* We stall if we're far enough ahead that we - * couldn't transparently rewind. - * To know if we could transparently rewind, - * we need to know how long running a frame takes. - * We record that every frame and get a running (window) average. */ - retro_time_t frame_run_time[NETPLAY_FRAME_RUN_TIME_WINDOW]; - retro_time_t frame_run_time_sum, frame_run_time_avg; - struct retro_callbacks cbs; /* Compression transcoder */ - struct compression_transcoder compress_nil, - compress_zlib; + struct compression_transcoder compress_nil; + struct compression_transcoder compress_zlib; /* MITM session id */ mitm_id_t mitm_session_id; - struct netplay_connection one_connection; /* Client only */ - /* All of our connections */ - struct netplay_connection *connections; + /* Chat messages */ + struct netplay_chat chat; /* MITM connection handler */ struct netplay_mitm_pending *mitm_pending; - /* Our local socket info */ - struct addrinfo *addr; + /* All of our connections */ + struct netplay_connection *connections; struct delta_frame *buffer; @@ -475,21 +501,21 @@ struct netplay /* The frame we're currently inputting */ size_t self_ptr; - /* The frame we're currently running, which may be + /* The frame we're currently running, which may be * behind the frame we're currently inputting if * we're using input latency */ size_t run_ptr; /* The first frame at which some data might be unreliable */ size_t other_ptr; - /* Pointer to the first frame for which we're missing + /* Pointer to the first frame for which we're missing * the data of at least one connected player excluding ourself. - * Generally, other_ptr <= unread_ptr <= self_ptr, - * but unread_ptr can get ahead of self_ptr if the peer + * Generally, other_ptr <= unread_ptr <= self_ptr, + * but unread_ptr can get ahead of self_ptr if the peer * is running fast. */ size_t unread_ptr; /* Pointer to the next frame to read from each client */ size_t read_ptr[MAX_CLIENTS]; - /* Pointer to the next frame to read from the server + /* Pointer to the next frame to read from the server * (as it might not be a player but still synchronizes) */ size_t server_ptr; @@ -499,9 +525,6 @@ struct netplay /* Pseudo random seed */ unsigned long simple_rand_next; - /* TCP connection for listening (server only) */ - int listen_fd; - /* Our client number */ uint32_t self_client_num; @@ -516,15 +539,11 @@ struct netplay uint32_t client_devices[MAX_CLIENTS]; /* For each device, the bitmap of clients connected */ - client_bitmap_t device_clients[MAX_INPUT_DEVICES]; + uint32_t device_clients[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; - /* The device types for every connected device. * We store them and ignore any menu changes, * as netplay needs fixed devices. */ @@ -538,31 +557,46 @@ struct netplay uint32_t server_frame_count; uint32_t replay_frame_count; - int frame_run_time_ptr; - - /* Counter for timeouts */ - unsigned timeout_cnt; - - /* Latency frames; positive to hide network latency, - * negative to hide input latency */ - int input_latency_frames; - - /* Frequency with which to check CRCs */ - int check_frames; - /* How far behind did we fall? */ uint32_t catch_up_behind; + /* Number of desync operations we're currently performing. + * If set, we don't attempt to stay in sync. */ + uint32_t desync; + /* Host settings */ int32_t input_latency_frames_min; int32_t input_latency_frames_max; - /* Are we stalled? */ - enum rarch_netplay_stall_reason stall; + /* Counter for timeouts */ + unsigned timeout_cnt; + + /* TCP connection for listening (server only) */ + int listen_fd; + + int frame_run_time_ptr; + + /* Frequency with which to check CRCs */ + int check_frames; + + /* Latency frames; positive to hide network latency, + * negative to hide input latency */ + int input_latency_frames; + + int reannounce; + + int reping; /* Our mode and status */ enum rarch_netplay_connection_mode self_mode; + /* Are we stalled? */ + enum rarch_netplay_stall_reason stall; + + /* Keyboard mapping (network and host) */ + uint16_t mapping_hton[RETROK_LAST]; + uint16_t mapping_ntoh[NETPLAY_KEY_LAST]; + /* TCP port (only set if serving) */ uint16_t tcp_port; uint16_t ext_tcp_port; @@ -573,8 +607,6 @@ struct netplay /* Our nickname */ char nick[NETPLAY_NICK_LEN]; - bool nat_traversal; - /* 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, @@ -583,11 +615,30 @@ struct netplay * up/down states will proceed as expected. */ bool have_updown_device; + /* If true, never progress without peer input + * (stateless/rewindless mode) */ + bool stateless_mode; + + /* Are we the server? */ + bool is_server; + + bool nat_traversal; + + /* Have we checked whether CRCs are valid at all? */ + bool crc_validity_checked; + + /* Are they valid? */ + bool crcs_valid; + + /* Netplay pausing */ + bool local_paused; + bool remote_paused; + /* Are we replaying old frames? */ bool is_replay; - /* We don't want to poll several times on a frame. */ - bool can_poll; + /* Opposite of stalling, should we be catching up? */ + bool catch_up; /* Force a rewind to other_frame_count/other_ptr. * This is for synchronized events, such as restarting @@ -603,33 +654,17 @@ struct netplay /* Have we requested a savestate as a sync point? */ bool savestate_request_outstanding; - /* Netplay pausing */ - bool local_paused; - bool remote_paused; - - /* If true, never progress without peer input - * (stateless/rewindless mode) */ - bool stateless_mode; - - /* Opposite of stalling, should we be catching up? */ - bool catch_up; - - /* Have we checked whether CRCs are valid at all? */ - bool crc_validity_checked; - - /* Are they valid? */ - bool crcs_valid; - - /* Are we the server? */ - bool is_server; - - /* Are we the connected? */ - bool is_connected; - /* Host settings */ bool allow_pausing; }; +void video_frame_net(const void *data, + unsigned width, unsigned height, size_t pitch); +void audio_sample_net(int16_t left, int16_t right); +size_t audio_sample_batch_net(const int16_t *data, size_t frames); +int16_t input_state_net(unsigned port, unsigned device, + unsigned idx, unsigned id); + /*************************************************************** * NETPLAY-BUF.C **************************************************************/ @@ -699,69 +734,22 @@ bool netplay_delta_frame_ready(netplay_t *netplay, struct delta_frame *delta, uint32_t frame); -/** - * 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-FRONTEND.C **************************************************************/ /** * input_poll_net + * @netplay : pointer to netplay object * * Poll the network if necessary. */ -void input_poll_net(void); - -/*************************************************************** - * NETPLAY-HANDSHAKE.C - **************************************************************/ - -/** - * netplay_handshake_init_send - * - * Initialize our handshake and send the first - * part of the handshake protocol. - */ -bool netplay_handshake_init_send(netplay_t *netplay, - struct netplay_connection *connection, uint32_t protocol); - -/** - * netplay_handshake - * - * Data receiver for all handshake states. - */ -bool netplay_handshake(netplay_t *netplay, - struct netplay_connection *connection, bool *had_input); +void input_poll_net(netplay_t *netplay); /*************************************************************** * NETPLAY-INIT.C **************************************************************/ -/** - * netplay_try_init_serialization - * - * Try to initialize serialization. For quirky cores. - * - * Returns true if serialization is now ready, false otherwise. - */ -bool netplay_try_init_serialization(netplay_t *netplay); - /** * netplay_wait_and_init_serialization * @@ -772,59 +760,10 @@ bool netplay_try_init_serialization(netplay_t *netplay); */ bool netplay_wait_and_init_serialization(netplay_t *netplay); -/** - * netplay_new: - * @server : IP address of server. - * @mitm : IP address of the MITM/tunnel server. - * @port : Port of server. - * @mitm_session : Session id for MITM/tunnel. - * @stateless_mode : Shall we run in stateless mode? - * @check_frames : Frequency with which to check CRCs. - * @cb : Libretro callbacks. - * @nat_traversal : If true, attempt NAT traversal. - * @nick : Nickname of user. - * @quirks : Netplay quirks required for this session. - * - * Creates a new netplay handle. A NULL server means we're - * hosting. - * - * Returns: new netplay data. - */ -netplay_t *netplay_new(const char *server, const char *mitm, uint16_t port, - const char *mitm_session, - bool stateless_mode, int check_frames, - const struct retro_callbacks *cb, - bool nat_traversal, const char *nick, - uint64_t quirks); - -/** - * netplay_free - * @netplay : pointer to netplay object - * - * Frees netplay data/ - */ -void netplay_free(netplay_t *netplay); - /*************************************************************** * NETPLAY-IO.C **************************************************************/ -/** - * netplay_hangup: - * - * Disconnects an active Netplay connection due to an error - */ -void netplay_hangup(netplay_t *netplay, - struct netplay_connection *connection); - -/** - * netplay_delayed_state_change: - * - * Handle any pending state changes which are ready as - * of the beginning of the current frame. - */ -void netplay_delayed_state_change(netplay_t *netplay); - /** * netplay_send_cur_input * @@ -866,85 +805,20 @@ void netplay_send_raw_cmd_all(netplay_t *netplay, bool netplay_cmd_mode(netplay_t *netplay, enum rarch_netplay_connection_mode mode); -/** - * netplay_poll_net_input - * - * Poll input from the network - */ -int netplay_poll_net_input(netplay_t *netplay, bool block); - -/** - * netplay_handle_slaves - * - * Handle any slave connections - */ -void netplay_handle_slaves(netplay_t *netplay); - -/** - * netplay_init_nat_traversal - * - * Initialize the NAT traversal library and try to open a port - */ -void netplay_init_nat_traversal(netplay_t *netplay); - -void netplay_deinit_nat_traversal(void); - -/*************************************************************** - * NETPLAY-KEYBOARD.C - **************************************************************/ - -/* 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 **************************************************************/ /** - * netplay_update_unread_ptr - * - * Update the global unread_ptr and unread_frame_count to - * correspond to the earliest unread frame count of any - * connected player. - */ -void netplay_update_unread_ptr(netplay_t *netplay); - -/** - * 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. - */ -bool netplay_resolve_input(netplay_t *netplay, - size_t sim_ptr, bool resim); - -/** - * netplay_sync_pre_frame + * netplay_load_savestate * @netplay : pointer to netplay object - * @disconnect : disconnect netplay + * @serial_info : the savestate being loaded, NULL means + * "load it yourself" + * @save : Whether to save the provided serial_info + * into the frame buffer * - * Pre-frame for Netplay synchronization. - */ -bool netplay_sync_pre_frame(netplay_t *netplay, bool *disconnect); - -/** - * netplay_sync_post_frame - * @netplay : pointer to netplay object - * @stalled : true if we're currently stalled - * - * Post-frame for Netplay synchronization. - * We check if we have new input and replay from recorded input. - */ -void netplay_sync_post_frame(netplay_t *netplay, bool stalled); - + * Inform Netplay of a savestate load and send it to the other side + **/ +void netplay_load_savestate(netplay_t *netplay, + retro_ctx_serialize_info_t *serial_info, bool save); #endif