diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index 30e6f79187..543e03a220 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -1641,6 +1641,11 @@ void friendFinderPoll() { // 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; + // A joiner's GAMEMODE is queued as soon as the server answers, and held until the host shows up. + // It must not come after all (pspautotests net/adhoc/gamemode). + __DropAdhocctlEvents(ADHOCCTL_EVENT_GAME); + // Back to DISCONNECTED with no event but the error, out of the server's group too. + NetAdhocctl_GameModeTimedOut(); // adhocctl's own TIMEOUT (pspautotests net/adhoc/gamemode). notifyAdhocctlHandlers(ADHOCCTL_EVENT_ERROR, SCE_NET_ADHOCCTL_ERROR_TIMEOUT); } diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 542d5dfb05..2950809e11 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -2120,6 +2120,25 @@ void __UpdateAdhocctlHandlers(u32 flag, u32 error) { adhocctlEvents.push_back({ flag, error }); } +void NetAdhocctl_GameModeTimedOut() { + if (adhocctlInServerGroup) { + const std::lock_guard adhocctl_guard(adhocctlMutex); + if (adhocctlHandle != NULL) { + adhocctl_disconnect(adhocctlHandle); + } + adhocctlInServerGroup = false; + } + adhocctlCurrentMode = ADHOCCTL_MODE_NONE; + isAdhocctlBusy = false; +} + +void __DropAdhocctlEvents(u32 flag) { + std::lock_guard adhocGuard(adhocEvtMtx); + adhocctlEvents.erase(std::remove_if(adhocctlEvents.begin(), adhocctlEvents.end(), [flag](const std::pair &e) { + return e.first == flag; + }), adhocctlEvents.end()); +} + void __AdhocNotifInit() { adhocctlNotifyEvent = CoreTiming::RegisterEvent("__AdhocctlNotify", __AdhocctlNotify); adhocSocketNotifyEvent = CoreTiming::RegisterEvent("__AdhocSocketNotify", __AdhocSocketNotify); @@ -4231,6 +4250,21 @@ int NetAdhocctl_CreateEnterGameMode(const char* group_name, int game_type, int n if (game_type < ADHOCCTL_GAMETYPE_1A || game_type > ADHOCCTL_GAMETYPE_2A || num_members < 2 || num_members > 16 || (game_type == ADHOCCTL_GAMETYPE_1A && num_members > 4)) return SCE_NET_ADHOCCTL_ERROR_INVALID_ARG; + // In a normal group, it reports ALREADY_CONNECTED through the handlers and stays in it + // (pspautotests net/adhoc/gamemode). + if (adhocctlState == ADHOCCTL_STATE_CONNECTED) { + notifyAdhocctlHandlers(ADHOCCTL_EVENT_ERROR, SCE_NET_ADHOCCTL_ERROR_ALREADY_CONNECTED); + return 0; + } + + // The first member has to be this console: if it isn't, the call succeeds and the handlers get + // ERROR with INVALID_ADDR (pspautotests net/adhoc/gamemode). + SceNetEtherAddr firstMember = *PSPPointer::Create(membersAddr); + if (!isLocalMAC(&firstMember)) { + notifyAdhocctlHandlers(ADHOCCTL_EVENT_ERROR, SCE_NET_ADHOC_ERROR_INVALID_ADDR); + return 0; + } + deleteAllGMB(); gameModePeerPorts.clear(); @@ -6441,6 +6475,11 @@ const HLEFunction sceNetAdhoc[] = { }; int NetAdhocctl_ExitGameMode() { + // Outside game mode, also after entering it timed out, it returns 0 with no event (pspautotests + // net/adhoc/gamemode). + if (adhocctlCurrentMode != ADHOCCTL_MODE_GAMEMODE) + return adhocctlState == ADHOCCTL_STATE_CONNECTED ? SCE_NET_ADHOCCTL_ERROR_INVALID_IBSS : 0; + if (gameModeSocket > 0) { NetAdhocPdp_Delete(gameModeSocket, 0); gameModeSocket = (int)INVALID_SOCKET; diff --git a/Core/HLE/sceNetAdhoc.h b/Core/HLE/sceNetAdhoc.h index 721442383f..09c97c7630 100644 --- a/Core/HLE/sceNetAdhoc.h +++ b/Core/HLE/sceNetAdhoc.h @@ -142,6 +142,10 @@ void __NetAdhocDoState(PointerWrap &p); // TODO: expose it via "sceNetAdhocctl.h" void __UpdateAdhocctlHandlers(u32 flags, u32 error); +// Drops queued events of one kind that haven't been delivered yet. +void __DropAdhocctlEvents(u32 flags); +// Leaves the server's group without an event, after entering game mode timed out. +void NetAdhocctl_GameModeTimedOut(); bool __NetAdhocConnected();