diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index 56371b5264..30e6f79187 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -24,6 +24,7 @@ #include "ppsspp_config.h" #include +#include #include #include @@ -114,7 +115,6 @@ std::map localIpCache; SceNetAdhocctlParameter parameter; SceNetAdhocctlAdhocId product_code; -std::thread friendFinderThread; std::recursive_mutex peerlock; AdhocSocket* adhocSockets[MAX_SOCKET]; bool isOriPort = false; @@ -1392,104 +1392,58 @@ static bool should_abort_adhocctl_connection(void *_arg) { return !friendFinderRunning; } -// TODO: We should probably change this thread into PSPThread (or merging it into the existing AdhocThread PSPThread) as there are too many global vars being used here which also being used within some HLEs -int friendFinder() { - SetCurrentThreadName("FriendFinder"); - auto n = GetI18NCategory(I18NCat::NETWORKING); +// Resolving the server and logging in can take seconds, so they run on a thread of their own. +// friendFinderPoll() starts it, and takes over the session once it's done. +static std::thread adhocctlLoginThread; +static std::atomic adhocctlLoginDone(false); +// Written by the login thread before it sets adhocctlLoginDone. +static void *adhocctlLoginHandle = nullptr; +static bool adhocctlLoginRunning = false; +static bool adhocServerResolved = false; +// Whether the client has logged in since friendFinderStart(), so the next login is after a lost connection. +static bool adhocctlLoggedIn = false; +// Guarded by adhocctlMutex, since the keepalive thread pings too. +static uint64_t lastAdhocctlPing = 0; - // Chat Packet - SceNetAdhocctlChatPacketC2S chat; - chat.base.opcode = OPCODE_CHAT; +// The poll only runs while emulation does, so while it's paused (the pause menu, a debugger) this +// keeps the server from dropping the client, as the old friendFinder thread did. +static std::thread adhocctlKeepaliveThread; +static std::mutex adhocctlKeepaliveMutex; +static std::condition_variable adhocctlKeepaliveCond; +static bool adhocctlKeepaliveStop = false; - // Last Ping Time - uint64_t lastping = 0; - - // Last Time Reception got updated - uint64_t lastreceptionupdate = 0; - - uint64_t now; - - // Log Startup - INFO_LOG(Log::sceNet, "FriendFinder: Begin of Friend Finder Thread"); - - static const auto maintain_adhocctl_client = [] (SceNetAdhocctlAdhocId *adhoc_id) -> AdhocctlClientStatus { +static void AdhocctlKeepalive() { + SetCurrentThreadName("AdhocctlKeepalive"); + while (true) { { - const std::lock_guard adhocctl_guard(adhocctlMutex); - if (adhocctlHandle) { - return AdhocctlClientStatus::ALIVE; + std::unique_lock lock(adhocctlKeepaliveMutex); + adhocctlKeepaliveCond.wait_for(lock, std::chrono::milliseconds(500), [] { return adhocctlKeepaliveStop; }); + if (adhocctlKeepaliveStop) { + return; } } - - auto n = GetI18NCategory(I18NCat::NETWORKING); - int iResult = 0; - - auto server_ip = GetAdhocServerIP(); - - struct adhocctl_addr_v4 adhocctl_addr; - adhocctl_addr.ip = server_ip.in.sin_addr.s_addr; - adhocctl_addr.port = server_ip.in.sin_port; - - struct adhocctl_addr_v4 _adhocctl_bind_addr = {0}; - struct adhocctl_addr_v4 *adhocctl_bind_addr = NULL; - - // If Server is at localhost and we are in P2P mode, try to bind socket to specific adapter before connecting to prevent 2nd instance being recognized as already existing 127.0.0.1 by adhocctl server - // (may not works in WinXP/2003 for IPv4 due to "Weak End System" model) - if (isLocalServer && !serverHasRelay) { - _adhocctl_bind_addr.ip = g_localhostIP.in.sin_addr.s_addr; - _adhocctl_bind_addr.port = 0; - adhocctl_bind_addr = &_adhocctl_bind_addr; - } - - // Default/Initial Network Parameters - memset(¶meter, 0, sizeof(parameter)); - strncpy((char *)¶meter.nickname.data, g_Config.sNickName.c_str(), ADHOCCTL_NICKNAME_LEN); - parameter.nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; - parameter.channel = g_Config.iWlanAdhocChannel; - // Assign a Valid Channel when connected to AP/Adhoc if it's Auto. JPCSP use 11 as default for Auto (Commonly for Auto: 1, 6, 11) - if (parameter.channel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC) parameter.channel = defaultWlanChannel; // Faked Active channel to default channel - //getLocalMac(¶meter.bssid.mac_addr); - - // Default ProductId - product_code.type = adhoc_id->type; - memcpy(product_code.data, adhoc_id->data, ADHOCCTL_ADHOCID_LEN); - - // Don't need to connect if AdhocServer DNS was not resolved - if (server_ip.in.sin_addr.s_addr == INADDR_NONE) { - return AdhocctlClientStatus::DEAD; - } - - SceNetEtherAddr address; - getLocalMac(&address); - - // Connect to Adhoc Server - void *new_adhocctl_handle = create_adhocctl_v1_session_v4(&adhocctl_addr, adhocctl_bind_addr, (char *)product_code.data, (char *)parameter.nickname.data, (char *)address.data, netAdhocUseUdp, should_abort_adhocctl_connection, NULL); - - if (!new_adhocctl_handle) { - ERROR_LOG(Log::sceNet, "Failed connecting to adhocctl server [%s/%s:%u]", g_Config.sProAdhocServer.c_str(), ip2str(server_ip.in.sin_addr).c_str(), ntohs(server_ip.in.sin_port)); - g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("Failed to connect to Adhoc Server"))); - return AdhocctlClientStatus::DEAD; - } - const std::lock_guard adhocctl_guard(adhocctlMutex); - if (adhocctlHandle) { - // we have two friend finders?? DO NOT try to create adhocctl client outside of this lambda - ERROR_LOG(Log::sceNet, "%s: unreachable codepath at %s %d", __func__, __FILE__, __LINE__); - _dbg_assert_(false); - killAdhocctlClient(false, adhocctl_guard); + const uint64_t now = time_now_d() * 1000000.0; + // A failed ping is left for the poll to notice and handle. + if (adhocctlHandle && static_cast(now - lastAdhocctlPing) >= PSP_ADHOCCTL_PING_TIMEOUT && adhocctl_ping(adhocctlHandle) == ADHOCCTL_CALL_SUCCESS) { + lastAdhocctlPing = now; } - adhocctlHandle = new_adhocctl_handle; - int new_adhocctl_id = 2; - if (new_adhocctl_id == adhocctlId) { - new_adhocctl_id++; - } - adhocctlId = new_adhocctl_id; + } +} - // warm the local ip cache - sockaddr_in addr; - getLocalIp(&addr); +static void StopAdhocctlKeepalive() { + { + std::lock_guard lock(adhocctlKeepaliveMutex); + adhocctlKeepaliveStop = true; + } + adhocctlKeepaliveCond.notify_one(); + if (adhocctlKeepaliveThread.joinable()) { + adhocctlKeepaliveThread.join(); + } +} - return AdhocctlClientStatus::SPAWNED; - }; +static void ResolveAdhocServer() { + auto n = GetI18NCategory(I18NCat::NETWORKING); // Resolve and cache AdhocServer DNS addrinfo* resolved = nullptr; @@ -1518,101 +1472,215 @@ int friendFinder() { if (resolved) { clearLocalIpCache(); } +} - // Finder Loop. The flag was set by whoever started us, and cleared to stop us. - while (friendFinderRunning) { - // Acquire Network Lock - //_acquireNetworkLock(); +static void AdhocctlLogin(bool resolve, bool bind, struct adhocctl_addr_v4 bindAddr, SceNetAdhocctlAdhocId productCode, SceNetAdhocctlNickname nickname, SceNetEtherAddr mac, bool useUdp) { + SetCurrentThreadName("AdhocctlLogin"); + if (resolve) { + ResolveAdhocServer(); + } - // Reconnect when disconnected while Adhocctl is still inited + void *handle = nullptr; + auto server_ip = GetAdhocServerIP(); + // Don't need to connect if AdhocServer DNS was not resolved + if (server_ip.in.sin_addr.s_addr != INADDR_NONE) { + struct adhocctl_addr_v4 adhocctl_addr; + adhocctl_addr.ip = server_ip.in.sin_addr.s_addr; + adhocctl_addr.port = server_ip.in.sin_port; + + // Connect to Adhoc Server + handle = create_adhocctl_v1_session_v4(&adhocctl_addr, bind ? &bindAddr : NULL, (char *)productCode.data, (char *)nickname.data, (char *)mac.data, useUdp, should_abort_adhocctl_connection, NULL); + if (!handle && friendFinderRunning) { + auto n = GetI18NCategory(I18NCat::NETWORKING); + ERROR_LOG(Log::sceNet, "Failed connecting to adhocctl server [%s/%s:%u]", g_Config.sProAdhocServer.c_str(), ip2str(server_ip.in.sin_addr).c_str(), ntohs(server_ip.in.sin_port)); + g_OSD.Show(OSDType::MESSAGE_ERROR, std::string(n->T("Failed to connect to Adhoc Server"))); + } + } + + adhocctlLoginHandle = handle; + adhocctlLoginDone = true; +} + +static void StartAdhocctlLogin() { + // Default/Initial Network Parameters + memset(¶meter, 0, sizeof(parameter)); + strncpy((char *)¶meter.nickname.data, g_Config.sNickName.c_str(), ADHOCCTL_NICKNAME_LEN); + parameter.nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; + parameter.channel = g_Config.iWlanAdhocChannel; + // Assign a Valid Channel when connected to AP/Adhoc if it's Auto. JPCSP use 11 as default for Auto (Commonly for Auto: 1, 6, 11) + if (parameter.channel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC) parameter.channel = defaultWlanChannel; // Faked Active channel to default channel + + // If Server is at localhost and we are in P2P mode, try to bind socket to specific adapter before connecting to prevent 2nd instance being recognized as already existing 127.0.0.1 by adhocctl server + // (may not works in WinXP/2003 for IPv4 due to "Weak End System" model) + struct adhocctl_addr_v4 bindAddr = {0}; + const bool bind = isLocalServer && !serverHasRelay; + if (bind) { + bindAddr.ip = g_localhostIP.in.sin_addr.s_addr; + bindAddr.port = 0; + } + + SceNetEtherAddr address; + getLocalMac(&address); + + adhocctlLoginDone = false; + adhocctlLoginHandle = nullptr; + adhocctlLoginRunning = true; + adhocctlLoginThread = std::thread(AdhocctlLogin, !adhocServerResolved, bind, bindAddr, product_code, parameter.nickname, address, netAdhocUseUdp); + adhocServerResolved = true; +} + +static void FinishAdhocctlLogin() { + adhocctlLoginThread.join(); + adhocctlLoginRunning = false; + void *handle = adhocctlLoginHandle; + adhocctlLoginHandle = nullptr; + // sceNetAdhocctlScan & Create puts this back to true, which will trigger retry + isAdhocctlNeedLogin = false; + + if (!handle) { + g_adhocServerConnected = false; + return; + } + + { + const std::lock_guard adhocctl_guard(adhocctlMutex); + if (adhocctlHandle) { + // Only the login thread creates sessions, one at a time. + ERROR_LOG(Log::sceNet, "%s: unreachable codepath at %s %d", __func__, __FILE__, __LINE__); + _dbg_assert_(false); + killAdhocctlClient(false, adhocctl_guard); + } + adhocctlHandle = handle; + int new_adhocctl_id = 2; + if (new_adhocctl_id == adhocctlId) { + new_adhocctl_id++; + } + adhocctlId = new_adhocctl_id; + + // warm the local ip cache + sockaddr_in addr; + getLocalIp(&addr); + } + + g_adhocServerConnected = true; + INFO_LOG(Log::sceNet, "FriendFinder: Network [RE]Initialized"); + if (!adhocctlLoggedIn) { + // The first login after sceNetAdhocctlInit. A request the game made meanwhile is waiting for it, + // and still busy. + adhocctlLoggedIn = true; + return; + } + // At this point we are most-likely not in a Group within the Adhoc Server, so we should probably reset AdhocctlState + adhocctlState = ADHOCCTL_STATE_DISCONNECTED; + netAdhocGameModeEntered = false; + isAdhocctlBusy = false; +} + +void friendFinderStart() { + adhocServerResolved = false; + adhocctlLoggedIn = false; + { + const std::lock_guard adhocctl_guard(adhocctlMutex); + lastAdhocctlPing = 0; + } + // In case the client was dropped without friendFinderStop(), as __NetAdhocInit() does. + StopAdhocctlKeepalive(); + adhocctlKeepaliveStop = false; + adhocctlKeepaliveThread = std::thread(AdhocctlKeepalive); + INFO_LOG(Log::sceNet, "FriendFinder: Begin"); +} + +void friendFinderStop() { + StopAdhocctlKeepalive(); + // friendFinderRunning is already false, which makes a login in progress give up. + if (adhocctlLoginThread.joinable()) { + adhocctlLoginThread.join(); + } + if (adhocctlLoginHandle) { + destroy_adhocctl_session(adhocctlLoginHandle); + adhocctlLoginHandle = nullptr; + } + adhocctlLoginRunning = false; + + // Prevent the games from having trouble to reInitiate Adhoc (the next NetInit -> PdpCreate after NetTerm) + adhocctlState = ADHOCCTL_STATE_DISCONNECTED; + INFO_LOG(Log::sceNet, "FriendFinder: End"); +} + +void friendFinderPoll() { + if (!friendFinderRunning) { + return; + } + + if (adhocctlLoginRunning && adhocctlLoginDone) { + FinishAdhocctlLogin(); + } + + // Reconnect when disconnected while Adhocctl is still inited + if (!adhocctlLoginRunning) { if (netAdhocctlInited && isAdhocctlNeedLogin && g_Config.bEnableWlan) { - AdhocctlClientStatus status = maintain_adhocctl_client(&product_code); - switch (status) { - case AdhocctlClientStatus::SPAWNED: { - g_adhocServerConnected = true; - INFO_LOG(Log::sceNet, "FriendFinder: Network [RE]Initialized"); - // At this point we are most-likely not in a Group within the Adhoc Server, so we should probably reset AdhocctlState - adhocctlState = ADHOCCTL_STATE_DISCONNECTED; - netAdhocGameModeEntered = false; - isAdhocctlBusy = false; - break; - } - case AdhocctlClientStatus::DEAD: { - g_adhocServerConnected = false; - break; - } - case AdhocctlClientStatus::ALIVE: { - break; - } - default: { - ERROR_LOG(Log::sceNet, "%s: unreachable codepath at %s %d", __func__, __FILE__, __LINE__); - _dbg_assert_(false); + bool alive; + { + const std::lock_guard adhocctl_guard(adhocctlMutex); + alive = adhocctlHandle != NULL; } + if (alive) { + isAdhocctlNeedLogin = false; + } else { + // isAdhocctlNeedLogin stays set until the login is done, so the requests waiting for it keep waiting. + StartAdhocctlLogin(); } + } else { + // sceNetAdhocctlScan & Create puts this back to true, which will trigger retry + isAdhocctlNeedLogin = false; } + } - // sceNetAdhocctlScan & Create puts this back to true, which will trigger retry - isAdhocctlNeedLogin = false; + uint64_t now = time_now_d() * 1000000.0; // Use time_now_d()*1000000.0 instead of CoreTiming::GetGlobalTimeUsScaled() if the game gets disconnected from AdhocServer too soon when FPS wasn't stable - now = time_now_d() * 1000000.0; // Use time_now_d()*1000000.0 instead of CoreTiming::GetGlobalTimeUsScaled() if the game gets disconnected from AdhocServer too soon when FPS wasn't stable - - // Calculate EnterGameMode Timeout to prevent waiting forever for disconnected players - if (isAdhocctlBusy && adhocctlState == ADHOCCTL_STATE_DISCONNECTED && adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE && netAdhocGameModeEntered && static_cast(now - adhocctlStartTime) > netAdhocEnterGameModeTimeout) { - netAdhocGameModeEntered = false; - // adhocctl's own TIMEOUT (pspautotests net/adhoc/gamemode). - notifyAdhocctlHandlers(ADHOCCTL_EVENT_ERROR, SCE_NET_ADHOCCTL_ERROR_TIMEOUT); - } - - auto friend_finder_wait = [] { - // This delay time should be 100ms when there is an event otherwise 500ms ? - sleep_ms(10, "pro-adhoc-poll-2"); // Using 1ms for faster response just like AdhocServer? - - // Don't do anything if it's paused, otherwise the log will be flooded - while (Core_IsStepping() && coreState != CORE_POWERDOWN && friendFinderRunning) { - sleep_ms(10, "pro-adhoc-paused-poll-2"); - } - }; + // Calculate EnterGameMode Timeout to prevent waiting forever for disconnected players + if (isAdhocctlBusy && adhocctlState == ADHOCCTL_STATE_DISCONNECTED && adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE && netAdhocGameModeEntered && static_cast(now - adhocctlStartTime) > netAdhocEnterGameModeTimeout) { + netAdhocGameModeEntered = false; + // adhocctl's own TIMEOUT (pspautotests net/adhoc/gamemode). + notifyAdhocctlHandlers(ADHOCCTL_EVENT_ERROR, SCE_NET_ADHOCCTL_ERROR_TIMEOUT); + } + // Handle everything the server has sent since the last poll. + for (int i = 0; i < 64; i++) { struct adhocctl_event adhocctl_event; - bool wait_and_continue = false; - do { + { // we only want a lock until an event is fetched, so that we don't hold adhocctl & peer the same time and rig outselves into weird hold and wait down the road const std::lock_guard adhocctl_guard(adhocctlMutex); if (!adhocctlHandle) { - wait_and_continue = true; - break; + return; } // Ping Server // original code : ((sceKernelGetSystemTimeWide() - lastping) >= ADHOCCTL_PING_TIMEOUT) - if (static_cast(now - lastping) >= PSP_ADHOCCTL_PING_TIMEOUT) { // We may need to use lower interval to prevent getting timeout at Pro Adhoc Server through internet + if (static_cast(now - lastAdhocctlPing) >= PSP_ADHOCCTL_PING_TIMEOUT) { // We may need to use lower interval to prevent getting timeout at Pro Adhoc Server through internet enum adhocctl_call_status ping_status = adhocctl_ping(adhocctlHandle); if (ping_status != ADHOCCTL_CALL_SUCCESS) { ERROR_LOG(Log::sceNet, "FriendFinder: adhocctl client error during ping"); killAdhocctlClient(true, adhocctl_guard); - wait_and_continue = true; - break; - } else { - // Update Ping Time - lastping = now; - VERBOSE_LOG(Log::sceNet, "FriendFinder: Sending OPCODE_PING (%llu)", static_cast(now)); + return; } + // Update Ping Time + lastAdhocctlPing = now; + VERBOSE_LOG(Log::sceNet, "FriendFinder: Sending OPCODE_PING (%llu)", static_cast(now)); } adhocctl_get_event(adhocctlHandle, &adhocctl_event); if (adhocctl_event.type == ADHOCCTL_CLIENT_EVENT_ERROR) { ERROR_LOG(Log::sceNet, "FriendFinder: adhocctl client error during event fetch"); killAdhocctlClient(true, adhocctl_guard); - wait_and_continue = true; - break; + return; } - } while (false); + } - if (wait_and_continue) { - friend_finder_wait(); - continue; + if (adhocctl_event.type == ADHOCCTL_CLIENT_EVENT_WOULD_BLOCK) { + return; } switch (adhocctl_event.type) { @@ -1826,20 +1894,7 @@ int friendFinder() { _dbg_assert_(false); } - friend_finder_wait(); } - - // Groups/Networks should be deallocated isn't? - - // Prevent the games from having trouble to reInitiate Adhoc (the next NetInit -> PdpCreate after NetTerm) - adhocctlState = ADHOCCTL_STATE_DISCONNECTED; - friendFinderRunning = false; - - // Log Shutdown - INFO_LOG(Log::sceNet, "FriendFinder: End of Friend Finder Thread"); - - // Return Success - return 0; } int getActivePeerCount(const bool excludeTimedout) { diff --git a/Core/HLE/proAdhoc.h b/Core/HLE/proAdhoc.h index 6d4d77d478..5cd99ff9c4 100644 --- a/Core/HLE/proAdhoc.h +++ b/Core/HLE/proAdhoc.h @@ -780,7 +780,6 @@ extern int adhocctlId; // for tracking whether there was an instance swap extern std::mutex adhocctlMutex; extern SceNetAdhocctlParameter parameter; extern SceNetAdhocctlAdhocId product_code; -extern std::thread friendFinderThread; extern std::recursive_mutex peerlock; extern AdhocSocket* adhocSockets[MAX_SOCKET]; @@ -968,13 +967,13 @@ void freeFriendsRecursive(SceNetAdhocctlPeerInfo * node, int32_t* count = nullpt void timeoutFriendsRecursive(SceNetAdhocctlPeerInfo * node, int32_t* count = nullptr); -/** - * Friend Finder Thread (Receives Peer Information) - * @param args Length of argp in Bytes (Unused) - * @param argp Argument (Unused) - * @return Unused Value - Return 0 - */ -int friendFinder(); +// The adhocctl client: logs in to the Adhoc Server (on a thread of its own) and handles what the +// server sends. friendFinderPoll() runs on the CPU thread, from a CoreTiming event, while +// friendFinderRunning is set. +void friendFinderStart(); +void friendFinderPoll(); +// Call after clearing friendFinderRunning. +void friendFinderStop(); /** * Find Free Matching ID diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 8b24d1ff18..7078c3f3cf 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -95,6 +95,7 @@ std::deque> adhocctlEvents; std::map adhocctlHandlers; int adhocctlNotifyEvent = -1; int adhocctlStateEvent = -1; +int adhocctlPollEvent = -1; int adhocSocketNotifyEvent = -1; std::map adhocctlRequests; std::map adhocSocketRequests; @@ -531,6 +532,16 @@ static void __GameModeNotify(u64 userdata, int cyclesLate) { } } +// How often the adhocctl client looks at what the Adhoc Server sent, in emulated time. +static const int ADHOCCTL_POLL_INTERVAL_US = 1000; + +static void __AdhocctlPoll(u64 userdata, int cyclesLate) { + friendFinderPoll(); + if (friendFinderRunning) { + CoreTiming::ScheduleEvent(usToCycles(ADHOCCTL_POLL_INTERVAL_US) - cyclesLate, adhocctlPollEvent, 0); + } +} + static void __AdhocctlNotify(u64 userdata, int cyclesLate) { SceUID threadID = userdata >> 32; int uid = (int)(userdata & 0xFFFFFFFF); @@ -1885,25 +1896,20 @@ static void SetupAdhocRelayMode() { } static void StartFriendFinder() { - // TODO: Merging friendFinder (real) thread to AdhocThread (fake) thread on PSP side if (!friendFinderRunning) { - // Set before the thread starts, not by it: shutting down before it got going used to clear - // the flag first, and then the thread set it again and never stopped, hanging the join. - if (friendFinderThread.joinable()) { - friendFinderThread.join(); - } + friendFinderStart(); friendFinderRunning = true; - friendFinderThread = std::thread(friendFinder); + } + if (!CoreTiming::IsScheduled(adhocctlPollEvent)) { + CoreTiming::ScheduleEvent(usToCycles(ADHOCCTL_POLL_INTERVAL_US), adhocctlPollEvent, 0); } } -// Stops the friendFinder thread and drops the connection to the Adhoc Server, leaving the PSP side +// Stops the adhocctl client and drops the connection to the Adhoc Server, leaving the PSP side // (handlers, AdhocThread) alone. static void StopFriendFinder() { friendFinderRunning = false; - if (friendFinderThread.joinable()) { - friendFinderThread.join(); - } + friendFinderStop(); int32_t peercount = 0; freeFriendsRecursive(friends, &peercount); @@ -1914,7 +1920,7 @@ static void StopFriendFinder() { } void __NetAdhocDoState(PointerWrap &p) { - auto s = p.Section("sceNetAdhoc", 1, 10); + auto s = p.Section("sceNetAdhoc", 1, 11); if (!s) return; @@ -2007,6 +2013,12 @@ void __NetAdhocDoState(PointerWrap &p) { // The login after a load sends it, so a state loaded into a fresh boot needs it. p.DoVoid(&product_code, (int)sizeof(product_code)); } + if (s >= 11) { + Do(p, adhocctlPollEvent); + } else { + adhocctlPollEvent = -1; + } + CoreTiming::RestoreRegisterEvent(adhocctlPollEvent, "__AdhocctlPoll", __AdhocctlPoll); if (p.mode == p.MODE_READ) { // Discard leftover events @@ -2040,6 +2052,10 @@ void __NetAdhocDoState(PointerWrap &p) { } isAdhocctlNeedLogin = false; } + // A state from before the poll event, or one saved with the client stopped, has none queued. + if (netAdhocctlInited) { + StartFriendFinder(); + } if (!netAdhocInited && cur_netAdhocInited) { // cleanup adhoc if (aemuPostofficeEnetThread.joinable()) { @@ -2063,6 +2079,7 @@ void __AdhocNotifInit() { adhocSocketNotifyEvent = CoreTiming::RegisterEvent("__AdhocSocketNotify", __AdhocSocketNotify); gameModeNotifyEvent = CoreTiming::RegisterEvent("__GameModeNotify", __GameModeNotify); adhocctlStateEvent = CoreTiming::RegisterEvent("__AdhocctlState", __AdhocctlState); + adhocctlPollEvent = CoreTiming::RegisterEvent("__AdhocctlPoll", __AdhocctlPoll); adhocctlRequests.clear(); adhocSocketRequests.clear(); @@ -2090,7 +2107,7 @@ void __NetAdhocInit() { __AdhocServerInit(); // Create built-in AdhocServer Thread. The flag is set here rather than by the thread, so that a - // shutdown that clears it before the thread gets going can't be undone (see friendFinder). + // shutdown that clears it before the thread gets going can't be undone. adhocServerRunning = false; if (adhocServerThread.joinable()) { adhocServerThread.join(); @@ -2227,14 +2244,9 @@ int sceNetAdhocctlInit(MIPSState *mips, int stackSize, int prio, u32 productAddr StartFriendFinder(); - // Need to make sure to be connected to Adhoc Server (indicated by networkInited) before returning to prevent GTA VCS failed to create/join a group and unable to see any game room - int us = adhocDefaultDelay; - if (g_Config.bEnableWlan && !g_adhocServerConnected) { - AdhocctlRequest dummyreq = { OPCODE_LOGIN, {0} }; - return hleLogDebugOrWarn(Log::sceNet, WaitBlockingAdhocctlSocket(dummyreq, us, "adhocctl init")); - } - // Give a little time for friendFinder thread to be ready before the game use the next sceNet functions, should've checked for friendFinderRunning status instead of guessing the time? - hleEatMicro(us); + // Init doesn't wait for the login to the Adhoc Server, and a Create, Connect or Scan right after it + // works, as on a PSP (pspautotests net/adhoc/ctlinit): those requests wait for the login instead. + hleEatMicro(adhocDefaultDelay); return hleLogDebug(Log::sceNet, 0); } @@ -3861,11 +3873,9 @@ int NetAdhocctl_Term() { NetAdhocctl_Disconnect(); } - // Terminate Adhoc Threads + // Stop the adhocctl client friendFinderRunning = false; - if (friendFinderThread.joinable()) { - friendFinderThread.join(); - } + friendFinderStop(); // TODO: May need to block current thread to make sure all Adhocctl callbacks have been fully executed before terminating Adhoc PSPThread (ie. threadAdhocID). diff --git a/test.py b/test.py index d0497722e7..6983b05cba 100755 --- a/test.py +++ b/test.py @@ -670,6 +670,7 @@ tests_next = [ "net/http/http", "net/adhoc/adhocctl", "net/adhoc/alerts", + "net/adhoc/ctlinit", "net/adhoc/discover", "net/adhoc/gamemode", "net/adhoc/limits",