From 1a95e9e4e9ad3b2953c50618570f9c9908ff7f2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 5 Mar 2026 23:51:42 +0100 Subject: [PATCH] Fetch the public adhoc server list from metadata.ppsspp.org/adhoc-servers.json --- Common/Net/HTTPRequest.cpp | 23 ++------------ Common/Net/HTTPRequest.h | 10 +------ Core/Config.cpp | 7 +++-- Core/Config.h | 2 ++ Core/HLE/sceNetAdhoc.cpp | 61 ++++++++++++++++++++++++++++---------- Core/RetroAchievements.cpp | 7 +++-- UI/AdhocServerScreen.cpp | 2 +- UI/Store.cpp | 31 ++++++++----------- 8 files changed, 74 insertions(+), 69 deletions(-) diff --git a/Common/Net/HTTPRequest.cpp b/Common/Net/HTTPRequest.cpp index b956d16b76..fbd4a1c4df 100644 --- a/Common/Net/HTTPRequest.cpp +++ b/Common/Net/HTTPRequest.cpp @@ -71,7 +71,7 @@ static std::shared_ptr CreateRequest(RequestMethod method, std::string_ } } -std::shared_ptr RequestManager::StartDownload(std::string_view url, const Path &outfile, RequestFlags flags, const char *acceptMime) { +std::shared_ptr RequestManager::StartDownload(std::string_view url, const Path &outfile, RequestFlags flags, const char *acceptMime, std::string_view name, std::function callback) { const bool enableCache = !cacheDir_.empty() && (flags & RequestFlags::Cached24H); // Come up with a cache file path. @@ -93,6 +93,7 @@ std::shared_ptr RequestManager::StartDownload(std::string_view url, con // All is well, but we've indented a bit much here. std::shared_ptr dl(new CachedRequest(RequestMethod::GET, url, KeepAfterLast(url, '/'), nullptr, flags, contents)); newDownloads_.push_back(dl); + dl->SetCallback(callback); return dl; } else { INFO_LOG(Log::sceNet, "Failed reading from cache, proceeding with request"); @@ -105,7 +106,7 @@ std::shared_ptr RequestManager::StartDownload(std::string_view url, con } } - std::shared_ptr dl = CreateRequest(RequestMethod::GET, url, "", "", outfile, flags, nullptr, ""); + std::shared_ptr dl = CreateRequest(RequestMethod::GET, url, "", "", outfile, flags, nullptr, name); // OK, didn't get it from cache, so let's continue with the download, putting it in the cache. if (enableCache) { @@ -119,24 +120,6 @@ std::shared_ptr RequestManager::StartDownload(std::string_view url, con if (acceptMime) { dl->SetAccept(acceptMime); } - newDownloads_.push_back(dl); - dl->Start(); - return dl; -} - -std::shared_ptr RequestManager::StartDownloadWithCallback( - std::string_view url, - const Path &outfile, - RequestFlags flags, - std::function callback, - std::string_view name, - const char *acceptMime) { - std::shared_ptr dl = CreateRequest(RequestMethod::GET, url, "", "", outfile, flags, nullptr, name); - - if (!userAgent_.empty()) - dl->SetUserAgent(userAgent_); - if (acceptMime) - dl->SetAccept(acceptMime); dl->SetCallback(callback); newDownloads_.push_back(dl); dl->Start(); diff --git a/Common/Net/HTTPRequest.h b/Common/Net/HTTPRequest.h index 31867eb4a4..708f6c1b1e 100644 --- a/Common/Net/HTTPRequest.h +++ b/Common/Net/HTTPRequest.h @@ -105,15 +105,7 @@ public: } // NOTE: This is the only version that supports the cache flag (for now). - std::shared_ptr StartDownload(std::string_view url, const Path &outfile, RequestFlags flags, const char *acceptMime = nullptr); - - std::shared_ptr StartDownloadWithCallback( - std::string_view url, - const Path &outfile, - RequestFlags flags, - std::function callback, - std::string_view name = "", - const char *acceptMime = nullptr); + std::shared_ptr StartDownload(std::string_view url, const Path &outfile, RequestFlags flags, const char *acceptMime = nullptr, std::string_view name = "", std::function callback = {}); std::shared_ptr AsyncPostWithCallback( std::string_view url, diff --git a/Core/Config.cpp b/Core/Config.cpp index e66c0aed3a..8492b2a528 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -1027,6 +1027,8 @@ static const ConfigSetting controlSettings[] = { ConfigSetting("RapidFileInterval", SETTING(g_Config, iRapidFireInterval), 5, CfgFlag::DEFAULT), }; +static const std::vector emptyList; + static const ConfigSetting networkSettings[] = { ConfigSetting("EnableWlan", SETTING(g_Config, bEnableWlan), false, CfgFlag::PER_GAME), ConfigSetting("EnableAdhocServer", SETTING(g_Config, bEnableAdhocServer), false, CfgFlag::PER_GAME), @@ -1043,7 +1045,8 @@ static const ConfigSetting networkSettings[] = { ConfigSetting("AllowSavestateWhileConnected", SETTING(g_Config, bAllowSavestateWhileConnected), false, CfgFlag::DONT_SAVE), ConfigSetting("AllowSpeedControlWhileConnected", SETTING(g_Config, bAllowSpeedControlWhileConnected), false, CfgFlag::PER_GAME), ConfigSetting("DontDownloadInfraJson", SETTING(g_Config, bDontDownloadInfraJson), false, CfgFlag::DONT_SAVE), - ConfigSetting("proAdhocServerList", SETTING(g_Config, vCustomAdhocServerList), &std::vector(), CfgFlag::DEFAULT), // Customizable server list. + ConfigSetting("proAdhocServerList", SETTING(g_Config, vCustomAdhocServerList), &emptyList, CfgFlag::DEFAULT), // Customizable server list. + ConfigSetting("AdhocServerListUrl", SETTING(g_Config, sAdhocServerListUrl), "http://metadata.ppsspp.org/adhoc-servers.json", CfgFlag::DEFAULT), // URL for the server list. Can be set to a local path too. ConfigSetting("EnableNetworkChat", SETTING(g_Config, bEnableNetworkChat), false, CfgFlag::PER_GAME), ConfigSetting("ChatButtonPosition", SETTING(g_Config, iChatButtonPosition), (int)ScreenEdgePosition::BOTTOM_LEFT, CfgFlag::PER_GAME), ConfigSetting("ChatScreenPosition", SETTING(g_Config, iChatScreenPosition), (int)ScreenEdgePosition::BOTTOM_LEFT, CfgFlag::PER_GAME), @@ -1658,7 +1661,7 @@ void Config::CheckForUpdate() { if (checkThisTime) { const char *versionUrl = "http://www.ppsspp.org/version.json"; const char *acceptMime = "application/json, text/*; q=0.9, */*; q=0.8"; - g_DownloadManager.StartDownloadWithCallback(versionUrl, Path(), http::RequestFlags::Default, [this](http::Request &download) { VersionJsonDownloadCompleted(download); }, "version", acceptMime); + g_DownloadManager.StartDownload(versionUrl, Path(), http::RequestFlags::Default, acceptMime, "version", [this](http::Request &download) { VersionJsonDownloadCompleted(download); }); } } diff --git a/Core/Config.h b/Core/Config.h index 4cd0342b64..6723e716b6 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -560,6 +560,8 @@ public: bool bInfrastructureAutoDNS; bool bAllowSavestateWhileConnected; // Developer option, ini-only. No normal users need this, it's always wrong to save/load state when online. bool bAllowSpeedControlWhileConnected; // Useful in some games but not recommended. + + std::string sAdhocServerListUrl; std::vector vCustomAdhocServerList; bool bEnableWlan; diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 4c9eb851f8..216e45d623 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -26,6 +26,7 @@ #include "Common/Net/SocketCompat.h" #include "Common/Data/Format/JSONReader.h" #include "Common/Data/Text/I18n.h" +#include "Common/File/FileUtil.h" #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" #include "Common/Serialize/SerializeMap.h" @@ -34,6 +35,7 @@ #include "Common/File/VFS/VFS.h" #include "Common/Thread/ThreadUtil.h" #include "Common/TimeUtil.h" +#include "Common/Net/HTTPRequest.h" #include "Core/MIPS/MIPSCodeUtils.h" #include "Core/Util/PortManager.h" @@ -117,7 +119,7 @@ static const char *AdhocDataModeToString(AdhocDataMode mode) { std::mutex g_proAdhocServerListMutex; std::vector g_proAdhocServerList; -bool ParseServerListEntriesJSON(std::string_view json, std::vector *result) { +bool ParseServerListEntriesJSON(std::string_view json) { using namespace json; // Use our JSONReader to parse json into a list of AdhocServerListEntry. @@ -132,7 +134,7 @@ bool ParseServerListEntriesJSON(std::string_view json, std::vectorclear(); + std::vector newList; for (const JsonNode *iter : servers->value) { JsonGet server = iter->value; @@ -149,8 +151,14 @@ bool ParseServerListEntriesJSON(std::string_view json, std::vectorpush_back(entry); + newList.push_back(entry); } + + { + std::lock_guard guard(g_proAdhocServerListMutex); + g_proAdhocServerList = newList; + } + System_PostUIMessage(UIMessage::ADHOC_SERVER_LIST_CHANGED); return true; } @@ -162,14 +170,39 @@ void AdhocLoadServerList() { } } - // Do this async. - std::thread thread([]() { - // Pretend for now, actually load later. - // We'll first load the default hardcoded list. Then we try: - // 1. Cached list in PSP/SYSTEM/CACHE - // 2. Online list from some url, and cache it in PSP/SYSTEM/CACHE for next time. - std::lock_guard guard(g_proAdhocServerListMutex); + // NOTE: If you want to load from a local file, set g_Config.sAdhocServerListUrl directly to the local path. + // There's currently no file:// support, as far as I remember. + if (startsWith(g_Config.sAdhocServerListUrl, "http")) { + // Download the list. + g_DownloadManager.StartDownload(g_Config.sAdhocServerListUrl, Path(), http::RequestFlags::Cached24H, nullptr, "adhoc-servers", [url = g_Config.sAdhocServerListUrl](http::Request &request) { + if (request.Failed()) { + INFO_LOG(Log::sceNet, "Failed to download adhoc server list from %s, falling back.", url.c_str()); + + // Fall back to the asset. Don't bother doing it on a thread. + size_t jsonSize; + std::unique_ptr jsonStr(g_VFS.ReadFile("adhoc-servers.json", &jsonSize)); + if (!jsonStr) { + _dbg_assert_(false); + // Something went wrong. This shouldn't happen. + return; + } + ParseServerListEntriesJSON(std::string_view((char*)jsonStr.get(), jsonSize)); + return; + } + + INFO_LOG(Log::sceNet, "Successfully downloaded adhoc server list from %s", url.c_str()); + + std::string json; + request.buffer().TakeAll(&json); + ParseServerListEntriesJSON(std::string_view(json.data(), json.size())); + }); + } else if (!g_Config.sAdhocServerListUrl.empty()) { + // Try to read local file. + std::string json; + File::ReadTextFileToString(Path(g_Config.sAdhocServerListUrl), &json); + ParseServerListEntriesJSON(std::string_view(json.data(), json.size())); + } else { size_t jsonSize; std::unique_ptr jsonStr(g_VFS.ReadFile("adhoc-servers.json", &jsonSize)); if (!jsonStr) { @@ -177,12 +210,8 @@ void AdhocLoadServerList() { // Something went wrong. This shouldn't happen. return; } - - ParseServerListEntriesJSON(std::string_view((char*)jsonStr.get(), jsonSize), &g_proAdhocServerList); - - System_PostUIMessage(UIMessage::ADHOC_SERVER_LIST_CHANGED); - }); - thread.detach(); + ParseServerListEntriesJSON(std::string_view((char*)jsonStr.get(), jsonSize)); + } } std::vector AdhocGetServerList() { diff --git a/Core/RetroAchievements.cpp b/Core/RetroAchievements.cpp index 09c1a628a7..1f30c0cb8c 100644 --- a/Core/RetroAchievements.cpp +++ b/Core/RetroAchievements.cpp @@ -298,7 +298,8 @@ static void server_call_callback(const rc_api_request_t *request, callback(&response, callback_data); }, ac->T("Contacting RetroAchievements server...")); } else { - std::shared_ptr download = g_DownloadManager.StartDownloadWithCallback(std::string(request->url), Path(), http::RequestFlags::ProgressBar | http::RequestFlags::ProgressBarDelayed, + std::shared_ptr download = g_DownloadManager.StartDownload(std::string(request->url), Path(), http::RequestFlags::ProgressBar | http::RequestFlags::ProgressBarDelayed, nullptr, + ac->T("Contacting RetroAchievements server..."), [callback, callback_data](http::Request &download) { std::string buffer; download.buffer().TakeAll(&buffer); @@ -307,7 +308,7 @@ static void server_call_callback(const rc_api_request_t *request, response.body_length = buffer.size(); response.http_status_code = download.ResultCode(); callback(&response, callback_data); - }, ac->T("Contacting RetroAchievements server...")); + }); } } @@ -909,7 +910,7 @@ bool HasAchievementsOrLeaderboards() { void DownloadImageIfMissing(std::string_view url) { if (g_iconCache.MarkPending(url)) { INFO_LOG(Log::Achievements, "Downloading image: %.*s", STR_VIEW(url)); - g_DownloadManager.StartDownloadWithCallback(url, Path(), http::RequestFlags::Default, [](http::Request &download) { + g_DownloadManager.StartDownload(url, Path(), http::RequestFlags::Default, nullptr, "", [](http::Request &download) { if (download.ResultCode() != 200) return; std::string data; diff --git a/UI/AdhocServerScreen.cpp b/UI/AdhocServerScreen.cpp index 5a19269d29..cfa375fb89 100644 --- a/UI/AdhocServerScreen.cpp +++ b/UI/AdhocServerScreen.cpp @@ -119,7 +119,7 @@ void AdhocServerScreen::CreatePopupContents(UI::ViewGroup *parent) { AddButtonFromEntry(publicSection, entry); } - CollapsibleSection *localSection = innerView->Add(new CollapsibleSection(n->T("Local addresses"), new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); + CollapsibleSection *localSection = innerView->Add(new CollapsibleSection(n->T("Local network addresses"), new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT))); for (const auto &entry : localEntries) { AddButtonFromEntry(localSection, entry); } diff --git a/UI/Store.cpp b/UI/Store.cpp index 53b755f7cb..b84ec6f8d7 100644 --- a/UI/Store.cpp +++ b/UI/Store.cpp @@ -64,7 +64,7 @@ public: if (useIconCache && g_iconCache.MarkPending(path_)) { const char *acceptMime = "image/png, image/jpeg, image/*; q=0.9, */*; q=0.8"; - requestManager_->StartDownloadWithCallback(path_, Path(), http::RequestFlags::ProgressBar | http::RequestFlags::ProgressBarDelayed, [](http::Request &download) { + requestManager_->StartDownload(path_, Path(), http::RequestFlags::ProgressBar | http::RequestFlags::ProgressBarDelayed, acceptMime, "", [](http::Request &download) { // Can't touch 'this' in this function! Don't use captures! std::string path = download.url(); if (download.ResultCode() == 200) { @@ -78,7 +78,7 @@ public: } else { g_iconCache.CancelPending(path); } - }, acceptMime); + }); } } @@ -102,8 +102,6 @@ public: const std::string &GetFilename() const { return path_; } private: - void DownloadCompletedCallback(http::Request &download); - bool canFocus_ = false; bool useIconCache_ = false; std::string path_; // or cache key @@ -161,26 +159,23 @@ void HttpImageFileView::SetFilename(const std::string &filename) { } } -void HttpImageFileView::DownloadCompletedCallback(http::Request &download) { - if (download.IsCancelled()) { - // We were probably destroyed. Can't touch "this" (heh). - return; - } - if (download.ResultCode() == 200) { - download.buffer().TakeAll(&textureData_); - } else { - textureFailed_ = true; - } -} - void HttpImageFileView::Draw(UIContext &dc) { using namespace Draw; if (!useIconCache_) { if (!texture_ && !textureFailed_ && !path_.empty() && !download_) { - auto cb = std::bind(&HttpImageFileView::DownloadCompletedCallback, this, std::placeholders::_1); const char *acceptMime = "image/png, image/jpeg, image/*; q=0.9, */*; q=0.8"; - requestManager_->StartDownloadWithCallback(path_, Path(), http::RequestFlags::Default, cb, acceptMime); + requestManager_->StartDownload(path_, Path(), http::RequestFlags::Default, acceptMime, "", [this](http::Request &download) { + if (download.IsCancelled()) { + // We were probably destroyed. Can't touch "this" (heh). + return; + } + if (download.ResultCode() == 200) { + download.buffer().TakeAll(&textureData_); + } else { + textureFailed_ = true; + } + }); } if (!textureData_.empty()) {