mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-23 13:30:02 +00:00
Fix Apctl and Adhocctl state change timings to works properly. Which fix the stuck MessageBox on Naruto Shippuden Ultimate Ninja Heroes 3 and .hack//Link properly.
This commit is contained in:
parent
59f395f6ef
commit
8a96112e1a
@ -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);
|
||||
|
@ -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<ApctlArgs> 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<std::recursive_mutex> 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<int, NpAuthHandler>::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<int, ApctlHandler>::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<int, NpAuthHandler>::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;
|
||||
}
|
||||
|
||||
|
@ -219,6 +219,7 @@ private:
|
||||
|
||||
extern bool netInetInited;
|
||||
extern bool netApctlInited;
|
||||
extern u32 netApctlState;
|
||||
extern SceNetApctlInfoInternal netApctlInfo;
|
||||
|
||||
template <typename I> std::string num2hex(I w, size_t hex_len = sizeof(I) << 1);
|
||||
|
@ -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<SceUID> matchingThreads;
|
||||
int IsAdhocctlInCB = 0;
|
||||
|
||||
int adhocctlNotifyEvent = -1;
|
||||
int adhocctlStateEvent = -1;
|
||||
int adhocSocketNotifyEvent = -1;
|
||||
std::map<int, AdhocctlRequest> adhocctlRequests;
|
||||
std::map<u64, AdhocSocketRequest> 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<u64>(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<std::recursive_mutex> 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?
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user