From 7ec860c24f56d76477e2b3abb139844214655b20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 29 Sep 2026 12:37:48 -0600 Subject: [PATCH] Adhoc: Stop waiting for a login that has already failed sceNetAdhocctlInit waits until the friend finder has logged into the adhoc server, polling in emulated time but giving up only after 5s of wall time. The friend finder makes one attempt per login request, and it too waited the full 5s on a connection that had already been refused, because it only looked for success. Now it checks the socket error and gives up at once, and records the failure so the wait ends with it. In headless, which runs far ahead of real time, Gods Eater Burst sat in this wait for its whole run. Co-Authored-By: Claude Opus 5.5 (1M context) --- Core/HLE/NetAdhocCommon.cpp | 1 + Core/HLE/NetAdhocCommon.h | 4 ++++ Core/HLE/proAdhoc.cpp | 11 +++++++++++ Core/HLE/sceNetAdhoc.cpp | 6 +++++- 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Core/HLE/NetAdhocCommon.cpp b/Core/HLE/NetAdhocCommon.cpp index 242735a3c6..9b5ea7a63c 100644 --- a/Core/HLE/NetAdhocCommon.cpp +++ b/Core/HLE/NetAdhocCommon.cpp @@ -45,3 +45,4 @@ u32 matchingThreadHackAddr = 0; u32_le matchingThreadCode[3]; bool g_adhocServerConnected = false; +std::atomic g_adhocServerLoginFailed(false); diff --git a/Core/HLE/NetAdhocCommon.h b/Core/HLE/NetAdhocCommon.h index 1e0b532b06..125f492764 100644 --- a/Core/HLE/NetAdhocCommon.h +++ b/Core/HLE/NetAdhocCommon.h @@ -20,6 +20,7 @@ #include "CommonTypes.h" #include "Swap.h" +#include #include extern u32 dummyThreadHackAddr; @@ -52,5 +53,8 @@ extern u32 matchingThreadHackAddr; extern u32_le matchingThreadCode[3]; extern bool g_adhocServerConnected; +// Set by the friend finder when its attempt to connect and log in for the latest login request +// failed, so a caller waiting on that login can stop waiting. +extern std::atomic g_adhocServerLoginFailed; constexpr u32 defaultLastRecvDelta = 10000; //10000 usec worked well for games published by Falcom (ie. Ys vs Sora Kiseki, Vantage Master Portable) diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index 802e2baf31..1ed893dda8 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -1420,6 +1420,7 @@ int friendFinder() { } else { g_adhocServerConnected = false; + g_adhocServerLoginFailed = true; shutdown((int)metasocket, SD_BOTH); closesocket((int)metasocket); metasocket = (int)INVALID_SOCKET; @@ -2251,6 +2252,16 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ return iResult; done = (IsSocketReady((int)metasocket, false, true) > 0); + if (done) { + // Writable can also mean the attempt failed (refused, say). Then there's no point + // waiting out the timeout. + int soError = 0; + socklen_t soErrorLen = sizeof(soError); + if (getsockopt((int)metasocket, SOL_SOCKET, SO_ERROR, (char *)&soError, &soErrorLen) == 0 && soError != 0) { + errorcode = soError; + break; + } + } struct sockaddr_in sin; socklen_t sinlen = sizeof(sin); memset(&sin, 0, sinlen); diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 6dc1c26967..74e475c577 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -560,7 +560,8 @@ static void __AdhocctlNotify(u64 userdata, int cyclesLate) { } // Retry until successfully sent. Login packet sent after successfully connected to Adhoc Server (indicated by networkInited), so we're not sending Login again here - if ((req.opcode == OPCODE_LOGIN && !g_adhocServerConnected) || (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK))) { + // A login stops waiting early once the friend finder has tried and failed to connect. + if ((req.opcode == OPCODE_LOGIN && !g_adhocServerConnected && !g_adhocServerLoginFailed) || (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK))) { u64 now = (u64)(time_now_d() * 1000000.0); if (now - adhocctlStartTime <= static_cast(adhocDefaultTimeout) + 500) { // Try again in another 0.5ms until timedout. @@ -2001,6 +2002,7 @@ int sceNetAdhocctlInit(int stackSize, int prio, u32 productAddr) { adhocctlEvents.clear(); netAdhocctlInited = true; //needed for cleanup during AdhocctlTerm even when it failed to connect to Adhoc Server (since it's being faked as success) + g_adhocServerLoginFailed = false; isAdhocctlNeedLogin = true; // Create fake PSP Thread for callback @@ -3191,6 +3193,7 @@ int sceNetAdhocctlScan() { // Only scan when in Disconnected state, otherwise AdhocServer will kick you out if (adhocctlState == ADHOCCTL_STATE_DISCONNECTED && !isAdhocctlBusy) { isAdhocctlBusy = true; + g_adhocServerLoginFailed = false; isAdhocctlNeedLogin = true; adhocctlState = ADHOCCTL_STATE_SCANNING; adhocctlCurrentMode = ADHOCCTL_MODE_NORMAL; @@ -3684,6 +3687,7 @@ int NetAdhocctl_Create(const char *groupName) { // Disconnected State if (adhocctlState == ADHOCCTL_STATE_DISCONNECTED && !isAdhocctlBusy) { isAdhocctlBusy = true; + g_adhocServerLoginFailed = false; isAdhocctlNeedLogin = true; // Set Network Name