From 335650da6cd160d3527a56f4cd6b4dff471d268d Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Wed, 21 Oct 2020 07:45:16 +0700 Subject: [PATCH 01/17] Prevent Port leaks after loading SaveState in the middle of multiplayer resulting to "port in use" error on the next attempt to play multiplayer. --- Core/HLE/sceNetAdhoc.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 0ec8c85ecf30..9cbbd132e7ed 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -960,6 +960,7 @@ void __NetAdhocDoState(PointerWrap &p) { adhocctlRequests.clear(); adhocSocketRequests.clear(); sendTargetPeers.clear(); + deleteAllAdhocSockets(); // Let's not change "Inited" value when Loading SaveState to prevent memory & port leaks netAdhocMatchingInited = cur_netAdhocMatchingInited; From 5216433e0cd55e50627c930da61a469736497a0e Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Wed, 21 Oct 2020 07:47:25 +0700 Subject: [PATCH 02/17] Prevent memory leaks when attempting to play GameMode multiplayer again after loading from SaveState in the middle of GameMode multiplayer. --- Core/HLE/sceNetAdhoc.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 9cbbd132e7ed..b9ac49fb4e41 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -2580,9 +2580,9 @@ int NetAdhocctl_CreateEnterGameMode(const char* group_name, int game_type, int n if (game_type <= 0 || game_type > 3 || num_members < 2 || num_members > 16 || (game_type == 1 && num_members > 4)) return ERROR_NET_ADHOCCTL_INVALID_ARG; + deleteAllGMB(); + SceNetEtherAddr* addrs = PSPPointer::Create(membersAddr); // List of participating MAC addresses (started from host) - gameModeMacs.clear(); - requiredGameModeMacs.clear(); for (int i = 0; i < num_members; i++) { requiredGameModeMacs.push_back(*addrs); DEBUG_LOG(SCENET, "GameMode macAddress#%d=%s", i, mac2str(addrs).c_str()); @@ -2643,6 +2643,8 @@ static int sceNetAdhocctlJoinEnterGameMode(const char * group_name, const char * if (!hostMac) return hleLogError(SCENET, ERROR_NET_ADHOCCTL_INVALID_ARG, "invalid arg"); + deleteAllGMB(); + // Add host mac first gameModeMacs.push_back(*(SceNetEtherAddr*)hostMac); @@ -2681,12 +2683,12 @@ int NetAdhoc_Term() { // Library is initialized if (netAdhocInited) { - // Delete Adhoc Sockets - deleteAllAdhocSockets(); - // Delete GameMode Buffers deleteAllGMB(); + // Delete Adhoc Sockets + deleteAllAdhocSockets(); + // Terminate Internet Library //sceNetInetTerm(); From 6e50487fcd43cbc399565a52bd75eed1d7434faa Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Thu, 22 Oct 2020 00:58:39 +0700 Subject: [PATCH 03/17] Updated error code on sceNetGetLocalEtherAddr --- Core/HLE/sceNet.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index 15e5179d5e3d..448b0ba66e19 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -676,8 +676,6 @@ static u32 sceWlanGetEtherAddr(u32 addrAddr) { return hleLogError(SCENET, SCE_KERNEL_ERROR_ILLEGAL_ADDR, "illegal address"); } - // FIXME: Return 0x80410180 (pspnet[_core] error code?) when Adhocctl not connected to a group (ie. ADHOCCTL_STATE_DISCONNECTED)? - u8 *addr = Memory::GetPointer(addrAddr); if (PPSSPP_ID > 1) { Memory::Memset(addrAddr, PPSSPP_ID, 6); @@ -697,6 +695,10 @@ static u32 sceWlanGetEtherAddr(u32 addrAddr) { } static u32 sceNetGetLocalEtherAddr(u32 addrAddr) { + // FIXME: Return 0x80410180 (pspnet[_core] error code?) before successful attempt to Create/Connect/Join a Group? (ie. adhocctlCurrentMode == ADHOCCTL_MODE_NONE) + if (adhocctlCurrentMode == ADHOCCTL_MODE_NONE) + return hleLogDebug(SCENET, 0x80410180, "address not available?"); + return sceWlanGetEtherAddr(addrAddr); } From 3020efdbe7a1a0c5a53f37f969522a9be9a45f7a Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Tue, 20 Oct 2020 13:09:28 +0700 Subject: [PATCH 04/17] Added hleEatMicro (may not be accurate tho) on PdpSend, PdpRecv, and GetPeerInfo. --- Core/HLE/sceNetAdhoc.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index b9ac49fb4e41..1ddda409d479 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -1360,6 +1360,7 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int // Free Network Lock //_freeNetworkLock(); + hleEatMicro(1000); // Can be longer than 1ms tho // Sent Data if (sent >= 0) { DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u]: Sent %u bytes to %s:%u\n", id, getLocalPort(pdpsocket.id), sent, inet_ntoa(target.sin_addr), ntohs(target.sin_port)); @@ -1451,6 +1452,7 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int // Free Network Lock //_freeNetworkLock(); + hleEatMicro(1000); // Success, Broadcast never fails! return 0; // len; } @@ -1478,7 +1480,6 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int // Library is uninitialized return ERROR_NET_ADHOC_NOT_INITIALIZED; - } /** @@ -1598,6 +1599,7 @@ static int sceNetAdhocPdpRecv(int id, void *addr, void * port, void *buf, void * VERBOSE_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPdpRecv[%i:%u] [size=%i]", error, id, pdpsocket.lport, *len); } + hleEatMicro(1000); // Received Data. UDP can also receives 0 data, while on TCP 0 data = connection gracefully closed, but not sure about PDP tho if (received > 0) { DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %u bytes from %s:%u\n", id, getLocalPort(pdpsocket.id), received, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); @@ -2421,6 +2423,7 @@ int sceNetAdhocctlGetPeerInfo(const char *mac, int size, u32 peerInfoAddr) { // Multithreading Unlock peerlock.unlock(); } + hleEatMicro(1000); return retval; } From 8762718abf3075f70cd4a054995b948458135648 Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Tue, 20 Oct 2020 16:52:47 +0700 Subject: [PATCH 05/17] Updated Loggings and return value of PdpSend and PdpRecv --- Core/HLE/sceNetAdhoc.cpp | 55 +++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 1ddda409d479..877f87e172bb 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -354,7 +354,7 @@ int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) { if (ret >= 0 && ret <= *req.length) { ret = recvfrom(uid, (char*)req.buffer, *req.length, MSG_NOSIGNAL, (sockaddr*)&sin, &sinlen); // UDP can also receives 0 data, while on TCP receiving 0 data = connection gracefully closed, but not sure whether PDP can send/recv 0 data or not tho - if (ret > 0) { + if (ret >= 0) { DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %u bytes from %s:%u\n", req.id, getLocalPort(uid), ret, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); // Peer MAC @@ -403,7 +403,7 @@ int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) { result = ERROR_NET_ADHOC_TIMEOUT; } else - result = ERROR_NET_ADHOC_INVALID_ARG; // ERROR_NET_ADHOC_DISCONNECTED + result = ERROR_NET_ADHOC_TIMEOUT; // ERROR_NET_ADHOC_INVALID_ARG; // ERROR_NET_ADHOC_DISCONNECTED if (ret == SOCKET_ERROR) DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv[%i]: Socket Error (%i)", req.id, sockerr); @@ -1344,7 +1344,7 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | pdpsocket.id; if (sendTargetPeers.find(threadSocketId) != sendTargetPeers.end()) { DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u]: Socket(%d) is Busy!", id, getLocalPort(pdpsocket.id), pdpsocket.id); - return ERROR_NET_ADHOC_BUSY; + return hleLogError(SCENET, ERROR_NET_ADHOC_BUSY, "busy?"); } AdhocSendTargets dest = { len, {}, false }; @@ -1371,10 +1371,10 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int // Non-Blocking if (flag) - return ERROR_NET_ADHOC_WOULD_BLOCK; + return hleLogSuccessVerboseI(SCENET, ERROR_NET_ADHOC_WOULD_BLOCK, "would block"); // Does PDP can Timeout? There is no concept of Timeout when sending UDP due to no ACK, but might happen if the socket buffer is full, not sure about PDP since some games did use the timeout arg - return ERROR_NET_ADHOC_TIMEOUT; // ERROR_NET_ADHOC_INVALID_ADDR; + return hleLogDebug(SCENET, ERROR_NET_ADHOC_TIMEOUT, "timeout?"); // ERROR_NET_ADHOC_INVALID_ADDR; } //VERBOSE_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u]: Unknown Target Peer %s:%u\n", id, getLocalPort(pdpsocket.id), mac2str(daddr, tmpmac), ntohs(target.sin_port)); } @@ -1419,7 +1419,7 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | pdpsocket.id; if (sendTargetPeers.find(threadSocketId) != sendTargetPeers.end()) { DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u](BC): Socket(%d) is Busy!", id, getLocalPort(pdpsocket.id), pdpsocket.id); - return ERROR_NET_ADHOC_BUSY; + return hleLogError(SCENET, ERROR_NET_ADHOC_BUSY, "busy?"); } sendTargetPeers[threadSocketId] = dest; @@ -1459,27 +1459,27 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int } // Invalid Destination Address - return ERROR_NET_ADHOC_INVALID_ADDR; + return hleLogError(SCENET, ERROR_NET_ADHOC_INVALID_ADDR, "invalid address"); } // Invalid Argument - return ERROR_NET_ADHOC_INVALID_ARG; + return hleLogError(SCENET, ERROR_NET_ADHOC_INVALID_ARG, "invalid arg"); } // Invalid Socket ID - return ERROR_NET_ADHOC_INVALID_SOCKET_ID; + return hleLogError(SCENET, ERROR_NET_ADHOC_INVALID_SOCKET_ID, "invalid socket id"); } // Invalid Data Length - return ERROR_NET_ADHOC_INVALID_DATALEN; + return hleLogError(SCENET, ERROR_NET_ADHOC_INVALID_DATALEN, "invalid data length"); } // Invalid Destination Port - return ERROR_NET_ADHOC_INVALID_PORT; + return hleLogError(SCENET, ERROR_NET_ADHOC_INVALID_PORT, "invalid port"); } // Library is uninitialized - return ERROR_NET_ADHOC_NOT_INITIALIZED; + return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_INITIALIZED, "not initialized"); } /** @@ -1584,24 +1584,25 @@ static int sceNetAdhocPdpRecv(int id, void *addr, void * port, void *buf, void * // Free Network Lock //_freeNetworkLock(); - return ERROR_NET_ADHOC_NOT_ENOUGH_SPACE; //received; + return hleLogSuccessVerboseI(SCENET, ERROR_NET_ADHOC_NOT_ENOUGH_SPACE, "not enough space"); //received; } received = recvfrom(pdpsocket.id, (char*)buf, *len, MSG_NOSIGNAL, (sockaddr*)&sin, &sinlen); error = errno; - if (received == SOCKET_ERROR && (error == EAGAIN || error == EWOULDBLOCK)) { + // On Windows: recvfrom on UDP can get error WSAECONNRESET when previous sendto's destination is unreachable (or destination port is not bound), may need to disable SIO_UDP_CONNRESET + if (received == SOCKET_ERROR && (error == EAGAIN || error == EWOULDBLOCK || error == ECONNRESET)) { if (flag == 0) { // Simulate blocking behaviour with non-blocking socket u64 threadSocketId = ((u64)__KernelGetCurThread()) << 32 | pdpsocket.id; return WaitBlockingAdhocSocket(threadSocketId, PDP_RECV, id, buf, len, timeout, saddr, sport, "pdp recv"); } - VERBOSE_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPdpRecv[%i:%u] [size=%i]", error, id, pdpsocket.lport, *len); + return hleLogSuccessVerboseI(SCENET, ERROR_NET_ADHOC_WOULD_BLOCK, "would block"); } hleEatMicro(1000); // Received Data. UDP can also receives 0 data, while on TCP 0 data = connection gracefully closed, but not sure about PDP tho - if (received > 0) { + if (received >= 0) { DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %u bytes from %s:%u\n", id, getLocalPort(pdpsocket.id), received, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); // Peer MAC @@ -1650,29 +1651,22 @@ static int sceNetAdhocPdpRecv(int id, void *addr, void * port, void *buf, void * if (wouldblock) flag = 1; #endif - // Nothing received. On Windows: recvfrom on UDP can get error WSAECONNRESET when previous sendto's destination is unreachable (or destination port is not bound), may need to disable SIO_UDP_CONNRESET - if (received == SOCKET_ERROR && (error == EAGAIN || error == EWOULDBLOCK || error == ECONNRESET || error == ETIMEDOUT)) { - // Blocking Situation - if (flag) return ERROR_NET_ADHOC_WOULD_BLOCK; - - // Timeout - return ERROR_NET_ADHOC_TIMEOUT; - } + DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Result:%i (Error:%i)", id, pdpsocket.lport, received, error); - // Disconnected - return ERROR_NET_ADHOC_DISCONNECTED; + // Timeout? + return hleLogError(SCENET, ERROR_NET_ADHOC_TIMEOUT, "timeout"); } // Invalid Argument - return ERROR_NET_ADHOC_INVALID_ARG; + return hleLogError(SCENET, ERROR_NET_ADHOC_INVALID_ARG, "invalid arg"); } // Invalid Socket ID - return ERROR_NET_ADHOC_INVALID_SOCKET_ID; + return hleLogError(SCENET, ERROR_NET_ADHOC_INVALID_SOCKET_ID, "invalid socket id"); } // Library is uninitialized - return ERROR_NET_ADHOC_NOT_INITIALIZED; + return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_INITIALIZED, "not initialized"); } int NetAdhoc_SetSocketAlert(int id, s32_le flag) { @@ -3756,6 +3750,9 @@ static int sceNetAdhocPtpRecv(int id, u32 dataAddr, u32 dataSizeAddr, int timeou DEBUG_LOG(SCENET, "sceNetAdhocPtpRecv[%i:%u]: Result:%i (Error:%i)", id, ptpsocket.lport, received, error); + if (*len == 0) + return 0; + // Change Socket State ptpsocket.state = ADHOC_PTP_STATE_CLOSED; From 40102787c85f25445319c257f82b5abb7c2aa7a6 Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Thu, 22 Oct 2020 08:44:23 +0700 Subject: [PATCH 06/17] Updated validity checking of mac address on PdpCreate, PtpListen, and PtpOpen --- Core/HLE/sceNetAdhoc.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 877f87e172bb..8854e4687f8e 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -1125,8 +1125,8 @@ static int sceNetAdhocPdpCreate(const char *mac, int port, int bufferSize, u32 f if (saddr != NULL) { getLocalMac(saddr); } - // Valid MAC supplied - if (isLocalMAC(saddr)) { + // Valid MAC supplied. FIXME: MAC only valid after successful attempt to Create/Connect/Join a Group? (ie. adhocctlCurrentMode != ADHOCCTL_MODE_NONE) + if ((adhocctlCurrentMode != ADHOCCTL_MODE_NONE) && isLocalMAC(saddr)) { // Create Internet UDP Socket int usocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // Valid Socket produced @@ -2912,8 +2912,8 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, if (saddr != NULL) { getLocalMac(saddr); } - // Valid Addresses - if (saddr != NULL && isLocalMAC(saddr) && daddr != NULL && !isBroadcastMAC(daddr) && !isZeroMAC(daddr)) { + // Valid Addresses. FIXME: MAC only valid after successful attempt to Create/Connect/Join a Group? (ie. adhocctlCurrentMode != ADHOCCTL_MODE_NONE) + if ((adhocctlCurrentMode != ADHOCCTL_MODE_NONE) && saddr != NULL && isLocalMAC(saddr) && daddr != NULL && !isBroadcastMAC(daddr) && !isZeroMAC(daddr)) { // Dissidia 012 will try to reOpen the port without Closing the old one first when PtpConnect failed to try again. if (isPTPPortInUse(sport, false)) return hleLogDebug(SCENET, ERROR_NET_ADHOC_PORT_IN_USE, "port in use"); @@ -3438,8 +3438,8 @@ static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int if (saddr != NULL) { getLocalMac(saddr); } - // Valid Address - if (saddr != NULL && isLocalMAC(saddr)) { + // Valid Address. FIXME: MAC only valid after successful attempt to Create/Connect/Join a Group? (ie. adhocctlCurrentMode != ADHOCCTL_MODE_NONE) + if ((adhocctlCurrentMode != ADHOCCTL_MODE_NONE) && saddr != NULL && isLocalMAC(saddr)) { // It's allowed to Listen and Open the same PTP port, But it's not allowed to Listen or Open the same PTP port twice. if (isPTPPortInUse(sport, true)) return hleLogDebug(SCENET, ERROR_NET_ADHOC_PORT_IN_USE, "port in use"); From 0363319f141721107db60cff0a2b94173eba439c Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Wed, 21 Oct 2020 08:43:34 +0700 Subject: [PATCH 07/17] Prevent Scanning issue after reconnected to AdhocServer, also Updated logging to showing the error code on sceNetAdhocctlScan --- Core/HLE/proAdhoc.cpp | 2 ++ Core/HLE/sceNetAdhoc.cpp | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index c408aa8b52f9..9eaf171a0251 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -1334,6 +1334,8 @@ int friendFinder(){ if (initNetwork(&product_code) == 0) { networkInited = true; INFO_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; } else { networkInited = false; diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 8854e4687f8e..70a00000e14e 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -1974,7 +1974,7 @@ int sceNetAdhocctlScan() { ERROR_LOG(SCENET, "Socket error (%i) when sending", error); adhocctlState = ADHOCCTL_STATE_DISCONNECTED; //if (error == ECONNABORTED || error == ECONNRESET || error == ENOTCONN) return ERROR_NET_ADHOCCTL_NOT_INITIALIZED; // A case where it need to reconnect to AdhocServer - return ERROR_NET_ADHOCCTL_BUSY; + return hleLogError(SCENET, ERROR_NET_ADHOCCTL_BUSY, "busy"); } else if (friendFinderRunning) { AdhocctlRequest req = { OPCODE_SCAN, {0} }; @@ -1989,7 +1989,7 @@ int sceNetAdhocctlScan() { } } else if (adhocctlState == ADHOCCTL_STATE_SCANNING) - return ERROR_NET_ADHOCCTL_BUSY; + return hleLogError(SCENET, ERROR_NET_ADHOCCTL_BUSY, "busy"); // Already connected to a group. Should we fake a success? // We need to notify the handler on success, even if it was faked. Using flag = 0/ADHOCCTL_EVENT_ERROR for error? @@ -2000,7 +2000,7 @@ int sceNetAdhocctlScan() { } // Library uninitialized - return ERROR_NET_ADHOCCTL_NOT_INITIALIZED; + return hleLogError(SCENET, ERROR_NET_ADHOCCTL_NOT_INITIALIZED, "not initialized"); } int sceNetAdhocctlGetScanInfo(u32 sizeAddr, u32 bufAddr) { From fa95afab0716ab085515226c564d56511aa88a3c Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Thu, 22 Oct 2020 09:46:22 +0700 Subject: [PATCH 08/17] Do not clear socket alert flag after signaled. --- Core/HLE/sceNetAdhoc.cpp | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 70a00000e14e..435f6251248b 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -391,8 +391,6 @@ int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) { auto sock = adhocSockets[req.id - 1]; if (sock->flags & ADHOC_F_ALERTRECV) { result = ERROR_NET_ADHOC_SOCKET_ALERTED; - // FIXME: Should we clear the flag after alert signaled? - sock->flags &= ~ADHOC_F_ALERTRECV; sock->alerted_flags |= ADHOC_F_ALERTRECV; } else if (req.timeout == 0 || now - req.startTime <= req.timeout) { @@ -437,8 +435,6 @@ int DoBlockingPdpSend(int uid, AdhocSocketRequest& req, s64& result, AdhocSendTa u64 now = (u64)(time_now_d() * 1000000.0); if (sock->flags & ADHOC_F_ALERTSEND) { result = ERROR_NET_ADHOC_SOCKET_ALERTED; - // FIXME: Should we clear the flag after alert signaled? - sock->flags &= ~ADHOC_F_ALERTSEND; sock->alerted_flags |= ADHOC_F_ALERTSEND; break; } @@ -484,8 +480,6 @@ int DoBlockingPtpSend(int uid, AdhocSocketRequest& req, s64& result) { u64 now = (u64)(time_now_d() * 1000000.0); if (sock->flags & ADHOC_F_ALERTSEND) { result = ERROR_NET_ADHOC_SOCKET_ALERTED; - // FIXME: Should we clear the flag after alert signaled? - sock->flags &= ~ADHOC_F_ALERTSEND; sock->alerted_flags |= ADHOC_F_ALERTSEND; } else if (req.timeout == 0 || now - req.startTime <= req.timeout) { @@ -533,8 +527,6 @@ int DoBlockingPtpRecv(int uid, AdhocSocketRequest& req, s64& result) { u64 now = (u64)(time_now_d() * 1000000.0); if (sock->flags & ADHOC_F_ALERTRECV) { result = ERROR_NET_ADHOC_SOCKET_ALERTED; - // FIXME: Should we clear the flag after alert signaled? - sock->flags &= ~ADHOC_F_ALERTRECV; sock->alerted_flags |= ADHOC_F_ALERTRECV; } else if (req.timeout == 0 || now - req.startTime <= req.timeout) { @@ -583,8 +575,6 @@ int DoBlockingPtpAccept(int uid, AdhocSocketRequest& req, s64& result) { u64 now = (u64)(time_now_d() * 1000000.0); if (sock->flags & ADHOC_F_ALERTACCEPT) { result = ERROR_NET_ADHOC_SOCKET_ALERTED; - // FIXME: Should we clear the flag after alert signaled? - sock->flags &= ~ADHOC_F_ALERTACCEPT; sock->alerted_flags |= ADHOC_F_ALERTACCEPT; } else if (req.timeout == 0 || now - req.startTime <= req.timeout) { @@ -631,8 +621,6 @@ int DoBlockingPtpConnect(int uid, AdhocSocketRequest& req, s64& result) { u64 now = (u64)(time_now_d() * 1000000.0); if (sock->flags & ADHOC_F_ALERTCONNECT) { result = ERROR_NET_ADHOC_SOCKET_ALERTED; - // FIXME: Should we clear the flag after alert signaled? - sock->flags &= ~ADHOC_F_ALERTCONNECT; sock->alerted_flags |= ADHOC_F_ALERTCONNECT; } else if (req.timeout == 0 || now - req.startTime <= req.timeout) { @@ -667,8 +655,6 @@ int DoBlockingPtpFlush(int uid, AdhocSocketRequest& req, s64& result) { u64 now = (u64)(time_now_d() * 1000000.0); if (sock->flags & ADHOC_F_ALERTFLUSH) { result = ERROR_NET_ADHOC_SOCKET_ALERTED; - // FIXME: Should we clear the flag after alert signaled? - sock->flags &= ~ADHOC_F_ALERTFLUSH; sock->alerted_flags |= ADHOC_F_ALERTFLUSH; } else if (req.timeout == 0 || now - req.startTime <= req.timeout) { @@ -1758,8 +1744,6 @@ int PollAdhocSocket(SceNetAdhocPollSd* sds, int count, int timeout, int nonblock } if (sock->flags & ADHOC_F_ALERTPOLL) { - // FIXME: Should we clear the flag after alert signaled? - sock->flags &= ~ADHOC_F_ALERTPOLL; sock->alerted_flags |= ADHOC_F_ALERTPOLL; return ERROR_NET_ADHOC_SOCKET_ALERTED; From d87713226c270af0c3a890a2b36ff25c7adf781e Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Thu, 22 Oct 2020 12:19:11 +0700 Subject: [PATCH 09/17] Socket alerts can be triggered regardless of the nonblocking flag --- Core/HLE/sceNetAdhoc.cpp | 126 +++++++++++++++++++++++++++------------ 1 file changed, 88 insertions(+), 38 deletions(-) diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 435f6251248b..cc305523915b 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -343,6 +343,13 @@ int StartGameModeScheduler(int bufSize) { } int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) { + auto sock = adhocSockets[req.id - 1]; + if (sock->flags & ADHOC_F_ALERTRECV) { + result = ERROR_NET_ADHOC_SOCKET_ALERTED; + sock->alerted_flags |= ADHOC_F_ALERTRECV; + return 0; + } + sockaddr_in sin; memset(&sin, 0, sizeof(sin)); socklen_t sinlen = sizeof(sin); @@ -388,12 +395,7 @@ int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) { // On Windows: recvfrom on UDP can get error WSAECONNRESET when previous sendto's destination is unreachable (or destination port is not bound yet), may need to disable SIO_UDP_CONNRESET error else if (sockerr == EAGAIN || sockerr == EWOULDBLOCK || sockerr == ECONNRESET) { u64 now = (u64)(time_now_d() * 1000000.0); - auto sock = adhocSockets[req.id - 1]; - if (sock->flags & ADHOC_F_ALERTRECV) { - result = ERROR_NET_ADHOC_SOCKET_ALERTED; - sock->alerted_flags |= ADHOC_F_ALERTRECV; - } - else if (req.timeout == 0 || now - req.startTime <= req.timeout) { + if (req.timeout == 0 || now - req.startTime <= req.timeout) { // Try again later return -1; } @@ -412,6 +414,11 @@ int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) { int DoBlockingPdpSend(int uid, AdhocSocketRequest& req, s64& result, AdhocSendTargets& targetPeers) { auto sock = adhocSockets[req.id - 1]; auto& pdpsocket = sock->data.pdp; + if (sock->flags & ADHOC_F_ALERTSEND) { + result = ERROR_NET_ADHOC_SOCKET_ALERTED; + sock->alerted_flags |= ADHOC_F_ALERTSEND; + return 0; + } result = 0; bool retry = false; @@ -433,12 +440,7 @@ int DoBlockingPdpSend(int uid, AdhocSocketRequest& req, s64& result, AdhocSendTa else { if (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK)) { u64 now = (u64)(time_now_d() * 1000000.0); - if (sock->flags & ADHOC_F_ALERTSEND) { - result = ERROR_NET_ADHOC_SOCKET_ALERTED; - sock->alerted_flags |= ADHOC_F_ALERTSEND; - break; - } - else if (req.timeout == 0 || now - req.startTime <= req.timeout) { + if (req.timeout == 0 || now - req.startTime <= req.timeout) { retry = true; } else @@ -461,6 +463,11 @@ int DoBlockingPdpSend(int uid, AdhocSocketRequest& req, s64& result, AdhocSendTa int DoBlockingPtpSend(int uid, AdhocSocketRequest& req, s64& result) { auto sock = adhocSockets[req.id - 1]; auto& ptpsocket = sock->data.ptp; + if (sock->flags & ADHOC_F_ALERTSEND) { + result = ERROR_NET_ADHOC_SOCKET_ALERTED; + sock->alerted_flags |= ADHOC_F_ALERTSEND; + return 0; + } // Send Data int ret = send(uid, (const char*)req.buffer, *req.length, MSG_NOSIGNAL); @@ -478,11 +485,7 @@ int DoBlockingPtpSend(int uid, AdhocSocketRequest& req, s64& result) { } else if (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK)) { u64 now = (u64)(time_now_d() * 1000000.0); - if (sock->flags & ADHOC_F_ALERTSEND) { - result = ERROR_NET_ADHOC_SOCKET_ALERTED; - sock->alerted_flags |= ADHOC_F_ALERTSEND; - } - else if (req.timeout == 0 || now - req.startTime <= req.timeout) { + if (req.timeout == 0 || now - req.startTime <= req.timeout) { return -1; } else @@ -505,6 +508,11 @@ int DoBlockingPtpSend(int uid, AdhocSocketRequest& req, s64& result) { int DoBlockingPtpRecv(int uid, AdhocSocketRequest& req, s64& result) { auto sock = adhocSockets[req.id - 1]; auto& ptpsocket = sock->data.ptp; + if (sock->flags & ADHOC_F_ALERTRECV) { + result = ERROR_NET_ADHOC_SOCKET_ALERTED; + sock->alerted_flags |= ADHOC_F_ALERTRECV; + return 0; + } int ret = recv(uid, (char*)req.buffer, *req.length, MSG_NOSIGNAL); int sockerr = errno; @@ -525,11 +533,7 @@ int DoBlockingPtpRecv(int uid, AdhocSocketRequest& req, s64& result) { } else if (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK)) { u64 now = (u64)(time_now_d() * 1000000.0); - if (sock->flags & ADHOC_F_ALERTRECV) { - result = ERROR_NET_ADHOC_SOCKET_ALERTED; - sock->alerted_flags |= ADHOC_F_ALERTRECV; - } - else if (req.timeout == 0 || now - req.startTime <= req.timeout) { + if (req.timeout == 0 || now - req.startTime <= req.timeout) { return -1; } else @@ -552,6 +556,12 @@ int DoBlockingPtpRecv(int uid, AdhocSocketRequest& req, s64& result) { int DoBlockingPtpAccept(int uid, AdhocSocketRequest& req, s64& result) { auto sock = adhocSockets[req.id - 1]; auto& ptpsocket = sock->data.ptp; + if (sock->flags & ADHOC_F_ALERTACCEPT) { + result = ERROR_NET_ADHOC_SOCKET_ALERTED; + sock->alerted_flags |= ADHOC_F_ALERTACCEPT; + return 0; + } + sockaddr_in sin; memset(&sin, 0, sizeof(sin)); socklen_t sinlen = sizeof(sin); @@ -573,11 +583,7 @@ int DoBlockingPtpAccept(int uid, AdhocSocketRequest& req, s64& result) { } else if (ret == 0 || (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK))) { u64 now = (u64)(time_now_d() * 1000000.0); - if (sock->flags & ADHOC_F_ALERTACCEPT) { - result = ERROR_NET_ADHOC_SOCKET_ALERTED; - sock->alerted_flags |= ADHOC_F_ALERTACCEPT; - } - else if (req.timeout == 0 || now - req.startTime <= req.timeout) { + if (req.timeout == 0 || now - req.startTime <= req.timeout) { return -1; } else { @@ -596,8 +602,13 @@ int DoBlockingPtpAccept(int uid, AdhocSocketRequest& req, s64& result) { int DoBlockingPtpConnect(int uid, AdhocSocketRequest& req, s64& result) { auto sock = adhocSockets[req.id - 1]; auto& ptpsocket = sock->data.ptp; - int sockerr; + if (sock->flags & ADHOC_F_ALERTCONNECT) { + result = ERROR_NET_ADHOC_SOCKET_ALERTED; + sock->alerted_flags |= ADHOC_F_ALERTCONNECT; + return 0; + } + int sockerr; // Wait for Connection (assuming "connect" has been called before) int ret = IsSocketReady(uid, false, true, &sockerr); @@ -619,11 +630,7 @@ int DoBlockingPtpConnect(int uid, AdhocSocketRequest& req, s64& result) { // Timeout else if (ret == 0) { u64 now = (u64)(time_now_d() * 1000000.0); - if (sock->flags & ADHOC_F_ALERTCONNECT) { - result = ERROR_NET_ADHOC_SOCKET_ALERTED; - sock->alerted_flags |= ADHOC_F_ALERTCONNECT; - } - else if (req.timeout == 0 || now - req.startTime <= req.timeout) { + if (req.timeout == 0 || now - req.startTime <= req.timeout) { return -1; } else { @@ -646,6 +653,11 @@ int DoBlockingPtpConnect(int uid, AdhocSocketRequest& req, s64& result) { int DoBlockingPtpFlush(int uid, AdhocSocketRequest& req, s64& result) { auto sock = adhocSockets[req.id - 1]; auto& ptpsocket = sock->data.ptp; + if (sock->flags & ADHOC_F_ALERTFLUSH) { + result = ERROR_NET_ADHOC_SOCKET_ALERTED; + sock->alerted_flags |= ADHOC_F_ALERTFLUSH; + return 0; + } // Try Sending Empty Data int sockerr = FlushPtpSocket(uid); @@ -653,11 +665,7 @@ int DoBlockingPtpFlush(int uid, AdhocSocketRequest& req, s64& result) { if (sockerr == EAGAIN || sockerr == EWOULDBLOCK) { u64 now = (u64)(time_now_d() * 1000000.0); - if (sock->flags & ADHOC_F_ALERTFLUSH) { - result = ERROR_NET_ADHOC_SOCKET_ALERTED; - sock->alerted_flags |= ADHOC_F_ALERTFLUSH; - } - else if (req.timeout == 0 || now - req.startTime <= req.timeout) { + if (req.timeout == 0 || now - req.startTime <= req.timeout) { return -1; } else @@ -1305,6 +1313,12 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int if (timeout > 0) setSockTimeout(pdpsocket.id, SO_SNDTIMEO, timeout); + if (socket->flags & ADHOC_F_ALERTSEND) { + socket->alerted_flags |= ADHOC_F_ALERTSEND; + + return hleLogError(SCENET, ERROR_NET_ADHOC_SOCKET_ALERTED, "socket alerted"); + } + // Single Target if (!isBroadcastMAC(daddr)) { // Fill in Target Structure @@ -1530,6 +1544,12 @@ static int sceNetAdhocPdpRecv(int id, void *addr, void * port, void *buf, void * if (timeout > 0) setSockTimeout(pdpsocket.id, SO_RCVTIMEO, timeout); + if (socket->flags & ADHOC_F_ALERTRECV) { + socket->alerted_flags |= ADHOC_F_ALERTRECV; + + return hleLogError(SCENET, ERROR_NET_ADHOC_SOCKET_ALERTED, "socket alerted"); + } + // Sender Address sockaddr_in sin; @@ -3172,6 +3192,12 @@ static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int auto& ptpsocket = socket->data.ptp; socket->nonblocking = flag; + if (socket->flags & ADHOC_F_ALERTACCEPT) { + socket->alerted_flags |= ADHOC_F_ALERTACCEPT; + + return hleLogError(SCENET, ERROR_NET_ADHOC_SOCKET_ALERTED, "socket alerted"); + } + // Listener Socket if (ptpsocket.state == ADHOC_PTP_STATE_LISTEN) { hleEatMicro(500); @@ -3255,6 +3281,12 @@ static int sceNetAdhocPtpConnect(int id, int timeout, int flag) { auto& ptpsocket = socket->data.ptp; socket->nonblocking = flag; + if (socket->flags & ADHOC_F_ALERTCONNECT) { + socket->alerted_flags |= ADHOC_F_ALERTCONNECT; + + return hleLogError(SCENET, ERROR_NET_ADHOC_SOCKET_ALERTED, "socket alerted"); + } + // Phantasy Star Portable 2 will try to reconnect even when previous connect already success, so we should return success too if it's already connected if (ptpsocket.state == ADHOC_PTP_STATE_ESTABLISHED) return 0; @@ -3597,6 +3629,12 @@ static int sceNetAdhocPtpSend(int id, u32 dataAddr, u32 dataSizeAddr, int timeou // Apply Send Timeout Settings to Socket if (timeout > 0) setSockTimeout(ptpsocket.id, SO_SNDTIMEO, timeout); + + if (socket->flags & ADHOC_F_ALERTSEND) { + socket->alerted_flags |= ADHOC_F_ALERTSEND; + + return hleLogError(SCENET, ERROR_NET_ADHOC_SOCKET_ALERTED, "socket alerted"); + } // Acquire Network Lock // _acquireNetworkLock(); @@ -3689,6 +3727,12 @@ static int sceNetAdhocPtpRecv(int id, u32 dataAddr, u32 dataSizeAddr, int timeou if (timeout > 0) setSockTimeout(ptpsocket.id, SO_RCVTIMEO, timeout); + if (socket->flags & ADHOC_F_ALERTRECV) { + socket->alerted_flags |= ADHOC_F_ALERTRECV; + + return hleLogError(SCENET, ERROR_NET_ADHOC_SOCKET_ALERTED, "socket alerted"); + } + // Acquire Network Lock // _acquireNetworkLock(); @@ -3797,6 +3841,12 @@ static int sceNetAdhocPtpFlush(int id, int timeout, int nonblock) { auto& ptpsocket = socket->data.ptp; socket->nonblocking = nonblock; + if (socket->flags & ADHOC_F_ALERTFLUSH) { + socket->alerted_flags |= ADHOC_F_ALERTFLUSH; + + return hleLogError(SCENET, ERROR_NET_ADHOC_SOCKET_ALERTED, "socket alerted"); + } + // Connected Socket if (ptpsocket.state == ADHOC_PTP_STATE_ESTABLISHED) { hleEatMicro(1000); From 1bfe0897852bf50c12f47afe1ef660c064b279f2 Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Sun, 25 Oct 2020 05:06:55 +0700 Subject: [PATCH 10/17] Added delay result to SetSocketAlert for other threads that use the socket to trigger the alert. --- Core/HLE/sceNetAdhoc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index cc305523915b..14124abc2a7b 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -1685,7 +1685,7 @@ int NetAdhoc_SetSocketAlert(int id, s32_le flag) { adhocSockets[id - 1]->flags = flg; adhocSockets[id - 1]->alerted_flags = 0; - return 0; + return hleDelayResult(0, "set socket alert delay", 1000); } // Flags seems to be bitmasks of ADHOC_F_ALERT... (need more games to test this) From bff18c07d00a1ffb6e754ab5da76313d2460ea3b Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Thu, 22 Oct 2020 16:09:17 +0700 Subject: [PATCH 11/17] Fixed unable to see game room issue on Air Conflicts - Aces Of World War 2, should also fix other games with similar issue (due to invalid group mode) --- Core/HLE/proAdhoc.cpp | 7 ++++++- Core/HLE/proAdhoc.h | 8 ++++---- Core/HLE/sceNetAdhoc.cpp | 5 +++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index 9eaf171a0251..689d6ac73d9d 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -21,6 +21,10 @@ // This is a direct port of Coldbird's code from http://code.google.com/p/aemu/ // All credit goes to him! +#if defined(_WIN32) +#include "Common/CommonWindows.h" +#endif + #if !defined(_WIN32) #include #include @@ -1635,7 +1639,8 @@ int friendFinder(){ // Set group parameters // Since 0 is not a valid active channel we fake the channel for Automatic Channel (JPCSP use 11 as default). Ridge Racer 2 will ignore any groups with channel 0 or that doesn't matched with channel value returned from sceUtilityGetSystemParamInt (which mean sceUtilityGetSystemParamInt must not return channel 0 when connected to a network?) group->channel = parameter.channel; //(parameter.channel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC) ? defaultWlanChannel : parameter.channel; - group->mode = adhocctlCurrentMode; + // This Mode should be a valid mode (>=0), probably should be sent by AdhocServer since there are 2 possibilities (Normal and GameMode). Air Conflicts - Aces Of World War 2 (which use GameMode) seems to relies on this Mode value. + group->mode = std::max(ADHOCCTL_MODE_NORMAL, adhocctlCurrentMode); // default to ADHOCCTL_MODE_NORMAL // Link into Group List newnetworks = group; diff --git a/Core/HLE/proAdhoc.h b/Core/HLE/proAdhoc.h index 9edc7d89e2e4..236d07e5b3da 100644 --- a/Core/HLE/proAdhoc.h +++ b/Core/HLE/proAdhoc.h @@ -116,7 +116,7 @@ inline bool isDisconnected(int errcode) { return (errcode == EPIPE || errcode == #define GAMEMODE_INIT_DELAY 10000 // psp strutcs and definitions -#define ADHOCCTL_MODE_NONE -1 +#define ADHOCCTL_MODE_NONE -1 // We only use this internally as initial value before attempting to create/connect/join/scan any group #define ADHOCCTL_MODE_NORMAL 0 // ADHOCCTL_MODE_ADHOC #define ADHOCCTL_MODE_GAMEMODE 1 @@ -141,7 +141,7 @@ inline bool isDisconnected(int errcode) { return (errcode == EPIPE || errcode == // ProductType ( extracted from SSID along with ProductId & GroupName, Pattern = "PSP_([AXS])(.........)_([LG])_(.*)" ) #define PSP_ADHOCCTL_TYPE_COMMERCIAL 0 #define PSP_ADHOCCTL_TYPE_DEBUG 1 -#define PSP_ADHOCCTL_TYPE_SYSTEM 2 +#define PSP_ADHOCCTL_TYPE_SYSTEM 2 // Used for GameSharing? // Kernel Utility Netconf Adhoc Types #define UTILITY_NETCONF_TYPE_CONNECT_ADHOC 2 @@ -393,8 +393,8 @@ typedef struct SceNetAdhocGameModeBufferStat { // Adhoc ID (Game Product Key) #define ADHOCCTL_ADHOCID_LEN 9 typedef struct SceNetAdhocctlAdhocId { - s32_le type; - uint8_t data[ADHOCCTL_ADHOCID_LEN]; + s32_le type; // Air Conflicts - Aces Of World War 2 is using 2 for GameSharing? + uint8_t data[ADHOCCTL_ADHOCID_LEN]; // Air Conflicts - Aces Of World War 2 is using "000000001" for GameSharing? uint8_t padding[3]; } PACK SceNetAdhocctlAdhocId; // should this be packed? #ifdef _MSC_VER diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 14124abc2a7b..3bd7ca2a0849 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -1959,6 +1959,7 @@ int sceNetAdhocctlScan() { // Only scan when in Disconnected state, otherwise AdhocServer will kick you out if (adhocctlState == ADHOCCTL_STATE_DISCONNECTED) { adhocctlState = ADHOCCTL_STATE_SCANNING; + adhocctlCurrentMode = ADHOCCTL_MODE_NORMAL; // Reset Networks/Group list to prevent other threads from using these soon to be replaced networks peerlock.lock(); @@ -2013,7 +2014,7 @@ int sceNetAdhocctlGetScanInfo(u32 sizeAddr, u32 bufAddr) { SceNetAdhocctlScanInfoEmu *buf = NULL; if (Memory::IsValidAddress(bufAddr)) buf = (SceNetAdhocctlScanInfoEmu *)Memory::GetPointer(bufAddr); - INFO_LOG(SCENET, "sceNetAdhocctlGetScanInfo([%08x]=%i, %08x)", sizeAddr, Memory::Read_U32(sizeAddr), bufAddr); + INFO_LOG(SCENET, "sceNetAdhocctlGetScanInfo([%08x]=%i, %08x) at %08x", sizeAddr, Memory::Read_U32(sizeAddr), bufAddr, currentMIPS->pc); if (!g_Config.bEnableWlan) { return 0; } @@ -2484,7 +2485,7 @@ int NetAdhocctl_Create(const char* groupName) { // Wait for Status to be connected to prevent Ford Street Racing from Failed to create game session int us = adhocDefaultDelay; - if (adhocctlState != ADHOCCTL_STATE_CONNECTED && iResult == SOCKET_ERROR && friendFinderRunning) { + if (adhocctlState != ADHOCCTL_STATE_CONNECTED && adhocctlState != ADHOCCTL_STATE_GAMEMODE && iResult == SOCKET_ERROR && friendFinderRunning) { AdhocctlRequest req = { OPCODE_CONNECT, {0} }; if (groupNameStruct != NULL) req.group = *groupNameStruct; return WaitBlockingAdhocctlSocket(req, us, "adhocctl connect"); From 22419bfc6ca0292bea44d490944fcb117db348f8 Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Fri, 23 Oct 2020 00:45:08 +0700 Subject: [PATCH 12/17] Prevent memory leaks when clearing AdhocMatching events. --- Core/HLE/sceNetAdhoc.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 3bd7ca2a0849..176e021b25e0 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -848,6 +848,19 @@ int WaitBlockingAdhocSocket(u64 threadSocketId, int type, int pspSocketId, void* return ERROR_NET_ADHOC_TIMEOUT; } +// Using matchingId = -1 to delete all matching events +void deleteMatchingEvents(const int matchingId = -1) { + for (auto it = matchingEvents.begin(); it != matchingEvents.end(); ) { + if (matchingId < 0 || it->data[0] == matchingId) { + if (Memory::IsValidAddress(it->data[2])) + userMemory.Free(it->data[2]); + it = matchingEvents.erase(it); + } + else + ++it; + } +} + void netAdhocValidateLoopMemory() { // Allocate Memory if it wasn't valid/allocated after loaded from old SaveState if (!dummyThreadHackAddr || (dummyThreadHackAddr && strcmp("dummythreadhack", kernelMemory.GetBlockTag(dummyThreadHackAddr)) != 0)) { @@ -950,11 +963,11 @@ void __NetAdhocDoState(PointerWrap &p) { if (p.mode == p.MODE_READ) { // Discard leftover events adhocctlEvents.clear(); - matchingEvents.clear(); adhocctlRequests.clear(); adhocSocketRequests.clear(); sendTargetPeers.clear(); deleteAllAdhocSockets(); + deleteMatchingEvents(); // Let's not change "Inited" value when Loading SaveState to prevent memory & port leaks netAdhocMatchingInited = cur_netAdhocMatchingInited; @@ -4194,12 +4207,7 @@ int NetAdhocMatching_Delete(int matchingId) { item = NULL; // Making sure there are no leftover matching events from this session which could cause a crash on the next session - for (auto it = matchingEvents.begin(); it != matchingEvents.end(); ) { - if (it->data[0] == matchingId) - it = matchingEvents.erase(it); - else - ++it; - } + deleteMatchingEvents(matchingId); // Stop Search break; @@ -4238,7 +4246,7 @@ int sceNetAdhocMatchingInit(u32 memsize) { fakePoolSize = memsize; // Initialize Library - matchingEvents.clear(); + deleteMatchingEvents(); netAdhocMatchingInited = true; // Return Success From 0157fe49886ce8dbef607c38b2827bde4430f73a Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Fri, 23 Oct 2020 00:58:56 +0700 Subject: [PATCH 13/17] Updated GameMode API (Fixed Pocket Pool) --- Core/HLE/proAdhoc.cpp | 27 +++++++-------- Core/HLE/proAdhoc.h | 5 +++ Core/HLE/sceNetAdhoc.cpp | 72 +++++++++++++++++++++++++++++++--------- Core/HLE/sceNetAdhoc.h | 1 + 4 files changed, 76 insertions(+), 29 deletions(-) diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index 689d6ac73d9d..56299de4aade 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -1340,6 +1340,7 @@ int friendFinder(){ INFO_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; } else { networkInited = false; @@ -1425,14 +1426,14 @@ int friendFinder(){ }) == gameModeMacs.end()) { // Arrange the order to be consistent on all players (Host on top), Starting from our self the rest of new players will be added to the back gameModeMacs.push_back(localMac); + + if (gameModeMacs.size() >= requiredGameModeMacs.size()) { + //adhocctlState = ADHOCCTL_STATE_GAMEMODE; + notifyAdhocctlHandlers(ADHOCCTL_EVENT_GAME, 0); + } } else WARN_LOG(SCENET, "GameMode SelfMember [%s] Already Existed!", mac2str(&localMac).c_str()); - - if (gameModeMacs.size() >= requiredGameModeMacs.size()) { - //adhocctlState = ADHOCCTL_STATE_GAMEMODE; - notifyAdhocctlHandlers(ADHOCCTL_EVENT_GAME, 0); - } } else { //adhocctlState = ADHOCCTL_STATE_CONNECTED; @@ -1527,17 +1528,17 @@ int friendFinder(){ it = gameModeMacs.begin() + 1; gameModeMacs.insert(it, packet->mac); } + + // From JPCSP: Join complete when all the required MACs have joined + if (requiredGameModeMacs.size() > 0 && gameModeMacs.size() == requiredGameModeMacs.size()) { + // TODO: Should we replace gameModeMacs contents with requiredGameModeMacs contents to make sure they are in the same order with macs from sceNetAdhocctlCreateEnterGameMode? But may not be consistent with the list on client side! + //gameModeMacs = requiredGameModeMacs; + //adhocctlState = ADHOCCTL_STATE_GAMEMODE; + notifyAdhocctlHandlers(ADHOCCTL_EVENT_GAME, 0); + } } else WARN_LOG(SCENET, "GameMode Member [%s] Already Existed!", mac2str(&packet->mac).c_str()); - - // From JPCSP: Join complete when all the required MACs have joined - if (requiredGameModeMacs.size() > 0 && gameModeMacs.size() >= requiredGameModeMacs.size()) { - // TODO: Should we replace gameModeMacs contents with requiredGameModeMacs contents to make sure they are in the same order with macs from sceNetAdhocctlCreateEnterGameMode? But may not be consistent with the list on client side! - //gameModeMacs = requiredGameModeMacs; - //adhocctlState = ADHOCCTL_STATE_GAMEMODE; - notifyAdhocctlHandlers(ADHOCCTL_EVENT_GAME, 0); - } } // Update HUD User Count diff --git a/Core/HLE/proAdhoc.h b/Core/HLE/proAdhoc.h index 236d07e5b3da..b956c902f880 100644 --- a/Core/HLE/proAdhoc.h +++ b/Core/HLE/proAdhoc.h @@ -115,6 +115,11 @@ inline bool isDisconnected(int errcode) { return (errcode == EPIPE || errcode == #define GAMEMODE_UPDATE_INTERVAL 500 // 12000 usec on JPCSP, but lower value works better on BattleZone (in order to get full speed 60 FPS) #define GAMEMODE_INIT_DELAY 10000 +// GameMode Type +#define ADHOCCTL_GAMETYPE_1A 1 +#define ADHOCCTL_GAMETYPE_1B 2 +#define ADHOCCTL_GAMETYPE_2A 3 + // psp strutcs and definitions #define ADHOCCTL_MODE_NONE -1 // We only use this internally as initial value before attempting to create/connect/join/scan any group #define ADHOCCTL_MODE_NORMAL 0 // ADHOCCTL_MODE_ADHOC diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 176e021b25e0..860fa1cdce64 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -65,7 +65,7 @@ bool netAdhocInited; bool netAdhocctlInited; bool networkInited = false; -bool netAdhocGameModeEntered; +bool netAdhocGameModeEntered = false; bool netAdhocMatchingInited; int netAdhocMatchingStarted = 0; @@ -105,6 +105,7 @@ int matchingInputThread(int matchingId); int AcceptPtpSocket(int ptpId, int newsocket, sockaddr_in& peeraddr, SceNetEtherAddr* addr, u16_le* port); int PollAdhocSocket(SceNetAdhocPollSd* sds, int count, int timeout, int nonblock); int FlushPtpSocket(int socketId); +int NetAdhocGameMode_DeleteMaster(); int NetAdhocctl_ExitGameMode(); static int sceNetAdhocPdpSend(int id, const char* mac, u32 port, void* data, int len, int timeout, int flag); static int sceNetAdhocPdpRecv(int id, void* addr, void* port, void* buf, void* dataLength, u32 timeout, int flag); @@ -1031,6 +1032,11 @@ u32 sceNetAdhocInit() { // Library initialized netAdhocInited = true; + // FIXME: It seems official prx is using sceNetAdhocGameModeDeleteMaster in here? + NetAdhocGameMode_DeleteMaster(); + // Since we are deleting GameMode Master here, we should probably need to make sure GameMode resources all cleared too. + deleteAllGMB(); + // Return Success return hleLogSuccessInfoI(SCENET, 0, "at %08x", currentMIPS->pc); } @@ -2271,6 +2277,10 @@ int NetAdhocctl_Term() { friendFinderThread.join(); } + // Clear GameMode resources + NetAdhocGameMode_DeleteMaster(); + deleteAllGMB(); + // Clear Peer List int32_t peercount = 0; freeFriendsRecursive(friends, &peercount); @@ -2592,7 +2602,7 @@ int NetAdhocctl_CreateEnterGameMode(const char* group_name, int game_type, int n if (!Memory::IsValidAddress(membersAddr)) return ERROR_NET_ADHOCCTL_INVALID_ARG; - if (game_type <= 0 || game_type > 3 || num_members < 2 || num_members > 16 || (game_type == 1 && num_members > 4)) + 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 ERROR_NET_ADHOCCTL_INVALID_ARG; deleteAllGMB(); @@ -2608,6 +2618,14 @@ int NetAdhocctl_CreateEnterGameMode(const char* group_name, int game_type, int n getLocalMac(&localMac); gameModeMacs.push_back(localMac); + // FIXME: There seems to be an internal Adhocctl Handler on official prx (running on "SceNetAdhocctl" thread) that will try to sync GameMode timings, by using blocking PTP socket: + // 1). PtpListen (srcMacAddress=0x09F20CB4, srcPort=0x8001, bufSize=0x2000, retryDelay=0x30D40, retryCount=0x33, queue=0x1, unk1=0x0) + // 2). PtpAccpet (peerMacAddr=0x09FE2020, peerPortAddr=0x09FE2010, timeout=0x765BB0, nonblock=0x0) - probably for each clients + // 3). PtpSend (data=0x09F20E18, dataSizeAddr=0x09FE2094, timeout=0x627EDA, nonblock=0x0) - not sure what kind of data nor the size (more than 6 bytes) + // 4). PtpFlush (timeout=0x2DC6C0, nonblock=0x0) + // 5 & 6). PtpClose (accepted socket & listen socket) + // When timeout reached, notify user-defined Adhocctl Handlers with ERROR event (ERROR_NET_ADHOC_TIMEOUT) instead of GAMEMODE event + // We have to wait for all the MACs to have joined to go into CONNECTED state adhocctlCurrentMode = ADHOCCTL_MODE_GAMEMODE; adhocConnectionType = ADHOC_CREATE; @@ -2663,6 +2681,13 @@ static int sceNetAdhocctlJoinEnterGameMode(const char * group_name, const char * // Add host mac first gameModeMacs.push_back(*(SceNetEtherAddr*)hostMac); + // FIXME: There seems to be an internal Adhocctl Handler on official prx (running on "SceNetAdhocctl" thread) that will try to sync GameMode timings, by using blocking PTP socket: + // 1). PtpOpen (srcMacAddress=0x09FE2080, srcPort=0x8001, destMacAddress=0x09F20CB4, destPort=0x8001, bufSize=0x2000, retryDelay=0x30D40, retryCount=0x33, unk1=0x0) + // 2). PtpConnect (timeout=0x874CAC, nonblock=0x0) - to host/creator + // 3). PtpRecv (data=0x09F20E18, dataSizeAddr=0x09FE2044, timeout=0x647553, nonblock=0x0) - repeated until data fully received with data address/offset adjusted (increased) and timeout adjusted (decreased), probably also adjusted data size (decreased) on each call + // 4). PtpClose + // When timeout reached, notify user-defined Adhocctl Handlers with ERROR event (ERROR_NET_ADHOC_TIMEOUT) instead of GAMEMODE event + adhocctlCurrentMode = ADHOCCTL_MODE_GAMEMODE; adhocConnectionType = ADHOC_JOIN; netAdhocGameModeEntered = true; @@ -3939,7 +3964,8 @@ static int sceNetAdhocGameModeCreateMaster(u32 dataAddr, int size) { StartGameModeScheduler(size); } - return 0; // returned an id just like CreateReplica? always return 0? + hleEatMicro(GAMEMODE_INIT_DELAY); + return hleLogDebug(SCENET, 0, "success"); // returned an id just like CreateReplica? always return 0? } /** @@ -3965,6 +3991,7 @@ static int sceNetAdhocGameModeCreateReplica(const char *mac, u32 dataAddr, int s if (mac == nullptr || size < 0 || !Memory::IsValidAddress(dataAddr)) return hleLogError(SCENET, ERROR_NET_ADHOCCTL_INVALID_ARG, "invalid arg"); + hleEatMicro(1000); int maxid = 0; auto it = std::find_if(replicaGameModeAreas.begin(), replicaGameModeAreas.end(), [mac, &maxid](GameModeArea const& e) { @@ -4006,28 +4033,33 @@ static int sceNetAdhocGameModeUpdateMaster() { masterGameModeArea.updateTimestamp = CoreTiming::GetGlobalTimeUsScaled(); } + hleEatMicro(1000); return 0; } -static int sceNetAdhocGameModeDeleteMaster() { - WARN_LOG(SCENET, "UNTESTED sceNetAdhocGameModeDeleteMaster()"); - if (isZeroMAC(&masterGameModeArea.mac)) - return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_CREATED, "not created"); - +int NetAdhocGameMode_DeleteMaster() { if (masterGameModeArea.data) { free(masterGameModeArea.data); } - //sceNetAdhocPdpDelete(masterGameModeArea.socket, 0); + //NetAdhocPdp_Delete(masterGameModeArea.socket, 0); masterGameModeArea = { 0 }; - + if (replicaGameModeAreas.size() <= 0) { - sceNetAdhocPdpDelete(gameModeSocket, 0); + NetAdhocPdp_Delete(gameModeSocket, 0); gameModeSocket = (int)INVALID_SOCKET; } - + return 0; } +static int sceNetAdhocGameModeDeleteMaster() { + WARN_LOG(SCENET, "UNTESTED sceNetAdhocGameModeDeleteMaster() at %08x", currentMIPS->pc); + if (isZeroMAC(&masterGameModeArea.mac)) + return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_CREATED, "not created"); + + return NetAdhocGameMode_DeleteMaster(); +} + static int sceNetAdhocGameModeUpdateReplica(int id, u32 infoAddr) { DEBUG_LOG(SCENET, "UNTESTED sceNetAdhocGameModeUpdateReplica(%i, %08x) at %08x", id, infoAddr, currentMIPS->pc); if (!netAdhocctlInited) @@ -4056,7 +4088,7 @@ static int sceNetAdhocGameModeUpdateReplica(int id, u32 infoAddr) { memcpy(Memory::GetPointer(gma.addr), gma.data, gma.size); gma.dataUpdated = 0; gmuinfo->updated = 1; - gmuinfo->timeStamp = std::max(gma.updateTimestamp, CoreTiming::GetGlobalTimeUsScaled() - 1000); + gmuinfo->timeStamp = std::max(gma.updateTimestamp, CoreTiming::GetGlobalTimeUsScaled() - defaultLastRecvDelta); } else { gmuinfo->updated = 0; @@ -4066,11 +4098,12 @@ static int sceNetAdhocGameModeUpdateReplica(int id, u32 infoAddr) { } } + hleEatMicro(1000); return 0; } static int sceNetAdhocGameModeDeleteReplica(int id) { - WARN_LOG(SCENET, "UNTESTED sceNetAdhocGameModeDeleteReplica(%i)", id); + WARN_LOG(SCENET, "UNTESTED sceNetAdhocGameModeDeleteReplica(%i) at %08x", id, currentMIPS->pc); auto it = std::find_if(replicaGameModeAreas.begin(), replicaGameModeAreas.end(), [id](GameModeArea const& e) { return e.id == id; @@ -5262,6 +5295,10 @@ void __NetTriggerCallbacks() { newState = ADHOCCTL_STATE_GAMEMODE; delayus = adhocEventDelay; + // TODO: Use blocking PTP connection to sync the timing just like official prx did (which is done before notifying user-defined Adhocctl Handlers) + // Workaround: Extra delay to prevent Joining player to progress faster than the Creator on Pocket Pool, but unbalanced delays could cause an issue on Shaun White Snowboarding :( + if (adhocConnectionType == ADHOC_JOIN) + delayus += adhocExtraDelay * 3; // Shows player list INFO_LOG(SCENET, "GameMode - All players have joined:"); int i = 0; @@ -5384,8 +5421,10 @@ const HLEFunction sceNetAdhocMatching[] = { }; int NetAdhocctl_ExitGameMode() { - if (gameModeSocket > 0) - sceNetAdhocPdpDelete(gameModeSocket, 0); + if (gameModeSocket > 0) { + NetAdhocPdp_Delete(gameModeSocket, 0); + gameModeSocket = (int)INVALID_SOCKET; + } deleteAllGMB(); @@ -5419,6 +5458,7 @@ static int sceNetAdhocctlGetGameModeInfo(u32 infoAddr) { break; } + hleEatMicro(1000); return 0; } diff --git a/Core/HLE/sceNetAdhoc.h b/Core/HLE/sceNetAdhoc.h index 682a609b2f78..b0d2f36fe7fd 100644 --- a/Core/HLE/sceNetAdhoc.h +++ b/Core/HLE/sceNetAdhoc.h @@ -98,6 +98,7 @@ int NetAdhoc_Term(); extern bool netAdhocInited; extern bool netAdhocctlInited; extern bool networkInited; +extern bool netAdhocGameModeEntered; extern int adhocDefaultTimeout; //3000000 usec extern int adhocDefaultDelay; //10000 extern int adhocExtraDelay; //20000 From 399f56f73b00296992f47b0c7b0f72aaa6dd0a16 Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Sat, 24 Oct 2020 00:50:50 +0700 Subject: [PATCH 14/17] Implementing Adhocctl Busy state to prevent getting kicked out from Adhoc Server on games that tries to leave the group multiple times in a row. Also Updated Adhocctl behavior. --- Core/HLE/proAdhoc.cpp | 7 +- Core/HLE/proAdhoc.h | 3 +- Core/HLE/sceNetAdhoc.cpp | 146 +++++++++++++++++++++++++++------------ 3 files changed, 110 insertions(+), 46 deletions(-) diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index 56299de4aade..2a69c352ff8f 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -66,6 +66,7 @@ SceNetAdhocctlPeerInfo * friends = NULL; SceNetAdhocctlScanInfo * networks = NULL; SceNetAdhocctlScanInfo * newnetworks = NULL; u64 adhocctlStartTime = 0; +bool isAdhocctlBusy = false; int adhocctlState = ADHOCCTL_STATE_DISCONNECTED; int adhocctlCurrentMode = ADHOCCTL_MODE_NONE; int adhocConnectionType = ADHOC_CONNECT; @@ -319,7 +320,7 @@ void changeBlockingMode(int fd, int nonblocking) { #endif } -int countAvailableNetworks() { +int countAvailableNetworks(const bool excludeSelf) { // Network Count int count = 0; @@ -327,7 +328,7 @@ int countAvailableNetworks() { SceNetAdhocctlScanInfo * group = networks; // Count Groups - for (; group != NULL; group = group->next) count++; + for (; group != NULL && (!excludeSelf || !isLocalMAC(&group->bssid.mac_addr)); group = group->next) count++; // Return Network Count return count; @@ -1169,6 +1170,7 @@ void AfterAdhocMipsCall::run(MipsCall& call) { u32 v0 = currentMIPS->r[MIPS_REG_V0]; if (__IsInInterrupt()) ERROR_LOG(SCENET, "AfterAdhocMipsCall::run [ID=%i][Event=%d] is Returning Inside an Interrupt!", HandlerID, EventID); SetAdhocctlInCallback(false); + isAdhocctlBusy = false; DEBUG_LOG(SCENET, "AfterAdhocMipsCall::run [ID=%i][Event=%d] [cbId: %u][retV0: %08x]", HandlerID, EventID, call.cbId, v0); //call.setReturnValue(v0); } @@ -1341,6 +1343,7 @@ int friendFinder(){ // 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; } else { networkInited = false; diff --git a/Core/HLE/proAdhoc.h b/Core/HLE/proAdhoc.h index b956c902f880..0fa4cc8c9011 100644 --- a/Core/HLE/proAdhoc.h +++ b/Core/HLE/proAdhoc.h @@ -918,6 +918,7 @@ extern bool friendFinderRunning; extern SceNetAdhocctlPeerInfo * friends; extern SceNetAdhocctlScanInfo * networks; extern u64 adhocctlStartTime; +extern bool isAdhocctlBusy; extern int adhocctlState; extern int adhocctlCurrentMode; extern int adhocConnectionType; @@ -1030,7 +1031,7 @@ void changeBlockingMode(int fd, int nonblocking); * Count Virtual Networks by analyzing the Friend List * @return Number of Virtual Networks */ -int countAvailableNetworks(); +int countAvailableNetworks(const bool excludeSelf = false); /* * Find an existing group in networks diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 860fa1cdce64..9ad7c6d67317 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -74,7 +74,7 @@ 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 +int adhocEventDelay = 2000000; //2000000 on real PSP ? u32 defaultLastRecvDelta = 10000; //10000 usec worked well for games published by Falcom (ie. Ys vs Sora Kiseki, Vantage Master Portable) SceUID threadAdhocID; @@ -287,6 +287,12 @@ static void __AdhocctlState(u64 userdata, int cyclesLate) { u32 waitVal = __KernelGetWaitValue(threadID, error); if (error == 0) { adhocctlState = waitVal; + // FIXME: It seems Adhocctl is still busy within the Adhocctl Handler function (ie. during callbacks), + // so we should probably set isAdhocctlBusy to false after mispscall are fully executed (ie. in afterAction). + // But since Adhocctl Handler is optional, there might be cases where there are no handler thus no callback/mipcall being triggered, + // so we should probably need to set isAdhocctlBusy to false here too as a workaround (or may be there is internal handler by default?) + if (adhocctlHandlers.empty()) + isAdhocctlBusy = false; } __KernelResumeThreadFromWait(threadID, result); @@ -877,7 +883,7 @@ void netAdhocValidateLoopMemory() { } void __NetAdhocDoState(PointerWrap &p) { - auto s = p.Section("sceNetAdhoc", 1, 7); + auto s = p.Section("sceNetAdhoc", 1, 8); if (!s) return; @@ -960,6 +966,14 @@ void __NetAdhocDoState(PointerWrap &p) { else { adhocctlStateEvent = -1; } + if (s >= 8) { + Do(p, isAdhocctlBusy); + Do(p, netAdhocGameModeEntered); + } + else { + isAdhocctlBusy = false; + netAdhocGameModeEntered = false; + } if (p.mode == p.MODE_READ) { // Discard leftover events @@ -1031,6 +1045,7 @@ u32 sceNetAdhocInit() { if (!netAdhocInited) { // Library initialized netAdhocInited = true; + isAdhocctlBusy = false; // FIXME: It seems official prx is using sceNetAdhocGameModeDeleteMaster in here? NetAdhocGameMode_DeleteMaster(); @@ -1128,9 +1143,10 @@ static int sceNetAdhocPdpCreate(const char *mac, int port, int bufferSize, u32 f // Valid Arguments are supplied if (mac != NULL && bufferSize > 0) { // Port is in use by another PDP Socket. - // TODO: Need to test whether using the same PDP port is allowed or not, since PDP/UDP is connectionless it might be allowed. - if (isPDPPortInUse(port)) + if (isPDPPortInUse(port)) { + // FIXME: When PORT_IN_USE error occured it seems the index to the socket id also increased, which means it tries to create & bind the socket first and then closes it due to failed to bind return hleLogDebug(SCENET, ERROR_NET_ADHOC_PORT_IN_USE, "port in use"); + } //sport 0 should be shifted back to 0 when using offset Phantasy Star Portable 2 use this if (port == 0) port = -static_cast(portOffset); @@ -1183,6 +1199,7 @@ static int sceNetAdhocPdpCreate(const char *mac, int port, int bufferSize, u32 f // Allocated Memory if (internal != NULL) { // Find Free Translator Index + // FIXME: We should probably use an increasing index instead of looking for an empty slot from beginning if we want to simulate a real socket id int i = 0; for (; i < MAX_SOCKET; i++) if (adhocSockets[i] == NULL) break; @@ -1973,10 +1990,18 @@ int sceNetAdhocctlScan() { // Library initialized if (netAdhocctlInited) { - int us = adhocDefaultDelay * 2; + int us = adhocDefaultDelay; + // FIXME: When tested with JPCSP + official prx files it seems when adhocctl in a connected state (ie. joined to a group) attempting to create/connect/join/scan will return a success (without doing anything?) + if ((adhocctlState == ADHOCCTL_STATE_CONNECTED) || (adhocctlState == ADHOCCTL_STATE_GAMEMODE)) { + // TODO: Valhalla Knights 2 need handler notification, but need to test this on games that doesn't use Adhocctl Handler too (not sure if there are games like that tho) + notifyAdhocctlHandlers(ADHOCCTL_EVENT_ERROR, ERROR_NET_ADHOCCTL_ALREADY_CONNECTED); + hleEatMicro(500); + return 0; + } // Only scan when in Disconnected state, otherwise AdhocServer will kick you out - if (adhocctlState == ADHOCCTL_STATE_DISCONNECTED) { + if (adhocctlState == ADHOCCTL_STATE_DISCONNECTED && !isAdhocctlBusy) { + isAdhocctlBusy = true; adhocctlState = ADHOCCTL_STATE_SCANNING; adhocctlCurrentMode = ADHOCCTL_MODE_NORMAL; @@ -2005,22 +2030,16 @@ int sceNetAdhocctlScan() { 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 - hleEatMicro(us); - return 0; - } + + // 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 + hleEatMicro(us); + // FIXME: When tested using JPCSP + official prx files it seems sceNetAdhocctlScan switching to a different thread for at least 100ms after returning success and before executing the next line? + return hleDelayResult(0, "scan delay", adhocEventPollDelay); } - else if (adhocctlState == ADHOCCTL_STATE_SCANNING) - return hleLogError(SCENET, ERROR_NET_ADHOCCTL_BUSY, "busy"); - - // Already connected to a group. Should we fake a success? - // We need to notify the handler on success, even if it was faked. Using flag = 0/ADHOCCTL_EVENT_ERROR for error? - notifyAdhocctlHandlers(ADHOCCTL_EVENT_ERROR, ERROR_NET_ADHOCCTL_ALREADY_CONNECTED); - // 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 - hleEatMicro(us); - return 0; + + // FIXME: Returning BUSY when previous adhocctl handler's callback is not fully executed yet, But returning success and notifying handler's callback with error (ie. ALREADY_CONNECTED) when previous adhocctl handler's callback is fully executed? Is there a case where error = BUSY sent through handler's callback? + return hleLogError(SCENET, ERROR_NET_ADHOCCTL_BUSY, "busy"); } // Library uninitialized @@ -2045,12 +2064,20 @@ int sceNetAdhocctlGetScanInfo(u32 sizeAddr, u32 bufAddr) { // Minimum Argument Requirements if (buflen != NULL) { + // FIXME: Do we need to exclude Groups created by this device it's self? + bool excludeSelf = false; + // Multithreading Lock peerlock.lock(); + // FIXME: When already connected to a group GetScanInfo will return size = 0 ? or may be only hides the group created by it's self? + if (adhocctlState == ADHOCCTL_STATE_CONNECTED || adhocctlState == ADHOCCTL_STATE_GAMEMODE) { + *buflen = 0; + DEBUG_LOG(SCENET, "NetworkList [Available: 0] Already in a Group"); + } // Length Returner Mode - if (buf == NULL) { - int availNetworks = countAvailableNetworks(); + else if (buf == NULL) { + int availNetworks = countAvailableNetworks(excludeSelf); *buflen = availNetworks * sizeof(SceNetAdhocctlScanInfoEmu); DEBUG_LOG(SCENET, "NetworkList [Available: %i]", availNetworks); } @@ -2071,7 +2098,7 @@ int sceNetAdhocctlGetScanInfo(u32 sizeAddr, u32 bufAddr) { SceNetAdhocctlScanInfo * group = networks; // Iterate Group List - for (; group != NULL && discovered < requestcount; group = group->next) { + for (; group != NULL && (!excludeSelf || !isLocalMAC(&group->bssid.mac_addr)) && discovered < requestcount; group = group->next) { // Copy Group Information //buf[discovered] = *group; buf[discovered].group_name = group->group_name; @@ -2106,6 +2133,7 @@ int sceNetAdhocctlGetScanInfo(u32 sizeAddr, u32 bufAddr) { // Multithreading Unlock peerlock.unlock(); + hleEatMicro(2000); // Return Success return 0; } @@ -2164,9 +2192,17 @@ u32 NetAdhocctl_Disconnect() { // Library initialized if (netAdhocctlInited) { int iResult, error; - int us = adhocDefaultDelay * 5; - // Connected State (Adhoc Mode) - if (adhocctlState != ADHOCCTL_STATE_DISCONNECTED) { // (threadStatus == ADHOCCTL_STATE_CONNECTED) + int us = adhocDefaultDelay * 3; + hleEatMicro(1000); + + if (isAdhocctlBusy) { + return ERROR_NET_ADHOCCTL_BUSY; + } + + // Connected State (Adhoc Mode). Attempting to leave a group while not in a group will be kicked out by Adhoc Server (ie. some games tries to disconnect more than once within a short time) + if (adhocctlState != ADHOCCTL_STATE_DISCONNECTED) { + isAdhocctlBusy = true; + // Clear Network Name memset(¶meter.group_name, 0, sizeof(parameter.group_name)); @@ -2196,7 +2232,6 @@ u32 NetAdhocctl_Disconnect() { } else { // Set Disconnected State - //adhocctlState = ADHOCCTL_STATE_DISCONNECTED; return ERROR_NET_ADHOCCTL_BUSY; } } @@ -2226,12 +2261,16 @@ u32 NetAdhocctl_Disconnect() { adhocctlCurrentMode = ADHOCCTL_MODE_NONE; // Notify Event Handlers (even if we weren't connected, not doing this will freeze games like God Eater, which expect this behaviour) - notifyAdhocctlHandlers(ADHOCCTL_EVENT_DISCONNECT, 0); - //hleCheckCurrentCallbacks(); + // FIXME: When there are no handler the state will immediately became ADHOCCTL_STATE_DISCONNECTED ? + if (adhocctlHandlers.empty()) { + adhocctlState = ADHOCCTL_STATE_DISCONNECTED; + } + else { + notifyAdhocctlHandlers(ADHOCCTL_EVENT_DISCONNECT, 0); + } - hleEatMicro(us); // Return Success, some games might ignore returned value and always treat it as success, otherwise repeatedly calling this function - return 0; + return hleDelayResult(0, "disconnect delay", us); } // Library uninitialized @@ -2302,6 +2341,7 @@ int NetAdhocctl_Term() { } threadAdhocID = 0; adhocctlCurrentMode = ADHOCCTL_MODE_NONE; + isAdhocctlBusy = false; netAdhocctlInited = false; } @@ -2459,8 +2499,18 @@ int NetAdhocctl_Create(const char* groupName) { if (netAdhocctlInited) { // Valid Argument if (validNetworkName(groupNameStruct)) { - // Disconnected State, may also need to check for Scanning state to prevent some games from failing to host a game session - if ((adhocctlState == ADHOCCTL_STATE_DISCONNECTED) || (adhocctlState == ADHOCCTL_STATE_SCANNING)) { + // FIXME: When tested with JPCSP + official prx files it seems when adhocctl in a connected state (ie. joined to a group) attempting to create/connect/join/scan will return a success (without doing anything?) + if ((adhocctlState == ADHOCCTL_STATE_CONNECTED) || (adhocctlState == ADHOCCTL_STATE_GAMEMODE)) { + // TODO: Need to test this on games that doesn't use Adhocctl Handler too (not sure if there are games like that tho) + notifyAdhocctlHandlers(ADHOCCTL_EVENT_ERROR, ERROR_NET_ADHOCCTL_ALREADY_CONNECTED); + hleEatMicro(500); + return 0; + } + + // Disconnected State + if (adhocctlState == ADHOCCTL_STATE_DISCONNECTED && !isAdhocctlBusy) { + isAdhocctlBusy = true; + // Set Network Name if (groupNameStruct != NULL) parameter.group_name = *groupNameStruct; @@ -2487,7 +2537,7 @@ int NetAdhocctl_Create(const char* groupName) { if (iResult == SOCKET_ERROR && error != EAGAIN && error != EWOULDBLOCK) { ERROR_LOG(SCENET, "Socket error (%i) when sending", error); - //return ERROR_NET_ADHOCCTL_NOT_INITIALIZED; // ERROR_NET_ADHOCCTL_DISCONNECTED; // ERROR_NET_ADHOCCTL_BUSY; + //Faking success, to prevent Full Auto 2 from freezing while Initializing Network if (adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE) { adhocctlState = ADHOCCTL_STATE_GAMEMODE; @@ -2516,7 +2566,8 @@ int NetAdhocctl_Create(const char* groupName) { hleEatMicro(us); // Return Success - return 0; + // FIXME: When tested using JPCSP + official prx files it seems sceNetAdhocctlCreate switching to a different thread for at least 100ms after returning success and before executing the next line. + return hleDelayResult(0, "create/connect/join delay", adhocEventPollDelay); } // Connected State @@ -2958,8 +3009,10 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, // Valid Addresses. FIXME: MAC only valid after successful attempt to Create/Connect/Join a Group? (ie. adhocctlCurrentMode != ADHOCCTL_MODE_NONE) if ((adhocctlCurrentMode != ADHOCCTL_MODE_NONE) && saddr != NULL && isLocalMAC(saddr) && daddr != NULL && !isBroadcastMAC(daddr) && !isZeroMAC(daddr)) { // Dissidia 012 will try to reOpen the port without Closing the old one first when PtpConnect failed to try again. - if (isPTPPortInUse(sport, false)) + if (isPTPPortInUse(sport, false)) { + // FIXME: When PORT_IN_USE error occured it seems the index to the socket id also increased, which means it tries to create & bind the socket first and then closes it due to failed to bind return hleLogDebug(SCENET, ERROR_NET_ADHOC_PORT_IN_USE, "port in use"); + } // Random Port required if (sport == 0) { @@ -3019,6 +3072,7 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, // Allocated Memory if (internal != NULL) { // Find Free Translator ID + // FIXME: We should probably use an increasing index instead of looking for an empty slot from beginning if we want to simulate a real socket id int i = 0; for (; i < MAX_SOCKET; i++) if (adhocSockets[i] == NULL) break; @@ -3122,6 +3176,7 @@ int AcceptPtpSocket(int ptpId, int newsocket, sockaddr_in& peeraddr, SceNetEther // Allocated Memory if (internal != NULL) { // Find Free Translator ID + // FIXME: We should probably use an increasing index instead of looking for an empty slot from beginning if we want to simulate a real socket id int i = 0; for (; i < MAX_SOCKET; i++) if (adhocSockets[i] == NULL) break; @@ -3496,8 +3551,10 @@ static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int // Valid Address. FIXME: MAC only valid after successful attempt to Create/Connect/Join a Group? (ie. adhocctlCurrentMode != ADHOCCTL_MODE_NONE) if ((adhocctlCurrentMode != ADHOCCTL_MODE_NONE) && saddr != NULL && isLocalMAC(saddr)) { // It's allowed to Listen and Open the same PTP port, But it's not allowed to Listen or Open the same PTP port twice. - if (isPTPPortInUse(sport, true)) + if (isPTPPortInUse(sport, true)) { + // FIXME: When PORT_IN_USE error occured it seems the index to the socket id also increased, which means it tries to create & bind the socket first and then closes it due to failed to bind return hleLogDebug(SCENET, ERROR_NET_ADHOC_PORT_IN_USE, "port in use"); + } // Random Port required if (sport == 0) { @@ -3558,6 +3615,7 @@ static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int // Allocated Memory if (internal != NULL) { // Find Free Translator ID + // FIXME: We should probably use an increasing index instead of looking for an empty slot from beginning if we want to simulate a real socket id int i = 0; for (; i < MAX_SOCKET; i++) if (adhocSockets[i] == NULL) break; @@ -5255,7 +5313,7 @@ void __NetTriggerCallbacks() { std::lock_guard adhocGuard(adhocEvtMtx); hleSkipDeadbeef(); - int delayus = 10000; + int delayus = adhocDefaultDelay; auto params = adhocctlEvents.begin(); if (params != adhocctlEvents.end()) @@ -5282,11 +5340,12 @@ void __NetTriggerCallbacks() newState = ADHOCCTL_STATE_CONNECTED; if (adhocConnectionType == ADHOC_CREATE) delayus = adhocEventDelay; // May affects Dissidia 012 and GTA VCS - else if (adhocConnectionType == ADHOC_CONNECT && getActivePeerCount() == 0) + else if (adhocConnectionType == ADHOC_CONNECT) delayus = adhocEventDelay / 2; break; case ADHOCCTL_EVENT_SCAN: // notified only when scan completed? newState = ADHOCCTL_STATE_DISCONNECTED; + //delayus = adhocEventDelay / 2; break; case ADHOCCTL_EVENT_DISCONNECT: newState = ADHOCCTL_STATE_DISCONNECTED; @@ -5316,8 +5375,7 @@ void __NetTriggerCallbacks() newState = ADHOCCTL_STATE_WOL; break; case ADHOCCTL_EVENT_ERROR: - // FIXME: Should we change the state on error or leave it alone? for example after Creating/Joining a group, doing Scan could trigger an error through handler, what about the AdhocctlState after this error? - //newState = ADHOCCTL_STATE_DISCONNECTED; + delayus = adhocDefaultDelay * 3; break; } @@ -5477,7 +5535,7 @@ static int sceNetAdhocctlGetPeerList(u32 sizeAddr, u32 bufAddr) { if (netAdhocctlInited) { // Minimum Arguments if (buflen != NULL) { - // FIXME: Sometimes returing 0x80410682 before AdhocctlGetState became ADHOCCTL_STATE_CONNECTED or related to Auth/Library ? + // FIXME: Sometimes returing 0x80410682 when Adhocctl is still BUSY or before AdhocctlGetState became ADHOCCTL_STATE_CONNECTED or related to Auth/Library ? // Multithreading Lock peerlock.lock(); @@ -5497,6 +5555,8 @@ static int sceNetAdhocctlGetPeerList(u32 sizeAddr, u32 bufAddr) { // Calculate Request Count int requestcount = *buflen / sizeof(SceNetAdhocctlPeerInfoEmu); + // FIXME: When bufAddr is not null but buffer size is smaller than activePeers * sizeof(SceNetAdhocctlPeerInfoEmu), simply return buffer size = 0 without filling the buffer? + // Clear Memory memset(buf, 0, *buflen); From 3aed0c258b467d267417b1364370ec8ee4a5bd34 Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Sun, 25 Oct 2020 06:33:10 +0700 Subject: [PATCH 15/17] Fixed an issue with AdhocMatching where there is a possibility that some data didn't get sent upon AdhocMatchingStop causing Joined players to get timeout. (Fixed Star Wars: The Force Unleashed) --- Core/HLE/sceNetAdhoc.cpp | 84 +++++++++++++++++++++++++++++++++------- 1 file changed, 71 insertions(+), 13 deletions(-) diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 9ad7c6d67317..89f15f0e3ad9 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -4738,23 +4738,19 @@ static int sceNetAdhocMatchingSelectTarget(int matchingId, const char *macAddres return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED, "adhocmatching not initialized"); } -int sceNetAdhocMatchingCancelTargetWithOpt(int matchingId, const char *macAddress, int optLen, u32 optDataPtr) { - WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingCancelTargetWithOpt(%i, %s, %i, %08x) at %08x", matchingId, mac2str((SceNetEtherAddr*)macAddress).c_str(), optLen, optDataPtr, currentMIPS->pc); - if (!g_Config.bEnableWlan) - return -1; - +int NetAdhocMatching_CancelTargetWithOpt(int matchingId, const char* macAddress, int optLen, u32 optDataPtr) { // Initialized Library if (netAdhocMatchingInited) { - SceNetEtherAddr * target = (SceNetEtherAddr *)macAddress; - void * opt = NULL; + SceNetEtherAddr* target = (SceNetEtherAddr*)macAddress; + void* opt = NULL; if (Memory::IsValidAddress(optDataPtr)) opt = Memory::GetPointer(optDataPtr); // Valid Arguments if (target != NULL && ((optLen == 0) || (optLen > 0 && opt != NULL))) { // Find Matching Context - SceNetAdhocMatchingContext * context = findMatchingContext(matchingId); + SceNetAdhocMatchingContext* context = findMatchingContext(matchingId); // Found Matching Context if (context != NULL) @@ -4763,7 +4759,7 @@ int sceNetAdhocMatchingCancelTargetWithOpt(int matchingId, const char *macAddres if (context->running) { // Find Peer - SceNetAdhocMatchingMemberInternal * peer = findPeer(context, (SceNetEtherAddr *)target); + SceNetAdhocMatchingMemberInternal* peer = findPeer(context, (SceNetEtherAddr*)target); // Found Peer if (peer != NULL) @@ -4792,6 +4788,7 @@ int sceNetAdhocMatchingCancelTargetWithOpt(int matchingId, const char *macAddres // Marking peer to be timedout instead of deleting immediately peer->lastping = 0; + hleEatCycles(adhocDefaultDelay); // Return Success return 0; } @@ -4819,11 +4816,18 @@ int sceNetAdhocMatchingCancelTargetWithOpt(int matchingId, const char *macAddres return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED, "adhocmatching not initialized"); } +int sceNetAdhocMatchingCancelTargetWithOpt(int matchingId, const char *macAddress, int optLen, u32 optDataPtr) { + WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingCancelTargetWithOpt(%i, %s, %i, %08x) at %08x", matchingId, mac2str((SceNetEtherAddr*)macAddress).c_str(), optLen, optDataPtr, currentMIPS->pc); + if (!g_Config.bEnableWlan) + return -1; + return NetAdhocMatching_CancelTargetWithOpt(matchingId, macAddress, optLen, optDataPtr); +} + int sceNetAdhocMatchingCancelTarget(int matchingId, const char *macAddress) { WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingCancelTarget(%i, %s)", matchingId, mac2str((SceNetEtherAddr*)macAddress).c_str()); if (!g_Config.bEnableWlan) return -1; - return sceNetAdhocMatchingCancelTargetWithOpt(matchingId, macAddress, 0, 0); + return NetAdhocMatching_CancelTargetWithOpt(matchingId, macAddress, 0, 0); } int sceNetAdhocMatchingGetHelloOpt(int matchingId, u32 optLenAddr, u32 optDataAddr) { @@ -5400,7 +5404,7 @@ void __NetTriggerCallbacks() void __NetMatchingCallbacks() //(int matchingId) { std::lock_guard adhocGuard(adhocEvtMtx); - int delayus = 10000; + int delayus = adhocDefaultDelay; auto params = matchingEvents.begin(); if (params != matchingEvents.end()) @@ -6878,6 +6882,7 @@ int matchingEventThread(int matchingId) context->eventlock->lock(); // Iterate Message List + int msg_count = 0; ThreadMessage * msg = context->event_stack; for (; msg != NULL; msg = msg->next) { @@ -6895,6 +6900,7 @@ int matchingEventThread(int matchingId) // Notify Event Handlers notifyMatchingHandler(context, msg, opt, bufAddr, bufLen, args); //context->eventlock->lock(); + msg_count++; } // Clear Event Message Stack @@ -6902,6 +6908,7 @@ int matchingEventThread(int matchingId) // Free Stack context->eventlock->unlock(); + INFO_LOG(SCENET, "EventLoop[%d]: Finished (%d msg)", matchingId, msg_count); } // Free memory @@ -7114,11 +7121,62 @@ int matchingInputThread(int matchingId) // TODO: The MatchingInput thread is usi } if (contexts != NULL) { + // Process Last Messages + if (context->input_stack != NULL) + { + // Claim Stack + context->inputlock->lock(); + + // Iterate Message List + int msg_count = 0; + ThreadMessage* msg = context->input_stack; + for (; msg != NULL; msg = msg->next) + { + // Default Optional Data + void* opt = NULL; + + // Grab Optional Data + if (msg->optlen > 0) opt = ((u8*)msg) + sizeof(ThreadMessage); + + // Send Accept Packet + if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_ACCEPT) sendAcceptPacket(context, &msg->mac, msg->optlen, opt); + + // Send Join Packet + else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_JOIN) sendJoinPacket(context, &msg->mac, msg->optlen, opt); + + // Send Cancel Packet + else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_CANCEL) sendCancelPacket(context, &msg->mac, msg->optlen, opt); + + // Send Bulk Data Packet + else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_BULK) sendBulkDataPacket(context, &msg->mac, msg->optlen, opt); + + // Send Birth Packet + else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_BIRTH) sendBirthPacket(context, &msg->mac); + + // Send Death Packet + else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_DEATH) sendDeathPacket(context, &msg->mac); + + // Cancel Bulk Data Transfer (does nothing as of now as we fire and forget anyway) // Do we need to check DeathPacket and ByePacket here? + //else if(msg->opcode == PSP_ADHOC_MATCHING_PACKET_BULK_ABORT) sendAbortBulkDataPacket(context, &msg->mac, msg->optlen, opt); + + // Pop input stack from front (this should be queue instead of stack?) + context->input_stack = msg->next; + free(msg); + msg = context->input_stack; + msg_count++; + } + + // Free Stack + context->inputlock->unlock(); + INFO_LOG(SCENET, "InputLoop[%d]: Finished (%d msg)", matchingId, msg_count); + } + // Clear IO Message Stack clearStack(context, PSP_ADHOC_MATCHING_INPUT_STACK); - // Send Bye Messages - sendByePacket(context); + // Send Bye Messages. FIXME: Official prx seems to be sending DEATH instead of BYE packet during MatchingStop? + //sendByePacket(context); + sendDeathPacket(context, &context->mac); // Free Peer List Buffer clearPeerList(context); //deleteAllMembers(context); From a47cb101bb5dc0f865c4ea0fc7d1df1cea5cf024 Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Thu, 29 Oct 2020 05:58:12 +0700 Subject: [PATCH 16/17] Moving socket creation from AdhocMatchingCreate to AdhocMatchingStart. (Fixes Dissidia 012 unable to see any room issue) --- Core/HLE/sceNetAdhoc.cpp | 158 ++++++++++++++++++--------------------- 1 file changed, 72 insertions(+), 86 deletions(-) diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 89f15f0e3ad9..65501c38ca3f 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -4224,6 +4224,12 @@ int NetAdhocMatching_Stop(int matchingId) { } matchingThreads[item->matching_thid] = 0; + // Make sure nobody locking/using the socket + item->socketlock->lock(); + // Delete the socket + NetAdhocPdp_Delete(item->socket, 0); // item->connected = (sceNetAdhocPdpDelete(item->socket, 0) < 0); + item->socketlock->unlock(); + // Multithreading Lock peerlock.lock(); @@ -4274,11 +4280,7 @@ int NetAdhocMatching_Delete(int matchingId) { // Delete the Fake PSP Thread //__KernelDeleteThread(item->matching_thid, SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocMatching deleted"); //delete item->matchingThread; - // Make sure nobody locking/using the socket - item->socketlock->lock(); - // Delete the socket - NetAdhocPdp_Delete(item->socket, 0); // item->connected = (sceNetAdhocPdpDelete(item->socket, 0) < 0); - item->socketlock->unlock(); + // Free allocated memories free(item->hello); free(item->rxbuf); @@ -4395,7 +4397,8 @@ static int sceNetAdhocMatchingCreate(int mode, int maxnum, int port, int rxbufle SceNetAdhocMatchingContext * item = contexts; for (; item != NULL; item = item->next) { // Port Match found - if (item->port == port) return ERROR_NET_ADHOC_MATCHING_PORT_IN_USE; + if (item->port == port) + return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_PORT_IN_USE, "adhoc matching port in use"); } // Allocate Context Memory @@ -4406,95 +4409,85 @@ static int sceNetAdhocMatchingCreate(int mode, int maxnum, int port, int rxbufle // Create PDP Socket SceNetEtherAddr localmac; getLocalMac(&localmac); - int socket = sceNetAdhocPdpCreate((const char*)&localmac, (uint32_t)port, rxbuflen, 0); - // Created PDP Socket - if (socket > 0) { - // Clear Memory - memset(context, 0, sizeof(SceNetAdhocMatchingContext)); - - // Allocate Receive Buffer - context->rxbuf = (uint8_t *)malloc(rxbuflen); - - // Allocated Memory - if (context->rxbuf != NULL) { - // Clear Memory - memset(context->rxbuf, 0, rxbuflen); - - // Fill in Context Data - context->id = findFreeMatchingID(); - context->mode = mode; - context->maxpeers = maxnum; - context->port = port; - context->socket = socket; - context->rxbuflen = rxbuflen; - context->resendcounter = init_count; - context->resend_int = rexmt_int; // used as ack timeout on lost packet (ie. not receiving anything after sending)? - context->hello_int = hello_int; // client might set this to 0 - 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; // For internet play we need higher timeout than what the game wanted - context->handler = handler; - - // Fill in Selfpeer - context->mac = localmac; - - // Create locks - context->socketlock = new std::recursive_mutex; - context->eventlock = new std::recursive_mutex; - context->inputlock = new std::recursive_mutex; - - // Multithreading Lock - peerlock.lock(); //contextlock.lock(); - - // Add Callback Handler - context->handler.entryPoint = callbackAddr; - context->matching_thid = static_cast(matchingThreads.size()); - matchingThreads.push_back(0); - - // Link Context - //context->connected = true; - context->next = contexts; - contexts = context; - // Multithreading UnLock - peerlock.unlock(); //contextlock.unlock(); - - // Just to make sure Adhoc is already connected - //hleDelayResult(context->id, "give time to init/cleanup", adhocEventDelayMS * 1000); + // Clear Memory + memset(context, 0, sizeof(SceNetAdhocMatchingContext)); - // Return Matching ID - return context->id; - } + // Allocate Receive Buffer + context->rxbuf = (uint8_t*)malloc(rxbuflen); - // Close PDP Socket - sceNetAdhocPdpDelete(socket, 0); // context->connected = (sceNetAdhocPdpDelete(socket, 0) < 0); + // Allocated Memory + if (context->rxbuf != NULL) { + // Clear Memory + memset(context->rxbuf, 0, rxbuflen); + + // Fill in Context Data + context->id = findFreeMatchingID(); + context->mode = mode; + context->maxpeers = maxnum; + context->port = port; + context->rxbuflen = rxbuflen; + context->resendcounter = init_count; + context->resend_int = rexmt_int; // used as ack timeout on lost packet (ie. not receiving anything after sending)? + context->hello_int = hello_int; // client might set this to 0 + 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; // For internet play we need higher timeout than what the game wanted + context->handler = handler; + + // Fill in Selfpeer + context->mac = localmac; + + // Create locks + context->socketlock = new std::recursive_mutex; + context->eventlock = new std::recursive_mutex; + context->inputlock = new std::recursive_mutex; + + // Multithreading Lock + peerlock.lock(); //contextlock.lock(); + + // Add Callback Handler + context->handler.entryPoint = callbackAddr; + context->matching_thid = static_cast(matchingThreads.size()); + matchingThreads.push_back(0); + + // Link Context + //context->connected = true; + context->next = contexts; + contexts = context; + + // Multithreading UnLock + peerlock.unlock(); //contextlock.unlock(); + + // Just to make sure Adhoc is already connected + //hleDelayResult(context->id, "give time to init/cleanup", adhocEventDelayMS * 1000); + + // Return Matching ID + return hleLogDebug(SCENET, context->id, "success"); } // Free Memory free(context); - - // Port in use - if (socket < 1) return ERROR_NET_ADHOC_MATCHING_PORT_IN_USE; // ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED; // -1; // ERROR_NET_ADHOC_MATCHING_NOT_ESTABLISHED; } // Out of Memory - return ERROR_NET_ADHOC_MATCHING_NO_SPACE; + return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_NO_SPACE, "adhoc matching no space"); } // InvalidERROR_NET_Arguments - return ERROR_NET_ADHOC_MATCHING_INVALID_ARG; + return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_ARG, "adhoc matching invalid arg"); } // Invalid Receive Buffer Size - return ERROR_NET_ADHOC_MATCHING_RXBUF_TOO_SHORT; + return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_RXBUF_TOO_SHORT, "adhoc matching rxbuf too short"); } // Invalid Member Limit - return ERROR_NET_ADHOC_MATCHING_INVALID_MAXNUM; + return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_INVALID_MAXNUM, "adhoc matching invalid maxnum"); } // Uninitialized Library - return ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED; + return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED, "adhoc matching not initialized"); } int NetAdhocMatching_Start(int matchingId, int evthPri, int evthPartitionId, int evthStack, int inthPri, int inthPartitionId, int inthStack, int optLen, u32 optDataAddr) { @@ -4518,18 +4511,11 @@ int NetAdhocMatching_Start(int matchingId, int evthPri, int evthPartitionId, int } //else return ERROR_NET_ADHOC_MATCHING_INVALID_ARG; // ERROR_NET_ADHOC_MATCHING_INVALID_OPTLEN; // Returning Not Success will cause GTA:VC stuck unable to choose host/join menu - //Add your own MAC as a member (only if it's empty?) - /*SceNetAdhocMatchingMemberInternal * peer = addMember(item, &item->mac); - switch (item->mode) { - case PSP_ADHOC_MATCHING_MODE_PARENT: - peer->state = PSP_ADHOC_MATCHING_PEER_OFFER; - break; - case PSP_ADHOC_MATCHING_MODE_CHILD: - peer->state = PSP_ADHOC_MATCHING_PEER_CHILD; - break; - case PSP_ADHOC_MATCHING_MODE_P2P: - peer->state = PSP_ADHOC_MATCHING_PEER_P2P; - }*/ + // Create PDP Socket + int sock = sceNetAdhocPdpCreate((const char*)&item->mac, static_cast(item->port), item->rxbuflen, 0); + item->socket = sock; + if (sock < 1) + return hleLogError(SCENET, ERROR_NET_ADHOC_MATCHING_PORT_IN_USE, "adhoc matching port in use"); // Create & Start the Fake PSP Thread ("matching_ev%d" and "matching_io%d") netAdhocValidateLoopMemory(); From 4eca1247108d79ce4bc7b64a0703ca68e88086b3 Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Thu, 29 Oct 2020 05:48:55 +0700 Subject: [PATCH 17/17] Blocks current thread to synchronize initial GameMode data. Fixes desync issue when GameMode multiplayer mission is being started. --- Core/HLE/proAdhoc.cpp | 26 +++++--- Core/HLE/proAdhoc.h | 2 + Core/HLE/sceNetAdhoc.cpp | 140 +++++++++++++++++++++++++++++---------- Core/HLE/sceNetAdhoc.h | 1 + 4 files changed, 123 insertions(+), 46 deletions(-) diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index 2a69c352ff8f..416716561a1c 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -1405,6 +1405,12 @@ int friendFinder(){ } } + // 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; + notifyAdhocctlHandlers(ADHOCCTL_EVENT_ERROR, ERROR_NET_ADHOC_TIMEOUT); + } + // Handle Packets if (rxpos > 0) { // BSSID Packet @@ -1430,8 +1436,8 @@ int friendFinder(){ // Arrange the order to be consistent on all players (Host on top), Starting from our self the rest of new players will be added to the back gameModeMacs.push_back(localMac); - if (gameModeMacs.size() >= requiredGameModeMacs.size()) { - //adhocctlState = ADHOCCTL_STATE_GAMEMODE; + // FIXME: OPCODE_CONNECT_BSSID only triggered once, but the timing of ADHOCCTL_EVENT_GAME notification could be too soon, since there could be more players that need to join before the event should be notified + if (netAdhocGameModeEntered && gameModeMacs.size() >= requiredGameModeMacs.size()) { notifyAdhocctlHandlers(ADHOCCTL_EVENT_GAME, 0); } } @@ -1533,10 +1539,9 @@ int friendFinder(){ } // From JPCSP: Join complete when all the required MACs have joined - if (requiredGameModeMacs.size() > 0 && gameModeMacs.size() == requiredGameModeMacs.size()) { + if (netAdhocGameModeEntered && requiredGameModeMacs.size() > 0 && gameModeMacs.size() == requiredGameModeMacs.size()) { // TODO: Should we replace gameModeMacs contents with requiredGameModeMacs contents to make sure they are in the same order with macs from sceNetAdhocctlCreateEnterGameMode? But may not be consistent with the list on client side! //gameModeMacs = requiredGameModeMacs; - //adhocctlState = ADHOCCTL_STATE_GAMEMODE; notifyAdhocctlHandlers(ADHOCCTL_EVENT_GAME, 0); } } @@ -1582,13 +1587,14 @@ int friendFinder(){ // Log Incoming Peer Delete Request INFO_LOG(SCENET, "FriendFinder: Incoming Peer Data Delete Request..."); - /*if (adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE) { + if (adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE) { auto peer = findFriendByIP(packet->ip); - gameModeMacs.erase(std::remove_if(gameModeMacs.begin(), gameModeMacs.end(), - [peer](SceNetEtherAddr const& e) { - return isMacMatch(&e, &peer->mac_addr); - }), gameModeMacs.end()); - }*/ + for (auto& gma : replicaGameModeAreas) + if (isMacMatch(&gma.mac, &peer->mac_addr)) { + gma.updateTimestamp = 0; + break; + } + } // Delete User by IP, should delete by MAC since IP can be shared (behind NAT) isn't? deleteFriendByIP(packet->ip); diff --git a/Core/HLE/proAdhoc.h b/Core/HLE/proAdhoc.h index 0fa4cc8c9011..c90a3f9eeac9 100644 --- a/Core/HLE/proAdhoc.h +++ b/Core/HLE/proAdhoc.h @@ -114,6 +114,7 @@ inline bool isDisconnected(int errcode) { return (errcode == EPIPE || errcode == #define ADHOC_GAMEMODE_PORT 31000 #define GAMEMODE_UPDATE_INTERVAL 500 // 12000 usec on JPCSP, but lower value works better on BattleZone (in order to get full speed 60 FPS) #define GAMEMODE_INIT_DELAY 10000 +#define GAMEMODE_SYNC_TIMEOUT 250000 // GameMode Type #define ADHOCCTL_GAMETYPE_1A 1 @@ -326,6 +327,7 @@ typedef struct GameModeArea { //int socket; // PDP socket? u64 updateTimestamp; int dataUpdated; + int dataSent; SceNetEtherAddr mac; u8* data; // upto "size" bytes started from "addr" ? } PACK GameModeArea; diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 65501c38ca3f..ccfff8378a62 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -66,6 +66,7 @@ bool netAdhocctlInited; bool networkInited = false; bool netAdhocGameModeEntered = false; +int netAdhocEnterGameModeTimeout = 15000000; // 15 sec as default timeout, to wait for all players to join bool netAdhocMatchingInited; int netAdhocMatchingStarted = 0; @@ -147,37 +148,79 @@ static void __GameModeNotify(u64 userdata, int cyclesLate) { SceUID threadID = userdata >> 32; int uid = (int)(userdata & 0xFFFFFFFF); - // TODO: May be we should check for timeout too? since EnterGammeMode have timeout arg if (IsGameModeActive()) { auto sock = adhocSockets[gameModeSocket - 1]; - // Send Master data - if (masterGameModeArea.dataUpdated) { - for (auto& gma : replicaGameModeAreas) { - if (IsGameModeActive() && IsSocketReady(sock->data.pdp.id, false, true) > 0) { - int sent = sceNetAdhocPdpSend(gameModeSocket, (const char*)&gma.mac, ADHOC_GAMEMODE_PORT, masterGameModeArea.data, masterGameModeArea.size, 0, ADHOC_F_NONBLOCK); - if (sent >= 0) { - masterGameModeArea.dataUpdated = 0; - DEBUG_LOG(SCENET, "GameMode Sent %d bytes to %s", masterGameModeArea.size, mac2str(&gma.mac).c_str()); + // Need to make sure all replicas have been created before we start syncing data + if (replicaGameModeAreas.size() == (gameModeMacs.size() - 1)) { + // Send Master data + if (masterGameModeArea.dataUpdated) { + int sentcount = 0; + for (auto& gma : replicaGameModeAreas) { + if (!gma.dataSent && IsSocketReady(sock->data.pdp.id, false, true) > 0) { + int sent = sceNetAdhocPdpSend(gameModeSocket, (const char*)&gma.mac, ADHOC_GAMEMODE_PORT, masterGameModeArea.data, masterGameModeArea.size, 0, ADHOC_F_NONBLOCK); + if (sent != ERROR_NET_ADHOC_WOULD_BLOCK) { + gma.dataSent = 1; + DEBUG_LOG(SCENET, "GameMode: Master data Sent %d bytes to Area #%d [%s]", masterGameModeArea.size, gma.id, mac2str(&gma.mac).c_str()); + sentcount++; + } + } + else if (gma.dataSent) sentcount++; + } + if (sentcount == replicaGameModeAreas.size()) + masterGameModeArea.dataUpdated = 0; + } + // Need to sync (send + recv) all players initial data (data from CreateMaster) after Master + All Replicas are created, and before the first UpdateMaster / UpdateReplica is called for Star Wars The Force Unleashed to show the correct players color on minimap (also prevent Starting issue on other GameMode games) + else { + u32 error; + SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_NET, error); + if (error == 0 && waitID == sock->data.pdp.id) { + // Resume thread after all replicas data have been received + int recvd = 0; + for (auto& gma : replicaGameModeAreas) { + // Either replicas new data has been received or that player has been disconnected + if (gma.dataUpdated || gma.updateTimestamp == 0) + recvd++; + } + // Resume blocked thread + u64 now = CoreTiming::GetGlobalTimeUsScaled(); + if (recvd == replicaGameModeAreas.size()) { + u32 waitVal = __KernelGetWaitValue(threadID, error); + if (error == 0) { + DEBUG_LOG(SCENET, "GameMode: Resuming Thread %d after Master data Synced (Result = %08x)", threadID, waitVal); + __KernelResumeThreadFromWait(threadID, waitVal); + } + else + ERROR_LOG(SCENET, "GameMode: Error (%08x) on WaitValue %d ThreadID %d", error, waitVal, threadID); + } + // Attempt to Re-Send initial Master data (in case previous packets were lost) + else if (static_cast(now - masterGameModeArea.updateTimestamp) > GAMEMODE_SYNC_TIMEOUT) { + DEBUG_LOG(SCENET, "GameMode: Attempt to Re-Send Master data after Sync Timeout (%d us)", GAMEMODE_SYNC_TIMEOUT); + // Reset Sent marker on players who haven't replied yet (except disconnected players) + for (auto& gma : replicaGameModeAreas) + if (!gma.dataUpdated && gma.updateTimestamp != 0) + gma.dataSent = 0; + masterGameModeArea.updateTimestamp = now; + masterGameModeArea.dataUpdated = 1; } } } - } - // Recv new Replica data. We shouldn't do too much activity on a single event and just try again later after a short delay (1ms or lower, to prevent long sync with max players) - if (IsGameModeActive() && IsSocketReady(sock->data.pdp.id, true, false) > 0) { - SceNetEtherAddr sendermac; - s32_le senderport = ADHOC_GAMEMODE_PORT; - s32_le bufsz = uid; // GAMEMODE_BUFFER_SIZE; - int ret = sceNetAdhocPdpRecv(gameModeSocket, &sendermac, &senderport, gameModeBuffer, &bufsz, 0, ADHOC_F_NONBLOCK); - if (ret >= 0 && bufsz > 0) { - for (auto& gma : replicaGameModeAreas) { - if (IsMatch(gma.mac, sendermac)) { - DEBUG_LOG(SCENET, "GameMode Received %d bytes of new Data for Area [%s]", bufsz, mac2str(&sendermac).c_str()); - memcpy(gma.data, gameModeBuffer, std::min(gma.size, bufsz)); - gma.dataUpdated = 1; - gma.updateTimestamp = CoreTiming::GetGlobalTimeUsScaled(); - break; + // Recv new Replica data when available + if (IsSocketReady(sock->data.pdp.id, true, false) > 0) { + SceNetEtherAddr sendermac; + s32_le senderport = ADHOC_GAMEMODE_PORT; + s32_le bufsz = uid; // GAMEMODE_BUFFER_SIZE; + int ret = sceNetAdhocPdpRecv(gameModeSocket, &sendermac, &senderport, gameModeBuffer, &bufsz, 0, ADHOC_F_NONBLOCK); + if (ret >= 0 && bufsz > 0) { + for (auto& gma : replicaGameModeAreas) { + if (IsMatch(gma.mac, sendermac)) { + DEBUG_LOG(SCENET, "GameMode: Replica data Received %d bytes for Area #%d [%s]", bufsz, gma.id, mac2str(&sendermac).c_str()); + memcpy(gma.data, gameModeBuffer, std::min(gma.size, bufsz)); + gma.dataUpdated = 1; + gma.updateTimestamp = CoreTiming::GetGlobalTimeUsScaled(); + break; + } } } } @@ -969,10 +1012,12 @@ void __NetAdhocDoState(PointerWrap &p) { if (s >= 8) { Do(p, isAdhocctlBusy); Do(p, netAdhocGameModeEntered); + Do(p, netAdhocEnterGameModeTimeout); } else { isAdhocctlBusy = false; netAdhocGameModeEntered = false; + netAdhocEnterGameModeTimeout = 15000000; } if (p.mode == p.MODE_READ) { @@ -2534,6 +2579,7 @@ int NetAdhocctl_Create(const char* groupName) { // Send Packet int iResult = send(metasocket, (const char*)&packet, sizeof(packet), MSG_NOSIGNAL); int error = errno; + adhocctlStartTime = (u64)(time_now_d() * 1000000.0); if (iResult == SOCKET_ERROR && error != EAGAIN && error != EWOULDBLOCK) { ERROR_LOG(SCENET, "Socket error (%i) when sending", error); @@ -2681,6 +2727,7 @@ int NetAdhocctl_CreateEnterGameMode(const char* group_name, int game_type, int n adhocctlCurrentMode = ADHOCCTL_MODE_GAMEMODE; adhocConnectionType = ADHOC_CREATE; netAdhocGameModeEntered = true; + netAdhocEnterGameModeTimeout = timeout; return NetAdhocctl_Create(group_name); } @@ -2742,6 +2789,7 @@ static int sceNetAdhocctlJoinEnterGameMode(const char * group_name, const char * adhocctlCurrentMode = ADHOCCTL_MODE_GAMEMODE; adhocConnectionType = ADHOC_JOIN; netAdhocGameModeEntered = true; + netAdhocEnterGameModeTimeout = timeout; return hleLogDebug(SCENET, NetAdhocctl_Create(group_name), ""); } @@ -4007,6 +4055,7 @@ static int sceNetAdhocGameModeCreateMaster(u32 dataAddr, int size) { if (size < 0 || !Memory::IsValidAddress(dataAddr)) return hleLogError(SCENET, ERROR_NET_ADHOCCTL_INVALID_ARG, "invalid arg"); + hleEatMicro(1000); SceNetEtherAddr localMac; getLocalMac(&localMac); u8* buf = (u8*)realloc(gameModeBuffer, size); @@ -4016,14 +4065,21 @@ static int sceNetAdhocGameModeCreateMaster(u32 dataAddr, int size) { u8* data = (u8*)malloc(size); if (data) { Memory::Memcpy(data, dataAddr, size); - masterGameModeArea = { 0, size, dataAddr, CoreTiming::GetGlobalTimeUsScaled(), 1, localMac, data }; + masterGameModeArea = { 0, size, dataAddr, CoreTiming::GetGlobalTimeUsScaled(), 1, 0, localMac, data }; // Socket's buffer size should fit the largest size from master/replicas, should we waited until master & all replicas to be created first before creating the socket? (ie. the first time UpdateMaster being called?) gameModeSocket = sceNetAdhocPdpCreate((const char*)&localMac, ADHOC_GAMEMODE_PORT, size, 0); StartGameModeScheduler(size); - } - hleEatMicro(GAMEMODE_INIT_DELAY); - return hleLogDebug(SCENET, 0, "success"); // returned an id just like CreateReplica? always return 0? + // Block current thread to sync initial master data + if (gameModeSocket > 0 && replicaGameModeAreas.size() == (gameModeMacs.size() - 1)) { + auto sock = adhocSockets[gameModeSocket - 1]; + __KernelWaitCurThread(WAITTYPE_NET, sock->data.pdp.id, 0, 0, false, "syncing master data"); + DEBUG_LOG(SCENET, "GameMode: Blocking Thread %d to Sync initial Master data", __KernelGetCurThread()); + } + return hleLogDebug(SCENET, 0, "success"); // returned an id just like CreateReplica? always return 0? + } + + return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_CREATED, "not created"); } /** @@ -4059,7 +4115,7 @@ static int sceNetAdhocGameModeCreateReplica(const char *mac, u32 dataAddr, int s // MAC address already existed! if (it != replicaGameModeAreas.end()) { WARN_LOG(SCENET, "sceNetAdhocGameModeCreateReplica - [%s] is already existed (id: %d)", mac2str((SceNetEtherAddr*)mac).c_str(), it->id); - return it->id; + return it->id; // ERROR_NET_ADHOC_ALREADY_CREATED } int ret = 0; @@ -4067,11 +4123,20 @@ static int sceNetAdhocGameModeCreateReplica(const char *mac, u32 dataAddr, int s if (data) { Memory::Memcpy(data, dataAddr, size); //int sock = sceNetAdhocPdpCreate(mac, ADHOC_GAMEMODE_PORT, size, 0); - GameModeArea gma = { maxid + 1, size, dataAddr, CoreTiming::GetGlobalTimeUsScaled(), 0, *(SceNetEtherAddr*)mac, data }; + GameModeArea gma = { maxid + 1, size, dataAddr, CoreTiming::GetGlobalTimeUsScaled(), 0, 0, *(SceNetEtherAddr*)mac, data }; replicaGameModeAreas.push_back(gma); - ret = gma.id; + ret = gma.id; // Valid id for replica is higher than 0? + + // Block current thread to sync initial master data + if (gameModeSocket > 0 && replicaGameModeAreas.size() == (gameModeMacs.size() - 1)) { + auto sock = adhocSockets[gameModeSocket - 1]; + __KernelWaitCurThread(WAITTYPE_NET, sock->data.pdp.id, ret, 0, false, "syncing master data"); + DEBUG_LOG(SCENET, "GameMode: Blocking Thread %d to Sync initial Master data", __KernelGetCurThread()); + } + return hleLogSuccessInfoI(SCENET, ret, "success"); } - return hleLogSuccessInfoI(SCENET, ret, "success"); // valid id for replica started from 1? + + return hleLogError(SCENET, ERROR_NET_ADHOC_NOT_CREATED, "not created"); } static int sceNetAdhocGameModeUpdateMaster() { @@ -4089,8 +4154,11 @@ static int sceNetAdhocGameModeUpdateMaster() { Memory::Memcpy(masterGameModeArea.data, masterGameModeArea.addr, masterGameModeArea.size); masterGameModeArea.dataUpdated = 1; masterGameModeArea.updateTimestamp = CoreTiming::GetGlobalTimeUsScaled(); + // Reset sent marker + for (auto& gma : replicaGameModeAreas) + gma.dataSent = 0; } - + hleEatMicro(1000); return 0; } @@ -4143,7 +4211,7 @@ static int sceNetAdhocGameModeUpdateReplica(int id, u32 infoAddr) { GameModeUpdateInfo* gmuinfo = (GameModeUpdateInfo*)Memory::GetPointer(infoAddr); gmuinfo->length = sizeof(GameModeUpdateInfo); if (gma.data && gma.dataUpdated) { - memcpy(Memory::GetPointer(gma.addr), gma.data, gma.size); + Memory::Memcpy(gma.addr, gma.data, gma.size); gma.dataUpdated = 0; gmuinfo->updated = 1; gmuinfo->timeStamp = std::max(gma.updateTimestamp, CoreTiming::GetGlobalTimeUsScaled() - defaultLastRecvDelta); @@ -5314,10 +5382,10 @@ void __NetTriggerCallbacks() u32_le args[3] = { 0, 0, 0 }; args[0] = flags; args[1] = error; + u64 now = (u64)(time_now_d() * 1000000.0); // 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() * 1000000.0); - if ((flags != ADHOCCTL_EVENT_CONNECT && flags != ADHOCCTL_EVENT_GAME) || adhocConnectionType != ADHOC_JOIN || getActivePeerCount() > 0 || now - adhocctlStartTime > adhocDefaultTimeout) + if ((flags != ADHOCCTL_EVENT_CONNECT && flags != ADHOCCTL_EVENT_GAME) || adhocConnectionType != ADHOC_JOIN || getActivePeerCount() > 0 || static_cast(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 if (actionAfterAdhocMipsCall < 0) { diff --git a/Core/HLE/sceNetAdhoc.h b/Core/HLE/sceNetAdhoc.h index b0d2f36fe7fd..73016cc20d6f 100644 --- a/Core/HLE/sceNetAdhoc.h +++ b/Core/HLE/sceNetAdhoc.h @@ -99,6 +99,7 @@ extern bool netAdhocInited; extern bool netAdhocctlInited; extern bool networkInited; extern bool netAdhocGameModeEntered; +extern int netAdhocEnterGameModeTimeout; extern int adhocDefaultTimeout; //3000000 usec extern int adhocDefaultDelay; //10000 extern int adhocExtraDelay; //20000