diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index 425bb63413..7a1f79063e 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -2078,9 +2078,9 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ errorcode = errno; if (iResult == SOCKET_ERROR && errorcode != EISCONN) { - u64 startTime = (u64)(time_now_d() * 1000.0); + u64 startTime = (u64)(time_now_d() * 1000000.0); while (IsSocketReady(metasocket, false, true) <= 0) { - u64 now = (u64)(time_now_d() * 1000.0); + u64 now = (u64)(time_now_d() * 1000000.0); if (coreState == CORE_POWERDOWN) return iResult; if (now - startTime > adhocDefaultTimeout) break; sleep_ms(10); @@ -2102,7 +2102,7 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ packet.name.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; memcpy(packet.game.data, adhoc_id->data, ADHOCCTL_ADHOCID_LEN); - IsSocketReady(metasocket, false, true, nullptr, adhocDefaultTimeout * 1000); + IsSocketReady(metasocket, false, true, nullptr, adhocDefaultTimeout); int sent = send(metasocket, (char*)&packet, sizeof(packet), MSG_NOSIGNAL); if (sent > 0) { socklen_t addrLen = sizeof(LocalIP); diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index 74d01aa789..757221f1db 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -68,6 +68,7 @@ u32 netApctlState; u32 apctlThreadHackAddr = 0; u32_le apctlThreadCode[3]; SceUID apctlThreadID = 0; +int apctlStateEvent = -1; int actionAfterApctlMipsCall; std::recursive_mutex apctlEvtMtx; std::deque apctlEvents; @@ -125,10 +126,47 @@ void InitLocalhostIP() { isLocalServer = (!strcasecmp(serverStr.c_str(), "localhost") || serverStr.find("127.") == 0); } +static void __ApctlState(u64 userdata, int cyclesLate) { + SceUID threadID = userdata >> 32; + int uid = (int)(userdata & 0xFFFFFFFF); + int event = uid - 1; + + s64 result = 0; + u32 error = 0; + + SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_NET, error); + if (waitID == 0 || error != 0) + return; + + u32 waitVal = __KernelGetWaitValue(threadID, error); + if (error == 0) { + netApctlState = waitVal; + } + + __KernelResumeThreadFromWait(threadID, result); + DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %d) Result (%08x) of sceNetApctl - Event: %d, State: %d", waitID, error, (int)result, event, netApctlState); +} + +// Used to change Apctl State after a delay and before executing callback mipscall (since we don't have beforeAction) +int ScheduleApctlState(int event, int newState, int usec, const char* reason) { + int uid = event + 1; + + if (apctlStateEvent < 0) + apctlStateEvent = CoreTiming::RegisterEvent("__ApctlState", __ApctlState); + + u64 param = ((u64)__KernelGetCurThread()) << 32 | uid; + CoreTiming::ScheduleEvent(usToCycles(usec), apctlStateEvent, param); + __KernelWaitCurThread(WAITTYPE_NET, uid, newState, 0, false, reason); + + return 0; +} + void __NetApctlInit() { netApctlInited = false; netApctlState = PSP_NET_APCTL_STATE_DISCONNECTED; + apctlStateEvent = CoreTiming::RegisterEvent("__ApctlState", __ApctlState); apctlHandlers.clear(); + apctlEvents.clear(); memset(&netApctlInfo, 0, sizeof(netApctlInfo)); } @@ -182,6 +220,8 @@ void __NetApctlShutdown() { kernelMemory.Free(apctlThreadHackAddr); apctlThreadHackAddr = 0; } + apctlHandlers.clear(); + apctlEvents.clear(); } void __NetShutdown() { @@ -216,7 +256,7 @@ void netValidateLoopMemory() { // This feels like a dubious proposition, mostly... void __NetDoState(PointerWrap &p) { - auto s = p.Section("sceNet", 1, 4); + auto s = p.Section("sceNet", 1, 5); if (!s) return; @@ -260,6 +300,15 @@ void __NetDoState(PointerWrap &p) { apctlThreadHackAddr = 0; apctlThreadID = 0; } + if (s >= 5) { + Do(p, apctlStateEvent); + if (apctlStateEvent != -1) { + CoreTiming::RestoreRegisterEvent(apctlStateEvent, "__ApctlState", __ApctlState); + } + } + else { + apctlStateEvent = -1; + } if (p.mode == p.MODE_READ) { // Let's not change "Inited" value when Loading SaveState in the middle of multiplayer to prevent memory & port leaks @@ -370,78 +419,106 @@ std::string error2str(u32 errorCode) { void __NetApctlCallbacks() { std::lock_guard apctlGuard(apctlEvtMtx); + hleSkipDeadbeef(); int delayus = 10000; + // We are temporarily borrowing APctl thread for NpAuth callbacks for testing to simulate authentication + if (!npAuthEvents.empty()) + { + auto args = npAuthEvents.front(); + auto& id = args.data[0]; + auto& result = args.data[1]; + auto& argAddr = args.data[2]; + npAuthEvents.pop_front(); + + delayus = (adhocEventDelay + adhocExtraDelay); + + int handlerID = id - 1; + for (std::map::iterator it = npAuthHandlers.begin(); it != npAuthHandlers.end(); ++it) { + if (it->first == handlerID) { + DEBUG_LOG(SCENET, "NpAuthCallback [HandlerID=%i][RequestID=%d][Result=%d][ArgsPtr=%08x]", it->first, id, result, it->second.argument); + // TODO: Update result / args.data[1] with the actual ticket length (or error code?) + hleEnqueueCall(it->second.entryPoint, 3, args.data); + } + } + } + // How AP works probably like this: Game use sceNetApctl function -> sceNetApctl let the hardware know and do their's thing and have a new State -> Let the game know the resulting State through Event on their handler if (!apctlEvents.empty()) { auto args = apctlEvents.front(); - auto oldState = &args.data[0]; - auto newState = &args.data[1]; - auto event = &args.data[2]; - auto error = &args.data[3]; + auto& oldState = args.data[0]; + auto& newState = args.data[1]; + auto& event = args.data[2]; + auto& error = args.data[3]; apctlEvents.pop_front(); - // Adjust delay according to current event. Added an extra delay to prevent I/O Timing method from causing disconnection - if (*event == PSP_NET_APCTL_EVENT_CONNECT_REQUEST || *event == PSP_NET_APCTL_EVENT_GET_IP || *event == PSP_NET_APCTL_EVENT_SCAN_REQUEST) - delayus = (adhocEventDelayMS + 2 * adhocExtraPollDelayMS) * 1000; + // Adjust delay according to current event. + if (event == PSP_NET_APCTL_EVENT_CONNECT_REQUEST || event == PSP_NET_APCTL_EVENT_GET_IP || event == PSP_NET_APCTL_EVENT_SCAN_REQUEST) + delayus = adhocEventDelay; else - delayus = (adhocEventPollDelayMS + 2 * adhocExtraPollDelayMS) * 1000; + delayus = adhocEventPollDelay; // Do we need to change the oldState? even if there was error? - //if (*error == 0) - *oldState = netApctlState; + //if (error == 0) + // oldState = netApctlState; // Need to make sure netApctlState is updated before calling the callback's mipscall so the game can GetState()/GetInfo() within their handler's subroutine and make use the new State/Info - // Should we update NewState & Error accordingly to Event before executing the mipscall ? sceNetApctl* functions might want to set the error value tho, so we probably should leave it untouched - //*error = 0; - switch (*event) { + // Should we update NewState & Error accordingly to Event before executing the mipscall ? sceNetApctl* functions might want to set the error value tho, so we probably should leave it untouched, right? + //error = 0; + switch (event) { case PSP_NET_APCTL_EVENT_CONNECT_REQUEST: - netApctlState = PSP_NET_APCTL_STATE_JOINING; // Should we set the State to PSP_NET_APCTL_STATE_DISCONNECTED if there was error? - if (*error == 0) apctlEvents.push_front({ netApctlState, netApctlState, PSP_NET_APCTL_EVENT_ESTABLISHED, 0 }); // Should we use PSP_NET_APCTL_EVENT_EAP_AUTH if securityType is not NONE? + newState = PSP_NET_APCTL_STATE_JOINING; // Should we set the State to PSP_NET_APCTL_STATE_DISCONNECTED if there was error? + if (error == 0) + apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_ESTABLISHED, 0 }); // Should we use PSP_NET_APCTL_EVENT_EAP_AUTH if securityType is not NONE? break; case PSP_NET_APCTL_EVENT_ESTABLISHED: - netApctlState = PSP_NET_APCTL_STATE_GETTING_IP; - if (*error == 0) apctlEvents.push_front({ netApctlState, netApctlState, PSP_NET_APCTL_EVENT_GET_IP, 0 }); + newState = PSP_NET_APCTL_STATE_GETTING_IP; + if (error == 0) + apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_GET_IP, 0 }); break; case PSP_NET_APCTL_EVENT_GET_IP: - netApctlState = PSP_NET_APCTL_STATE_GOT_IP; + newState = PSP_NET_APCTL_STATE_GOT_IP; NetApctl_InitInfo(); break; case PSP_NET_APCTL_EVENT_DISCONNECT_REQUEST: - netApctlState = PSP_NET_APCTL_STATE_DISCONNECTED; + newState = PSP_NET_APCTL_STATE_DISCONNECTED; break; case PSP_NET_APCTL_EVENT_SCAN_REQUEST: - netApctlState = PSP_NET_APCTL_STATE_SCANNING; - if (*error == 0) apctlEvents.push_front({ netApctlState, netApctlState, PSP_NET_APCTL_EVENT_SCAN_COMPLETE, 0 }); + newState = PSP_NET_APCTL_STATE_SCANNING; + if (error == 0) + apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_SCAN_COMPLETE, 0 }); break; case PSP_NET_APCTL_EVENT_SCAN_COMPLETE: - netApctlState = PSP_NET_APCTL_STATE_DISCONNECTED; + newState = PSP_NET_APCTL_STATE_DISCONNECTED; break; case PSP_NET_APCTL_EVENT_EAP_AUTH: // Is this suppose to happen between JOINING and ESTABLISHED ? - netApctlState = PSP_NET_APCTL_STATE_EAP_AUTH; - if (*error == 0) apctlEvents.push_front({ netApctlState, netApctlState, PSP_NET_APCTL_EVENT_KEY_EXCHANGE, 0 }); // not sure if KEY_EXCHANGE is the next step after AUTH or not tho + newState = PSP_NET_APCTL_STATE_EAP_AUTH; + if (error == 0) + apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_KEY_EXCHANGE, 0 }); // not sure if KEY_EXCHANGE is the next step after AUTH or not tho break; case PSP_NET_APCTL_EVENT_KEY_EXCHANGE: // Is this suppose to happen between JOINING and ESTABLISHED ? - netApctlState = PSP_NET_APCTL_STATE_KEY_EXCHANGE; - if (*error == 0) apctlEvents.push_front({ netApctlState, netApctlState, PSP_NET_APCTL_EVENT_ESTABLISHED, 0 }); + newState = PSP_NET_APCTL_STATE_KEY_EXCHANGE; + if (error == 0) + apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_ESTABLISHED, 0 }); break; case PSP_NET_APCTL_EVENT_RECONNECT: - netApctlState = PSP_NET_APCTL_STATE_DISCONNECTED; - if (*error == 0) apctlEvents.push_front({ netApctlState, netApctlState, PSP_NET_APCTL_EVENT_CONNECT_REQUEST, 0 }); + newState = PSP_NET_APCTL_STATE_DISCONNECTED; + if (error == 0) + apctlEvents.push_front({ newState, newState, PSP_NET_APCTL_EVENT_CONNECT_REQUEST, 0 }); break; } // Do we need to change the newState? even if there were error? - //if (*error == 0) - *newState = netApctlState; + //if (error != 0) + // newState = netApctlState; // Since 0 is a valid index to types_ we use -1 to detects if it was loaded from an old save state if (actionAfterApctlMipsCall < 0) { @@ -450,38 +527,19 @@ void __NetApctlCallbacks() // Run mipscall. Should we skipped executing the mipscall if oldState == newState? for (std::map::iterator it = apctlHandlers.begin(); it != apctlHandlers.end(); ++it) { - DEBUG_LOG(SCENET, "ApctlCallback [ID=%i][OldState=%d][NewState=%d][Event=%d][Error=%d][ArgsPtr=%08x]", it->first, *oldState, *newState, *event, *error, it->second.argument); + DEBUG_LOG(SCENET, "ApctlCallback [ID=%i][OldState=%d][NewState=%d][Event=%d][Error=%08x][ArgsPtr=%08x]", it->first, oldState, newState, event, error, it->second.argument); args.data[4] = it->second.argument; AfterApctlMipsCall* after = (AfterApctlMipsCall*)__KernelCreateAction(actionAfterApctlMipsCall); - after->SetData(it->first, *oldState, *newState, *event, *error, it->second.argument); + after->SetData(it->first, oldState, newState, event, error, it->second.argument); hleEnqueueCall(it->second.entryPoint, 5, args.data, after); } + // Similar to Adhocctl, new State might need to be set after delayed, right before executing the mipscall (ie. simulated beforeAction) + ScheduleApctlState(event, newState, delayus, "apctl callback state"); + return; } - // We are temporarily borrowing APctl thread for NpAuth callbacks for testing to simulate authentication - if (!npAuthEvents.empty()) - { - auto args = npAuthEvents.front(); - auto id = &args.data[0]; - auto result = &args.data[1]; - auto argAddr = &args.data[2]; - npAuthEvents.pop_front(); - - delayus = (adhocEventDelayMS + 2 * adhocExtraPollDelayMS) * 1000; - - int handlerID = *id - 1; - for (std::map::iterator it = npAuthHandlers.begin(); it != npAuthHandlers.end(); ++it) { - if (it->first == handlerID) { - DEBUG_LOG(SCENET, "NpAuthCallback [HandlerID=%i][RequestID=%d][Result=%d][ArgsPtr=%08x]", it->first, *id, *result, it->second.argument); - // TODO: Update result / args.data[1] with the actual ticket length (or error code?) - hleEnqueueCall(it->second.entryPoint, 3, args.data); - } - } - } - - // Must be delayed long enough whenever there is a pending callback. + // Must be delayed long enough whenever there is a pending callback to make sure previous callback & it's afterAction are fully executed sceKernelDelayThread(delayus); - hleSkipDeadbeef(); } static inline u32 AllocUser(u32 size, bool fromTop, const char *name) { @@ -535,7 +593,7 @@ static u32 sceNetTerm() { int retval = Net_Term(); // Give time to make sure everything are cleaned up - hleDelayResult(retval, "give time to init/cleanup", adhocEventDelayMS * 1000); + hleEatMicro(adhocDefaultDelay); return retval; } diff --git a/Core/HLE/sceNet.h b/Core/HLE/sceNet.h index 8374a878ff..3ea8b9811d 100644 --- a/Core/HLE/sceNet.h +++ b/Core/HLE/sceNet.h @@ -219,6 +219,7 @@ private: extern bool netInetInited; extern bool netApctlInited; +extern u32 netApctlState; extern SceNetApctlInfoInternal netApctlInfo; template std::string num2hex(I w, size_t hex_len = sizeof(I) << 1); diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 656968482f..09eb99de1a 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -69,11 +69,12 @@ bool netAdhocGameModeEntered; bool netAdhocMatchingInited; int netAdhocMatchingStarted = 0; -int adhocDefaultTimeout = 2000; //5000 ms -int adhocExtraPollDelayMS = 10; //10 -int adhocEventPollDelayMS = 100; //100; Seems to be the same with PSP_ADHOCCTL_RECV_TIMEOUT -int adhocMatchingEventDelayMS = 30; //30 -int adhocEventDelayMS = 300; //500; This will affect the duration of "Connecting..." dialog/message box in .Hack//Link and Naruto Ultimate Ninja Heroes 3 +int adhocDefaultTimeout = 3000000; //3000000 usec +int adhocDefaultDelay = 10000; //10000 +int adhocExtraDelay = 20000; //20000 +int adhocEventPollDelay = 100000; //100000; // Same timings with PSP_ADHOCCTL_RECV_TIMEOUT ? +int adhocMatchingEventDelay = 30000; //30000 +int adhocEventDelay = 1000000; //1000000 SceUID threadAdhocID; @@ -85,6 +86,7 @@ std::vector matchingThreads; int IsAdhocctlInCB = 0; int adhocctlNotifyEvent = -1; +int adhocctlStateEvent = -1; int adhocSocketNotifyEvent = -1; std::map adhocctlRequests; std::map adhocSocketRequests; @@ -197,10 +199,10 @@ static void __AdhocctlNotify(u64 userdata, int cyclesLate) { if (waitID == 0 || error != 0) return; - // Socket not found?! Should never happened! but if it ever happen should we just exit here or need to wake the thread first? + // Socket not found?! Should never happen! But if it ever happened (ie. loaded from SaveState where adhocctlRequests got cleared) return BUSY and let the game try again. if (adhocctlRequests.find(uid) == adhocctlRequests.end()) { WARN_LOG(SCENET, "sceNetAdhocctl Socket WaitID(%i) not found!", uid); - //__KernelResumeThreadFromWait(threadID, ERROR_NET_ADHOCCTL_BUSY); + __KernelResumeThreadFromWait(threadID, ERROR_NET_ADHOCCTL_BUSY); return; } @@ -223,38 +225,39 @@ static void __AdhocctlNotify(u64 userdata, int cyclesLate) { break; } - // Send Packet if it wasn't succesfully sent before - if (len > 0) { - if (IsSocketReady(metasocket, false, true) > 0) { - int ret = send(metasocket, (const char*)&packet, len, MSG_NOSIGNAL); - int sockerr = errno; - - if (ret > 0 || (ret == SOCKET_ERROR && sockerr != EAGAIN && sockerr != EWOULDBLOCK)) { - // Prevent from sending again - req.opcode = 0; - if (ret == SOCKET_ERROR) - DEBUG_LOG(SCENET, "sceNetAdhocctl[%i]: Socket Error (%i)", uid, sockerr); + if (g_Config.bEnableWlan) { + // Send Packet if it wasn't succesfully sent before + int ret = 0; + int sockerr = 0; + if (len > 0) { + ret = SOCKET_ERROR; + sockerr = EAGAIN; + if (IsSocketReady(metasocket, false, true) > 0) { + ret = send(metasocket, (const char*)&packet, len, MSG_NOSIGNAL); + sockerr = errno; + // Successfully Sent or Connection has been closed or Connection failure occurred + if (ret >= 0 || (ret == SOCKET_ERROR && sockerr != EAGAIN && sockerr != EWOULDBLOCK)) { + // Prevent from sending again + req.opcode = 0; + if (ret == SOCKET_ERROR) + DEBUG_LOG(SCENET, "sceNetAdhocctl[%i]: Socket Error (%i)", uid, sockerr); + } } } - } - // Now we just need to wait for replies from adhoc server and the change of state - int waitVal = __KernelGetWaitValue(threadID, error); - if (adhocctlState != waitVal && error == 0) { - // Detecting Adhocctl Initialization using waitVal < 0 - if (waitVal >= 0 || (waitVal < 0 && (g_Config.bEnableWlan && !networkInited))) { - u64 now = (u64)(time_now_d() * 1000.0); - if (now - adhocctlStartTime <= adhocDefaultTimeout) { - // Try again in another 0.5ms until state matched or timedout. + if ((req.opcode == OPCODE_LOGIN && !networkInited) || (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. CoreTiming::ScheduleEvent(usToCycles(500) - cyclesLate, adhocctlNotifyEvent, userdata); return; } else - result = 0; // ERROR_NET_ADHOCCTL_BUSY + result = ERROR_NET_ADHOCCTL_BUSY; } - else - result = 0; // Faking successfully connected to adhoc server } + else + result = ERROR_NET_ADHOCCTL_WLAN_SWITCH_OFF; __KernelResumeThreadFromWait(threadID, result); DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %d) Result (%08x) of sceNetAdhocctl - State: %d", waitID, error, (int)result, adhocctlState); @@ -263,8 +266,30 @@ static void __AdhocctlNotify(u64 userdata, int cyclesLate) { adhocctlRequests.erase(uid); } -int WaitAdhocctlState(AdhocctlRequest request, int state, int usec, const char* reason) { - int uid = (state < 0) ? 1 : metasocket; +static void __AdhocctlState(u64 userdata, int cyclesLate) { + SceUID threadID = userdata >> 32; + int uid = (int)(userdata & 0xFFFFFFFF); + int event = uid - 1; + + s64 result = 0; + u32 error = 0; + + SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_NET, error); + if (waitID == 0 || error != 0) + return; + + u32 waitVal = __KernelGetWaitValue(threadID, error); + if (error == 0) { + adhocctlState = waitVal; + } + + __KernelResumeThreadFromWait(threadID, result); + DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %d) Result (%08x) of sceNetAdhocctl - Event: %d, State: %d", waitID, error, (int)result, event, adhocctlState); +} + +// Used to simulate blocking on metasocket when send OP code to AdhocServer +int WaitBlockingAdhocctlSocket(AdhocctlRequest request, int usec, const char* reason) { + int uid = (metasocket <= 0) ? 1 : metasocket; if (adhocctlRequests.find(uid) != adhocctlRequests.end()) { WARN_LOG(SCENET, "sceNetAdhocctl - WaitID[%d] already existed, Socket is busy!", uid); @@ -275,12 +300,26 @@ int WaitAdhocctlState(AdhocctlRequest request, int state, int usec, const char* adhocctlNotifyEvent = CoreTiming::RegisterEvent("__AdhocctlNotify", __AdhocctlNotify); u64 param = ((u64)__KernelGetCurThread()) << 32 | uid; - adhocctlStartTime = (u64)(time_now_d() * 1000.0); + adhocctlStartTime = (u64)(time_now_d() * 1000000.0); adhocctlRequests[uid] = request; CoreTiming::ScheduleEvent(usToCycles(usec), adhocctlNotifyEvent, param); - __KernelWaitCurThread(WAITTYPE_NET, uid, state, 0, false, reason); + __KernelWaitCurThread(WAITTYPE_NET, uid, request.opcode, 0, false, reason); + + // Always returning a success when waiting for callback, since error code returned via callback? + return 0; +} + +// Used to change Adhocctl State after a delay and before executing callback mipscall (since we don't have beforeAction) +int ScheduleAdhocctlState(int event, int newState, int usec, const char* reason) { + int uid = event + 1; + + if (adhocctlStateEvent < 0) + adhocctlStateEvent = CoreTiming::RegisterEvent("__AdhocctlState", __AdhocctlState); + + u64 param = ((u64)__KernelGetCurThread()) << 32 | uid; + CoreTiming::ScheduleEvent(usToCycles(usec), adhocctlStateEvent, param); + __KernelWaitCurThread(WAITTYPE_NET, uid, newState, 0, false, reason); - // faked success return 0; } @@ -801,7 +840,7 @@ void netAdhocValidateLoopMemory() { } void __NetAdhocDoState(PointerWrap &p) { - auto s = p.Section("sceNetAdhoc", 1, 6); + auto s = p.Section("sceNetAdhoc", 1, 7); if (!s) return; @@ -875,6 +914,15 @@ void __NetAdhocDoState(PointerWrap &p) { else { gameModeNotifyEvent = -1; } + if (s >= 7) { + Do(p, adhocctlStateEvent); + if (adhocctlStateEvent != -1) { + CoreTiming::RestoreRegisterEvent(adhocctlStateEvent, "__AdhocctlState", __AdhocctlState); + } + } + else { + adhocctlStateEvent = -1; + } if (p.mode == p.MODE_READ) { // Discard leftover events @@ -918,6 +966,7 @@ void __AdhocNotifInit() { adhocctlNotifyEvent = CoreTiming::RegisterEvent("__AdhocctlNotify", __AdhocctlNotify); adhocSocketNotifyEvent = CoreTiming::RegisterEvent("__AdhocSocketNotify", __AdhocSocketNotify); gameModeNotifyEvent = CoreTiming::RegisterEvent("__GameModeNotify", __GameModeNotify); + adhocctlStateEvent = CoreTiming::RegisterEvent("__AdhocctlState", __AdhocctlState); adhocctlRequests.clear(); adhocSocketRequests.clear(); @@ -980,14 +1029,13 @@ static u32 sceNetAdhocctlInit(int stackSize, int prio, u32 productAddr) { } // Need to make sure to be connected to adhoc server before returning to prevent GTA VCS failed to create/join a group and unable to see any game room - int us = adhocExtraPollDelayMS * 1000; + int us = adhocDefaultDelay; if (g_Config.bEnableWlan && !networkInited) { AdhocctlRequest dummyreq = { OPCODE_LOGIN, {0} }; - return WaitAdhocctlState(dummyreq, -1, us, "adhocctl init"); + return 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? - else - hleDelayResult(0, "give some time", us); + hleEatMicro(us); return 0; } @@ -1707,9 +1755,9 @@ int sceNetAdhocPollSocket(u32 socketStructAddr, int count, int timeout, int nonb // Blocking Mode else - // Does timeout = 0 means undefinite on PSP? - if (timeout == 0) - timeout = adhocDefaultTimeout * 1000; // minSocketTimeoutUS; + // Does timeout = 0 means indefinite on PSP? + if (timeout <= 0) + timeout = adhocDefaultTimeout; // minSocketTimeoutUS; if (count > (int)FD_SETSIZE) count = FD_SETSIZE; // return 0; //ERROR_NET_ADHOC_INVALID_ARG @@ -1846,7 +1894,7 @@ int sceNetAdhocctlScan() { // Library initialized if (netAdhocctlInited) { - int us = adhocEventPollDelayMS * 1000; + int us = adhocDefaultDelay * 2; // Only scan when in Disconnected state, otherwise AdhocServer will kick you out if (adhocctlState == ADHOCCTL_STATE_DISCONNECTED) { @@ -1874,13 +1922,14 @@ int sceNetAdhocctlScan() { } else if (friendFinderRunning) { AdhocctlRequest req = { OPCODE_SCAN, {0} }; - return WaitAdhocctlState(req, ADHOCCTL_STATE_DISCONNECTED, us, "adhocctl scan"); + return WaitBlockingAdhocctlSocket(req, us, "adhocctl scan"); } } else { // Return Success and let friendFinder thread to notify the handler when scan completed // Not delaying here may cause Naruto Shippuden Ultimate Ninja Heroes 3 to get disconnected when the mission started - return hleDelayResult(0, "give a little time to be ready to receive the callback", us); + hleEatMicro(us); + return 0; } } else if (adhocctlState == ADHOCCTL_STATE_SCANNING) @@ -1890,7 +1939,8 @@ int sceNetAdhocctlScan() { // We need to notify the handler on success, even if it was faked notifyAdhocctlHandlers(ADHOCCTL_EVENT_SCAN, 0); // FIXME: returning ERROR_NET_ADHOCCTL_BUSY may trigger the game (ie. Ford Street Racing) to call sceNetAdhocctlDisconnect, But Not returning a Success(0) will cause Valhalla Knights 2 not working properly - return hleDelayResult(0, "give a little time to be ready to receive the callback", us); + hleEatMicro(us); + return 0; } // Library uninitialized @@ -2034,7 +2084,7 @@ u32 NetAdhocctl_Disconnect() { // Library initialized if (netAdhocctlInited) { int iResult, error; - int us = adhocEventPollDelayMS * 1000; + int us = adhocDefaultDelay * 5; // Connected State (Adhoc Mode) if (adhocctlState != ADHOCCTL_STATE_DISCONNECTED) { // (threadStatus == ADHOCCTL_STATE_CONNECTED) // Clear Network Name @@ -2062,7 +2112,7 @@ u32 NetAdhocctl_Disconnect() { } else if (friendFinderRunning) { AdhocctlRequest req = { OPCODE_DISCONNECT, {0} }; - WaitAdhocctlState(req, ADHOCCTL_STATE_DISCONNECTED, us, "adhocctl disconnect"); + WaitBlockingAdhocctlSocket(req, us, "adhocctl disconnect"); } else { // Set Disconnected State @@ -2099,6 +2149,7 @@ u32 NetAdhocctl_Disconnect() { notifyAdhocctlHandlers(ADHOCCTL_EVENT_DISCONNECT, 0); //hleCheckCurrentCallbacks(); + hleEatMicro(us); // Return Success, some games might ignore returned value and always treat it as success, otherwise repeatedly calling this function return 0; } @@ -2177,8 +2228,10 @@ int sceNetAdhocctlTerm() { INFO_LOG(SCENET, "sceNetAdhocctlTerm()"); //if (netAdhocMatchingInited) NetAdhocMatching_Term(); + int retval = NetAdhocctl_Term(); - return NetAdhocctl_Term(); + hleEatMicro(adhocDefaultDelay); + return retval; } static int sceNetAdhocctlGetNameByAddr(const char *mac, u32 nameAddr) { @@ -2369,13 +2422,14 @@ int NetAdhocctl_Create(const char* groupName) { //setConnectionStatus(1); // Wait for Status to be connected to prevent Ford Street Racing from Failed to create game session - int us = adhocEventDelayMS * 1000; + int us = adhocDefaultDelay; if (adhocctlState != ADHOCCTL_STATE_CONNECTED && iResult == SOCKET_ERROR && friendFinderRunning) { AdhocctlRequest req = { OPCODE_CONNECT, {0} }; if (groupNameStruct != NULL) req.group = *groupNameStruct; - return WaitAdhocctlState(req, ADHOCCTL_STATE_CONNECTED, us, "adhocctl connect"); + return WaitBlockingAdhocctlSocket(req, us, "adhocctl connect"); } + hleEatMicro(us); // Return Success return 0; } @@ -2602,8 +2656,10 @@ int NetAdhoc_Term() { int sceNetAdhocTerm() { // WLAN might be disabled in the middle of successfull multiplayer, but we still need to cleanup all the sockets right? + int retval = NetAdhoc_Term(); - return hleLogSuccessInfoI(SCENET, NetAdhoc_Term()); + hleEatMicro(adhocDefaultDelay); + return hleLogSuccessInfoI(SCENET, retval); } static int sceNetAdhocGetPdpStat(u32 structSize, u32 structAddr) { @@ -4152,7 +4208,7 @@ static int sceNetAdhocMatchingCreate(int mode, int maxnum, int port, int rxbufle if (keepalive_int < 1) context->keepalive_int = PSP_ADHOCCTL_PING_TIMEOUT; else context->keepalive_int = keepalive_int; // client might set this to 0 context->keepalivecounter = init_count; // used to multiply keepalive_int as timeout context->timeout = (((u64)(keepalive_int) + (u64)rexmt_int) * (u64)init_count); - context->timeout += (adhocDefaultTimeout * 1000ULL); // For internet play we need higher timeout than what the game wanted + context->timeout += adhocDefaultTimeout; // For internet play we need higher timeout than what the game wanted context->handler = handler; // Fill in Selfpeer @@ -4279,10 +4335,7 @@ int NetAdhocMatching_Start(int matchingId, int evthPri, int evthPartitionId, int peerlock.unlock(); // Give a little time to make sure matching Threads are ready before the game use the next sceNet functions, should've checked for status instead of guessing the time? - //sleep_ms(adhocMatchingEventDelayMS); - hleDelayResult(0, "give some time", adhocMatchingEventDelayMS * 1000); - - return 0; + return hleDelayResult(0, "give some time", adhocMatchingEventDelay); } #define KERNEL_PARTITION_ID 1 @@ -5089,11 +5142,13 @@ int sceNetAdhocMatchingGetPoolStat(u32 poolstatPtr) { void __NetTriggerCallbacks() { std::lock_guard adhocGuard(adhocEvtMtx); + hleSkipDeadbeef(); int delayus = 10000; - + auto params = adhocctlEvents.begin(); if (params != adhocctlEvents.end()) { + int newState = adhocctlState; u32 flags = params->first; u32 error = params->second; u32_le args[3] = { 0, 0, 0 }; @@ -5101,7 +5156,7 @@ void __NetTriggerCallbacks() args[1] = error; // FIXME: When Joining a group, Do we need to wait for group creator's peer data before triggering the callback to make sure the game not to thinks we're the group creator? - u64 now = (u64)(time_now_d() * 1000.0); + u64 now = (u64)(time_now_d() * 1000000.0); if ((flags != ADHOCCTL_EVENT_CONNECT && flags != ADHOCCTL_EVENT_GAME) || adhocConnectionType != ADHOC_JOIN || getActivePeerCount() > 0 || now - adhocctlStartTime > adhocDefaultTimeout) { // Since 0 is a valid index to types_ we use -1 to detects if it was loaded from an old save state @@ -5109,30 +5164,42 @@ void __NetTriggerCallbacks() actionAfterAdhocMipsCall = __KernelRegisterActionType(AfterAdhocMipsCall::Create); } - delayus = (adhocEventPollDelayMS + 2 * adhocExtraPollDelayMS) * 1000; // Added an extra delay to prevent I/O Timing method from causing disconnection + delayus = adhocEventPollDelay; // May need to add an extra delay if a certain I/O Timing method causing disconnection issue switch (flags) { case ADHOCCTL_EVENT_CONNECT: - adhocctlState = ADHOCCTL_STATE_CONNECTED; - delayus = (adhocEventDelayMS + 2 * adhocExtraPollDelayMS) * 1000; // May affects Dissidia 012 and GTA VCS + newState = ADHOCCTL_STATE_CONNECTED; + if (adhocConnectionType != ADHOC_JOIN) + delayus = adhocEventDelay; // May affects Dissidia 012 and GTA VCS break; case ADHOCCTL_EVENT_SCAN: // notified only when scan completed? - adhocctlState = ADHOCCTL_STATE_DISCONNECTED; + newState = ADHOCCTL_STATE_DISCONNECTED; break; case ADHOCCTL_EVENT_DISCONNECT: - adhocctlState = ADHOCCTL_STATE_DISCONNECTED; - break; - case ADHOCCTL_EVENT_GAME: - adhocctlState = ADHOCCTL_STATE_GAMEMODE; - delayus = (adhocEventDelayMS + 2 * adhocExtraPollDelayMS) * 1000; + newState = ADHOCCTL_STATE_DISCONNECTED; break; + case ADHOCCTL_EVENT_GAME: + { + newState = ADHOCCTL_STATE_GAMEMODE; + if (adhocConnectionType != ADHOC_JOIN) + delayus = adhocEventDelay; + // Shows player list + INFO_LOG(SCENET, "GameMode - All players have joined:"); + int i = 0; + for (auto& mac : gameModeMacs) { + INFO_LOG(SCENET, "GameMode macAddress#%d=%s", i++, mac2str(&mac).c_str()); + if (i >= ADHOCCTL_GAMEMODE_MAX_MEMBERS) + break; + } + } + break; case ADHOCCTL_EVENT_DISCOVER: - adhocctlState = ADHOCCTL_STATE_DISCOVER; + newState = ADHOCCTL_STATE_DISCOVER; break; case ADHOCCTL_EVENT_WOL_INTERRUPT: - adhocctlState = ADHOCCTL_STATE_WOL; + newState = ADHOCCTL_STATE_WOL; break; case ADHOCCTL_EVENT_ERROR: - adhocctlState = ADHOCCTL_STATE_DISCONNECTED; + newState = ADHOCCTL_STATE_DISCONNECTED; break; } @@ -5144,12 +5211,14 @@ void __NetTriggerCallbacks() hleEnqueueCall(it->second.entryPoint, 3, args, after); } adhocctlEvents.pop_front(); + // Since we don't have beforeAction, simulate it using ScheduleEvent + ScheduleAdhocctlState(flags, newState, delayus, "adhocctl callback state"); + return; } } // Must be delayed long enough whenever there is a pending callback. Should it be 100-500ms for Adhocctl Events? or Not Less than the delays on sceNetAdhocctl HLE? sceKernelDelayThread(delayus); - hleSkipDeadbeef(); } void __NetMatchingCallbacks() //(int matchingId) @@ -5172,7 +5241,7 @@ void __NetMatchingCallbacks() //(int matchingId) after->SetData(args[0], args[1], args[2]); hleEnqueueCall(args[5], 5, args, after); matchingEvents.pop_front(); - delayus = (adhocMatchingEventDelayMS + 2 * adhocExtraPollDelayMS) * 1000; // Added an extra delay to prevent I/O Timing method from causing disconnection + delayus = (adhocMatchingEventDelay + adhocExtraDelay); // Added an extra delay to prevent I/O Timing method from causing disconnection } // Must be delayed long enough whenever there is a pending callback. Should it be 10-100ms for Matching Events? or Not Less than the delays on sceNetAdhocMatching HLE? diff --git a/Core/HLE/sceNetAdhoc.h b/Core/HLE/sceNetAdhoc.h index 0ec9218bb5..682a609b2f 100644 --- a/Core/HLE/sceNetAdhoc.h +++ b/Core/HLE/sceNetAdhoc.h @@ -98,11 +98,12 @@ int NetAdhoc_Term(); extern bool netAdhocInited; extern bool netAdhocctlInited; extern bool networkInited; -extern int adhocDefaultTimeout; -extern int adhocExtraPollDelayMS; -extern int adhocEventPollDelayMS; -extern int adhocMatchingEventDelayMS; -extern int adhocEventDelayMS; // This will affect the duration of "Connecting..." dialog/message box in .Hack//Link and Naruto Ultimate Ninja Heroes 3 +extern int adhocDefaultTimeout; //3000000 usec +extern int adhocDefaultDelay; //10000 +extern int adhocExtraDelay; //20000 +extern int adhocEventPollDelay; //100000; // Seems to be the same with PSP_ADHOCCTL_RECV_TIMEOUT +extern int adhocMatchingEventDelay; //30000 +extern int adhocEventDelay; //1000000 extern std::recursive_mutex adhocEvtMtx; extern int IsAdhocctlInCB;