diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 203b9c2b0a..5b7b11f03b 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -115,12 +115,13 @@ static const char *AdhocDataModeToString(AdhocDataMode mode) { return "unknown"; } -// TODO download the list from somewhere and probably cache it on disk -// should also make the url configurable and support file:/ local list besides https:// online lists +// We download the list and cache it on disk. +// The URL can also be a local file path, in which case the download doesn't happen. This is only +// for power users / debugging. std::mutex g_proAdhocServerListMutex; std::vector g_proAdhocServerList; -bool ParseServerListEntriesJSON(std::string_view json) { +static bool ParseServerListEntriesJSON(std::string_view json) { using namespace json; // Use our JSONReader to parse json into a list of AdhocServerListEntry. @@ -148,6 +149,7 @@ bool ParseServerListEntriesJSON(std::string_view json) { entry.location = server.getStringOr("location", ""); entry.description = server.getStringOr("description", ""); entry.mode = equals(server.getStringOr("data_mode", ""), "AemuPostoffice") ? AdhocDataMode::AemuPostoffice : AdhocDataMode::P2P; + entry.statusUrl = server.getStringOr("status_url", ""); if (entry.host.empty()) { // Skipping invalid entry. @@ -178,7 +180,7 @@ static void LoadFallbackServerList() { g_serverListLoaded = true; } -void AdhocLoadServerList(bool sync) { +void AdhocLoadServerList(AdhocLoadListMode loadMode) { { std::lock_guard guard(g_proAdhocServerListMutex); if (!g_proAdhocServerList.empty()) { @@ -190,6 +192,19 @@ void AdhocLoadServerList(bool sync) { // There's currently no file:// support, as far as I remember. if (startsWith(g_Config.sAdhocServerListUrl, "http")) { + if (loadMode == AdhocLoadListMode::CacheOnlySync) { + std::string json; + if (g_DownloadManager.ReadFileFromCache(g_Config.sAdhocServerListUrl, &json)) { + if (ParseServerListEntriesJSON(std::string_view(json.data(), json.size()))) { + g_serverListLoaded = true; + return; + } + } + ERROR_LOG(Log::sceNet, "Failed to load cached adhoc server list %s from cache, falling back.", g_Config.sAdhocServerListUrl.c_str()); + LoadFallbackServerList(); + return; + } + // Download the list. auto dl = g_DownloadManager.StartDownload(g_Config.sAdhocServerListUrl, Path(), http::RequestFlags::Cached24H, nullptr, "adhoc-servers", [url = g_Config.sAdhocServerListUrl](http::Request &request) { if (request.Failed()) { @@ -207,15 +222,6 @@ void AdhocLoadServerList(bool sync) { return; } }); - - if (sync) { - // Unfortunate. - do { - sleep_ms(10, "waiting-for-adhoc-server-list"); - g_DownloadManager.Update(); - } while (!dl->HasRunCallback()); - dl.reset(); - } g_serverListLoaded = true; } else if (!g_Config.sAdhocServerListUrl.empty()) { // Try to read local file. @@ -234,11 +240,11 @@ void AdhocLoadServerList(bool sync) { } } -std::vector AdhocGetServerList(bool sync) { +std::vector AdhocGetServerList(AdhocLoadListMode loadMode) { if (!g_serverListLoaded) { - // In-game - unfortunately we have to do a synchronous load here. - // If cached it will be fast though. - AdhocLoadServerList(sync); + // We're probably in-game - so we don't want to do a download and risk blocking. + // Instead we read out of cache, it should be good enough. + AdhocLoadServerList(loadMode); } std::lock_guard guard(g_proAdhocServerListMutex); @@ -246,7 +252,7 @@ std::vector AdhocGetServerList(bool sync) { } static AdhocDataMode AdhocGetServerDataMode(std::string_view server) { - std::vector list = AdhocGetServerList(true); + std::vector list = AdhocGetServerList(AdhocLoadListMode::CacheOnlySync); for (const auto &item : list) { if (equals(server, item.host)) { INFO_LOG(Log::sceNet, "server %.*s is in known list, using data mode %s", STR_VIEW(server), AdhocDataModeToString(item.mode)); @@ -254,6 +260,20 @@ static AdhocDataMode AdhocGetServerDataMode(std::string_view server) { } } + for (const auto &item : g_Config.vCustomAdhocServerList) { + if (equals(server, item)) { + INFO_LOG(Log::sceNet, "server %.*s is in custom list without relay, using data mode %s", STR_VIEW(server), AdhocDataModeToString(AdhocDataMode::P2P)); + return AdhocDataMode::P2P; + } + } + + for (const auto &item : g_Config.vCustomAdhocServerListWithRelay) { + if (equals(server, item)) { + INFO_LOG(Log::sceNet, "server %.*s is in custom list with relay, using data mode %s", STR_VIEW(server), AdhocDataModeToString(AdhocDataMode::AemuPostoffice)); + return AdhocDataMode::AemuPostoffice; + } + } + INFO_LOG(Log::sceNet, "server %.*s is not in known list, using data mode %s", STR_VIEW(server), AdhocDataModeToString(AdhocDataMode::P2P)); return AdhocDataMode::P2P; } diff --git a/Core/HLE/sceNetAdhoc.h b/Core/HLE/sceNetAdhoc.h index 29a5178b59..95ce139e2f 100644 --- a/Core/HLE/sceNetAdhoc.h +++ b/Core/HLE/sceNetAdhoc.h @@ -111,11 +111,17 @@ struct AdhocServerListEntry { std::string discord; std::string location; std::string description; + std::string statusUrl; // Usually empty or /data.json. AdhocDataMode mode = AdhocDataMode::P2P; }; -void AdhocLoadServerList(bool sync = false); -std::vector AdhocGetServerList(bool sync = false); +enum class AdhocLoadListMode { + CacheOnlySync, + AllSourcesAsync, +}; + +void AdhocLoadServerList(AdhocLoadListMode loadMode); +std::vector AdhocGetServerList(AdhocLoadListMode loadMode); class PointerWrap; diff --git a/UI/AdhocServerScreen.cpp b/UI/AdhocServerScreen.cpp index 06710671f0..e8ab045058 100644 --- a/UI/AdhocServerScreen.cpp +++ b/UI/AdhocServerScreen.cpp @@ -164,7 +164,7 @@ AdhocServerScreen::AdhocServerScreen(std::string *value, std::string_view title) thiz->ResolverThread(); }, this); editValue_ = *value; - AdhocLoadServerList(); + AdhocLoadServerList(AdhocLoadListMode::AllSourcesAsync); } AdhocServerScreen::~AdhocServerScreen() { @@ -188,7 +188,9 @@ void AdhocServerScreen::CreatePopupContents(UI::ViewGroup *parent) { auto di = GetI18NCategory(I18NCat::DIALOG); auto n = GetI18NCategory(I18NCat::NETWORKING); - auto listItems = AdhocGetServerList(); + // We already kicked off loading the server list in the constructor, so by now it should be either loaded or in the process of loading. + // We can get the cached list immediately, and then update it when the async load finishes. + auto listItems = AdhocGetServerList(AdhocLoadListMode::CacheOnlySync); PopupTextInputChoice *textInputChoice = parent->Add(new PopupTextInputChoice(GetRequesterToken(), &editValue_, n->T("Hostname"), "", 450, screenManager())); parent->Add(new Spacer(5.0f));