From fb7d50e5c20a6b3dc9eb0f83d84ae17f684620f5 Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Tue, 24 Dec 2019 00:04:03 +0700 Subject: [PATCH] Improved multiplayer compatibility on some games (ie. GTA VCS, Naruto Ultimate Ninja Heroes 3, DBZ Shin Budokai 2, Power Stone Collection, .hack//Link, etc) --- Core/HLE/proAdhoc.cpp | 456 +++++++----- Core/HLE/proAdhoc.h | 114 ++- Core/HLE/proAdhocServer.cpp | 72 +- Core/HLE/proAdhocServer.h | 6 +- Core/HLE/sceNet.cpp | 100 ++- Core/HLE/sceNet.h | 18 +- Core/HLE/sceNetAdhoc.cpp | 1309 ++++++++++++++++++++++++----------- Core/HLE/sceNetAdhoc.h | 10 + 8 files changed, 1428 insertions(+), 657 deletions(-) diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index c64b50a04e..043ca79e6e 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -27,6 +27,7 @@ #include #include "util/text/parsers.h" +#include "thread/threadutil.h" #include "Core/Core.h" #include "Core/Host.h" #include "Core/HLE/sceKernelInterrupt.h" @@ -46,7 +47,7 @@ SceNetAdhocctlScanInfo * networks = NULL; SceNetAdhocctlScanInfo * newnetworks = NULL; int threadStatus = ADHOCCTL_STATE_DISCONNECTED; -bool IsAdhocctlInCB = false; +int actionAfterAdhocMipsCall; int actionAfterMatchingMipsCall; // Broadcast MAC @@ -59,7 +60,6 @@ std::thread friendFinderThread; std::recursive_mutex peerlock; SceNetAdhocPdpStat * pdp[255]; SceNetAdhocPtpStat * ptp[255]; -uint32_t localip; std::vector chatLog; std::string name = ""; std::string incoming = ""; @@ -67,11 +67,10 @@ std::string message = ""; bool chatScreenVisible = false; bool updateChatScreen = false; int newChat = 0; - bool isLocalServer = false; sockaddr localIP; // This might serves the same purpose with existing "localip" above, but since this is copied from my old code so here it is (too lazy to rewrite the code) -int isLocalMAC(const SceNetEtherAddr * addr) { +bool isLocalMAC(const SceNetEtherAddr * addr) { SceNetEtherAddr saddr; getLocalMac(&saddr); @@ -82,32 +81,59 @@ int isLocalMAC(const SceNetEtherAddr * addr) { return (match == 0); } -int isPDPPortInUse(uint16_t port) { +bool isPDPPortInUse(uint16_t port) { // Iterate Elements - int i = 0; for (; i < 255; i++) if (pdp[i] != NULL && pdp[i]->lport == port) return 1; + for (int i = 0; i < 255; i++) if (pdp[i] != NULL && pdp[i]->lport == port) return true; // Unused Port - return 0; + return false; } -int isPTPPortInUse(uint16_t port) { +bool isPTPPortInUse(uint16_t port) { // Iterate Sockets - int i = 0; for(; i < 255; i++) if(ptp[i] != NULL && ptp[i]->lport == port) return 1; + for(int i = 0; i < 255; i++) if(ptp[i] != NULL && ptp[i]->lport == port) return true; // Unused Port - return 0; + return false; +} + +char* mac2str(SceNetEtherAddr* mac) { + if (mac == NULL) return ":::::"; +#if defined(_WIN32) + static __declspec(thread) char str[18]; +#else + static __thread char str[18]; +#endif + snprintf(str, sizeof(str), "%02x:%02x:%02x:%02x:%02x:%02x", mac->data[0], mac->data[1], mac->data[2], mac->data[3], mac->data[4], mac->data[5]); + + return str; +} + +char* mac2str(SceNetEtherAddr* mac, char* str, size_t size) { + if (mac == NULL || str == NULL || size < 18) return NULL; + + snprintf(str, size, "%02x:%02x:%02x:%02x:%02x:%02x", mac->data[0], mac->data[1], mac->data[2], mac->data[3], mac->data[4], mac->data[5]); + + return str; } SceNetAdhocMatchingMemberInternal* addMember(SceNetAdhocMatchingContext * context, SceNetEtherAddr * mac) { if (context == NULL || mac == NULL) return NULL; SceNetAdhocMatchingMemberInternal * peer = findPeer(context, mac); + // Already existed + if (peer != NULL) { + char tmpmac[18]; + WARN_LOG(SCENET, "Member Peer Already Existed! Updating [%s]", mac2str(mac, tmpmac)); + peer->lastping = CoreTiming::GetGlobalTimeUsScaled(); + } // Member is not added yet - if (peer == NULL) { + else { peer = (SceNetAdhocMatchingMemberInternal *)malloc(sizeof(SceNetAdhocMatchingMemberInternal)); if (peer != NULL) { memset(peer, 0, sizeof(SceNetAdhocMatchingMemberInternal)); peer->mac = *mac; + peer->lastping = CoreTiming::GetGlobalTimeUsScaled(); peer->next = context->peerlist; context->peerlist = peer; } @@ -124,14 +150,15 @@ void addFriend(SceNetAdhocctlConnectPacketS2C * packet) { SceNetAdhocctlPeerInfo * peer = findFriend(&packet->mac); // Already existed if (peer != NULL) { + char tmpmac[18]; + WARN_LOG(SCENET, "Friend Peer Already Existed! Updating [%s][%s][%s]", packet->name.data, mac2str(&packet->mac, tmpmac), inet_ntoa(*(in_addr*)&packet->ip)); peer->nickname = packet->name; peer->mac_addr = packet->mac; peer->ip_addr = packet->ip; // Update TimeStamp peer->last_recv = CoreTiming::GetGlobalTimeUsScaled(); } - else - { + else { // Allocate Structure peer = (SceNetAdhocctlPeerInfo *)malloc(sizeof(SceNetAdhocctlPeerInfo)); // Allocated Structure @@ -198,7 +225,7 @@ void changeBlockingMode(int fd, int nonblocking) { #endif } -int countAvailableNetworks(void) { +int countAvailableNetworks() { // Network Count int count = 0; @@ -236,11 +263,12 @@ void freeGroupsRecursive(SceNetAdhocctlScanInfo * node) { // Free Memory free(node); + node = NULL; } -void deleteAllPDP(void) { +void deleteAllPDP() { // Iterate Element - int i = 0; for (; i < 255; i++) { + for (int i = 0; i < 255; i++) { // Active Socket if (pdp[i] != NULL) { // Close Socket @@ -255,9 +283,9 @@ void deleteAllPDP(void) { } } -void deleteAllPTP(void) { +void deleteAllPTP() { // Iterate Element - int i = 0; for (; i < 255; i++) { + for (int i = 0; i < 255; i++) { // Active Socket if (ptp[i] != NULL) { // Close Socket @@ -283,36 +311,39 @@ void deleteFriendByIP(uint32_t ip) { for (; peer != NULL; peer = peer->next) { // Found Peer if (peer->ip_addr == ip) { - // Instead of removing it from the list we'll make it timeout since most Matching games are moving group and may still need the peer data - peer->last_recv = 0; - + // Multithreading Lock peerlock.lock(); // Unlink Left (Beginning) - if(prev == NULL)friends = peer->next; + /*if (prev == NULL) friends = peer->next; // Unlink Left (Other) else prev->next = peer->next; + */ + + char tmpmac[18]; + INFO_LOG(SCENET, "Removing Friend Peer %s [%s]", mac2str(&peer->mac_addr, tmpmac), inet_ntoa(*(in_addr*)&peer->ip_addr)); + + // Free Memory + //free(peer); + //peer = NULL; + // Instead of removing it from the list we'll make it timed out since most Matching games are moving group and may still need the peer data thus not recognizing it as Unknown peer + peer->last_recv = 0; //CoreTiming::GetGlobalTimeUsScaled(); // Multithreading Unlock peerlock.unlock(); - // Free Memory - free(peer); - peer = NULL; - // Stop Search break; } // Set Previous Reference - // TODO: Should this be used by something? prev = peer; } } -int findFreeMatchingID(void) { +int findFreeMatchingID() { // Minimum Matching ID int min = 1; @@ -320,13 +351,15 @@ int findFreeMatchingID(void) { int max = 0; // Find highest Matching ID - SceNetAdhocMatchingContext * item = contexts; for (; item != NULL; item = item->next) { + SceNetAdhocMatchingContext * item = contexts; + for (; item != NULL; item = item->next) { // New Maximum if (max < item->id) max = item->id; } // Find unoccupied ID - int i = min; for (; i < max; i++) { + int i = min; + for (; i < max; i++) { // Found unoccupied ID if (findMatchingContext(i) == NULL) return i; } @@ -337,7 +370,8 @@ int findFreeMatchingID(void) { SceNetAdhocMatchingContext * findMatchingContext(int id) { // Iterate Matching Context List - SceNetAdhocMatchingContext * item = contexts; for (; item != NULL; item = item->next) { // Found Matching ID + SceNetAdhocMatchingContext * item = contexts; + for (; item != NULL; item = item->next) { // Found Matching ID if (item->id == id) return item; } @@ -353,7 +387,8 @@ SceNetAdhocMatchingContext * findMatchingContext(int id) { SceNetAdhocMatchingMemberInternal * findOutgoingRequest(SceNetAdhocMatchingContext * context) { // Iterate Peer List for Matching Target - SceNetAdhocMatchingMemberInternal * peer = context->peerlist; for (; peer != NULL; peer = peer->next) + SceNetAdhocMatchingMemberInternal * peer = context->peerlist; + for (; peer != NULL; peer = peer->next) { // Found Peer in List if (peer->state == PSP_ADHOC_MATCHING_PEER_OUTGOING_REQUEST) return peer; @@ -369,6 +404,8 @@ SceNetAdhocMatchingMemberInternal * findOutgoingRequest(SceNetAdhocMatchingConte */ void postAcceptCleanPeerList(SceNetAdhocMatchingContext * context) { + int delcount = 0; + int peercount = 0; // Acquire Peer Lock peerlock.lock(); @@ -380,14 +417,20 @@ void postAcceptCleanPeerList(SceNetAdhocMatchingContext * context) SceNetAdhocMatchingMemberInternal * next = peer->next; // Unneeded Peer - if (peer->state != PSP_ADHOC_MATCHING_PEER_CHILD && peer->state != PSP_ADHOC_MATCHING_PEER_P2P && peer->state != PSP_ADHOC_MATCHING_PEER_PARENT) deletePeer(context, peer); + if (peer->state != PSP_ADHOC_MATCHING_PEER_CHILD && peer->state != PSP_ADHOC_MATCHING_PEER_P2P && peer->state != PSP_ADHOC_MATCHING_PEER_PARENT) { + deletePeer(context, peer); + delcount++; + } // Move to Next Peer peer = next; + peercount++; } // Free Peer Lock peerlock.unlock(); + + INFO_LOG(SCENET, "Removing Unneeded Peer (%i/%i)", delcount, peercount); } /** @@ -404,7 +447,7 @@ void postAcceptAddSiblings(SceNetAdhocMatchingContext * context, int siblingcoun uint8_t * siblings_u8 = (uint8_t *)siblings; // Iterate Siblings - int i = 0; for (; i < siblingcount; i++) + for (int i = 0; i < siblingcount; i++) { // Allocate Memory SceNetAdhocMatchingMemberInternal * sibling = (SceNetAdhocMatchingMemberInternal *)malloc(sizeof(SceNetAdhocMatchingMemberInternal)); @@ -431,7 +474,8 @@ void postAcceptAddSiblings(SceNetAdhocMatchingContext * context, int siblingcoun // Spawn Established Event spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_ESTABLISHED, &sibling->mac, 0, NULL); - INFO_LOG(SCENET, "Accepting Peer %02X:%02X:%02X:%02X:%02X:%02X", sibling->mac.data[0], sibling->mac.data[1], sibling->mac.data[2], sibling->mac.data[3], sibling->mac.data[4], sibling->mac.data[5]); + char tmpmac[18]; + INFO_LOG(SCENET, "Accepting Peer %s", mac2str(&sibling->mac, tmpmac)); } } } @@ -441,14 +485,17 @@ void postAcceptAddSiblings(SceNetAdhocMatchingContext * context, int siblingcoun * @param context Matching Context Pointer * @return Number of Children */ -s32_le countChildren(SceNetAdhocMatchingContext * context) +s32_le countChildren(SceNetAdhocMatchingContext * context, const bool excludeTimedout) { // Children Counter s32_le count = 0; // Iterate Peer List for Matching Target - SceNetAdhocMatchingMemberInternal * peer = context->peerlist; for (; peer != NULL; peer = peer->next) + SceNetAdhocMatchingMemberInternal * peer = context->peerlist; + for (; peer != NULL; peer = peer->next) { + // Exclude timedout members? + if (!excludeTimedout || peer->lastping != 0) // Increase Children Counter if (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD) count++; } @@ -466,7 +513,8 @@ s32_le countChildren(SceNetAdhocMatchingContext * context) SceNetAdhocMatchingMemberInternal * findPeer(SceNetAdhocMatchingContext * context, SceNetEtherAddr * mac) { // Iterate Peer List for Matching Target - SceNetAdhocMatchingMemberInternal * peer = context->peerlist; for (; peer != NULL; peer = peer->next) + SceNetAdhocMatchingMemberInternal * peer = context->peerlist; + for (; peer != NULL; peer = peer->next) { // Found Peer in List if (memcmp(&peer->mac, mac, sizeof(SceNetEtherAddr)) == 0) @@ -488,7 +536,8 @@ SceNetAdhocMatchingMemberInternal * findPeer(SceNetAdhocMatchingContext * contex SceNetAdhocMatchingMemberInternal * findParent(SceNetAdhocMatchingContext * context) { // Iterate Peer List for Matching Target - SceNetAdhocMatchingMemberInternal * peer = context->peerlist; for (; peer != NULL; peer = peer->next) + SceNetAdhocMatchingMemberInternal * peer = context->peerlist; + for (; peer != NULL; peer = peer->next) { // Found Peer in List if (peer->state == PSP_ADHOC_MATCHING_PEER_PARENT) return peer; @@ -503,12 +552,14 @@ SceNetAdhocMatchingMemberInternal * findParent(SceNetAdhocMatchingContext * cont * @param context Matching Context Pointer * @return Internal Peer Reference or... NULL */ -SceNetAdhocMatchingMemberInternal * findP2P(SceNetAdhocMatchingContext * context) +SceNetAdhocMatchingMemberInternal * findP2P(SceNetAdhocMatchingContext * context, const bool excludeTimedout) { // Iterate Peer List for Matching Target SceNetAdhocMatchingMemberInternal * peer = context->peerlist; for (; peer != NULL; peer = peer->next) { + // Exclude timedout members? + if (!excludeTimedout || peer->lastping != 0) // Found Peer in List if (peer->state == PSP_ADHOC_MATCHING_PEER_P2P) return peer; } @@ -550,7 +601,8 @@ void deletePeer(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingMemberI // Beginning Item else context->peerlist = item->next; - INFO_LOG(SCENET, "Removing Peer %02X:%02X:%02X:%02X:%02X:%02X", peer->mac.data[0], peer->mac.data[1], peer->mac.data[2], peer->mac.data[3], peer->mac.data[4], peer->mac.data[5]); + char tmpmac[18]; + INFO_LOG(SCENET, "Removing Member Peer %s", mac2str(&peer->mac, tmpmac)); } // Free Peer Memory @@ -741,7 +793,7 @@ void sendDeathMessage(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingM * @param context Matching Context Pointer * @return Number of Connected Peers */ -uint32_t countConnectedPeers(SceNetAdhocMatchingContext * context) +uint32_t countConnectedPeers(SceNetAdhocMatchingContext * context, const bool excludeTimedout) { // Peer Count uint32_t count = 0; @@ -750,7 +802,7 @@ uint32_t countConnectedPeers(SceNetAdhocMatchingContext * context) if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT) { // Number of Children + 1 Parent (Self) - count = countChildren(context) + 1; + count = countChildren(context, excludeTimedout) + 1; } // Child Mode @@ -763,7 +815,7 @@ uint32_t countConnectedPeers(SceNetAdhocMatchingContext * context) if (findParent(context) != NULL) { // Add Number of Siblings + 1 Parents - count += countChildren(context) + 1; + count += countChildren(context, excludeTimedout) + 1; } } @@ -774,7 +826,7 @@ uint32_t countConnectedPeers(SceNetAdhocMatchingContext * context) count = 1; // Connected to another P2P Client - if (findP2P(context) != NULL) + if (findP2P(context, excludeTimedout) != NULL) { // Add P2P Brother count++; @@ -801,31 +853,35 @@ void spawnLocalEvent(SceNetAdhocMatchingContext * context, int event, SceNetEthe /** * Handle Timeouts in Matching Context -* @param context Matchi]ng Context Pointer +* @param context Matching Context Pointer */ void handleTimeout(SceNetAdhocMatchingContext * context) { peerlock.lock(); // Iterate Peer List - SceNetAdhocMatchingMemberInternal * peer = context->peerlist; while (peer != NULL) + SceNetAdhocMatchingMemberInternal * peer = context->peerlist; + while (peer != NULL && contexts != NULL && coreState != CORE_POWERDOWN) { // Get Next Pointer (to avoid crash on memory freeing) SceNetAdhocMatchingMemberInternal * next = peer->next; u64_le now = CoreTiming::GetGlobalTimeUsScaled(); //real_time_now()*1000000.0 - // Timeout! + // Timeout!, may be we shouldn't kick timedout members ourself and let the game do it if ((now - peer->lastping) >= context->timeout) { // Spawn Timeout Event if ((context->mode == PSP_ADHOC_MATCHING_MODE_CHILD && (peer->state == PSP_ADHOC_MATCHING_PEER_CHILD || peer->state == PSP_ADHOC_MATCHING_PEER_PARENT)) || (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT && peer->state == PSP_ADHOC_MATCHING_PEER_CHILD) || - (context->mode == PSP_ADHOC_MATCHING_MODE_P2P && peer->state == PSP_ADHOC_MATCHING_PEER_P2P)) - spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_TIMEOUT, &peer->mac, 0, NULL); + (context->mode == PSP_ADHOC_MATCHING_MODE_P2P && peer->state == PSP_ADHOC_MATCHING_PEER_P2P)) { + spawnLocalEvent(context, PSP_ADHOC_MATCHING_EVENT_TIMEOUT, &peer->mac, 0, NULL); // This is the only code that use PSP_ADHOC_MATCHING_EVENT_TIMEOUT, should we let it timedout? + } - INFO_LOG(SCENET, "TimedOut Peer %02X:%02X:%02X:%02X:%02X:%02X (%lldms)", peer->mac.data[0], peer->mac.data[1], peer->mac.data[2], peer->mac.data[3], peer->mac.data[4], peer->mac.data[5], (context->timeout/1000)); + char tmpmac[18]; + INFO_LOG(SCENET, "TimedOut Member Peer %s (%lldms)", mac2str(&peer->mac, tmpmac), (context->timeout/1000)); // Delete Peer from List deletePeer(context, peer); + //peer->lastping = 0; //Let's just make the game kick timedout members during sceNetAdhocMatchingGetMembers } // Move Pointer @@ -845,6 +901,7 @@ void clearStackRecursive(ThreadMessage * node) // Free Last Existing Node of List (NULL is handled in _free) free(node); + node = NULL; } /** @@ -901,6 +958,8 @@ void clearPeerList(SceNetAdhocMatchingContext * context) // Delete Peer free(peer); //deletePeer(context, peer); + // Instead of removing peer immediately, We should give a little time before removing the peer and let it timed out? just in case the game is in the middle of communicating with the peer on another thread so it won't recognize it as Unknown peer + //peer->lastping = CoreTiming::GetGlobalTimeUsScaled(); // Move Pointer peer = context->peerlist; //peer = next; @@ -910,36 +969,23 @@ void clearPeerList(SceNetAdhocMatchingContext * context) peerlock.unlock(); } -bool IsMatchingInCallback(SceNetAdhocMatchingContext * context) { - bool inCB = false; - if (context == NULL) return inCB; - context->eventlock->lock(); //peerlock.lock(); - inCB = (/*context != NULL &&*/ context->IsMatchingInCB); - context->eventlock->unlock(); //peerlock.unlock(); - return inCB; -} - +// It seems After Actions being called in reverse order of Mipscall order (ie. MipsCall order of ACCEPT(6)->ESTABLISH(7) getting AfterAction order of ESTABLISH(7)->ACCEPT(6) void AfterMatchingMipsCall::run(MipsCall &call) { - if (!context || !context->eventlock) - return; - - DEBUG_LOG(SCENET, "Entering AfterMatchingMipsCall::run [ID=%i][Event=%d] [cbId: %u]", context->id, EventID, call.cbId); - //u32 v0 = currentMIPS->r[MIPS_REG_V0]; - if (__IsInInterrupt()) ERROR_LOG(SCENET, "AfterMatchingMipsCall::run [ID=%i][Event=%d] is Returning Inside an Interrupt!", context->id, EventID); - //while (__IsInInterrupt()) sleep_ms(1); // Must not sleep inside callback handler - context->eventlock->lock(); //peerlock.lock(); - //SceNetAdhocMatchingContext * context = findMatchingContext(ID); - //if (context != NULL) - { - context->IsMatchingInCB = false; + if (context == NULL) { + peerlock.lock(); + context = findMatchingContext(contextID); + peerlock.unlock(); } - context->eventlock->unlock(); //peerlock.unlock(); - //call.setReturnValue(v0); + u32 v0 = currentMIPS->r[MIPS_REG_V0]; + if (__IsInInterrupt()) ERROR_LOG(SCENET, "AfterMatchingMipsCall::run [ID=%i][Event=%d] is Returning Inside an Interrupt!", contextID, EventID); if (Memory::IsValidAddress(bufAddr)) userMemory.Free(bufAddr); - DEBUG_LOG(SCENET, "Leaving AfterMatchingMipsCall::run [ID=%i][Event=%d] [retV0: %08x]", context->id, EventID, currentMIPS->r[MIPS_REG_V0]); + SetMatchingInCallback(context, false); + DEBUG_LOG(SCENET, "AfterMatchingMipsCall::run [ID=%i][Event=%d] [cbId: %u][retV0: %08x]", contextID, EventID, call.cbId, v0); + //call.setReturnValue(v0); } -void AfterMatchingMipsCall::SetContextID(u32 ContextID, u32 eventId, u32_le BufAddr) { +void AfterMatchingMipsCall::SetData(int ContextID, int eventId, u32_le BufAddr) { + contextID = ContextID; EventID = eventId; bufAddr = BufAddr; peerlock.lock(); @@ -947,61 +993,100 @@ void AfterMatchingMipsCall::SetContextID(u32 ContextID, u32 eventId, u32_le BufA peerlock.unlock(); } +bool SetMatchingInCallback(SceNetAdhocMatchingContext* context, bool IsInCB) { + if (context == NULL) return false; + context->eventlock->lock(); //peerlock.lock(); + context->IsMatchingInCB = IsInCB; + context->eventlock->unlock(); //peerlock.unlock(); + return IsInCB; +} + +bool IsMatchingInCallback(SceNetAdhocMatchingContext* context) { + bool inCB = false; + if (context == NULL) return inCB; + context->eventlock->lock(); //peerlock.lock(); + inCB = (context->IsMatchingInCB); + context->eventlock->unlock(); //peerlock.unlock(); + return inCB; +} + +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); + DEBUG_LOG(SCENET, "AfterAdhocMipsCall::run [ID=%i][Event=%d] [cbId: %u][retV0: %08x]", HandlerID, EventID, call.cbId, v0); + //call.setReturnValue(v0); +} + +void AfterAdhocMipsCall::SetData(int handlerID, int eventId, u32_le ArgsAddr) { + HandlerID = handlerID; + EventID = eventId; + argsAddr = ArgsAddr; +} + +int SetAdhocctlInCallback(bool IsInCB) { + std::lock_guard adhocGuard(adhocEvtMtx); + IsAdhocctlInCB += (IsInCB?1:-1); + return IsAdhocctlInCB; +} + +int IsAdhocctlInCallback() { + std::lock_guard adhocGuard(adhocEvtMtx); + int inCB = IsAdhocctlInCB; + return inCB; +} + // Make sure MIPS calls have been fully executed before the next notifyAdhocctlHandlers void notifyAdhocctlHandlers(u32 flag, u32 error) { __UpdateAdhocctlHandlers(flag, error); - // TODO: We should use after action instead of guessing the time like this - //sleep_ms(20); // Ugly workaround to give time for the mips callback to fully executed, usually only need <16ms } // Matching callback is void function: typedef void(*SceNetAdhocMatchingHandler)(int id, int event, SceNetEtherAddr * peer, int optlen, void * opt); // Important! The MIPS call need to be fully executed before the next MIPS call invoked, as the game (ie. DBZ Tag Team) may need to prepare something for the next callback event to use // Note: Must not lock peerlock within this function to prevent race-condition with other thread whos owning peerlock and trying to lock context->eventlock owned by this thread -void notifyMatchingHandler(SceNetAdhocMatchingContext * context, ThreadMessage * msg, void * opt, u32 &bufAddr, u32 &bufLen, u32_le * args) { - //u32_le args[5] = { 0, 0, 0, 0, 0 }; - /*if ((s32)bufLen < (msg->optlen + 8)) { - bufLen = msg->optlen + 8; - if (Memory::IsValidAddress(bufAddr)) userMemory.Free(bufAddr); - bufAddr = userMemory.Alloc(bufLen); // Max bufLen should be context->rxbuflen - INFO_LOG(SCENET, "MatchingHandler: Alloc(%i -> %i) = %08x", msg->optlen + 8, bufLen, bufAddr); - }*/ +void notifyMatchingHandler(SceNetAdhocMatchingContext * context, ThreadMessage * msg, void * opt, u32_le &bufAddr, u32_le &bufLen, u32_le * args) { + // Don't share buffer address space with other mipscall in the queue since mipscalls aren't immediately executed MatchingArgs argsNew; - bufAddr = userMemory.Alloc(bufLen); // We will free this after returning from mipscall - u8 * optPtr = Memory::GetPointer(bufAddr); - memcpy(optPtr, &msg->mac, sizeof(msg->mac)); - if (msg->optlen > 0) memcpy(optPtr + 8, opt, msg->optlen); + u32_le dataBufLen = msg->optlen + 8; //max(bufLen, msg->optlen + 8); + u32_le dataBufAddr = userMemory.Alloc(dataBufLen); // We will free this memory after returning from mipscall + uint8_t * dataPtr = Memory::GetPointer(dataBufAddr); + memcpy(dataPtr, &msg->mac, sizeof(msg->mac)); + if (msg->optlen > 0) + memcpy(dataPtr + 8, opt, msg->optlen); argsNew.data[0] = context->id; argsNew.data[1] = msg->opcode; - argsNew.data[2] = bufAddr; // PSP_GetScratchpadMemoryBase() + 0x6000; + argsNew.data[2] = dataBufAddr; argsNew.data[3] = msg->optlen; - argsNew.data[4] = argsNew.data[2] + 8; // OptData Addr + argsNew.data[4] = dataBufAddr + 8; // OptData Addr argsNew.data[5] = context->handler.entryPoint; //not part of callback argument, just borrowing a space to store callback address so i don't need to search the context first later - context->eventlock->lock(); - context->IsMatchingInCB = true; - context->eventlock->unlock(); // ScheduleEvent_Threadsafe_Immediate seems to get mixed up with interrupt (returning from mipscall inside an interrupt) and getting invalid address before returning from interrupt __UpdateMatchingHandler(argsNew); - - // Make sure MIPS call have been fully executed before the next notifyMatchingHandler - /*int count = 0; - while ( IsMatchingInCallback(context) && (count < 250)) { - sleep_ms(1); - count++; - } - if (count >= 250) ERROR_LOG(SCENET, "MatchingHandler: Callback Failed to Return within %dms! [ID=%i][Opcode=%d][OptSize=%d][MAC=%012X]", count, context->id, msg->opcode, msg->optlen, htonl(*(u_long*)&msg->mac));*/ - //sleep_ms(20); // Wait a little more (for context switching may be?) to prevent DBZ Tag Team from getting connection lost, but this will cause lags on Lord of Arcana } -void freeFriendsRecursive(SceNetAdhocctlPeerInfo * node) { +void freeFriendsRecursive(SceNetAdhocctlPeerInfo * node, int32_t* count) { // End of List if (node == NULL) return; // Increase Recursion Depth - freeFriendsRecursive(node->next); + freeFriendsRecursive(node->next, count); // Free Memory free(node); + node = NULL; + if (count != NULL) (*count)++; +} + +void timeoutFriendsRecursive(SceNetAdhocctlPeerInfo* node, int32_t* count) { + // End of List + if (node == NULL) return; + + // Increase Recursion Depth + timeoutFriendsRecursive(node->next, count); + + // Set last timestamp + node->last_recv = 0; + if (count != NULL) (*count)++; } void sendChat(std::string chatString) { @@ -1044,6 +1129,7 @@ std::vector getChatLog() { } int friendFinder(){ + setCurrentThreadName("FriendFinder"); // Receive Buffer int rxpos = 0; uint8_t rx[1024]; @@ -1069,7 +1155,7 @@ int friendFinder(){ //_acquireNetworkLock(); // Ping Server - now = real_time_now()*1000000.0; // should be in microseconds, but it seems real_time_now() returns in seconds + now = real_time_now() * 1000000.0; // CoreTiming::GetGlobalTimeUsScaled(); // Use real_time_now()*1000000.0 if the game gets disconnected from AdhocServer too fast when FPS wasn't stable if (now - lastping >= PSP_ADHOCCTL_PING_TIMEOUT) { //100 // We need to use lower interval to prevent getting timeout at Pro Adhoc Server through internet // original code : ((sceKernelGetSystemTimeWide() - lastping) >= ADHOCCTL_PING_TIMEOUT) // Update Ping Time @@ -1106,18 +1192,33 @@ int friendFinder(){ if (rxpos > 0) { // BSSID Packet if (rx[0] == OPCODE_CONNECT_BSSID) { - INFO_LOG(SCENET, "FriendFinder: Incoming OPCODE_CONNECT_BSSID"); // Enough Data available if (rxpos >= (int)sizeof(SceNetAdhocctlConnectBSSIDPacketS2C)) { // Cast Packet SceNetAdhocctlConnectBSSIDPacketS2C * packet = (SceNetAdhocctlConnectBSSIDPacketS2C *)rx; - // Update BSSID - parameter.bssid.mac_addr = packet->mac; + + char tmpmac[18]; + INFO_LOG(SCENET, "FriendFinder: Incoming OPCODE_CONNECT_BSSID [%s]", mac2str(&packet->mac, tmpmac)); + // from JPCSP: Some games have problems when the PSP_ADHOCCTL_EVENT_CONNECTED is sent too quickly after connecting to a network. The connection will be set CONNECTED with a small delay (200ms or 200us?) + /*if (adhocctlCurrentMode == ADHOCCTL_MODE_GAMEMODE) { + setState(ADHOCCTL_STATE_GAMEMODE); + notifyAdhocctlHandlers(ADHOCCTL_EVENT_GAME, 0); + } + else { + setState(ADHOCCTL_STATE_CONNECTED); + notifyAdhocctlHandlers(ADHOCCTL_EVENT_CONNECT, 0); + }*/ + + // Update User BSSID + //parameter.bssid.mac_addr = packet->mac; // The MAC address in this packet seems to Always be the First player joining the Group (group Creator?), Shouldn't it be it self? + // Notify Event Handlers + //notifyAdhocctlHandlers(ADHOCCTL_EVENT_CONNECT, 0); // Change State threadStatus = ADHOCCTL_STATE_CONNECTED; - // Notify Event Handlers - notifyAdhocctlHandlers(ADHOCCTL_EVENT_CONNECT, 0); - + // Give time a little time + //sceKernelDelayThread(adhocEventDelayMS * 1000); + //sleep_ms(adhocEventDelayMS); + // Move RX Buffer memmove(rx, rx + sizeof(SceNetAdhocctlConnectBSSIDPacketS2C), sizeof(rx) - sizeof(SceNetAdhocctlConnectBSSIDPacketS2C)); @@ -1128,11 +1229,16 @@ int friendFinder(){ // Chat Packet else if (rx[0] == OPCODE_CHAT) { - INFO_LOG(SCENET, "FriendFinder: Incoming OPCODE_CHAT"); // Enough Data available if (rxpos >= (int)sizeof(SceNetAdhocctlChatPacketS2C)) { // Cast Packet SceNetAdhocctlChatPacketS2C * packet = (SceNetAdhocctlChatPacketS2C *)rx; + INFO_LOG(SCENET, "FriendFinder: Incoming OPCODE_CHAT"); + + // Fix strings with null-terminated + packet->name.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; + packet->base.message[ADHOCCTL_MESSAGE_LEN - 1] = 0; + // Add Incoming Chat to HUD NOTICE_LOG(SCENET, "Received chat message %s", packet->base.message); incoming = ""; @@ -1150,6 +1256,7 @@ int friendFinder(){ newChat += 1; } } + // Move RX Buffer memmove(rx, rx + sizeof(SceNetAdhocctlChatPacketS2C), sizeof(rx) - sizeof(SceNetAdhocctlChatPacketS2C)); @@ -1160,17 +1267,28 @@ int friendFinder(){ // Connect Packet else if (rx[0] == OPCODE_CONNECT) { - DEBUG_LOG(SCENET, "FriendFinder: OPCODE_CONNECT"); // Enough Data available if (rxpos >= (int)sizeof(SceNetAdhocctlConnectPacketS2C)) { - // Log Incoming Peer - INFO_LOG(SCENET, "Incoming Peer Data..."); - // Cast Packet SceNetAdhocctlConnectPacketS2C * packet = (SceNetAdhocctlConnectPacketS2C *)rx; + DEBUG_LOG(SCENET, "FriendFinder: OPCODE_CONNECT"); + + // Fix strings with null-terminated + packet->name.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; + + // Log Incoming Peer + INFO_LOG(SCENET, "Incoming Peer Data..."); + // Add User addFriend(packet); + + /* // Make sure GameMode participants are all joined (including self MAC) + if (adhocctlCurrentMode == PSP_ADHOCCTL_MODE_GAMEMODE) { + // From JPCSP: Join complete when all the required MACs have joined + }*/ + + // Update HUD User Count incoming = ""; incoming.append((char *)packet->name.data); incoming.append(" Joined "); @@ -1181,7 +1299,7 @@ int friendFinder(){ if (chatScreenVisible) { updateChatScreen = true; } - // Update HUD User Count + #ifdef LOCALHOST_AS_PEER setUserCount(getActivePeerCount()); #else @@ -1198,15 +1316,16 @@ int friendFinder(){ // Disconnect Packet else if (rx[0] == OPCODE_DISCONNECT) { - DEBUG_LOG(SCENET, "FriendFinder: OPCODE_DISCONNECT"); // Enough Data available if (rxpos >= (int)sizeof(SceNetAdhocctlDisconnectPacketS2C)) { - // Log Incoming Peer Delete Request - INFO_LOG(SCENET, "FriendFinder: Incoming Peer Data Delete Request..."); - // Cast Packet SceNetAdhocctlDisconnectPacketS2C * packet = (SceNetAdhocctlDisconnectPacketS2C *)rx; + DEBUG_LOG(SCENET, "FriendFinder: OPCODE_DISCONNECT"); + + // Log Incoming Peer Delete Request + INFO_LOG(SCENET, "FriendFinder: Incoming Peer Data Delete Request..."); + // Delete User by IP, should delete by MAC since IP can be shared (behind NAT) isn't? deleteFriendByIP(packet->ip); @@ -1227,14 +1346,16 @@ int friendFinder(){ // Scan Packet else if (rx[0] == OPCODE_SCAN) { - DEBUG_LOG(SCENET, "FriendFinder: OPCODE_SCAN"); // Enough Data available if (rxpos >= (int)sizeof(SceNetAdhocctlScanPacketS2C)) { - // Log Incoming Network Information - INFO_LOG(SCENET, "Incoming Group Information..."); // Cast Packet SceNetAdhocctlScanPacketS2C * packet = (SceNetAdhocctlScanPacketS2C *)rx; + DEBUG_LOG(SCENET, "FriendFinder: OPCODE_SCAN"); + + // Log Incoming Network Information + INFO_LOG(SCENET, "Incoming Group Information..."); + // Multithreading Lock peerlock.lock(); @@ -1242,20 +1363,17 @@ int friendFinder(){ SceNetAdhocctlScanInfo * group = findGroup(&packet->mac); if (group != NULL) { - // Copy Group Name - group->group_name = packet->group; + // Copy Group Name + group->group_name = packet->group; - // Set Group Host - group->bssid.mac_addr = packet->mac; - } - else - { + // Set Group Host + group->bssid.mac_addr = packet->mac; + } else { // Allocate Structure Data SceNetAdhocctlScanInfo * group = (SceNetAdhocctlScanInfo *)malloc(sizeof(SceNetAdhocctlScanInfo)); // Allocated Structure Data - if (group != NULL) - { + if (group != NULL) { // Clear Memory, should this be done only when allocating new group? memset(group, 0, sizeof(SceNetAdhocctlScanInfo)); @@ -1268,6 +1386,10 @@ int friendFinder(){ // Set Group Host group->bssid.mac_addr = packet->mac; + // Set group parameters + group->channel = parameter.channel; + group->mode = ADHOCCTL_MODE_ADHOC; //adhocctlCurrentMode; + // Link into Group List newnetworks = group; } @@ -1297,16 +1419,18 @@ int friendFinder(){ newnetworks = NULL; peerlock.unlock(); - // Change State - threadStatus = ADHOCCTL_STATE_DISCONNECTED; - // Notify Event Handlers - notifyAdhocctlHandlers(ADHOCCTL_EVENT_SCAN, 0); + //notifyAdhocctlHandlers(ADHOCCTL_EVENT_SCAN, 0); //int i = 0; for(; i < ADHOCCTL_MAX_HANDLER; i++) //{ // // Active Handler // if(_event_handler[i] != NULL) _event_handler[i](ADHOCCTL_EVENT_SCAN, 0, _event_args[i]); //} + // Change State + threadStatus = ADHOCCTL_STATE_DISCONNECTED; + // Give time a little time + //sceKernelDelayThread(adhocEventDelayMS * 1000); + //sleep_ms(adhocEventDelayMS); // Move RX Buffer memmove(rx, rx + 1, sizeof(rx) - 1); @@ -1315,11 +1439,11 @@ int friendFinder(){ rxpos -= 1; } } - // Original value was 10 ms, I think 100 is just fine - sleep_ms(1); // Using 1ms for faster response just like AdhocServer + // This delay time should be 100ms when there is an event otherwise 500ms ? + sleep_ms(1); // Using 1ms for faster response just like AdhocServer? // Don't do anything if it's paused, otherwise the log will be flooded - while (Core_IsStepping() && friendFinderRunning) sleep_ms(1); + while (Core_IsStepping() && coreState != CORE_POWERDOWN && friendFinderRunning) sleep_ms(1); } // Groups/Networks should be deallocated isn't? @@ -1334,7 +1458,7 @@ int friendFinder(){ return 0; } -int getActivePeerCount(void) { +int getActivePeerCount(const bool excludeTimedout) { // Counter int count = 0; @@ -1348,8 +1472,9 @@ int getActivePeerCount(void) { // Iterate Peers for (; peer != NULL; peer = peer->next) { - // Increase Counter - count++; + // Increase Counter, Should we exclude peers pending for timed out? + if (!excludeTimedout || peer->last_recv != 0) + count++; } // Return Result @@ -1365,7 +1490,7 @@ int getLocalIp(sockaddr_in * SocketAddress){ // Error handling } // Get local IP addresses - struct hostent *pHost = 0; + struct hostent *pHost = 0; pHost = ::gethostbyname(szHostName); if(pHost) { memcpy(&SocketAddress->sin_addr, pHost->h_addr_list[0], pHost->h_length); @@ -1376,7 +1501,6 @@ int getLocalIp(sockaddr_in * SocketAddress){ } return -1; #else - memcpy(&SocketAddress->sin_addr, &localip, sizeof(uint32_t)); char szHostName[256] = ""; gethostname(szHostName, sizeof(szHostName)); struct hostent* pHost = 0; @@ -1388,8 +1512,7 @@ int getLocalIp(sockaddr_in * SocketAddress){ } return 0; } - //return -1; - return 0; + return -1; #endif } @@ -1468,36 +1591,37 @@ int getNicknameCount(const char * nickname) * PDP Socket Counter * @return Number of internal PDP Sockets */ -int getPDPSocketCount(void) +int getPDPSocketCount() { // Socket Counter int counter = 0; // Count Sockets - int i = 0; for (; i < 255; i++) if (pdp[i] != NULL) counter++; + for (int i = 0; i < 255; i++) if (pdp[i] != NULL) counter++; // Return Socket Count return counter; } -int getPTPSocketCount(void) { +int getPTPSocketCount() { // Socket Counter int counter = 0; // Count Sockets - int i = 0; for (; i < 255; i++) if (ptp[i] != NULL) counter++; + for (int i = 0; i < 255; i++) if (ptp[i] != NULL) counter++; // Return Socket Count return counter; } int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ + auto n = GetI18NCategory("Networking"); int iResult = 0; metasocket = (int)INVALID_SOCKET; metasocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (metasocket == INVALID_SOCKET){ ERROR_LOG(SCENET, "Invalid socket"); - return -1; + return SOCKET_ERROR; } struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; @@ -1512,7 +1636,7 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ iResult = getaddrinfo(g_Config.proAdhocServer.c_str(),0,NULL,&resultAddr); if (iResult != 0) { ERROR_LOG(SCENET, "DNS Error (%s)\n", g_Config.proAdhocServer.c_str()); - host->NotifyUserMessage("DNS Error connecting to " + g_Config.proAdhocServer, 8.0f); + host->NotifyUserMessage(n->T("DNS Error connecting to ") + g_Config.proAdhocServer, 5.0f, 0x0000ff); return iResult; } for (ptr = resultAddr; ptr != NULL; ptr = ptr->ai_next) { @@ -1537,26 +1661,31 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ iResult = bind(metasocket, (struct sockaddr*) & localIP, sizeof(sockaddr)); if (iResult == SOCKET_ERROR) { ERROR_LOG(SCENET, "Bind to alternate localhost[%s] failed(%i).", inet_ntoa(((struct sockaddr_in*) & localIP)->sin_addr), iResult); + host->NotifyUserMessage(std::string(n->T("Failed to Bind Localhost IP")) + " " + inet_ntoa(((struct sockaddr_in*) & localIP)->sin_addr), 3.0, 0x0000ff); } } + // Default/Initial Network memset(¶meter, 0, sizeof(parameter)); strcpy((char *)¶meter.nickname.data, g_Config.sNickName.c_str()); - parameter.channel = 1; // Fake Channel 1 + parameter.channel = g_Config.iWlanAdhocChannel; // Fake Channel, 0 = Auto where JPCSP use 11 as default for Auto (Commonly for Auto: 1, 6, 11) + if (parameter.channel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC) parameter.channel = 1; getLocalMac(¶meter.bssid.mac_addr); + + // Default ProductId + product_code.type = adhoc_id->type; + memcpy(product_code.data, adhoc_id->data, ADHOCCTL_ADHOCID_LEN); + // Connect to Adhoc Server server_addr.sin_addr = serverIp; iResult = connect(metasocket,(sockaddr *)&server_addr,sizeof(server_addr)); if (iResult == SOCKET_ERROR) { - uint8_t * sip = (uint8_t *)&server_addr.sin_addr.s_addr; char buffer[512]; - snprintf(buffer, sizeof(buffer), "Socket error (%i) when connecting to %s/%u.%u.%u.%u:%u", errno, g_Config.proAdhocServer.c_str(), sip[0], sip[1], sip[2], sip[3], ntohs(server_addr.sin_port)); + snprintf(buffer, sizeof(buffer), "Socket error (%i) when connecting to AdhocServer [%s/%s:%u]", errno, g_Config.proAdhocServer.c_str(), inet_ntoa(server_addr.sin_addr), ntohs(server_addr.sin_port)); ERROR_LOG(SCENET, "%s", buffer); - host->NotifyUserMessage(buffer, 8.0f); + host->NotifyUserMessage(buffer, 5.0f, 0x0000ff); return iResult; } - //grab local ip for later use better than constant ip on non windows platform - localip = getLocalIp(metasocket); // Prepare Login Packet SceNetAdhocctlLoginPacketC2S packet; @@ -1569,12 +1698,11 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ int sent = send(metasocket, (char*)&packet, sizeof(packet), 0); changeBlockingMode(metasocket, 1); // Change to non-blocking if (sent > 0) { - auto n = GetI18NCategory("Networking"); host->NotifyUserMessage(n->T("Network Initialized"), 1.0); return 0; } else{ - return -1; + return SOCKET_ERROR; } } @@ -1665,7 +1793,7 @@ bool validNetworkName(const SceNetAdhocctlGroupName * group_name) { // Name given if (group_name != NULL) { // Iterate Name Characters - int i = 0; for (; i < ADHOCCTL_GROUPNAME_LEN && valid; i++) { + for (int i = 0; i < ADHOCCTL_GROUPNAME_LEN && valid; i++) { // End of Name if (group_name->data[i] == 0) break; diff --git a/Core/HLE/proAdhoc.h b/Core/HLE/proAdhoc.h index fe370c23e3..e4bae1266f 100644 --- a/Core/HLE/proAdhoc.h +++ b/Core/HLE/proAdhoc.h @@ -27,6 +27,7 @@ #include "Core/CoreTiming.h" #include "Core/MemMap.h" #include "Core/HLE/HLE.h" +#include "Core/HLE/HLEHelperThread.h" #include "Core/HLE/sceNetAdhoc.h" #include "Core/HLE/sceKernelThread.h" #include "Core/HLE/sceKernel.h" @@ -73,7 +74,7 @@ class PointerWrap; #define EINPROGRESS WSAEWOULDBLOCK #define EISCONN WSAEISCONN #define EALREADY WSAEALREADY -inline bool connectInProgress(int errcode){ return (errcode == WSAEWOULDBLOCK || errcode == WSAEINVAL || errcode == WSAEALREADY); } +inline bool connectInProgress(int errcode){ return (errcode == WSAEWOULDBLOCK || errcode == WSAEINPROGRESS || errcode == WSAEALREADY); } #else #define INVALID_SOCKET -1 #define SOCKET_ERROR -1 @@ -101,19 +102,32 @@ inline bool connectInProgress(int errcode){ return (errcode == EINPROGRESS || er #define SERVER_PORT 27312 // psp strutcs and definitions -#define ADHOCCTL_MODE_ADHOC 0 +#define ADHOCCTL_MODE_NONE -1 +#define ADHOCCTL_MODE_ADHOC 0 //ADHOCCTL_MODE_NORMAL #define ADHOCCTL_MODE_GAMEMODE 1 // Event Types for Event Handler +#define ADHOCCTL_EVENT_ERROR 0 #define ADHOCCTL_EVENT_CONNECT 1 #define ADHOCCTL_EVENT_DISCONNECT 2 #define ADHOCCTL_EVENT_SCAN 3 +#define ADHOCCTL_EVENT_GAME 4 +#define ADHOCCTL_EVENT_DISCOVER 5 +#define ADHOCCTL_EVENT_WOL 6 +#define ADHOCCTL_EVENT_WOL_INTERRUPT 7 // Internal Thread States #define ADHOCCTL_STATE_DISCONNECTED 0 #define ADHOCCTL_STATE_CONNECTED 1 #define ADHOCCTL_STATE_SCANNING 2 #define ADHOCCTL_STATE_GAMEMODE 3 +#define ADHOCCTL_STATE_DISCOVER 4 +#define ADHOCCTL_STATE_WOL 5 + +// 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 // Kernel Utility Netconf Adhoc Types #define UTILITY_NETCONF_TYPE_CONNECT_ADHOC 2 @@ -186,8 +200,8 @@ extern uint8_t broadcastMAC[ETHER_ADDR_LEN]; // Malloc Pool Information typedef struct SceNetMallocStat { - s32_le pool; // Pointer to the pool? - s32_le maximum; // Maximum size of the pool? + s32_le pool; // Pointer to the pool? // This should be the poolSize isn't? + s32_le maximum; // Maximum size of the pool? Maximum usage (ie. pool- free) ? s32_le free; // How much memory is free } PACK SceNetMallocStat; @@ -321,9 +335,6 @@ typedef struct SceNetAdhocGameModeBufferStat { u32_le master; SceNetAdhocGameModeOptData opt; } PACK SceNetAdhocGameModeBufferStat; -#ifdef _MSC_VER -#pragma pack(pop) -#endif // Adhoc ID (Game Product Key) #define ADHOCCTL_ADHOCID_LEN 9 @@ -331,7 +342,11 @@ typedef struct SceNetAdhocctlAdhocId { s32_le type; uint8_t data[ADHOCCTL_ADHOCID_LEN]; uint8_t padding[3]; -} SceNetAdhocctlAdhocId; // should this be packed? +} PACK SceNetAdhocctlAdhocId; // should this be packed? +#ifdef _MSC_VER +#pragma pack(pop) +#endif + // Internal Matching Peer Information typedef struct SceNetAdhocMatchingMemberInternal { @@ -459,8 +474,8 @@ typedef struct SceNetAdhocMatchingContext { u64_le timeout; // Helper Thread (fake PSP Thread) needed to execute callback - //HLEHelperThread *matchingThread; - //SceUID matching_thid; + HLEHelperThread *matchingThread; + SceUID matching_thid; // Event Caller Thread std::thread eventThread; // s32_le event_thid; @@ -704,10 +719,11 @@ typedef struct { SceNetAdhocctlGroupName group; } PACK SceNetAdhocctlConnectPacketC2S; +#define ADHOCCTL_MESSAGE_LEN 64 // C2S Chat Packet typedef struct { SceNetAdhocctlPacketBase base; - char message[64]; + char message[ADHOCCTL_MESSAGE_LEN]; } PACK SceNetAdhocctlChatPacketC2S; // S2C Connect Packet @@ -763,30 +779,56 @@ typedef struct { #pragma pack(pop) #endif +class AfterAdhocMipsCall : public PSPAction { +public: + AfterAdhocMipsCall() {} + static PSPAction* Create() { return new AfterAdhocMipsCall(); } + void DoState(PointerWrap& p) override { + auto s = p.Section("AfterAdhocMipsCall", 4, 4); + if (!s) + return; + + p.Do(HandlerID); + p.Do(EventID); + p.Do(argsAddr); + } + void run(MipsCall& call) override; + void SetData(int handlerID, int eventId, u32_le argsAddr); + +private: + int HandlerID = -1; + int EventID = -1; + u32_le argsAddr = 0; +}; + class AfterMatchingMipsCall : public PSPAction { public: AfterMatchingMipsCall() {} static PSPAction *Create() { return new AfterMatchingMipsCall(); } void DoState(PointerWrap &p) override { - auto s = p.Section("AfterMatchingMipsCall", 1, 2); + auto s = p.Section("AfterMatchingMipsCall", 1, 4); if (!s) return; p.Do(EventID); + if (s >= 4) { + p.Do(contextID); + p.Do(bufAddr); + } //context = NULL; } void run(MipsCall &call) override; - void SetContextID(u32 ContextID, u32 eventId, u32_le BufAddr); - void SetContext(SceNetAdhocMatchingContext* Context, u32 eventId, u32_le BufAddr) { context = Context; EventID = eventId; bufAddr = BufAddr; } + void SetData(int ContextID, int eventId, u32_le BufAddr); private: - u32 EventID = 0; - SceNetAdhocMatchingContext *context = nullptr; + int contextID = -1; + int EventID = -1; u32_le bufAddr = 0; + SceNetAdhocMatchingContext* context = nullptr; }; +extern int actionAfterAdhocMipsCall; extern int actionAfterMatchingMipsCall; -extern bool IsAdhocctlInCB; // Aux vars extern int metasocket; @@ -796,7 +838,6 @@ extern std::thread friendFinderThread; extern std::recursive_mutex peerlock; extern SceNetAdhocPdpStat * pdp[255]; extern SceNetAdhocPtpStat * ptp[255]; -extern std::map adhocctlHandlers; extern uint16_t portOffset; extern bool isLocalServer; @@ -812,27 +853,34 @@ extern int threadStatus; // Check if Matching callback is running bool IsMatchingInCallback(SceNetAdhocMatchingContext * context); +bool SetMatchingInCallback(SceNetAdhocMatchingContext* context, bool IsInCB); + +int IsAdhocctlInCallback(); +int SetAdhocctlInCallback(bool IsInCB); /** * Local MAC Check * @param saddr To-be-checked MAC Address * @return 1 if valid or... 0 */ -int isLocalMAC(const SceNetEtherAddr * addr); +bool isLocalMAC(const SceNetEtherAddr * addr); /** * PDP Port Check * @param port To-be-checked Port * @return 1 if in use or... 0 */ -int isPDPPortInUse(uint16_t port); +bool isPDPPortInUse(uint16_t port); /** * Check whether PTP Port is in use or not * @param port To-be-checked Port Number * @return 1 if in use or... 0 */ -int isPTPPortInUse(uint16_t port); +bool isPTPPortInUse(uint16_t port); + +char* mac2str(SceNetEtherAddr* mac); +char* mac2str(SceNetEtherAddr* mac, char* str, size_t size = 18); /* * Matching Members @@ -890,12 +938,12 @@ void freeGroupsRecursive(SceNetAdhocctlScanInfo * node); /** * Closes & Deletes all PDP Sockets */ -void deleteAllPDP(void); +void deleteAllPDP(); /** * Closes & Deletes all PTP sockets */ -void deleteAllPTP(void); +void deleteAllPTP(); /** * Delete Friend from Local List @@ -907,7 +955,9 @@ void deleteFriendByIP(uint32_t ip); * Recursive Memory Freeing-Helper for Friend-Structures * @param node Current Node in List */ -void freeFriendsRecursive(SceNetAdhocctlPeerInfo * node); +void freeFriendsRecursive(SceNetAdhocctlPeerInfo * node, int32_t* count); + +void timeoutFriendsRecursive(SceNetAdhocctlPeerInfo* node, int32_t* count); /** * Friend Finder Thread (Receives Peer Information) @@ -921,7 +971,7 @@ int friendFinder(); * Find Free Matching ID * @return First unoccupied Matching ID */ -int findFreeMatchingID(void); +int findFreeMatchingID(); /** * Find Internal Matching Context for Matching ID @@ -933,7 +983,7 @@ SceNetAdhocMatchingContext * findMatchingContext(int id); /* * Notify Matching Event Handler */ -void notifyMatchingHandler(SceNetAdhocMatchingContext * context, ThreadMessage * msg, void * opt, u32 &bufAddr, u32 &bufLen, u32_le * args); +void notifyMatchingHandler(SceNetAdhocMatchingContext * context, ThreadMessage * msg, void * opt, u32_le &bufAddr, u32_le &bufLen, u32_le * args); // Notifiy Adhocctl Handlers void notifyAdhocctlHandlers(u32 flag, u32 error); @@ -1030,7 +1080,7 @@ void sendDeathMessage(SceNetAdhocMatchingContext * context, SceNetAdhocMatchingM * @param context Matching Context Pointer * @return Number of Children */ -s32_le countChildren(SceNetAdhocMatchingContext * context); +s32_le countChildren(SceNetAdhocMatchingContext * context, const bool excludeTimedout = false); /** * Delete Peer from List @@ -1059,14 +1109,14 @@ SceNetAdhocMatchingMemberInternal * findParent(SceNetAdhocMatchingContext * cont * @param context Matching Context Pointer * @return Internal Peer Reference or... NULL */ -SceNetAdhocMatchingMemberInternal * findP2P(SceNetAdhocMatchingContext * context); +SceNetAdhocMatchingMemberInternal * findP2P(SceNetAdhocMatchingContext * context, const bool excludeTimedout = false); /** * Return Number of Connected Peers * @param context Matching Context Pointer * @return Number of Connected Peers */ -uint32_t countConnectedPeers(SceNetAdhocMatchingContext * context); +uint32_t countConnectedPeers(SceNetAdhocMatchingContext * context, const bool excludeTimedout = false); /** * Spawn Local Event for Event Thread @@ -1093,7 +1143,7 @@ void spawnLocalEvent(SceNetAdhocMatchingContext * context, int event, SceNetEthe * Return Number of active Peers in the same Network as the Local Player * @return Number of active Peers */ -int getActivePeerCount(void); +int getActivePeerCount(const bool excludeTimedout = true); /** * Returns the locall Ip of this machine, TODO: Implement the linux version @@ -1152,13 +1202,13 @@ uint16_t getLocalPort(int sock); * PDP Socket Counter * @return Number of internal PDP Sockets */ -int getPDPSocketCount(void); +int getPDPSocketCount(); /** * PTP Socket Counter * @return Number of internal PTP Sockets */ -int getPTPSocketCount(void); +int getPTPSocketCount(); /** * Initialize Networking Components for Adhocctl Emulator diff --git a/Core/HLE/proAdhocServer.cpp b/Core/HLE/proAdhocServer.cpp index 54eeefd09f..f509ba59ad 100644 --- a/Core/HLE/proAdhocServer.cpp +++ b/Core/HLE/proAdhocServer.cpp @@ -42,9 +42,12 @@ #include #include //#include +#include "thread/threadutil.h" #include "Common/FileUtil.h" #include "Core/Core.h" +#include "Core/Host.h" #include "Core/HLE/proAdhocServer.h" +#include "i18n/i18n.h" // User Count @@ -497,8 +500,7 @@ void login_user_stream(int fd, uint32_t ip) while(u != NULL && u->resolver.ip != ip) u = u->next; if (u != NULL) { // IP Already existed - uint8_t * ip4 = (uint8_t *)&u->resolver.ip; - WARN_LOG(SCENET, "AdhocServer: Already Existing IP: %u.%u.%u.%u\n", ip4[0], ip4[1], ip4[2], ip4[3]); + WARN_LOG(SCENET, "AdhocServer: Already Existing IP: %s\n", inet_ntoa(*(in_addr*)&u->resolver.ip)); } // Unique IP Address @@ -528,8 +530,7 @@ void login_user_stream(int fd, uint32_t ip) user->last_recv = time(NULL); // Notify User - uint8_t * ipa = (uint8_t *)&user->resolver.ip; - INFO_LOG(SCENET, "AdhocServer: New Connection from %u.%u.%u.%u", ipa[0], ipa[1], ipa[2], ipa[3]); + INFO_LOG(SCENET, "AdhocServer: New Connection from %s", inet_ntoa(*(in_addr*)&user->resolver.ip)); // Fix User Counter _db_user_count++; @@ -572,8 +573,7 @@ void login_user_data(SceNetAdhocctlUserNode * user, SceNetAdhocctlLoginPacketC2S while (u != NULL && !IsMatch(u->resolver.mac, data->mac)) u = u->next; if (u != NULL) { // MAC Already existed - uint8_t* ip4 = (uint8_t*)&u->resolver.ip; - WARN_LOG(SCENET, "AdhocServer: Already Existing MAC: %02X:%02X:%02X:%02X:%02X:%02X [%u.%u.%u.%u]\n", data->mac.data[0], data->mac.data[1], data->mac.data[2], data->mac.data[3], data->mac.data[4], data->mac.data[5], ip4[0], ip4[1], ip4[2], ip4[3]); + WARN_LOG(SCENET, "AdhocServer: Already Existing MAC: %02x:%02x:%02x:%02x:%02x:%02x [%s]\n", data->mac.data[0], data->mac.data[1], data->mac.data[2], data->mac.data[3], data->mac.data[4], data->mac.data[5], inet_ntoa(*(in_addr*)&u->resolver.ip)); } // Game Product Override @@ -621,11 +621,10 @@ void login_user_data(SceNetAdhocctlUserNode * user, SceNetAdhocctlLoginPacketC2S user->game = game; // Notify User - uint8_t * ip = (uint8_t *)&user->resolver.ip; char safegamestr[10]; memset(safegamestr, 0, sizeof(safegamestr)); strncpy(safegamestr, game->game.data, PRODUCT_CODE_LENGTH); - INFO_LOG(SCENET, "AdhocServer: %s (MAC: %02X:%02X:%02X:%02X:%02X:%02X - IP: %u.%u.%u.%u) started playing %s", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], ip[0], ip[1], ip[2], ip[3], safegamestr); + INFO_LOG(SCENET, "AdhocServer: %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x - IP: %s) started playing %s", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], inet_ntoa(*(in_addr*)&user->resolver.ip), safegamestr); // Update Status Log update_status(); @@ -639,8 +638,7 @@ void login_user_data(SceNetAdhocctlUserNode * user, SceNetAdhocctlLoginPacketC2S else { // Notify User - uint8_t * ip = (uint8_t *)&user->resolver.ip; - WARN_LOG(SCENET, "AdhocServer: Invalid Login Packet Contents from %u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); + WARN_LOG(SCENET, "AdhocServer: Invalid Login Packet Contents from %s", inet_ntoa(*(in_addr*)&user->resolver.ip)); } // Logout User - Out of Memory or Invalid Arguments @@ -672,11 +670,10 @@ void logout_user(SceNetAdhocctlUserNode * user) if(user->game != NULL) { // Notify User - uint8_t * ip = (uint8_t *)&user->resolver.ip; char safegamestr[10]; memset(safegamestr, 0, sizeof(safegamestr)); strncpy(safegamestr, user->game->game.data, PRODUCT_CODE_LENGTH); - INFO_LOG(SCENET, "AdhocServer: %s (MAC: %02X:%02X:%02X:%02X:%02X:%02X - IP: %u.%u.%u.%u) stopped playing %s", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], ip[0], ip[1], ip[2], ip[3], safegamestr); + INFO_LOG(SCENET, "AdhocServer: %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x - IP: %s) stopped playing %s", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], inet_ntoa(*(in_addr*)&user->resolver.ip), safegamestr); // Fix Game Player Count user->game->playercount--; @@ -702,8 +699,7 @@ void logout_user(SceNetAdhocctlUserNode * user) else { // Notify User - uint8_t * ip = (uint8_t *)&user->resolver.ip; - WARN_LOG(SCENET, "AdhocServer: Dropped Connection to %u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); + WARN_LOG(SCENET, "AdhocServer: Dropped Connection to %s", inet_ntoa(*(in_addr*)&user->resolver.ip)); } // Free Memory @@ -719,7 +715,7 @@ void logout_user(SceNetAdhocctlUserNode * user) /** * Free Database Memory */ -void free_database(void) +void free_database() { // There are users playing if(_db_user_count > 0) @@ -885,14 +881,13 @@ void connect_user(SceNetAdhocctlUserNode * user, SceNetAdhocctlGroupName * group if (iResult < 0) ERROR_LOG(SCENET, "AdhocServer: connect_user[send user bssid] (Socket error %d)", errno); // Notify User - uint8_t * ip = (uint8_t *)&user->resolver.ip; char safegamestr[10]; memset(safegamestr, 0, sizeof(safegamestr)); strncpy(safegamestr, user->game->game.data, PRODUCT_CODE_LENGTH); char safegroupstr[9]; memset(safegroupstr, 0, sizeof(safegroupstr)); strncpy(safegroupstr, (char *)user->group->group.data, ADHOCCTL_GROUPNAME_LEN); - INFO_LOG(SCENET, "AdhocServer: %s (MAC: %02X:%02X:%02X:%02X:%02X:%02X - IP: %u.%u.%u.%u) joined %s group %s", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], ip[0], ip[1], ip[2], ip[3], safegamestr, safegroupstr); + INFO_LOG(SCENET, "AdhocServer: %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x - IP: %s) joined %s group %s", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], inet_ntoa(*(in_addr*)&user->resolver.ip), safegamestr, safegroupstr); // Update Status Log update_status(); @@ -906,7 +901,6 @@ void connect_user(SceNetAdhocctlUserNode * user, SceNetAdhocctlGroupName * group else { // Notify User - uint8_t * ip = (uint8_t *)&user->resolver.ip; char safegamestr[10]; memset(safegamestr, 0, sizeof(safegamestr)); strncpy(safegamestr, user->game->game.data, PRODUCT_CODE_LENGTH); @@ -916,7 +910,7 @@ void connect_user(SceNetAdhocctlUserNode * user, SceNetAdhocctlGroupName * group char safegroupstr2[9]; memset(safegroupstr2, 0, sizeof(safegroupstr2)); strncpy(safegroupstr2, (char *)user->group->group.data, ADHOCCTL_GROUPNAME_LEN); - WARN_LOG(SCENET, "AdhocServer: %s (MAC: %02X:%02X:%02X:%02X:%02X:%02X - IP: %u.%u.%u.%u) attempted to join %s group %s without disconnecting from %s first", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], ip[0], ip[1], ip[2], ip[3], safegamestr, safegroupstr, safegroupstr2); + WARN_LOG(SCENET, "AdhocServer: %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x - IP: %s) attempted to join %s group %s without disconnecting from %s first", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], inet_ntoa(*(in_addr*)&user->resolver.ip), safegamestr, safegroupstr, safegroupstr2); } } @@ -924,14 +918,13 @@ void connect_user(SceNetAdhocctlUserNode * user, SceNetAdhocctlGroupName * group else { // Notify User - uint8_t * ip = (uint8_t *)&user->resolver.ip; char safegamestr[10]; memset(safegamestr, 0, sizeof(safegamestr)); strncpy(safegamestr, user->game->game.data, PRODUCT_CODE_LENGTH); char safegroupstr[9]; memset(safegroupstr, 0, sizeof(safegroupstr)); strncpy(safegroupstr, (char *)group->data, ADHOCCTL_GROUPNAME_LEN); - WARN_LOG(SCENET, "AdhocServer: %s (MAC: %02X:%02X:%02X:%02X:%02X:%02X - IP: %u.%u.%u.%u) attempted to join invalid %s group %s", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], ip[0], ip[1], ip[2], ip[3], safegamestr, safegroupstr); + WARN_LOG(SCENET, "AdhocServer: %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x - IP: %s) attempted to join invalid %s group %s", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], inet_ntoa(*(in_addr*)&user->resolver.ip), safegamestr, safegroupstr); } // Invalid State, Out of Memory or Invalid Group Name @@ -984,14 +977,13 @@ void disconnect_user(SceNetAdhocctlUserNode * user) } // Notify User - uint8_t * ip = (uint8_t *)&user->resolver.ip; char safegamestr[10]; memset(safegamestr, 0, sizeof(safegamestr)); strncpy(safegamestr, user->game->game.data, PRODUCT_CODE_LENGTH); char safegroupstr[9]; memset(safegroupstr, 0, sizeof(safegroupstr)); strncpy(safegroupstr, (char *)user->group->group.data, ADHOCCTL_GROUPNAME_LEN); - INFO_LOG(SCENET, "AdhocServer: %s (MAC: %02X:%02X:%02X:%02X:%02X:%02X - IP: %u.%u.%u.%u) left %s group %s", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], ip[0], ip[1], ip[2], ip[3], safegamestr, safegroupstr); + INFO_LOG(SCENET, "AdhocServer: %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x - IP: %s) left %s group %s", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], inet_ntoa(*(in_addr*)&user->resolver.ip), safegamestr, safegroupstr); // Empty Group if(user->group->playercount == 0) @@ -1028,11 +1020,10 @@ void disconnect_user(SceNetAdhocctlUserNode * user) else { // Notify User - uint8_t * ip = (uint8_t *)&user->resolver.ip; char safegamestr[10]; memset(safegamestr, 0, sizeof(safegamestr)); strncpy(safegamestr, user->game->game.data, PRODUCT_CODE_LENGTH); - WARN_LOG(SCENET, "AdhocServer: %s (MAC: %02X:%02X:%02X:%02X:%02X:%02X - IP: %u.%u.%u.%u) attempted to leave %s group without joining one first", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], ip[0], ip[1], ip[2], ip[3], safegamestr); + WARN_LOG(SCENET, "AdhocServer: %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x - IP: %s) attempted to leave %s group without joining one first", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], inet_ntoa(*(in_addr*)&user->resolver.ip), safegamestr); } // Delete User @@ -1087,11 +1078,10 @@ void send_scan_results(SceNetAdhocctlUserNode * user) if (iResult < 0) ERROR_LOG(SCENET, "AdhocServer: send_scan_result[send peer complete] (Socket error %d)", errno); // Notify User - uint8_t * ip = (uint8_t *)&user->resolver.ip; char safegamestr[10]; memset(safegamestr, 0, sizeof(safegamestr)); strncpy(safegamestr, user->game->game.data, PRODUCT_CODE_LENGTH); - INFO_LOG(SCENET, "AdhocServer: %s (MAC: %02X:%02X:%02X:%02X:%02X:%02X - IP: %u.%u.%u.%u) requested information on %d %s groups", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], ip[0], ip[1], ip[2], ip[3], user->game->groupcount, safegamestr); + INFO_LOG(SCENET, "AdhocServer: %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x - IP: %s) requested information on %d %s groups", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], inet_ntoa(*(in_addr*)&user->resolver.ip), user->game->groupcount, safegamestr); // Exit Function return; @@ -1101,14 +1091,13 @@ void send_scan_results(SceNetAdhocctlUserNode * user) else { // Notify User - uint8_t * ip = (uint8_t *)&user->resolver.ip; char safegamestr[10]; memset(safegamestr, 0, sizeof(safegamestr)); strncpy(safegamestr, user->game->game.data, PRODUCT_CODE_LENGTH); char safegroupstr[9]; memset(safegroupstr, 0, sizeof(safegroupstr)); strncpy(safegroupstr, (char *)user->group->group.data, ADHOCCTL_GROUPNAME_LEN); - WARN_LOG(SCENET, "AdhocServer: %s (MAC: %02X:%02X:%02X:%02X:%02X:%02X - IP: %u.%u.%u.%u) attempted to scan for %s groups without disconnecting from %s first", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], ip[0], ip[1], ip[2], ip[3], safegamestr, safegroupstr); + WARN_LOG(SCENET, "AdhocServer: %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x - IP: %s) attempted to scan for %s groups without disconnecting from %s first", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], inet_ntoa(*(in_addr*)&user->resolver.ip), safegamestr, safegroupstr); } // Delete User @@ -1200,14 +1189,13 @@ void spread_message(SceNetAdhocctlUserNode *user, const char *message) if(counter > 0) { // Notify User - uint8_t * ip = (uint8_t *)&user->resolver.ip; char safegamestr[10]; memset(safegamestr, 0, sizeof(safegamestr)); strncpy(safegamestr, user->game->game.data, PRODUCT_CODE_LENGTH); char safegroupstr[9]; memset(safegroupstr, 0, sizeof(safegroupstr)); strncpy(safegroupstr, (char *)user->group->group.data, ADHOCCTL_GROUPNAME_LEN); - INFO_LOG(SCENET, "AdhocServer: %s (MAC: %02X:%02X:%02X:%02X:%02X:%02X - IP: %u.%u.%u.%u) sent \"%s\" to %d players in %s group %s", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], ip[0], ip[1], ip[2], ip[3], message, counter, safegamestr, safegroupstr); + INFO_LOG(SCENET, "AdhocServer: %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x - IP: %s) sent \"%s\" to %d players in %s group %s", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], inet_ntoa(*(in_addr*)&user->resolver.ip), message, counter, safegamestr, safegroupstr); } // Exit Function @@ -1218,11 +1206,10 @@ void spread_message(SceNetAdhocctlUserNode *user, const char *message) else { // Notify User - uint8_t * ip = (uint8_t *)&user->resolver.ip; char safegamestr[10]; memset(safegamestr, 0, sizeof(safegamestr)); strncpy(safegamestr, user->game->game.data, PRODUCT_CODE_LENGTH); - WARN_LOG(SCENET, "AdhocServer: %s (MAC: %02X:%02X:%02X:%02X:%02X:%02X - IP: %u.%u.%u.%u) attempted to send a text message without joining a %s group first", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], ip[0], ip[1], ip[2], ip[3], safegamestr); + WARN_LOG(SCENET, "AdhocServer: %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x - IP: %s) attempted to send a text message without joining a %s group first", (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], inet_ntoa(*(in_addr*)&user->resolver.ip), safegamestr); } // Delete User @@ -1417,7 +1404,7 @@ void game_product_override(SceNetAdhocctlProductCode * product) /** * Update Status Logfile */ -void update_status(void) +void update_status() { // Open Logfile FILE * log = File::OpenCFile(SERVER_STATUS_XMLOUT, "w"); @@ -1672,6 +1659,7 @@ const char * strcpyxml(char * out, const char * in, uint32_t size) */ int proAdhocServerThread(int port) // (int argc, char * argv[]) { + setCurrentThreadName("AdhocServer"); // Result int result = 0; @@ -1806,7 +1794,7 @@ int create_listen_socket(uint16_t port) int bindresult = bind(fd, (struct sockaddr *)&local, sizeof(local)); // Bound Local Address to Socket - if(bindresult != -1) + if(bindresult != SOCKET_ERROR) { // Switch Socket into Listening Mode listen(fd, SERVER_LISTEN_BACKLOG); @@ -1816,7 +1804,11 @@ int create_listen_socket(uint16_t port) } // Notify User - else ERROR_LOG(SCENET, "AdhocServer: Bind returned %i (Socket error %d)", bindresult, errno); + else { + ERROR_LOG(SCENET, "AdhocServer: Bind returned %i (Socket error %d)", bindresult, errno); + I18NCategory* n = GetI18NCategory("Networking"); + host->NotifyUserMessage(std::string(n->T("AdhocServer Failed to Bind Port")) + " " + std::to_string(port), 3.0, 0x0000ff); + } // Close Socket closesocket(fd); @@ -1880,7 +1872,7 @@ int server_loop(int server) u8 *pip = (u8*)&sip; if (gethostbyname(str)->h_addrtype == AF_INET && gethostbyname(str)->h_addr_list[0] != NULL) pip = (u8*)gethostbyname(str)->h_addr_list[0]; sip = *(u32_le*)pip; - WARN_LOG(SCENET, "AdhocServer: Replacing IP %s with %u.%u.%u.%u", inet_ntoa(addr.sin_addr), pip[0], pip[1], pip[2], pip[3]); + WARN_LOG(SCENET, "AdhocServer: Replacing IP %s with %s", inet_ntoa(addr.sin_addr), inet_ntoa(*(in_addr*)&pip)); } */ login_user_stream(loginresult, sip); @@ -1942,8 +1934,7 @@ int server_loop(int server) else { // Notify User - uint8_t * ip = (uint8_t *)&user->resolver.ip; - WARN_LOG(SCENET, "AdhocServer: Invalid Opcode 0x%02X in Waiting State from %u.%u.%u.%u", user->rx[0], ip[0], ip[1], ip[2], ip[3]); + WARN_LOG(SCENET, "AdhocServer: Invalid Opcode 0x%02X in Waiting State from %s", user->rx[0], inet_ntoa(*(in_addr*)&user->resolver.ip)); // Logout User logout_user(user); @@ -2026,8 +2017,7 @@ int server_loop(int server) else { // Notify User - uint8_t * ip = (uint8_t *)&user->resolver.ip; - WARN_LOG(SCENET, "AdhocServer: Invalid Opcode 0x%02X in Logged-In State from %s (MAC: %02X:%02X:%02X:%02X:%02X:%02X - IP: %u.%u.%u.%u)", user->rx[0], (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], ip[0], ip[1], ip[2], ip[3]); + WARN_LOG(SCENET, "AdhocServer: Invalid Opcode 0x%02X in Logged-In State from %s (MAC: %02x:%02x:%02x:%02x:%02x:%02x - IP: %s)", user->rx[0], (char *)user->resolver.name.data, user->resolver.mac.data[0], user->resolver.mac.data[1], user->resolver.mac.data[2], user->resolver.mac.data[3], user->resolver.mac.data[4], user->resolver.mac.data[5], inet_ntoa(*(in_addr*)&user->resolver.ip)); // Logout User logout_user(user); diff --git a/Core/HLE/proAdhocServer.h b/Core/HLE/proAdhocServer.h index c86e33cf6f..3773749eea 100644 --- a/Core/HLE/proAdhocServer.h +++ b/Core/HLE/proAdhocServer.h @@ -46,7 +46,7 @@ #define SERVER_STATUS_XMLOUT "www/status.xml" // Server Shutdown Message -#define SERVER_SHUTDOWN_MESSAGE "PROMETHEUS HUB IS SHUTTING DOWN!" +#define SERVER_SHUTDOWN_MESSAGE "ADHOC SERVER HUB IS SHUTTING DOWN!" typedef struct db_crosslink{ char id_from[PRODUCT_CODE_LENGTH + 1]; //SceNetAdhocctlProductCode id_from; @@ -298,7 +298,7 @@ void logout_user(SceNetAdhocctlUserNode * user); /** * Free Database Memory */ -void free_database(void); +void free_database(); /** * Connect User to Game Group @@ -358,7 +358,7 @@ void game_product_override(SceNetAdhocctlProductCode * product); /** * Update Status Logfile */ -void update_status(void); +void update_status(); /** * Server Entry Point diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index e9996c464e..b9f9238200 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -45,8 +45,8 @@ #include "Core/Instance.h" static bool netInited; -static bool netInetInited; -static bool netApctlInited; +bool netInetInited; +bool netApctlInited; u32 netDropRate = 0; u32 netDropDuration = 0; u32 netPoolAddr = 0; @@ -118,12 +118,25 @@ void __NetInit() { net::Init(); InitLocalIP(); - INFO_LOG(SCENET, "LocalHost IP will be %s", inet_ntoa(((sockaddr_in*)&localIP)->sin_addr)); + + char tmpmac[18]; + SceNetEtherAddr mac; + getLocalMac(&mac); + INFO_LOG(SCENET, "LocalHost IP will be %s [MAC: %s]", inet_ntoa(((sockaddr_in*)&localIP)->sin_addr), mac2str(&mac, tmpmac)); //net::Init(); __ResetInitNetLib(); } void __NetShutdown() { + // Checks to avoid confusing logspam + if (netAdhocctlInited) sceNetAdhocctlTerm(); + if (netAdhocInited) sceNetAdhocTerm(); + + if (netApctlInited) sceNetApctlTerm(); + if (netInetInited) sceNetInetTerm(); + + if (netInited) sceNetTerm(); + __ResetInitNetLib(); net::Shutdown(); @@ -149,6 +162,10 @@ void __NetDoState(PointerWrap &p) { if (!s) return; + auto cur_netInited = netInited; + auto cur_netInetInited = netInetInited; + auto cur_netApctlInited = netApctlInited; + p.Do(netInited); p.Do(netInetInited); p.Do(netApctlInited); @@ -170,6 +187,12 @@ void __NetDoState(PointerWrap &p) { p.Do(netThread1Addr); p.Do(netThread2Addr); } + // Let's not change "Inited" value when Loading SaveState in the middle of multiplayer to prevent memory & port leaks + if (p.mode == p.MODE_READ) { + netApctlInited = cur_netApctlInited; + netInetInited = cur_netInetInited; + netInited = cur_netInited; + } } static inline u32 AllocUser(u32 size, bool fromTop, const char *name) { @@ -186,10 +209,32 @@ static inline void FreeUser(u32 &addr) { } static u32 sceNetTerm() { - //May also need to Terminate netAdhocctl and netAdhoc since the game (ie. GTA:VCS, Wipeout Pulse, etc) might not called them before calling sceNetTerm and causing them to behave strangely on the next sceNetInit+sceNetAdhocInit + // May also need to Terminate netAdhocctl and netAdhoc to free some resources & threads, since the game (ie. GTA:VCS, Wipeout Pulse, etc) might not called them before calling sceNetTerm and causing them to behave strangely on the next sceNetInit & sceNetAdhocInit if (netAdhocctlInited) sceNetAdhocctlTerm(); if (netAdhocInited) sceNetAdhocTerm(); + if (netApctlInited) sceNetApctlTerm(); + if (netInetInited) sceNetInetTerm(); + + // Library is initialized + if (netInited) { + // Delete PDP Sockets + deleteAllPDP(); + + // Delete PTP Sockets + deleteAllPTP(); + + // Delete GameMode Buffer + //deleteAllGMB(); + + // Terminate Internet Library + //sceNetInetTerm(); + + // Unload Internet Modules (Just keep it in memory... unloading crashes?!) + // if (_manage_modules != 0) sceUtilityUnloadModule(PSP_MODULE_NET_INET); + // Library shutdown + } + WARN_LOG(SCENET, "sceNetTerm()"); netInited = false; FreeUser(netPoolAddr); @@ -199,12 +244,20 @@ static u32 sceNetTerm() { return 0; } -// TODO: should that struct actually be initialized here? -static int sceNetInit(u32 poolSize, u32 calloutPri, u32 calloutStack, u32 netinitPri, u32 netinitStack) { +/* +Parameters: + poolsize - Memory pool size (appears to be for the whole of the networking library). + calloutprio - Priority of the SceNetCallout thread. + calloutstack - Stack size of the SceNetCallout thread (defaults to 4096 on non 1.5 firmware regardless of what value is passed). + netintrprio - Priority of the SceNetNetintr thread. + netintrstack - Stack size of the SceNetNetintr thread (defaults to 4096 on non 1.5 firmware regardless of what value is passed). +*/ +static u32 sceNetInit(u32 poolSize, u32 calloutPri, u32 calloutStack, u32 netinitPri, u32 netinitStack) { + // TODO: Create Network Threads using given priority & stack // TODO: The correct behavior is actually to allocate more and leak the other threads/pool. // But we reset here for historic reasons (GTA:VCS potentially triggers this.) if (netInited) - sceNetTerm(); + sceNetTerm(); // This cleanup attempt might not worked when SaveState were loaded in the middle of multiplayer game and re-entering multiplayer, thus causing memory leaks & wasting binded ports. May be we shouldn't save/load "Inited" vars on SaveState? if (poolSize == 0) { return hleLogError(SCENET, SCE_KERNEL_ERROR_ILLEGAL_MEMSIZE, "invalid pool size"); @@ -236,13 +289,32 @@ static int sceNetInit(u32 poolSize, u32 calloutPri, u32 calloutStack, u32 netini WARN_LOG(SCENET, "sceNetInit(poolsize=%d, calloutpri=%i, calloutstack=%d, netintrpri=%i, netintrstack=%d) at %08x", poolSize, calloutPri, calloutStack, netinitPri, netinitStack, currentMIPS->pc); netInited = true; - netMallocStat.maximum = poolSize; - netMallocStat.free = poolSize; - netMallocStat.pool = 0; + netMallocStat.pool = poolSize; // This should be the poolSize isn't? + netMallocStat.maximum = poolSize/2; // According to JPCSP's sceNetGetMallocStat this is Currently Used size = (poolSize - free), faked to half the pool + netMallocStat.free = poolSize - netMallocStat.maximum; + + // Clear Socket Translator Memory + memset(&pdp, 0, sizeof(pdp)); + memset(&ptp, 0, sizeof(ptp)); return hleLogSuccessI(SCENET, 0); } +// Free(delete) thread info / data. +// Normal usage: sceKernelDeleteThread followed by sceNetFreeThreadInfo with the same threadID as argument +static int sceNetFreeThreadinfo(SceUID thid) { + ERROR_LOG(SCENET, "UNIMPL sceNetFreeThreadinfo(%i)", thid); + + return 0; +} + +// Abort a thread. +static int sceNetThreadAbort(SceUID thid) { + ERROR_LOG(SCENET, "UNIMPL sceNetThreadAbort(%i)", thid); + + return 0; +} + static u32 sceWlanGetEtherAddr(u32 addrAddr) { if (!Memory::IsValidRange(addrAddr, 6)) { // More correctly, it should crash. @@ -364,7 +436,7 @@ static int sceNetInetInit() { return 0; } -static int sceNetInetTerm() { +int sceNetInetTerm() { ERROR_LOG(SCENET, "UNIMPL sceNetInetTerm()"); netInetInited = false; @@ -380,7 +452,7 @@ static int sceNetApctlInit() { return 0; } -static int sceNetApctlTerm() { +int sceNetApctlTerm() { ERROR_LOG(SCENET, "UNIMPL sceNeApctlTerm()"); netApctlInited = false; @@ -602,9 +674,9 @@ const HLEFunction sceNet[] = { {0X89360950, &WrapI_UU, "sceNetEtherNtostr", 'i', "xx" }, {0XD27961C9, &WrapI_UU, "sceNetEtherStrton", 'i', "xx" }, {0X0BF0A3AE, &WrapU_U, "sceNetGetLocalEtherAddr", 'x', "x" }, - {0X50647530, nullptr, "sceNetFreeThreadinfo", '?', "" }, + {0X50647530, &WrapI_I, "sceNetFreeThreadinfo", 'i', "i" }, {0XCC393E48, &WrapI_U, "sceNetGetMallocStat", 'i', "x" }, - {0XAD6844C6, nullptr, "sceNetThreadAbort", '?', "" }, + {0XAD6844C6, &WrapI_I, "sceNetThreadAbort", 'i', "i" }, }; const HLEFunction sceNetResolver[] = { diff --git a/Core/HLE/sceNet.h b/Core/HLE/sceNet.h index f5cec1206c..bf7d006884 100644 --- a/Core/HLE/sceNet.h +++ b/Core/HLE/sceNet.h @@ -60,7 +60,7 @@ typedef struct SceNetInetSockaddr { uint8_t sa_len; uint8_t sa_family; uint8_t sa_data[14]; -} SceNetInetSockaddr; +} PACK SceNetInetSockaddr; // Sockaddr_in typedef struct SceNetInetSockaddrIn { @@ -69,19 +69,19 @@ typedef struct SceNetInetSockaddrIn { u16_le sin_port; //uint16_t u32_le sin_addr; //uint32_t uint8_t sin_zero[8]; -} SceNetInetSockaddrIn; +} PACK SceNetInetSockaddrIn; // Polling Event Field typedef struct SceNetInetPollfd { //similar format to pollfd in 32bit (pollfd in 64bit have different size) s32_le fd; s16_le events; s16_le revents; -} SceNetInetPollfd; +} PACK SceNetInetPollfd; -struct ProductStruct { +typedef struct ProductStruct { // Similar to SceNetAdhocctlAdhocId ? s32_le unknown; // Unknown, set to 0 // Product Type ? char product[PRODUCT_CODE_LENGTH]; // Game ID (Example: ULUS10000) -}; +} PACK ProductStruct; struct ApctlHandler { u32 entryPoint; @@ -90,6 +90,9 @@ struct ApctlHandler { class PointerWrap; +extern bool netInetInited; +extern bool netApctlInited; + void Register_sceNet(); void Register_sceWlanDrv(); void Register_sceNetUpnp(); @@ -100,4 +103,7 @@ void __NetInit(); void __NetShutdown(); void __NetDoState(PointerWrap &p); -int sceNetInetPoll(void *fds, u32 nfds, int timeout); \ No newline at end of file +int sceNetInetPoll(void *fds, u32 nfds, int timeout); +int sceNetInetTerm(); +int sceNetApctlTerm(); +static u32 sceNetTerm(); diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 52dbcd9d8f..ce16cee15e 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -16,11 +16,13 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include "thread/threadutil.h" // sceNetAdhoc // This is a direct port of Coldbird's code from http://code.google.com/p/aemu/ // All credit goes to him! #include "Core/Core.h" +#include "Core/Host.h" #include "Core/MemMapHelpers.h" #include "Common/ChunkFile.h" #include "Core/MIPS/MIPSCodeUtils.h" @@ -28,14 +30,18 @@ #include "Core/HLE/HLEHelperThread.h" #include "Core/HLE/FunctionWrappers.h" #include "Core/HLE/sceKernelThread.h" -#include "Core/HLE/sceNetAdhoc.h" #include "Core/HLE/sceKernel.h" #include "Core/HLE/sceKernelMemory.h" #include "Core/HLE/sceKernelModule.h" #include "Core/HLE/sceKernelInterrupt.h" #include "Core/HLE/proAdhoc.h" +#include "Core/HLE/sceNetAdhoc.h" #include "Core/HLE/sceNet.h" #include "Core/HLE/proAdhocServer.h" +#include "i18n/i18n.h" + +#define PSP_THREAD_ATTR_KERNEL 0x00001000 // Using constants instead of numbers for readability reason, since PSP_THREAD_ATTR_KERNEL/USER is located in sceKernelThread.cpp instead of sceKernelThread.h +#define PSP_THREAD_ATTR_USER 0x80000000 // shared in sceNetAdhoc.h since it need to be used from sceNet.cpp also // TODO: Make accessor functions instead, and throw all this state in a struct. @@ -45,16 +51,23 @@ bool networkInited; static bool netAdhocMatchingInited; int netAdhocMatchingStarted = 0; +int adhocDefaultTimeout = 5000; +int adhocEventPollDelayMS = 10; // 16; +int adhocMatchingEventDelayMS = 100; // 33; +int adhocEventDelayMS = 500; // This will affect the duration of "Connecting..." dialog/message box in .Hack//Link and Naruto Ultimate Ninja Heroes 3 SceUID threadAdhocID; -std::mutex adhocEvtMtx; -std::vector> adhocctlEvents; -std::vector matchingEvents; +std::recursive_mutex adhocEvtMtx; +std::deque> adhocctlEvents; +std::deque matchingEvents; +std::map adhocctlHandlers; +int IsAdhocctlInCB = 0; + u32 dummyThreadHackAddr = 0; u32_le dummyThreadCode[3]; - -std::map adhocctlHandlers; +u32 matchingThreadHackAddr = 0; +u32_le matchingThreadCode[3]; int matchingEventThread(int matchingId); int matchingInputThread(int matchingId); @@ -65,7 +78,7 @@ int sceNetAdhocMatchingTerm(); int sceNetAdhocMatchingSetHelloOpt(int matchingId, int optLenAddr, u32 optDataAddr); void __NetAdhocShutdown() { - //Kill AdhocServer Thread + // Kill AdhocServer Thread if (adhocServerRunning) { adhocServerRunning = false; if (adhocServerThread.joinable()) { @@ -80,22 +93,28 @@ void __NetAdhocShutdown() { sceNetAdhocctlTerm(); } if (netAdhocInited) { - // Should not really call HLE funcs from shutdown. - // Prevent attempting to delete the thread, already deleted by shutdown. - threadAdhocID = 0; + // Should not really call HLE funcs from shutdown, but we need to cleanup sceNetAdhocTerm(); } if (dummyThreadHackAddr) { kernelMemory.Free(dummyThreadHackAddr); dummyThreadHackAddr = 0; } + if (matchingThreadHackAddr) { + kernelMemory.Free(matchingThreadHackAddr); + matchingThreadHackAddr = 0; + } } void __NetAdhocDoState(PointerWrap &p) { - auto s = p.Section("sceNetAdhoc", 1, 2); + auto s = p.Section("sceNetAdhoc", 1, 3); if (!s) return; + auto cur_netAdhocInited = netAdhocInited; + auto cur_netAdhocctlInited = netAdhocctlInited; + auto cur_netAdhocMatchingInited = netAdhocMatchingInited; + p.Do(netAdhocInited); p.Do(netAdhocctlInited); p.Do(netAdhocMatchingInited); @@ -106,26 +125,44 @@ void __NetAdhocDoState(PointerWrap &p) { __KernelRestoreActionType(actionAfterMatchingMipsCall, AfterMatchingMipsCall::Create); p.Do(dummyThreadHackAddr); - } else if (p.mode == p.MODE_READ) { + } + if (s >= 3) { + //p.Do(IsAdhocctlInCB); // This will cause a crash if adhocEvtMtx was locked + p.Do(actionAfterAdhocMipsCall); + __KernelRestoreActionType(actionAfterAdhocMipsCall, AfterAdhocMipsCall::Create); + + p.Do(matchingThreadHackAddr); + } + + if (p.mode == p.MODE_READ) { // Previously, this wasn't being saved. It needs its own space. - if (strcmp("dummythreadhack", kernelMemory.GetBlockTag(dummyThreadHackAddr)) != 0) { + if (dummyThreadHackAddr && strcmp("dummythreadhack", kernelMemory.GetBlockTag(dummyThreadHackAddr)) != 0) { u32 blockSize = sizeof(dummyThreadCode); dummyThreadHackAddr = kernelMemory.Alloc(blockSize, false, "dummythreadhack"); } - } - if (dummyThreadHackAddr) { - Memory::Memcpy(dummyThreadHackAddr, dummyThreadCode, sizeof(dummyThreadCode)); + if (matchingThreadHackAddr && strcmp("matchingThreadHack", kernelMemory.GetBlockTag(matchingThreadHackAddr)) != 0) { + u32 blockSize = sizeof(matchingThreadCode); + matchingThreadHackAddr = kernelMemory.Alloc(blockSize, false, "matchingThreadHack"); + } + + // Restore dummy Loop MIPS code to prevent crashes after loading from SaveState + if (dummyThreadHackAddr) Memory::Memcpy(dummyThreadHackAddr, dummyThreadCode, sizeof(dummyThreadCode)); + if (matchingThreadHackAddr) Memory::Memcpy(matchingThreadHackAddr, matchingThreadCode, sizeof(matchingThreadCode)); + + // Let's not change "Inited" value when Loading SaveState to prevent memory & port leaks + netAdhocMatchingInited = cur_netAdhocMatchingInited; + netAdhocctlInited = cur_netAdhocctlInited; + netAdhocInited = cur_netAdhocInited; } } void __UpdateAdhocctlHandlers(u32 flag, u32 error) { - std::lock_guard adhocGuard(adhocEvtMtx); + std::lock_guard adhocGuard(adhocEvtMtx); adhocctlEvents.push_back({ flag, error }); } -// TODO: MipsCall needs to be called from it's own PSP Thread instead of from any random PSP Thread void __UpdateMatchingHandler(MatchingArgs ArgsPtr) { - std::lock_guard adhocGuard(adhocEvtMtx); + std::lock_guard adhocGuard(adhocEvtMtx); matchingEvents.push_back(ArgsPtr); } @@ -138,6 +175,19 @@ static int getBlockingFlag(int id) { #endif } +u32_le __CreateHLELoop(u32_le *loopAddr, const char *sceFuncName, const char *hleFuncName, const char *tagName) { + if (loopAddr == NULL || sceFuncName == NULL || hleFuncName == NULL) + return 0; + + loopAddr[0] = MIPS_MAKE_SYSCALL(sceFuncName, hleFuncName); + loopAddr[1] = MIPS_MAKE_B(-2); + loopAddr[2] = MIPS_MAKE_NOP(); + u32 blockSize = sizeof(u32_le)*3; + u32_le dummyThreadHackAddr = kernelMemory.Alloc(blockSize, false, tagName); // blockSize will be rounded to 256 granularity + Memory::Memcpy(dummyThreadHackAddr, loopAddr, sizeof(u32_le) * 3); // This area will be cleared again after loading an old savestate :( + return dummyThreadHackAddr; +} + void __NetAdhocInit() { friendFinderRunning = false; netAdhocInited = false; @@ -145,13 +195,13 @@ void __NetAdhocInit() { netAdhocMatchingInited = false; adhocctlHandlers.clear(); __AdhocServerInit(); - dummyThreadCode[0] = MIPS_MAKE_SYSCALL("sceNetAdhoc", "__NetTriggerCallbacks"); - dummyThreadCode[1] = MIPS_MAKE_B(-2); - dummyThreadCode[2] = MIPS_MAKE_NOP(); - u32 blockSize = sizeof(dummyThreadCode); - dummyThreadHackAddr = kernelMemory.Alloc(blockSize, false, "dummythreadhack"); - Memory::Memcpy(dummyThreadHackAddr, dummyThreadCode, sizeof(dummyThreadCode)); // This area will be cleared again after loading an old savestate :( + + // Init Adhoc Callbacks + dummyThreadHackAddr = __CreateHLELoop(dummyThreadCode, "sceNetAdhoc", "__NetTriggerCallbacks", "dummythreadhack"); + matchingThreadHackAddr = __CreateHLELoop(matchingThreadCode, "sceNetAdhocMatching", "__NetMatchingCallbacks", "matchingThreadHack"); actionAfterMatchingMipsCall = __KernelRegisterActionType(AfterMatchingMipsCall::Create); + actionAfterAdhocMipsCall = __KernelRegisterActionType(AfterAdhocMipsCall::Create); + // Create built-in AdhocServer Thread if (g_Config.bEnableWlan && g_Config.bEnableAdhocServer) { adhocServerRunning = true; @@ -161,10 +211,6 @@ void __NetAdhocInit() { u32 sceNetAdhocInit() { if (!netAdhocInited) { - // Clear Translator Memory - memset(&pdp, 0, sizeof(pdp)); - memset(&ptp, 0, sizeof(ptp)); - // Library initialized netAdhocInited = true; @@ -183,22 +229,25 @@ static u32 sceNetAdhocctlInit(int stackSize, int prio, u32 productAddr) { // Create fake PSP Thread for callback // TODO: Should use a separated threads for friendFinder, matchingEvent, and matchingInput and created on AdhocctlInit & AdhocMatchingStart instead of here -#define PSP_THREAD_ATTR_KERNEL 0x00001000 // Using constants instead of numbers for readability reason, since PSP_THREAD_ATTR_KERNEL/USER is located in sceKernelThread.cpp instead of sceKernelThread.h -#define PSP_THREAD_ATTR_USER 0x80000000 - - threadAdhocID = __KernelCreateThread("AdhocThread", __KernelGetCurThreadModuleId(), dummyThreadHackAddr, prio, stackSize, PSP_THREAD_ATTR_USER, 0, false); + threadAdhocID = __KernelCreateThread("AdhocThread", __KernelGetCurThreadModuleId(), dummyThreadHackAddr, prio, stackSize, PSP_THREAD_ATTR_USER, 0, true); if (threadAdhocID > 0) { __KernelStartThread(threadAdhocID, 0, 0); } if(g_Config.bEnableWlan) { - if (initNetwork((SceNetAdhocctlAdhocId *)Memory::GetPointer(productAddr)) == 0) { + SceNetAdhocctlAdhocId* product = NULL; + if (Memory::IsValidAddress(productAddr)) product = (SceNetAdhocctlAdhocId*)Memory::GetPointer(productAddr); + + if (initNetwork(product) == 0) { // TODO: Merging friendFinder (real) thread to AdhocThread (fake) thread on PSP side if (!friendFinderRunning) { friendFinderRunning = true; friendFinderThread = std::thread(friendFinder); } networkInited = true; + + sleep_ms(adhocEventPollDelayMS); + //hleDelayResult(0, "give some time", adhocEventDelayMS * 1000); // Give a little time for friendFinder thread to be ready before the game use the next sceNet functions, should've checked for friendFinderRunning status instead of guessing the time? } else { WARN_LOG(SCENET, "sceNetAdhocctlInit: Failed to init the network but faking success"); networkInited = false; // TODO: What needs to check this? Pretty much everything? Maybe we should just set netAdhocctlInited to false.. @@ -237,8 +286,9 @@ static int sceNetAdhocctlGetState(u32 ptrToStatus) { * @return Socket ID > 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_SOCKET_ID_NOT_AVAIL, ADHOC_INVALID_ADDR, ADHOC_PORT_NOT_AVAIL, ADHOC_INVALID_PORT, ADHOC_PORT_IN_USE, NET_NO_SPACE */ // When choosing AdHoc menu in Wipeout Pulse sometimes it's saying that "WLAN is turned off" on game screen and getting "kUnityCommandCode_MediaDisconnected" error in the Log Console when calling sceNetAdhocPdpCreate, probably it needed to wait something from the thread before calling this (ie. need to receives 7 bytes from adhoc server 1st?) -static int sceNetAdhocPdpCreate(const char *mac, u32 port, int bufferSize, u32 unknown) { - INFO_LOG(SCENET, "sceNetAdhocPdpCreate(%s, %u, %u, %u) at %08x", mac, port, bufferSize, unknown, currentMIPS->pc); +static int sceNetAdhocPdpCreate(const char *mac, int port, int bufferSize, u32 unknown) { + char tmpmac[18]; + INFO_LOG(SCENET, "sceNetAdhocPdpCreate(%s, %u, %u, %u) at %08x", mac2str((SceNetEtherAddr*)mac, tmpmac), port, bufferSize, unknown, currentMIPS->pc); if (!g_Config.bEnableWlan) { return -1; } @@ -249,6 +299,12 @@ static int sceNetAdhocPdpCreate(const char *mac, u32 port, int bufferSize, u32 u if (netAdhocInited) { // Valid Arguments are supplied if (mac != NULL && bufferSize > 0) { + //sport 0 should be shifted back to 0 when using offset Phantasy Star Portable 2 use this + if (port == 0) port = -(int)portOffset; + // Some games (ie. DBZ Shin Budokai 2) might be getting the saddr/srcmac content from SaveState and causing problems :( So we try to fix it here + if (saddr != NULL) { + getLocalMac(saddr); + } // Valid MAC supplied if (isLocalMAC(saddr)) { //// Unused Port supplied @@ -276,18 +332,19 @@ static int sceNetAdhocPdpCreate(const char *mac, u32 port, int bufferSize, u32 u getLocalIp(&addr); } - //if (port < 7) addr.sin_port = htons(port + 1341); else // <= 443 - addr.sin_port = htons(port + portOffset ); // This not safe in any way... + addr.sin_port = htons(port + portOffset ); // The port might be under 1024 (ie. GTA:VCS use port 1, Ford Street Racing use port 0 (UNUSED_PORT), etc) and already used by other application/host OS, should we add 1024 to the port whenever it tried to use an already used port? // Bound Socket to local Port int iResult = bind(usocket, (sockaddr *)&addr, sizeof(addr)); - /*if (iResult == SOCKET_ERROR && errno == 10048) { //Forcing PdpCreate to be successfull using a different Port might not works and might affect other players sharing the same IP due to "deleteFriendByIP" (should delete by MAC instead of IP) - addr.sin_port = 0; //UNUSED_PORT - iResult = bind(usocket, (sockaddr *)&addr, sizeof(addr)); - WARN_LOG(SCENET, "Port %u is already used, replaced with UNUSED_PORT(%u)", port, ntohs(addr.sin_port)); - }*/ + if (iResult == 0) { + // Update sport with the port assigned internal->lport = ntohs(local.sin_port) + socklen_t len = sizeof(addr); + if (getsockname(usocket, (sockaddr*)&addr, &len) == 0) { + port = ntohs(addr.sin_port) - portOffset; + } + // Allocate Memory for Internal Data SceNetAdhocPdpStat * internal = (SceNetAdhocPdpStat *)malloc(sizeof(SceNetAdhocPdpStat)); @@ -297,21 +354,22 @@ static int sceNetAdhocPdpCreate(const char *mac, u32 port, int bufferSize, u32 u memset(internal, 0, sizeof(SceNetAdhocPdpStat)); // Find Free Translator Index - int i = 0; for (; i < 255; i++) if (pdp[i] == NULL) break; + int i = 0; + for (; i < 255; i++) if (pdp[i] == NULL) break; // Found Free Translator Index if (i < 255) { // Fill in Data internal->id = usocket; internal->laddr = *saddr; - internal->lport = getLocalPort(usocket) - portOffset; //should use the port given to the socket (in case it's UNUSED_PORT port) isn't? + internal->lport = port; //getLocalPort(usocket) - portOffset; //should use the port given to the socket (in case it's UNUSED_PORT port) isn't? internal->rcv_sb_cc = bufferSize; // Link Socket to Translator ID pdp[i] = internal; // Forward Port on Router - //sceNetPortOpen("UDP", sport); // I need to figure out how to use this in windows/linux + //sceNetPortOpen("UDP", port); // I need to figure out how to use this in windows/linux // Success return i + 1; @@ -326,6 +384,8 @@ static int sceNetAdhocPdpCreate(const char *mac, u32 port, int bufferSize, u32 u retval = ERROR_NET_ADHOC_PORT_IN_USE; if (iResult == SOCKET_ERROR) { ERROR_LOG(SCENET, "Socket error (%i) when binding port %u", errno, ntohs(addr.sin_port)); + I18NCategory* n = GetI18NCategory("Networking"); + host->NotifyUserMessage(std::string(n->T("Failed to Bind Port")) + " " + std::to_string(port + portOffset) + "\n" + std::string(n->T("Please change your Port Offset")), 3.0, 0x0000ff); } } @@ -355,7 +415,10 @@ static int sceNetAdhocPdpCreate(const char *mac, u32 port, int bufferSize, u32 u * @return 0 on success or... ADHOCCTL_NOT_INITIALIZED, ADHOCCTL_INVALID_ARG */ static int sceNetAdhocctlGetParameter(u32 paramAddr) { - DEBUG_LOG(SCENET, "sceNetAdhocctlGetParameter(%08x)",paramAddr); + char grpName[9] = { 0 }; char tmpmac[18]; + memcpy(grpName, parameter.group_name.data, ADHOCCTL_GROUPNAME_LEN); + parameter.nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; + DEBUG_LOG(SCENET, "sceNetAdhocctlGetParameter(%08x) [Ch=%i][Group=%s][BSSID=%s][name=%s]", paramAddr, parameter.channel, grpName, mac2str(¶meter.bssid.mac_addr, tmpmac), parameter.nickname.data); if (!g_Config.bEnableWlan) { return ERROR_NET_ADHOCCTL_DISCONNECTED; } @@ -390,7 +453,12 @@ static int sceNetAdhocctlGetParameter(u32 paramAddr) { * @return 0 on success or... ADHOC_INVALID_ARG, ADHOC_NOT_INITIALIZED, ADHOC_INVALID_SOCKET_ID, ADHOC_SOCKET_DELETED, ADHOC_INVALID_ADDR, ADHOC_INVALID_PORT, ADHOC_INVALID_DATALEN, ADHOC_SOCKET_ALERTED, ADHOC_TIMEOUT, ADHOC_THREAD_ABORTED, ADHOC_WOULD_BLOCK, NET_NO_SPACE, NET_INTERNAL */ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int len, int timeout, int flag) { - DEBUG_LOG(SCENET, "sceNetAdhocPdpSend(%i, %s, %i, %p, %i, %i, %i)", id, mac, port, data, len, timeout, flag); + char tmpmac[18]; + if (flag == 0) { // Prevent spamming Debug Log with retries of non-bocking socket + DEBUG_LOG(SCENET, "sceNetAdhocPdpSend(%i, %s, %i, %p, %i, %i, %i) at %08x", id, mac2str((SceNetEtherAddr*)mac, tmpmac), port, data, len, timeout, flag, currentMIPS->pc); + } else { + VERBOSE_LOG(SCENET, "sceNetAdhocPdpSend(%i, %s, %i, %p, %i, %i, %i) at %08x", id, mac2str((SceNetEtherAddr*)mac, tmpmac), port, data, len, timeout, flag, currentMIPS->pc); + } if (!g_Config.bEnableWlan) { return -1; } @@ -442,15 +510,14 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int if (sent == SOCKET_ERROR) { DEBUG_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPdpSend[%i:%u->%u] (size=%i)", error, id, getLocalPort(socket->id), ntohs(target.sin_port), len); } - changeBlockingMode(socket->id, 0); + //changeBlockingMode(socket->id, 0); // Free Network Lock //_freeNetworkLock(); - uint8_t * sip = (uint8_t *)&target.sin_addr.s_addr; // Sent Data if (sent == len) { - DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u]: Sent %u bytes to %u.%u.%u.%u:%u\n", id, getLocalPort(socket->id), sent, sip[0], sip[1], sip[2], sip[3], ntohs(target.sin_port)); + DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u]: Sent %u bytes to %s:%u\n", id, getLocalPort(socket->id), sent, inet_ntoa(target.sin_addr), ntohs(target.sin_port)); // Success return 0; @@ -462,6 +529,7 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int // Timeout return ERROR_NET_ADHOC_TIMEOUT; //-1; } + //VERBOSE_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u]: Unknown Target Peer %s:%u\n", id, getLocalPort(socket->id), mac2str(daddr, tmpmac), ntohs(target.sin_port)); } // Broadcast Target @@ -507,8 +575,7 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int } changeBlockingMode(socket->id, 0); if (sent >= 0) { - uint8_t * sip = (uint8_t *)&target.sin_addr.s_addr; - DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u](BC): Sent %u bytes to %u.%u.%u.%u:%u\n", id, getLocalPort(socket->id), sent, sip[0], sip[1], sip[2], sip[3], ntohs(target.sin_port)); + DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u](BC): Sent %u bytes to %s:%u\n", id, getLocalPort(socket->id), sent, inet_ntoa(target.sin_addr), ntohs(target.sin_port)); } } @@ -561,7 +628,12 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int * @return 0 on success or... ADHOC_INVALID_ARG, ADHOC_NOT_INITIALIZED, ADHOC_INVALID_SOCKET_ID, ADHOC_SOCKET_DELETED, ADHOC_SOCKET_ALERTED, ADHOC_WOULD_BLOCK, ADHOC_TIMEOUT, ADHOC_NOT_ENOUGH_SPACE, ADHOC_THREAD_ABORTED, NET_INTERNAL */ static int sceNetAdhocPdpRecv(int id, void *addr, void * port, void *buf, void *dataLength, u32 timeout, int flag) { - DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv(%i, %p, %p, %p, %p, %i, %i) at %08x", id, addr, port, buf, dataLength, timeout, flag, currentMIPS->pc); + if (flag == 0) { // Prevent spamming Debug Log with retries of non-bocking socket + DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv(%i, %p, %p, %p, %p, %i, %i) at %08x", id, addr, port, buf, dataLength, timeout, flag, currentMIPS->pc); + } else { + VERBOSE_LOG(SCENET, "sceNetAdhocPdpRecv(%i, %p, %p, %p, %p, %i, %i) at %08x", id, addr, port, buf, dataLength, timeout, flag, currentMIPS->pc); + } + if (!g_Config.bEnableWlan) { return -1; } @@ -613,19 +685,36 @@ static int sceNetAdhocPdpRecv(int id, void *addr, void * port, void *buf, void * // Acquire Network Lock //_acquireNetworkLock(); + // TODO: Use a different thread (similar to sceIo) for recvfrom, recv & accept to prevent blocking-socket from blocking emulation (ie. Hot Shots Tennis:GaG) + int received = 0; + int error = 0; + // Receive Data - changeBlockingMode(socket->id, flag); - int received = recvfrom(socket->id, (char *)buf, *len,0,(sockaddr *)&sin, &sinlen); - int error = errno; - if (received == SOCKET_ERROR) { - VERBOSE_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPdpRecv [size=%i]", error, *len); + changeBlockingMode(socket->id, 1); + received = recvfrom(socket->id, (char*)buf, *len, 0, (sockaddr*)&sin, &sinlen); + error = errno; + + if (flag == 0) { + // Simulate blocking behaviour with non-blocking socket + uint32_t starttime = (uint32_t)(real_time_now() * 1000.0); + // Wait for Connection + while ((timeout == 0 || ((uint32_t)(real_time_now() * 1000.0) - starttime) < (uint32_t)timeout) && (received == SOCKET_ERROR) && connectInProgress(error)) { + received = recvfrom(socket->id, (char*)buf, *len, 0, (sockaddr*)&sin, &sinlen); + error = errno; + // Wait 1ms + sleep_ms(1); + } } - changeBlockingMode(socket->id, 0); + + if (received == SOCKET_ERROR) { + VERBOSE_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPdpRecv[%i:%u] [size=%i]", id, socket->lport, error, *len); + } + changeBlockingMode(socket->id, 0); + // Received Data - if (received >= 0) { - uint8_t * sip = (uint8_t *)&sin.sin_addr.s_addr; - DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %u bytes from %u.%u.%u.%u:%u\n", id, getLocalPort(socket->id), received, sip[0], sip[1], sip[2], sip[3], ntohs(sin.sin_port)); + if (received > 0) { + DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %u bytes from %s:%u\n", id, getLocalPort(socket->id), received, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); // Peer MAC SceNetEtherAddr mac; @@ -645,8 +734,11 @@ static int sceNetAdhocPdpRecv(int id, void *addr, void * port, void *buf, void * // Return Success return 0; } - WARN_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %i bytes from Unknown Peer %u.%u.%u.%u:%u [%02X:%02X:%02X:%02X:%02X:%02X]", id, getLocalPort(socket->id), received, sip[0], sip[1], sip[2], sip[3], ntohs(sin.sin_port), mac.data[0], mac.data[1], mac.data[2], mac.data[3], mac.data[4], mac.data[5]); - + // Unknown Peer, let's not give any data + else { + *len = 0; + WARN_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %i bytes from Unknown Peer %s:%u", id, getLocalPort(socket->id), received, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); + } // Free Network Lock //_freeNetworkLock(); @@ -663,8 +755,16 @@ static int sceNetAdhocPdpRecv(int id, void *addr, void * port, void *buf, void * #endif // Nothing received - if (flag) return ERROR_NET_ADHOC_WOULD_BLOCK; - return ERROR_NET_ADHOC_TIMEOUT; + if (received == SOCKET_ERROR && error == EAGAIN) { + // Blocking Situation + if (flag) return ERROR_NET_ADHOC_WOULD_BLOCK; + + // Timeout + return ERROR_NET_ADHOC_TIMEOUT; + } + + // Disconnected + return ERROR_NET_ADHOC_DISCONNECTED; } // Invalid Argument @@ -699,7 +799,7 @@ int sceNetAdhocPollSocket(u32 socketStructAddr, int count, int timeout, int nonb if (sds != NULL && count > 0) { // Socket Check - int i = 0; for (; i < count; i++) + for (int i = 0; i < count; i++) { // Invalid Socket if (sds[i].id < 1 || sds[i].id > 255 || (pdp[sds[i].id - 1] == NULL && ptp[sds[i].id - 1] == NULL)) return ERROR_NET_ADHOC_INVALID_SOCKET_ID; @@ -873,7 +973,15 @@ static int sceNetAdhocctlScan() { // Library initialized if (netAdhocctlInited) { - // Not connected + // Wait until Not connected + if (friendFinderRunning) { + int cnt = 0; + while ((threadStatus != ADHOCCTL_STATE_DISCONNECTED) && (cnt < adhocDefaultTimeout)) { + sleep_ms(1); + cnt++; + } + } + if (threadStatus == ADHOCCTL_STATE_DISCONNECTED) { threadStatus = ADHOCCTL_STATE_SCANNING; @@ -890,15 +998,22 @@ static int sceNetAdhocctlScan() { return ERROR_NET_ADHOCCTL_DISCONNECTED; // ERROR_NET_ADHOCCTL_BUSY } + // Does Connected Event's mipscall need be executed after returning from sceNetAdhocctlScan ? + notifyAdhocctlHandlers(ADHOCCTL_EVENT_SCAN, 0); + // Wait for Status to be connected to prevent Ford Street Racing from Failed to find game session + // TODO: Do this async while Delaying HLE Result if (friendFinderRunning) { int cnt = 0; - while ((threadStatus == ADHOCCTL_STATE_SCANNING) && (cnt < 5000)) { + while ((threadStatus == ADHOCCTL_STATE_SCANNING) && (cnt < adhocDefaultTimeout)) { sleep_ms(1); cnt++; } } + //sceKernelDelayThread(adhocEventDelayMS * 1000); + hleDelayResult(0, "give time to init/cleanup", adhocEventDelayMS * 1000); + // Return Success return 0; } @@ -917,7 +1032,7 @@ static 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, buflen ? *buflen : -1, bufAddr); + INFO_LOG(SCENET, "sceNetAdhocctlGetScanInfo([%08x]=%i, %08x)", sizeAddr, /*buflen ? *buflen : -1*/Memory::Read_U32(sizeAddr), bufAddr); if (!g_Config.bEnableWlan) { return 0; } @@ -933,8 +1048,11 @@ static int sceNetAdhocctlGetScanInfo(u32 sizeAddr, u32 bufAddr) { peerlock.lock(); // Length Returner Mode - if (buf == NULL) *buflen = countAvailableNetworks() * sizeof(SceNetAdhocctlScanInfoEmu); - + if (buf == NULL) { + int availNetworks = countAvailableNetworks(); + *buflen = availNetworks * sizeof(SceNetAdhocctlScanInfoEmu); + DEBUG_LOG(SCENET, "NetworkList [Available: %i]", availNetworks); + } // Normal Information Mode else { // Clear Memory @@ -963,17 +1081,14 @@ static int sceNetAdhocctlGetScanInfo(u32 sizeAddr, u32 bufAddr) { // sceUtilityGetSystemParamInt(PSP_SYSTEMPARAM_ID_INT_ADHOC_CHANNEL, &buf[discovered].channel); // Fake Channel Number 1 on Automatic Channel - // if (buf[discovered].channel == 0) buf[discovered].channel = 1; - - //Always Fake Channel 1 - buf[discovered].channel = 1; + buf[discovered].channel = group->channel; //parameter.channel // Increase Discovery Counter discovered++; } // Link List - int i = 0; for (; i < discovered - 1; i++) { + for (int i = 0; i < discovered - 1; i++) { // Link Network buf[i].next = bufAddr + (sizeof(SceNetAdhocctlScanInfoEmu)*i) + sizeof(SceNetAdhocctlScanInfoEmu); // buf[i].next = &buf[i + 1]; } @@ -984,6 +1099,7 @@ static int sceNetAdhocctlGetScanInfo(u32 sizeAddr, u32 bufAddr) { // Fix Size *buflen = discovered * sizeof(SceNetAdhocctlScanInfoEmu); + DEBUG_LOG(SCENET, "NetworkList [Requested: %i][Discovered: %i]", requestcount, discovered); } // Multithreading Unlock @@ -1006,7 +1122,7 @@ static int sceNetAdhocctlGetScanInfo(u32 sizeAddr, u32 bufAddr) { static u32 sceNetAdhocctlAddHandler(u32 handlerPtr, u32 handlerArg) { bool foundHandler = false; u32 retval = 0; - struct AdhocctlHandler handler; + AdhocctlHandler handler; memset(&handler, 0, sizeof(handler)); while (adhocctlHandlers.find(retval) != adhocctlHandlers.end()) @@ -1044,12 +1160,9 @@ static u32 sceNetAdhocctlAddHandler(u32 handlerPtr, u32 handlerArg) { static u32 sceNetAdhocctlDisconnect() { // WLAN might be disabled in the middle of successfull multiplayer, but we still need to cleanup right? - INFO_LOG(SCENET, "sceNetAdhocctlDisconnect() at %08x [group=%s]", currentMIPS->pc, parameter.group_name.data); - /* - if (!g_Config.bEnableWlan) { - return 0; - } - */ + char grpName[9] = { 0 }; + memcpy(grpName, parameter.group_name.data, ADHOCCTL_GROUPNAME_LEN); // Copied to null-terminated var to prevent unexpected behaviour on Logs + INFO_LOG(SCENET, "sceNetAdhocctlDisconnect() at %08x [group=%s]", currentMIPS->pc, grpName); // Library initialized if (netAdhocctlInited) { @@ -1058,9 +1171,6 @@ static u32 sceNetAdhocctlDisconnect() { // Clear Network Name memset(¶meter.group_name, 0, sizeof(parameter.group_name)); - // Set Disconnected State - threadStatus = ADHOCCTL_STATE_DISCONNECTED; - // Set HUD Connection Status //setConnectionStatus(0); @@ -1076,34 +1186,37 @@ static u32 sceNetAdhocctlDisconnect() { ERROR_LOG(SCENET, "Socket error (%i) when sending", errno); } + // Set Disconnected State + threadStatus = ADHOCCTL_STATE_DISCONNECTED; + // Free Network Lock //_freeNetworkLock(); - - // Multithreading Lock - //peerlock.lock(); - - // Clear Peer List - freeFriendsRecursive(friends); - INFO_LOG(SCENET, "Cleared Peer List."); - - // Delete Peer Reference - friends = NULL; - - // Clear Group List - //freeGroupsRecursive(networks); - - // Delete Group Reference - //networks = NULL; - - // Multithreading Unlock - //peerlock.unlock(); } + + // Multithreading Lock + //peerlock.lock(); + + // Clear Peer List, since games are moving to a different a group when the mission started may be we shouldn't free all peers yet + int32_t peercount = 0; + timeoutFriendsRecursive(friends, &peercount); + INFO_LOG(SCENET, "Marked for Timedout Peer List (%i)", peercount); + // Delete Peer Reference + //friends = NULL; + + // Clear Group List + //freeGroupsRecursive(networks); + + // Delete Group Reference + //networks = NULL; + + // Multithreading Unlock + //peerlock.unlock(); // Notify Event Handlers (even if we weren't connected, not doing this will freeze games like God Eater, which expect this behaviour) - { - std::lock_guard adhocGuard(adhocEvtMtx); - adhocctlEvents.push_back({ ADHOCCTL_EVENT_DISCONNECT, 0 }); - } + notifyAdhocctlHandlers(ADHOCCTL_EVENT_DISCONNECT, 0); + + hleCheckCurrentCallbacks(); + // Return Success, some games might ignore returned value and always treat it as success, otherwise repeatedly calling this function return 0; } @@ -1115,9 +1228,9 @@ static u32 sceNetAdhocctlDisconnect() { static u32 sceNetAdhocctlDelHandler(u32 handlerID) { if (adhocctlHandlers.find(handlerID) != adhocctlHandlers.end()) { adhocctlHandlers.erase(handlerID); - WARN_LOG(SCENET, "UNTESTED sceNetAdhocctlDelHandler(%d): deleted handler %d", handlerID, handlerID); + WARN_LOG(SCENET, "UNTESTED sceNetAdhocctlDelHandler(%d)", handlerID); } else { - ERROR_LOG(SCENET, "UNTESTED sceNetAdhocctlDelHandler(%d): asked to delete invalid handler %d", handlerID, handlerID); + ERROR_LOG(SCENET, "UNTESTED sceNetAdhocctlDelHandler(%d): Invalid Handler ID", handlerID); } return 0; @@ -1127,14 +1240,23 @@ int sceNetAdhocctlTerm() { // WLAN might be disabled in the middle of successfull multiplayer, but we still need to cleanup right? INFO_LOG(SCENET, "sceNetAdhocctlTerm()"); - if (netAdhocMatchingInited) sceNetAdhocMatchingTerm(); + //if (netAdhocMatchingInited) sceNetAdhocMatchingTerm(); if (netAdhocctlInited) { - netAdhocctlInited = false; - friendFinderRunning = false; - if (friendFinderThread.joinable()) { - friendFinderThread.join(); + if (threadStatus != ADHOCCTL_STATE_DISCONNECTED) sceNetAdhocctlDisconnect(); + if (networkInited) { + // Terminate Adhoc Threads + friendFinderRunning = false; + if (friendFinderThread.joinable()) { + friendFinderThread.join(); + } } + // Clear Peer List + int32_t peercount = 0; + freeFriendsRecursive(friends, &peercount); + INFO_LOG(SCENET, "Cleared Peer List (%i)", peercount); + // Delete Peer Reference + friends = NULL; //May also need to clear Handlers adhocctlHandlers.clear(); // Free stuff here @@ -1146,16 +1268,15 @@ int sceNetAdhocctlTerm() { __KernelDeleteThread(threadAdhocID, SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocThread deleted"); threadAdhocID = 0; } -/*#ifdef _MSC_VER - WSACleanup(); // Might be better to call WSAStartup/WSACleanup from sceNetInit/sceNetTerm isn't? since it's the first/last network function being used, even better to put it in __NetInit/__NetShutdown as it's only called once -#endif*/ + netAdhocctlInited = false; } return 0; } static int sceNetAdhocctlGetNameByAddr(const char *mac, u32 nameAddr) { - DEBUG_LOG(SCENET, "UNTESTED sceNetAdhocctlGetNameByAddr(%s, %08x)", mac, nameAddr); + char tmpmac[18]; + DEBUG_LOG(SCENET, "UNTESTED sceNetAdhocctlGetNameByAddr(%s, %08x)", mac2str((SceNetEtherAddr*)mac, tmpmac), nameAddr); // Library initialized if (netAdhocctlInited) @@ -1244,7 +1365,8 @@ static int sceNetAdhocctlJoin(u32 scanInfoAddr) { } int sceNetAdhocctlGetPeerInfo(const char *mac, int size, u32 peerInfoAddr) { - VERBOSE_LOG(SCENET, "sceNetAdhocctlGetPeerInfo(%s, %i, %08x) at %08x", mac, size, peerInfoAddr, currentMIPS->pc); + char tmpmac[18]; + VERBOSE_LOG(SCENET, "sceNetAdhocctlGetPeerInfo(%s, %i, %08x) at %08x", mac2str((SceNetEtherAddr*)mac, tmpmac), size, peerInfoAddr, currentMIPS->pc); if (!g_Config.bEnableWlan) { return -1; } @@ -1272,7 +1394,7 @@ int sceNetAdhocctlGetPeerInfo(const char *mac, int size, u32 peerInfoAddr) { buf->nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; // last char need to be null-terminated char buf->mac_addr = *maddr; buf->ip_addr = addr.sin_addr.s_addr; // 0x11111111; - //buf->padding = 0x1111; //0; + buf->padding = 0; buf->last_recv = CoreTiming::GetGlobalTimeUsScaled(); // Success @@ -1294,7 +1416,7 @@ int sceNetAdhocctlGetPeerInfo(const char *mac, int size, u32 peerInfoAddr) { buf->nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; // last char need to be null-terminated char buf->mac_addr = *maddr; buf->ip_addr = peer->ip_addr; // 0x11111111; - //buf->padding = /*0;*/ 0x1111; + buf->padding = 0; buf->last_recv = peer->last_recv; //CoreTiming::GetGlobalTimeUsScaled(); //real_time_now()*1000000.0; //(uint64_t)time(NULL); //This timestamp is important issue on Dissidia 012 // Success @@ -1317,7 +1439,9 @@ int sceNetAdhocctlGetPeerInfo(const char *mac, int size, u32 peerInfoAddr) { * @return 0 on success or... ADHOCCTL_NOT_INITIALIZED, ADHOCCTL_INVALID_ARG, ADHOCCTL_BUSY */ int sceNetAdhocctlCreate(const char *groupName) { - INFO_LOG(SCENET, "sceNetAdhocctlCreate(%s) at %08x", groupName, currentMIPS->pc); + char grpName[9] = { 0 }; + memcpy(grpName, groupName, ADHOCCTL_GROUPNAME_LEN); // Copied to null-terminated var to prevent unexpected behaviour on Logs + INFO_LOG(SCENET, "sceNetAdhocctlCreate(%s) at %08x", grpName, currentMIPS->pc); if (!g_Config.bEnableWlan) { return -1; } @@ -1327,6 +1451,15 @@ int sceNetAdhocctlCreate(const char *groupName) { if (netAdhocctlInited) { // Valid Argument if (validNetworkName(groupNameStruct)) { + // Wait until Not connected + if (friendFinderRunning) { + int cnt = 0; + while ((threadStatus != ADHOCCTL_STATE_DISCONNECTED && threadStatus != ADHOCCTL_STATE_SCANNING) && (cnt < adhocDefaultTimeout)) { + sleep_ms(1); + cnt++; + } + } + // Disconnected State, may also need to check for Scanning state to prevent some games from failing to host a game session if ((threadStatus == ADHOCCTL_STATE_DISCONNECTED) || (threadStatus == ADHOCCTL_STATE_SCANNING)) { // Set Network Name @@ -1366,15 +1499,23 @@ int sceNetAdhocctlCreate(const char *groupName) { // Set HUD Connection Status //setConnectionStatus(1); + // Connected Event's mipscall need be executed before returning from sceNetAdhocctlCreate (or before the next sceNet function?) + notifyAdhocctlHandlers(ADHOCCTL_EVENT_CONNECT, 0); + // Wait for Status to be connected to prevent Ford Street Racing from Failed to create game session - if (friendFinderRunning) { + // TODO: Do this async while Delaying HLE Result + if (friendFinderRunning) { // This is thread-unsafe int cnt = 0; - while ((threadStatus != ADHOCCTL_STATE_CONNECTED) && (cnt < 5000)) { + while ((threadStatus != ADHOCCTL_STATE_CONNECTED) && (cnt < adhocDefaultTimeout)) { // This is thread-unsafe sleep_ms(1); cnt++; } } + //sceKernelDelayThreadCB(adhocEventDelayMS * 1000); + //hleCheckCurrentCallbacks(); + hleDelayResult(0, "give time to init/cleanup", adhocEventDelayMS * 1000); + // Return Success return 0; } @@ -1392,21 +1533,41 @@ int sceNetAdhocctlCreate(const char *groupName) { static int sceNetAdhocctlConnect(u32 ptrToGroupName) { if (Memory::IsValidAddress(ptrToGroupName)) { - INFO_LOG(SCENET, "sceNetAdhocctlConnect(groupName=%s) at %08x", Memory::GetCharPointer(ptrToGroupName), currentMIPS->pc); - return sceNetAdhocctlCreate(Memory::GetCharPointer(ptrToGroupName)); + const char* groupName = Memory::GetCharPointer(ptrToGroupName); + char grpName[9] = { 0 }; + memcpy(grpName, groupName, ADHOCCTL_GROUPNAME_LEN); // Copied to null-terminated var to prevent unexpected behaviour on Logs + + INFO_LOG(SCENET, "sceNetAdhocctlConnect(groupName=%s) at %08x", grpName, currentMIPS->pc); + return sceNetAdhocctlCreate(groupName); } return ERROR_NET_ADHOC_INVALID_ARG; // ERROR_NET_ADHOC_INVALID_ADDR; } +// Connect to the Adhoc control game mode (as a Host) static int sceNetAdhocctlCreateEnterGameMode(const char *groupName, int unknown, int playerNum, u32 macsAddr, int timeout, int unknown2) { - ERROR_LOG(SCENET, "UNIMPL sceNetAdhocctlCreateEnterGameMode(%s, %i, %i, %08x, %i, %i) at %08x", groupName, unknown, playerNum, macsAddr, timeout, unknown2, currentMIPS->pc); - return -1; + char grpName[9] = { 0 }; + memcpy(grpName, groupName, ADHOCCTL_GROUPNAME_LEN); // Copied to null-terminated var to prevent unexpected behaviour on Logs + + SceNetEtherAddr* addrs = NULL; // List of participating MAC addresses (started from host) + if (Memory::IsValidAddress(macsAddr)) { + addrs = PSPPointer::Create(macsAddr); + } + + ERROR_LOG(SCENET, "UNIMPL sceNetAdhocctlCreateEnterGameMode(%s, %i, %i, %08x, %i, %i) at %08x", grpName, unknown, playerNum, macsAddr, timeout, unknown2, currentMIPS->pc); + + return 0; } +// Connect to the Adhoc control game mode (as a Client) static int sceNetAdhocctlJoinEnterGameMode(const char *groupName, const char *macAddr, int timeout, int unknown2) { - ERROR_LOG(SCENET, "UNIMPL sceNetAdhocctlJoinEnterGameMode(%s, %s, %i, %i) at %08x", groupName, macAddr, timeout, unknown2, currentMIPS->pc); - return -1; + char grpName[9] = { 0 }; + memcpy(grpName, groupName, ADHOCCTL_GROUPNAME_LEN); // Copied to null-terminated var to prevent unexpected behaviour on Logs + + char tmpmac[18]; + ERROR_LOG(SCENET, "UNIMPL sceNetAdhocctlJoinEnterGameMode(%s, %s, %i, %i) at %08x", grpName, mac2str((SceNetEtherAddr*)macAddr, tmpmac), timeout, unknown2, currentMIPS->pc); + + return 0; } /** @@ -1422,14 +1583,18 @@ static int sceNetAdhocctlJoinEnterGameMode(const char *groupName, const char *ma */ int sceNetAdhocctlCreateEnterGameModeMin(const char *group_name, int game_type, int min_members, int num_members, u32 membersAddr, u32 timeout, int flag) { - ERROR_LOG(SCENET, "UNIMPL sceNetAdhocctlCreateEnterGameModeMin(%s, %i, %i, %i, %08x, %d, %i) at %08x", group_name, game_type, min_members, num_members, membersAddr, timeout, flag, currentMIPS->pc); + char grpName[9] = { 0 }; + memcpy(grpName, group_name, ADHOCCTL_GROUPNAME_LEN); // Copied to null-terminated var to prevent unexpected behaviour on Logs + ERROR_LOG(SCENET, "UNIMPL sceNetAdhocctlCreateEnterGameModeMin(%s, %i, %i, %i, %08x, %d, %i) at %08x", grpName, game_type, min_members, num_members, membersAddr, timeout, flag, currentMIPS->pc); // We don't really need the Minimum User Check - return sceNetAdhocctlCreateEnterGameMode(group_name, game_type, num_members, membersAddr, timeout, flag); + return sceNetAdhocctlCreateEnterGameMode(group_name, game_type, num_members, membersAddr, timeout, flag); //0; } int sceNetAdhocTerm() { // WLAN might be disabled in the middle of successfull multiplayer, but we still need to cleanup all the sockets right? + // Since Adhocctl & AdhocMatching uses Sockets & Threads we should terminate them also to release their resources + if (netAdhocMatchingInited) sceNetAdhocMatchingTerm(); if (netAdhocctlInited) sceNetAdhocctlTerm(); // Library is initialized @@ -1449,6 +1614,7 @@ int sceNetAdhocTerm() { // Unload Internet Modules (Just keep it in memory... unloading crashes?!) // if (_manage_modules != 0) sceUtilityUnloadModule(PSP_MODULE_NET_INET); // Library shutdown + netAdhocInited = false; return hleLogSuccessInfoI(SCENET, 0); } else { @@ -1493,7 +1659,7 @@ static int sceNetAdhocGetPdpStat(u32 structSize, u32 structAddr) { int i = 0; // Iterate Translation Table - int j = 0; for (; j < 255 && i < count; j++) + for (int j = 0; j < 255 && i < count; j++) { // Valid Socket Entry if (pdp[j] != NULL) @@ -1571,7 +1737,7 @@ static int sceNetAdhocGetPtpStat(u32 structSize, u32 structAddr) { int i = 0; // Iterate Sockets - int j = 0; for (; j < 255 && i < count; j++) { + for (int j = 0; j < 255 && i < count; j++) { // Active Socket if (ptp[j] != NULL) { // Copy Socket Data from internal Memory @@ -1621,7 +1787,8 @@ static int sceNetAdhocGetPtpStat(u32 structSize, u32 structAddr) { * @return Socket ID > 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_INVALID_ADDR, ADHOC_INVALID_PORT */ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, int dport, int bufsize, int rexmt_int, int rexmt_cnt, int unknown) { - INFO_LOG(SCENET, "sceNetAdhocPtpOpen(%s,%d,%s,%d,%d,%d,%d,%d)", srcmac, sport, dstmac,dport,bufsize, rexmt_int, rexmt_cnt, unknown); + char tmpmac[18]; + INFO_LOG(SCENET, "sceNetAdhocPtpOpen(%s, %d, %s, %d, %d, %d, %d, %d) at %08x", mac2str((SceNetEtherAddr*)srcmac, tmpmac), sport, mac2str((SceNetEtherAddr*)dstmac, tmpmac),dport,bufsize, rexmt_int, rexmt_cnt, unknown, currentMIPS->pc); if (!g_Config.bEnableWlan) { return 0; } @@ -1629,6 +1796,10 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, SceNetEtherAddr * daddr = (SceNetEtherAddr *)dstmac; // Library is initialized if (netAdhocInited) { + // Some games (ie. DBZ Shin Budokai 2) might be getting the saddr/srcmac content from SaveState and causing problems :( So we try to fix it here + if (saddr != NULL) { + getLocalMac(saddr); + } // Valid Addresses if (saddr != NULL && isLocalMAC(saddr) && daddr != NULL && !isBroadcastMAC(daddr)) { // Random Port required @@ -1670,7 +1841,7 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, // Bound Socket to local Port if (bind(tcpsocket, (sockaddr *)&addr, sizeof(addr)) == 0) { - // Update sport with the port assigned binternal->lport = ntohs(local.sin_port)y bind + // Update sport with the port assigned internal->lport = ntohs(local.sin_port) socklen_t len = sizeof(addr); if (getsockname(tcpsocket, (sockaddr *)&addr, &len) == 0) { sport = ntohs(addr.sin_port) - portOffset; @@ -1682,7 +1853,8 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, // Allocated Memory if (internal != NULL) { // Find Free Translator ID - int i = 0; for (; i < 255; i++) if (ptp[i] == NULL) break; + int i = 0; + for (; i < 255; i++) if (ptp[i] == NULL) break; // Found Free Translator ID if (i < 255) { @@ -1715,6 +1887,11 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, free(internal); } } + else { + ERROR_LOG(SCENET, "Socket error (%i) when binding port %u", errno, ntohs(addr.sin_port)); + I18NCategory* n = GetI18NCategory("Networking"); + host->NotifyUserMessage(std::string(n->T("Failed to Bind Port")) + " " + std::to_string(sport + portOffset) + "\n" + std::string(n->T("Please change your Port Offset")), 3.0, 0x0000ff); + } // Close Socket closesocket(tcpsocket); @@ -1756,8 +1933,12 @@ static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int if (Memory::IsValidAddress(peerPortPtr)) { port = (uint16_t *)Memory::GetPointer(peerPortPtr); } - - DEBUG_LOG(SCENET, "sceNetAdhocPtpAccept(%d,%08x,[%08x]=%u,%d,%u) at %08x", id, peerMacAddrPtr, peerPortPtr, port ? *port : -1, timeout, flag, currentMIPS->pc); + char tmpmac[18]; + if (flag == 0) { // Prevent spamming Debug Log with retries of non-bocking socket + DEBUG_LOG(SCENET, "sceNetAdhocPtpAccept(%d, [%08x]=%s, [%08x]=%u, %d, %u) at %08x", id, peerMacAddrPtr, mac2str(addr, tmpmac), peerPortPtr, port ? *port : -1, timeout, flag, currentMIPS->pc); + } else { + VERBOSE_LOG(SCENET, "sceNetAdhocPtpAccept(%d, [%08x]=%s, [%08x]=%u, %d, %u) at %08x", id, peerMacAddrPtr, mac2str(addr, tmpmac), peerPortPtr, port ? *port : -1, timeout, flag, currentMIPS->pc); + } if (!g_Config.bEnableWlan) { return 0; } @@ -1790,18 +1971,21 @@ static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int changeBlockingMode(socket->id,1); } + // TODO: Use a different thread (similar to sceIo) for recvfrom, recv & accept to prevent blocking-socket from blocking emulation // Accept Connection int newsocket = accept(socket->id, (sockaddr *)&peeraddr, &peeraddrlen); + int error = errno; // Blocking Behaviour - if (!flag && newsocket == -1) { + if (!flag && newsocket == SOCKET_ERROR) { // Get Start Time - uint32_t starttime = (uint32_t)(real_time_now()*1000000.0); + uint32_t starttime = (uint32_t)(real_time_now()*1000.0); // Retry until Timeout hits - while ((timeout == 0 ||((uint32_t)(real_time_now()*1000000.0) - starttime) < (uint32_t)timeout) && newsocket == -1) { + while ((timeout == 0 || ((uint32_t)(real_time_now()*1000.0) - starttime) < (uint32_t)timeout) && (newsocket == SOCKET_ERROR) && connectInProgress(error)) { // Accept Connection newsocket = accept(socket->id, (sockaddr *)&peeraddr, &peeraddrlen); + error = errno; // Wait a bit... sleep_ms(1); @@ -1809,8 +1993,12 @@ static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int } if (newsocket == SOCKET_ERROR) { - int error = errno; - DEBUG_LOG(SCENET, "sceNetAdhocPtpAccept[%i]: Socket Error (%i)", id, error); + if (flag == 0) { // Prevent spamming Debug Log with retries of non-bocking socket + DEBUG_LOG(SCENET, "sceNetAdhocPtpAccept[%i]: Socket Error (%i)", id, error); + } + else { + VERBOSE_LOG(SCENET, "sceNetAdhocPtpAccept[%i]: Socket Error (%i)", id, error); + } } // Restore Blocking Behaviour @@ -1839,7 +2027,8 @@ static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int // Allocated Memory if (internal != NULL) { // Find Free Translator ID - int i = 0; for (; i < 255; i++) if (ptp[i] == NULL) break; + int i = 0; + for (; i < 255; i++) if (ptp[i] == NULL) break; // Found Free Translator ID if (i < 255) { @@ -1876,8 +2065,7 @@ static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int // Add Port Forward to Router // sceNetPortOpen("TCP", internal->lport); - uint8_t * pip = (uint8_t *)&peeraddr.sin_addr.s_addr; - INFO_LOG(SCENET, "sceNetAdhocPtpAccept[%i->%i:%u]: Established (%u.%u.%u.%u:%u)", id, i+1, internal->lport, pip[0], pip[1], pip[2], pip[3], internal->pport); + INFO_LOG(SCENET, "sceNetAdhocPtpAccept[%i->%i:%u]: Established (%s:%u)", id, i+1, internal->lport, inet_ntoa(peeraddr.sin_addr), internal->pport); // Return Socket return i + 1; @@ -1941,7 +2129,7 @@ static int sceNetAdhocPtpConnect(int id, int timeout, int flag) { SceNetAdhocPtpStat * socket = ptp[id - 1]; // Valid Client Socket - if (socket->state == 0) { + if (socket->state == ADHOC_PTP_STATE_CLOSED) { // Target Address sockaddr_in sin; memset(&sin, 0, sizeof(sin)); @@ -1978,7 +2166,7 @@ static int sceNetAdhocPtpConnect(int id, int timeout, int flag) { } // Instant Connection (Lucky!) - if (connectresult == 0 || (connectresult == -1 && (errorcode == EISCONN /*|| errorcode == EALREADY)*/))) { + if (connectresult == 0 || (connectresult == SOCKET_ERROR && (errorcode == EISCONN /*|| errorcode == EALREADY)*/))) { // Set Connected State socket->state = ADHOC_PTP_STATE_ESTABLISHED; @@ -1988,21 +2176,23 @@ static int sceNetAdhocPtpConnect(int id, int timeout, int flag) { } // Connection in Progress - else if (connectresult == -1 && connectInProgress(errorcode)) { + else if (connectresult == SOCKET_ERROR && connectInProgress(errorcode)) { // Nonblocking Mode - if (flag) return ERROR_NET_ADHOC_WOULD_BLOCK; - + if (flag) { + //if (errorcode == EALREADY) return ERROR_NET_ADHOC_BUSY; + return ERROR_NET_ADHOC_WOULD_BLOCK; + } // Blocking Mode else { // Grab Connection Start Time - uint32_t starttime = (uint32_t)(real_time_now()*1000000.0); + uint32_t starttime = (uint32_t)(real_time_now()*1000.0); // Peer Information (for Connection-Polling) sockaddr_in peer; memset(&peer, 0, sizeof(peer)); socklen_t peerlen = sizeof(peer); // Wait for Connection - while ((timeout == 0 || ( (uint32_t)(real_time_now()*1000000.0) - starttime) < (uint32_t)timeout) && getpeername(socket->id, (sockaddr *)&peer, &peerlen) != 0) { + while ((timeout == 0 || ( (uint32_t)(real_time_now()*1000.0) - starttime) < (uint32_t)timeout) && getpeername(socket->id, (sockaddr *)&peer, &peerlen) != 0) { // Wait 1ms sleep_ms(1); } @@ -2012,8 +2202,7 @@ static int sceNetAdhocPtpConnect(int id, int timeout, int flag) { // Set Connected State socket->state = ADHOC_PTP_STATE_ESTABLISHED; - uint8_t * pip = (uint8_t *)&peer.sin_addr.s_addr; - INFO_LOG(SCENET, "sceNetAdhocPtpConnect[%i:%u]: Established (%u.%u.%u.%u:%u)", id, socket->lport, pip[0], pip[1], pip[2], pip[3], socket->pport); + INFO_LOG(SCENET, "sceNetAdhocPtpConnect[%i:%u]: Established (%s:%u)", id, socket->lport, inet_ntoa(peer.sin_addr), socket->pport); // Success return 0; @@ -2097,16 +2286,20 @@ static int sceNetAdhocPtpClose(int id, int unknown) { * @return Socket ID > 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_INVALID_ADDR, ADHOC_INVALID_PORT, ADHOC_SOCKET_ID_NOT_AVAIL, ADHOC_PORT_NOT_AVAIL, ADHOC_PORT_IN_USE, NET_NO_SPACE */ static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int rexmt_int, int rexmt_cnt, int backlog, int unk) { - INFO_LOG(SCENET, "sceNetAdhocPtpListen(%s,%d,%d,%d,%d,%d,%d)",srcmac,sport,bufsize,rexmt_int,rexmt_cnt,backlog,unk); + char tmpmac[18]; + INFO_LOG(SCENET, "sceNetAdhocPtpListen(%s, %d, %d, %d, %d, %d, %d) at %08x", mac2str((SceNetEtherAddr*)srcmac, tmpmac), sport,bufsize,rexmt_int,rexmt_cnt,backlog,unk, currentMIPS->pc); if (!g_Config.bEnableWlan) { return 0; } // Library is initialized SceNetEtherAddr * saddr = (SceNetEtherAddr *)srcmac; if (netAdhocInited) { + // Some games (ie. DBZ Shin Budokai 2) might be getting the saddr/srcmac content from SaveState and causing problems :( So we try to fix it here + if (saddr != NULL) { + getLocalMac(saddr); + } // Valid Address - if (saddr != NULL && isLocalMAC(saddr)) - { + if (saddr != NULL && isLocalMAC(saddr)) { // Random Port required if (sport == 0) { // Find unused Port @@ -2119,6 +2312,8 @@ static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int // Valid Ports if (!isPTPPortInUse(sport)) { + //sport 0 should be shifted back to 0 when using offset Phantasy Star Portable 2 use this + if (sport == 0) sport = -(int)portOffset; // Valid Arguments if (bufsize > 0 && rexmt_int > 0 && rexmt_cnt > 0 && backlog > 0) { @@ -2146,6 +2341,11 @@ static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int int iResult = 0; // Bound Socket to local Port if ((iResult = bind(tcpsocket, (sockaddr *)&addr, sizeof(addr))) == 0) { + // Update sport with the port assigned internal->lport = ntohs(local.sin_port) + socklen_t len = sizeof(addr); + if (getsockname(tcpsocket, (sockaddr*)&addr, &len) == 0) { + sport = ntohs(addr.sin_port) - portOffset; + } // Switch into Listening Mode if ((iResult = listen(tcpsocket, backlog)) == 0) { // Allocate Memory @@ -2154,7 +2354,8 @@ static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int // Allocated Memory if (internal != NULL) { // Find Free Translator ID - int i = 0; for (; i < 255; i++) if (ptp[i] == NULL) break; + int i = 0; + for (; i < 255; i++) if (ptp[i] == NULL) break; // Found Free Translator ID if (i < 255) { @@ -2189,6 +2390,10 @@ static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int } } } + else { + I18NCategory* n = GetI18NCategory("Networking"); + host->NotifyUserMessage(std::string(n->T("Failed to Bind Port")) + " " + std::to_string(sport + portOffset) + "\n" + std::string(n->T("Please change your Port Offset")), 3.0, 0x0000ff); + } if (iResult == SOCKET_ERROR) { int error = errno; @@ -2272,15 +2477,15 @@ static int sceNetAdhocPtpSend(int id, u32 dataAddr, u32 dataSizeAddr, int timeou // Save Length *len = sent; - uint8_t * smac = (uint8_t *)&socket->paddr; - INFO_LOG(SCENET, "sceNetAdhocPtpSend[%i:%u]: Sent %u bytes to %02X:%02X:%02X:%02X:%02X:%02X:%u", id, socket->lport, sent, smac[0], smac[1], smac[2], smac[3], smac[4], smac[5], socket->pport); + char tmpmac[18]; + DEBUG_LOG(SCENET, "sceNetAdhocPtpSend[%i:%u]: Sent %u bytes to %s:%u", id, socket->lport, sent, mac2str(&socket->paddr, tmpmac), socket->pport); // Return Success return 0; } // Non-Critical Error - else if (sent == -1 && error == EAGAIN) { + else if (sent == SOCKET_ERROR && error == EAGAIN) { // Blocking Situation if (flag) return ERROR_NET_ADHOC_WOULD_BLOCK; @@ -2322,7 +2527,7 @@ static int sceNetAdhocPtpSend(int id, u32 dataAddr, u32 dataSizeAddr, int timeou * @return 0 on success or... ADHOC_NOT_INITIALIZED, ADHOC_INVALID_ARG, ADHOC_INVALID_SOCKET_ID, ADHOC_SOCKET_DELETED, ADHOC_SOCKET_ALERTED, ADHOC_WOULD_BLOCK, ADHOC_TIMEOUT, ADHOC_THREAD_ABORTED, ADHOC_DISCONNECTED, NET_INTERNAL */ static int sceNetAdhocPtpRecv(int id, u32 dataAddr, u32 dataSizeAddr, int timeout, int flag) { - DEBUG_LOG(SCENET, "sceNetAdhocPtpRecv(%d,%08x,%08x,%d,%d)", id, dataAddr, dataSizeAddr, timeout, flag); + DEBUG_LOG(SCENET, "sceNetAdhocPtpRecv(%d,%08x,%08x,%d,%d) at %08x", id, dataAddr, dataSizeAddr, timeout, flag, currentMIPS->pc); if (!g_Config.bEnableWlan) { return 0; } @@ -2346,10 +2551,30 @@ static int sceNetAdhocPtpRecv(int id, u32 dataAddr, u32 dataSizeAddr, int timeou // Acquire Network Lock // _acquireNetworkLock(); + // TODO: Use a different thread (similar to sceIo) for recvfrom, recv & accept to prevent blocking-socket from blocking emulation + int received = 0; + int error = 0; + // Receive Data - changeBlockingMode(socket->id, flag); - int received = recv(socket->id, (char *)buf, *len, 0); - int error = errno; + changeBlockingMode(socket->id, 1); + received = recv(socket->id, (char*)buf, *len, 0); + error = errno; + + if (flag == 0) { + // Simulate blocking behaviour with non-blocking socket + uint32_t starttime = (uint32_t)(real_time_now() * 1000.0); + // Wait for Connection + while ((timeout == 0 || ((uint32_t)(real_time_now() * 1000.0) - starttime) < (uint32_t)timeout) && (received == SOCKET_ERROR) && connectInProgress(error)) { + received = recv(socket->id, (char*)buf, *len, 0); + error = errno; + // Wait 1ms + sleep_ms(1); + } + } + + if (received == SOCKET_ERROR) { + VERBOSE_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPtpRecv[%i:%u] [size=%i]", id, socket->lport, error, *len); + } changeBlockingMode(socket->id, 0); // Free Network Lock @@ -2360,15 +2585,15 @@ static int sceNetAdhocPtpRecv(int id, u32 dataAddr, u32 dataSizeAddr, int timeou // Save Length *len = received; - uint8_t * smac = (uint8_t *)&socket->paddr; - INFO_LOG(SCENET, "sceNetAdhocPtpRecv[%i:%u]: Received %u bytes from %02X:%02X:%02X:%02X:%02X:%02X:%u", id, socket->lport, received, smac[0], smac[1], smac[2], smac[3], smac[4], smac[5], socket->pport); + char tmpmac[18]; + DEBUG_LOG(SCENET, "sceNetAdhocPtpRecv[%i:%u]: Received %u bytes from %s:%u", id, socket->lport, received, mac2str(&socket->paddr, tmpmac), socket->pport); // Return Success return 0; } // Non-Critical Error - else if (received == -1 && error == EAGAIN) { + else if (received == SOCKET_ERROR /*&& error == EAGAIN*/) { // Blocking Situation if (flag) return ERROR_NET_ADHOC_WOULD_BLOCK; @@ -2412,6 +2637,31 @@ static int sceNetAdhocPtpFlush(int id, int timeout, int nonblock) { if (netAdhocInited) { // Valid Socket if (id > 0 && id <= 255 && ptp[id - 1] != NULL) { + // Cast Socket + SceNetAdhocPtpStat* socket = ptp[id - 1]; + + // Connected Socket + if (socket->state == ADHOC_PTP_STATE_ESTABLISHED) { + // Get original Nagle algo value + int n = 0; + socklen_t m = sizeof(n); + getsockopt(socket->id, IPPROTO_TCP, TCP_NODELAY, (char*)&n, &m); + + // Disable Nagle Algo to send immediately + int flag = 1; + setsockopt(socket->id, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(flag)); + + // Send Empty Data just to trigger Nagle on/off effect to flush the send buffer, Do we need to trigger this at all or is it automatically flushed? + changeBlockingMode(socket->id, nonblock); + int sent = send(socket->id, NULL, NULL, 0); + int error = errno; + changeBlockingMode(socket->id, 0); + + // Enable Nagle Algo + flag = n; //0; + setsockopt(socket->id, IPPROTO_TCP, TCP_NODELAY, (char*)&flag, sizeof(flag)); + } + // Dummy Result return 0; } @@ -2425,32 +2675,33 @@ static int sceNetAdhocPtpFlush(int id, int timeout, int nonblock) { static int sceNetAdhocGameModeCreateMaster(u32 data, int size) { ERROR_LOG(SCENET, "UNIMPL sceNetAdhocGameModeCreateMaster(%08x, %i) at %08x", data, size, currentMIPS->pc); - return -1; + return 0; } static int sceNetAdhocGameModeCreateReplica(const char *mac, u32 data, int size) { - ERROR_LOG(SCENET, "UNIMPL sceNetAdhocGameModeCreateReplica(%s, %08x, %i) at %08x", mac, data, size, currentMIPS->pc); - return -1; + char tmpmac[18]; + ERROR_LOG(SCENET, "UNIMPL sceNetAdhocGameModeCreateReplica(%s, %08x, %i) at %08x", mac2str((SceNetEtherAddr*)mac, tmpmac), data, size, currentMIPS->pc); + return 0; } static int sceNetAdhocGameModeUpdateMaster() { ERROR_LOG(SCENET, "UNIMPL sceNetAdhocGameModeUpdateMaster()"); - return -1; + return 0; } static int sceNetAdhocGameModeDeleteMaster() { ERROR_LOG(SCENET, "UNIMPL sceNetAdhocGameModeDeleteMaster()"); - return -1; + return 0; } static int sceNetAdhocGameModeUpdateReplica(int id, u32 infoAddr) { ERROR_LOG(SCENET, "UNIMPL sceNetAdhocGameModeUpdateReplica(%i, %08x)", id, infoAddr); - return -1; + return 0; } static int sceNetAdhocGameModeDeleteReplica(int id) { ERROR_LOG(SCENET, "UNIMPL sceNetAdhocGameModeDeleteReplica(%i)", id); - return -1; + return 0; } int sceNetAdhocGetSocketAlert(int id, u32 flagPtr) { @@ -2468,8 +2719,6 @@ int sceNetAdhocGetSocketAlert(int id, u32 flagPtr) { int sceNetAdhocMatchingStop(int matchingId) { WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingStop(%i) at %08x", matchingId, currentMIPS->pc); - //if (!g_Config.bEnableWlan) - // return -1; SceNetAdhocMatchingContext * item = findMatchingContext(matchingId); @@ -2485,8 +2734,16 @@ int sceNetAdhocMatchingStop(int matchingId) { } // Stop fake PSP Thread - //__KernelStopThread(item->matching_thid, SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocMatching stopped"); - //item->matchingThread->Terminate(); + if (item->matching_thid > 0) { + __KernelStopThread(item->matching_thid, SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocMatching stopped"); + __KernelDeleteThread(item->matching_thid, SCE_KERNEL_ERROR_THREAD_TERMINATED, "AdhocMatching deleted"); + /*item->matchingThread->Terminate(); + if (item->matchingThread && item->matchingThread->Stopped()) { + delete item->matchingThread; + item->matchingThread = nullptr; + }*/ + } + item->matching_thid = 0; // Multithreading Lock peerlock.lock(); @@ -2589,22 +2846,22 @@ int sceNetAdhocMatchingInit(u32 memsize) { } int sceNetAdhocMatchingTerm() { + WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingTerm()"); // Should we cleanup all created matching contexts first? just in case there are games that doesn't delete them before calling this if (netAdhocMatchingInited) { // Delete all Matching contexts - SceNetAdhocMatchingContext * next = NULL; + SceNetAdhocMatchingContext* next = NULL; SceNetAdhocMatchingContext * context = contexts; while (context != NULL) { next = context->next; - if (context->running) sceNetAdhocMatchingStop(context->id); + //if (context->running) sceNetAdhocMatchingStop(context->id); sceNetAdhocMatchingDelete(context->id); context = next; } + contexts = NULL; } - WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingTerm()"); netAdhocMatchingInited = false; - return 0; } @@ -2624,11 +2881,21 @@ static int sceNetAdhocMatchingCreate(int mode, int maxnum, int port, int rxbufle // Valid Member Limit if (maxnum > 1 && maxnum <= 16) { // Valid Receive Buffer size - if (rxbuflen >= 1024) { + if (rxbuflen >= 1) { //1024 //200 on DBZ Shin Budokai 2 // Valid Arguments if (mode >= 1 && mode <= 3) { + // Wait until Adhoc is fully connected (mipscall of ADHOCCTL_EVENT_CONNECT event is fully executed ?) + if (friendFinderRunning) { + int cnt = 0; + while ((threadStatus != ADHOCCTL_STATE_CONNECTED) && (cnt < adhocDefaultTimeout)) { + sleep_ms(1); + cnt++; + } + } + // Iterate Matching Contexts - SceNetAdhocMatchingContext * item = contexts; for (; item != NULL; item = item->next) { + SceNetAdhocMatchingContext * item = contexts; + for (; item != NULL; item = item->next) { // Port Match found if (item->port == port) return ERROR_NET_ADHOC_MATCHING_PORT_IN_USE; } @@ -2679,11 +2946,6 @@ static int sceNetAdhocMatchingCreate(int mode, int maxnum, int port, int rxbufle context->eventlock = new std::recursive_mutex; context->inputlock = new std::recursive_mutex; - // Create fake thread - //#define PSP_THREAD_ATTR_KERNEL 0x00001000 // PSP_THREAD_ATTR_KERNEL is located in sceKernelThread.cpp instead of sceKernelThread.h :( - //context->matching_thid = __KernelCreateThreadInternal("AdhocMatching", 0, idleThreadHackAddr, 0x7f, 4096, PSP_THREAD_ATTR_KERNEL); - //context->matchingThread = new HLEHelperThread("AdhocMatching", "AdhocMatchingMod", "AdhocMatchingFunc", 0x7f, 4096); //8192 - // Multithreading Lock peerlock.lock(); //contextlock.lock(); @@ -2732,7 +2994,7 @@ static int sceNetAdhocMatchingCreate(int mode, int maxnum, int port, int rxbufle return ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED; } -// TODO: Should we execute the callback used to create the Matching Id if it's a valid address? +// This should be similar with sceNetAdhocMatchingStart2 but using USER_PARTITION_ID (2) for PartitionId params static int sceNetAdhocMatchingStart(int matchingId, int evthPri, int evthStack, int inthPri, int inthStack, int optLen, u32 optDataAddr) { WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingStart(%i, %i, %i, %i, %i, %i, %08x) at %08x", matchingId, evthPri, evthStack, inthPri, inthStack, optLen, optDataAddr, currentMIPS->pc); if (!g_Config.bEnableWlan) @@ -2771,9 +3033,14 @@ static int sceNetAdhocMatchingStart(int matchingId, int evthPri, int evthStack, peer->state = PSP_ADHOC_MATCHING_PEER_P2P; }*/ - // Start the Fake PSP Thread - //__KernelStartThread(item->matching_thid, 0, 0); - //item->matchingThread->Start(0, 0); + // Create & Start the Fake PSP Thread + std::string thrname = std::string("MatchingThr") + std::to_string(matchingId); + item->matching_thid = __KernelCreateThread(thrname.c_str(), __KernelGetCurThreadModuleId(), matchingThreadHackAddr, evthPri, evthStack, PSP_THREAD_ATTR_USER, 0, true); + //item->matchingThread = new HLEHelperThread(thrname, "sceNetAdhocMatching", "AdhocMatchingFunc", inthPri, inthStack); + if (item->matching_thid > 0) { + __KernelStartThread(item->matching_thid, 0, 0); + //item->matchingThread->Start(0, 0); + } //Create the threads if (!item->eventRunning) { @@ -2793,11 +3060,88 @@ static int sceNetAdhocMatchingStart(int matchingId, int evthPri, int evthStack, // Multithreading Unlock peerlock.unlock(); + sleep_ms(adhocEventPollDelayMS); + //hleDelayResult(0, "give some time", adhocMatchingEventDelayMS * 1000); // Give a little time to make sure matching Threads are ready before the game use the next sceNet functions, should've checked for status instead of guessing the time? + return 0; } +// With params for Partition ID for the event & input handler stack +static int sceNetAdhocMatchingStart2(int matchingId, int evthPri, int evthPartitionId, int evthStack, int inthPri, int inthPartitionId, int inthStack, int optLen, u32 optDataAddr) { + WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingStart2(%i, %i, %i, %i, %i, %i, %i, %i, %08x) at %08x", matchingId, evthPri, evthPartitionId, evthStack, inthPri, inthPartitionId, inthStack, optLen, optDataAddr, currentMIPS->pc); + if (!g_Config.bEnableWlan) + return -1; + + // Multithreading Lock + peerlock.lock(); + + SceNetAdhocMatchingContext* item = findMatchingContext(matchingId); + + if (item != NULL) { + //sceNetAdhocMatchingSetHelloOpt(matchingId, optLen, optDataAddr); //SetHelloOpt only works when context is running + if ((optLen > 0) && Memory::IsValidAddress(optDataAddr)) { + // Allocate the memory and copy the content + if (item->hello != NULL) free(item->hello); + item->hello = (uint8_t*)malloc(optLen); + if (item->hello != NULL) { + Memory::Memcpy(item->hello, optDataAddr, optLen); + item->hellolen = optLen; + item->helloAddr = optDataAddr; + } + //else return ERROR_NET_ADHOC_MATCHING_NO_SPACE; //Faking success to prevent GTA:VCS from stuck unable to choose host/join menu + } + //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 & Start the Fake PSP Thread + std::string thrname = std::string("MatchingThr") + std::to_string(matchingId); + item->matching_thid = __KernelCreateThread(thrname.c_str(), __KernelGetCurThreadModuleId(), matchingThreadHackAddr, evthPri, evthStack, PSP_THREAD_ATTR_USER, 0, true); + //item->matchingThread = new HLEHelperThread(thrname, "sceNetAdhocMatching", "AdhocMatchingFunc", inthPri, inthStack); + if (item->matching_thid > 0) { + __KernelStartThread(item->matching_thid, 0, 0); + //item->matchingThread->Start(0, 0); + } + + //Create the threads + if (!item->eventRunning) { + item->eventRunning = true; + item->eventThread = std::thread(matchingEventThread, matchingId); + } + if (!item->inputRunning) { + item->inputRunning = true; + item->inputThread = std::thread(matchingInputThread, matchingId); + } + + item->running = 1; + netAdhocMatchingStarted++; + } + //else return ERROR_NET_ADHOC_MATCHING_INVALID_ID; //Faking success to prevent GTA:VCS from stuck unable to choose host/join menu + + // Multithreading Unlock + peerlock.unlock(); + + sleep_ms(adhocEventPollDelayMS); + //hleDelayResult(0,"give some time",adhocEventDelayMS*1000); // Give a little time to make sure matching Threads are ready before the game use the next sceNet functions, should've checked for status instead of guessing the time? + + return 0; +} + + static int sceNetAdhocMatchingSelectTarget(int matchingId, const char *macAddress, int optLen, u32 optDataPtr) { - WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingSelectTarget(%i, %s, %i, %08x) at %08x", matchingId, macAddress, optLen, optDataPtr, currentMIPS->pc); + char tmpmac[18]; + WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingSelectTarget(%i, %s, %i, %08x) at %08x", matchingId, mac2str((SceNetEtherAddr*)macAddress, tmpmac), optLen, optDataPtr, currentMIPS->pc); if (!g_Config.bEnableWlan) return -1; @@ -2950,7 +3294,8 @@ static int sceNetAdhocMatchingSelectTarget(int matchingId, const char *macAddres } int sceNetAdhocMatchingCancelTargetWithOpt(int matchingId, const char *macAddress, int optLen, u32 optDataPtr) { - WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingCancelTargetWithOpt(%i, %s, %i, %08x) at %08x", matchingId, macAddress, optLen, optDataPtr, currentMIPS->pc); + char tmpmac[18]; + WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingCancelTargetWithOpt(%i, %s, %i, %08x) at %08x", matchingId, mac2str((SceNetEtherAddr*)macAddress, tmpmac), optLen, optDataPtr, currentMIPS->pc); if (!g_Config.bEnableWlan) return -1; @@ -3029,7 +3374,8 @@ int sceNetAdhocMatchingCancelTargetWithOpt(int matchingId, const char *macAddres } int sceNetAdhocMatchingCancelTarget(int matchingId, const char *macAddress) { - WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingCancelTarget(%i, %s)", matchingId, macAddress); + char tmpmac[18]; + WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingCancelTarget(%i, %s)", matchingId, mac2str((SceNetEtherAddr*)macAddress, tmpmac)); if (!g_Config.bEnableWlan) return -1; return sceNetAdhocMatchingCancelTargetWithOpt(matchingId, macAddress, 0, 0); @@ -3067,7 +3413,7 @@ int sceNetAdhocMatchingGetHelloOpt(int matchingId, u32 optLenAddr, u32 optDataAd } int sceNetAdhocMatchingSetHelloOpt(int matchingId, int optLenAddr, u32 optDataAddr) { - DEBUG_LOG(SCENET, "UNTESTED sceNetAdhocMatchingSetHelloOpt(%i, %i, %08x) at %08x", matchingId, optLenAddr, optDataAddr, currentMIPS->pc); + VERBOSE_LOG(SCENET, "UNTESTED sceNetAdhocMatchingSetHelloOpt(%i, %i, %08x) at %08x", matchingId, optLenAddr, optDataAddr, currentMIPS->pc); if (!g_Config.bEnableWlan) return -1; @@ -3182,8 +3528,9 @@ static int sceNetAdhocMatchingGetMembers(int matchingId, u32 sizeAddr, u32 buf) buf2 = (SceNetAdhocMatchingMemberInfoEmu *)Memory::GetPointer(buf); } - // Number of Connected Peers - uint32_t peercount = countConnectedPeers(context); + // Number of Connected Peers, should we exclude timeout members? + bool excludeTimedout = false; + uint32_t peercount = countConnectedPeers(context, excludeTimedout); // Calculate Connected Peer Bytesize int available = sizeof(SceNetAdhocMatchingMemberInfoEmu) * peercount; @@ -3193,6 +3540,7 @@ static int sceNetAdhocMatchingGetMembers(int matchingId, u32 sizeAddr, u32 buf) { // Get Connected Peer Count *buflen = available; + DEBUG_LOG(SCENET, "MemberList [Connected: %i]", peercount); } // Normal Mode @@ -3226,7 +3574,7 @@ static int sceNetAdhocMatchingGetMembers(int matchingId, u32 sizeAddr, u32 buf) if (context->mode == PSP_ADHOC_MATCHING_MODE_P2P) { // Find P2P Brother - SceNetAdhocMatchingMemberInternal * p2p = findP2P(context); + SceNetAdhocMatchingMemberInternal * p2p = findP2P(context, excludeTimedout); // P2P Brother found if (p2p != NULL) @@ -3240,8 +3588,11 @@ static int sceNetAdhocMatchingGetMembers(int matchingId, u32 sizeAddr, u32 buf) else { // Iterate Peer List - SceNetAdhocMatchingMemberInternal * peer = context->peerlist; for (; peer != NULL && filledpeers < requestedpeers; peer = peer->next) + SceNetAdhocMatchingMemberInternal * peer = context->peerlist; + for (; peer != NULL && filledpeers < requestedpeers; peer = peer->next) { + // Should we exclude timedout members? + if (!excludeTimedout || peer->lastping != 0) // Parent Mode if (context->mode == PSP_ADHOC_MATCHING_MODE_PARENT) { @@ -3269,8 +3620,7 @@ static int sceNetAdhocMatchingGetMembers(int matchingId, u32 sizeAddr, u32 buf) } // Link Result List - int i = 0; - for (; i < filledpeers - 1; i++) + for (int i = 0; i < filledpeers - 1; i++) { // Link Next Element //buf2[i].next = &buf2[i + 1]; @@ -3282,6 +3632,7 @@ static int sceNetAdhocMatchingGetMembers(int matchingId, u32 sizeAddr, u32 buf) // Fix Buffer Size *buflen = sizeof(SceNetAdhocMatchingMemberInfoEmu) * filledpeers; + DEBUG_LOG(SCENET, "MemberList [Requested: %i][Discovered: %i]", requestedpeers, filledpeers); } // Return Success @@ -3301,7 +3652,8 @@ static int sceNetAdhocMatchingGetMembers(int matchingId, u32 sizeAddr, u32 buf) } int sceNetAdhocMatchingSendData(int matchingId, const char *mac, int dataLen, u32 dataAddr) { - WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingSendData(%i, %s, %i, %08x)", matchingId, mac, dataLen, dataAddr); + char tmpmac[18]; + WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingSendData(%i, %s, %i, %08x)", matchingId, mac2str((SceNetEtherAddr*)mac, tmpmac), dataLen, dataAddr); if (!g_Config.bEnableWlan) return -1; @@ -3377,7 +3729,8 @@ int sceNetAdhocMatchingSendData(int matchingId, const char *mac, int dataLen, u3 } int sceNetAdhocMatchingAbortSendData(int matchingId, const char *mac) { - WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingAbortSendData(%i, %s)", matchingId, mac); + char tmpmac[18]; + WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingAbortSendData(%i, %s)", matchingId, mac2str((SceNetEtherAddr*)mac, tmpmac)); if (!g_Config.bEnableWlan) return -1; @@ -3436,13 +3789,14 @@ int sceNetAdhocMatchingAbortSendData(int matchingId, const char *mac) { return ERROR_NET_ADHOC_MATCHING_NOT_INITIALIZED; } +// Get the maximum memory usage by the matching library static int sceNetAdhocMatchingGetPoolMaxAlloc() { ERROR_LOG(SCENET, "UNIMPL sceNetAdhocMatchingGetPoolMaxAlloc()"); if (!g_Config.bEnableWlan) return -1; // Lazy way out - hardcoded return value - return (50 * 1024); + return fakePoolSize/2; // (50 * 1024); } int sceNetAdhocMatchingGetPoolStat(u32 poolstatPtr) { @@ -3461,8 +3815,8 @@ int sceNetAdhocMatchingGetPoolStat(u32 poolstatPtr) { { // Fill Poolstat with Fake Data poolstat->pool = fakePoolSize; - poolstat->maximum = fakePoolSize / 8 * 6; - poolstat->free = fakePoolSize / 8 * 7; + poolstat->maximum = fakePoolSize / 2; // Max usage faked to halt the pool + poolstat->free = fakePoolSize - poolstat->maximum; // Return Success return 0; @@ -3478,12 +3832,13 @@ int sceNetAdhocMatchingGetPoolStat(u32 poolstatPtr) { void __NetTriggerCallbacks() { - { - std::lock_guard adhocGuard(adhocEvtMtx); + std::lock_guard adhocGuard(adhocEvtMtx); + int delayus = 1000; //adhocEventPollDelayMS * 1000; - for (auto ¶ms : adhocctlEvents) + /*if (__KernelGetCurThread() == threadAdhocID && (!__IsInInterrupt() && __KernelIsDispatchEnabled() && !__KernelInCallback()) && IsAdhocctlInCallback() == 0) { + for (auto& params : adhocctlEvents) //(auto params = adhocctlEvents.rbegin(); params != adhocctlEvents.rend(); ++params) { - int flags = params.first; + int flags = params.first; //params->first; int error = params.second; u32_le args[3] = { 0, 0, 0 }; args[0] = flags; @@ -3491,34 +3846,96 @@ void __NetTriggerCallbacks() for (std::map::iterator it = adhocctlHandlers.begin(); it != adhocctlHandlers.end(); ++it) { args[2] = it->second.argument; - __KernelSwitchToThread(threadAdhocID, "AdhocctlHandler Mipscall"); // it's not guaranteed to switch successfully tho - //while (__KernelInCallback()) sleep_ms(1); - __KernelDirectMipsCall(it->second.entryPoint, NULL, args, 3, false); + AfterAdhocMipsCall* after = (AfterAdhocMipsCall*)__KernelCreateAction(actionAfterAdhocMipsCall); + after->SetData(it->first, flags, args[2]); + SetAdhocctlInCallback(true); //IsAdhocctlInCB++; // + __KernelDirectMipsCall(it->second.entryPoint, after, args, 3, false); } } - adhocctlEvents.clear(); // We should only clear this after making sure all callbacks are placed on the right thread tho + adhocctlEvents.clear(); + }*/ + + auto params = adhocctlEvents.begin(); + if (params != adhocctlEvents.end()) + { + u32 flags = params->first; + u32 error = params->second; + u32_le args[3] = { 0, 0, 0 }; + args[0] = flags; + args[1] = error; - for (auto ¶m : matchingEvents) - { - u32_le *args = (u32_le*)¶m; - AfterMatchingMipsCall *after = (AfterMatchingMipsCall *) __KernelCreateAction(actionAfterMatchingMipsCall); - after->SetContextID(args[0], args[1], args[2]); - // Need to make sure currentThread is AdhocMatching's eventThread before calling the callback, and not in the middle of callback - __KernelSwitchToThread(threadAdhocID, "AdhocMatchingEvent Mipscall"); // it's not guaranteed to switch successfully tho - //while (__KernelInCallback()) sleep_ms(1); - __KernelDirectMipsCall(args[5], after, args, 5, false); + if (__KernelGetCurThread() == threadAdhocID && (!__IsInInterrupt() && __KernelIsDispatchEnabled() && !__KernelInCallback()) && IsAdhocctlInCallback() == 0) { // IsAdhocctlInCB + for (std::map::iterator it = adhocctlHandlers.begin(); it != adhocctlHandlers.end(); ++it) { + DEBUG_LOG(SCENET, "AdhocctlCallback: EVENT[%i] [ID=%i]", flags, it->first); + args[2] = it->second.argument; + AfterAdhocMipsCall* after = (AfterAdhocMipsCall*)__KernelCreateAction(actionAfterAdhocMipsCall); + after->SetData(it->first, flags, args[2]); + SetAdhocctlInCallback(true); //IsAdhocctlInCB++; // + __KernelDirectMipsCall(it->second.entryPoint, after, args, 3, true); + } + adhocctlEvents.pop_front(); + delayus = adhocEventDelayMS * 1000; } - matchingEvents.clear(); // We should only clear this after making sure all callbacks are placed on the right thread tho } - //magically make this work - hleDelayResult(0, "Prevent Adhoc thread from blocking", 1000); + + // Must be delayed long enough whenever there is a pending callback. Should it be 100-500ms for Adhocctl Events? or Not Less than the delays on sceNetAdhocctl HLE? + sceKernelDelayThreadCB(delayus); + //sceKernelDelayThreadCB(adhocEventDelayMS * 1000); + //hleCheckCurrentCallbacks(); + //hleDelayResult(0, "Prevent Adhoc thread from blocking", delayus); + //hleDelayResult(0, "Prevent Adhoc thread from blocking", adhocEventDelayMS * 1000); +} + +void __NetMatchingCallbacks() +{ + std::lock_guard adhocGuard(adhocEvtMtx); + int delayus = 1000; // adhocEventPollDelayMS * 1000; //adhocMatchingEventPollDelayMS * 1000; + + /*if (!__IsInInterrupt() && __KernelIsDispatchEnabled() && !__KernelInCallback()) { + for (auto& params : matchingEvents) //(auto ¶ms = matchingEvents.rbegin(); params != matchingEvents.rend(); ++params) + { + u32_le* args = (u32_le*)¶ms; //(u32_le*)& (*params); + auto context = findMatchingContext(args[0]); + //if (__KernelGetCurThread() == context->matching_thid && !IsMatchingInCallback(context)) + { + AfterMatchingMipsCall* after = (AfterMatchingMipsCall*)__KernelCreateAction(actionAfterMatchingMipsCall); + after->SetData(args[0], args[1], args[2]); + __KernelDirectMipsCall(args[5], after, args, 5, false); + } + } + matchingEvents.clear(); + }*/ + + auto params = matchingEvents.begin(); + if (params != matchingEvents.end()) + { + u32_le* args = (u32_le*)&(*params); + auto context = findMatchingContext(args[0]); + + if (__KernelGetCurThread() == context->matching_thid && (!__IsInInterrupt() && __KernelIsDispatchEnabled() && !__KernelInCallback()) && !IsMatchingInCallback(context)) { + DEBUG_LOG(SCENET, "AdhocMatchingCallback: EVENT[%i] [ID=%i]", args[1], args[0]); + AfterMatchingMipsCall* after = (AfterMatchingMipsCall*)__KernelCreateAction(actionAfterMatchingMipsCall); + after->SetData(args[0], args[1], args[2]); + SetMatchingInCallback(context, true); + __KernelDirectMipsCall(args[5], after, args, 5, true); + matchingEvents.pop_front(); + delayus = adhocMatchingEventDelayMS * 1000; //adhocEventPollDelayMS * 1000; + } + } + + // Must be delayed long enough whenever there is a pending callback. Should it be 10-100ms for Matching Events? or Not Less than the delays on sceNetAdhocMatching HLE? + sceKernelDelayThreadCB(delayus); + //sceKernelDelayThreadCB(adhocMatchingEventDelayMS * 1000); + //hleCheckCurrentCallbacks(); + //hleDelayResult(0, "Prevent AdhocMatching thread from blocking", delayus); + //hleDelayResult(0, "Prevent Adhoc thread from blocking", adhocEventPollDelayMS * 1000); } const HLEFunction sceNetAdhoc[] = { {0XE1D621D7, &WrapU_V, "sceNetAdhocInit", 'x', "" }, {0XA62C6F57, &WrapI_V, "sceNetAdhocTerm", 'i', "" }, {0X0AD043ED, &WrapI_U, "sceNetAdhocctlConnect", 'i', "x" }, - {0X6F92741B, &WrapI_CUIU, "sceNetAdhocPdpCreate", 'i', "sxix" }, + {0X6F92741B, &WrapI_CIIU, "sceNetAdhocPdpCreate", 'i', "siix" }, {0XABED3790, &WrapI_ICUVIII, "sceNetAdhocPdpSend", 'i', "isxpiii" }, {0XDFE53E03, &WrapI_IVVVVUI, "sceNetAdhocPdpRecv", 'i', "ippppxi" }, {0X7F27BB5E, &WrapI_II, "sceNetAdhocPdpDelete", 'i', "ii" }, @@ -3550,6 +3967,7 @@ const HLEFunction sceNetAdhocMatching[] = { {0X7945ECDA, &WrapI_V, "sceNetAdhocMatchingTerm", 'i', "" }, {0XCA5EDA6F, &WrapI_IIIIIIIIU, "sceNetAdhocMatchingCreate", 'i', "iiiiiiiix"}, {0X93EF3843, &WrapI_IIIIIIU, "sceNetAdhocMatchingStart", 'i', "iiiiiix" }, + {0xE8454C65, &WrapI_IIIIIIIIU, "sceNetAdhocMatchingStart2", 'i', "iiiiiiiix"}, {0X32B156B3, &WrapI_I, "sceNetAdhocMatchingStop", 'i', "i" }, {0XF16EAF4F, &WrapI_I, "sceNetAdhocMatchingDelete", 'i', "i" }, {0X5E3D4B79, &WrapI_ICIU, "sceNetAdhocMatchingSelectTarget", 'i', "isix" }, @@ -3562,6 +3980,8 @@ const HLEFunction sceNetAdhocMatching[] = { {0XEC19337D, &WrapI_IC, "sceNetAdhocMatchingAbortSendData", 'i', "is" }, {0X40F8F435, &WrapI_V, "sceNetAdhocMatchingGetPoolMaxAlloc", 'i', "" }, {0X9C5CFB7D, &WrapI_U, "sceNetAdhocMatchingGetPoolStat", 'i', "x" }, + // Fake function for PPSSPP's use. + {0X756E6F00, &WrapV_V<__NetMatchingCallbacks>, "__NetMatchingCallbacks", 'v', "" }, }; static int sceNetAdhocctlExitGameMode() { @@ -3571,7 +3991,12 @@ static int sceNetAdhocctlExitGameMode() { static int sceNetAdhocctlGetGameModeInfo(u32 infoAddr) { ERROR_LOG(SCENET, "UNIMPL sceNetAdhocctlGetGameModeInfo(%08x)", infoAddr); - return -1; + + SceNetAdhocctlGameModeInfo* gmInfo = NULL; + if (Memory::IsValidAddress(infoAddr)) gmInfo = (SceNetAdhocctlGameModeInfo*)Memory::GetPointer(infoAddr); + // TODO: Writes number of participants and each participating MAC address into infoAddr/gmInfo + + return 0; } static int sceNetAdhocctlGetPeerList(u32 sizeAddr, u32 bufAddr) { @@ -3580,7 +4005,7 @@ static int sceNetAdhocctlGetPeerList(u32 sizeAddr, u32 bufAddr) { SceNetAdhocctlPeerInfoEmu *buf = NULL; if (Memory::IsValidAddress(bufAddr)) buf = (SceNetAdhocctlPeerInfoEmu *)Memory::GetPointer(bufAddr); - DEBUG_LOG(SCENET, "sceNetAdhocctlGetPeerList([%08x]=%i, %08x) at %08x", sizeAddr, buflen ? *buflen : -1, bufAddr, currentMIPS->pc); + DEBUG_LOG(SCENET, "sceNetAdhocctlGetPeerList([%08x]=%i, %08x) at %08x", sizeAddr, /*buflen ? *buflen : -1*/Memory::Read_U32(sizeAddr), bufAddr, currentMIPS->pc); if (!g_Config.bEnableWlan) { return -1; } @@ -3593,8 +4018,11 @@ static int sceNetAdhocctlGetPeerList(u32 sizeAddr, u32 bufAddr) { peerlock.lock(); // Length Calculation Mode - if (buf == NULL) *buflen = getActivePeerCount() * sizeof(SceNetAdhocctlPeerInfoEmu); - + if (buf == NULL) { + int activePeers = getActivePeerCount(); + *buflen = activePeers * sizeof(SceNetAdhocctlPeerInfoEmu); + DEBUG_LOG(SCENET, "PeerList [Active: %i]", activePeers); + } // Normal Mode else { // Discovery Counter @@ -3613,20 +4041,23 @@ static int sceNetAdhocctlGetPeerList(u32 sizeAddr, u32 bufAddr) { // Iterate Peers for (; peer != NULL && discovered < requestcount; peer = peer->next) { - // Fake Receive Time - if (peer->last_recv != 0) peer->last_recv = CoreTiming::GetGlobalTimeUsScaled(); - - // Copy Peer Info - buf[discovered].nickname = peer->nickname; - buf[discovered].mac_addr = peer->mac_addr; - buf[discovered].ip_addr = peer->ip_addr; - buf[discovered].last_recv = peer->last_recv; - discovered++; + // Exclude Soon to be timedout peers + if (peer->last_recv != 0) { + // Fake Receive Time + //if (peer->last_recv != 0) + peer->last_recv = CoreTiming::GetGlobalTimeUsScaled(); + // Copy Peer Info + buf[discovered].nickname = peer->nickname; + buf[discovered].mac_addr = peer->mac_addr; + buf[discovered].ip_addr = peer->ip_addr; + buf[discovered].last_recv = peer->last_recv; + discovered++; + } } // Link List - int i = 0; for (; i < discovered - 1; i++) { + for (int i = 0; i < discovered - 1; i++) { // Link Network buf[i].next = bufAddr+(sizeof(SceNetAdhocctlPeerInfoEmu)*i) + sizeof(SceNetAdhocctlPeerInfoEmu); } @@ -3636,6 +4067,7 @@ static int sceNetAdhocctlGetPeerList(u32 sizeAddr, u32 bufAddr) { // Fix Size *buflen = discovered * sizeof(SceNetAdhocctlPeerInfoEmu); + DEBUG_LOG(SCENET, "PeerList [Requested: %i][Discovered: %i]", requestcount, discovered); } // Multithreading Unlock @@ -3656,8 +4088,12 @@ static int sceNetAdhocctlGetPeerList(u32 sizeAddr, u32 bufAddr) { static int sceNetAdhocctlGetAddrByName(const char *nickName, u32 sizeAddr, u32 bufAddr) { s32_le *buflen = NULL; //int32_t if (Memory::IsValidAddress(sizeAddr)) buflen = (s32_le *)Memory::GetPointer(sizeAddr); + + char nckName[ADHOCCTL_NICKNAME_LEN]; + memcpy(nckName, nickName, ADHOCCTL_NICKNAME_LEN); // Copied to null-terminated var to prevent unexpected behaviour on Logs + nckName[ADHOCCTL_NICKNAME_LEN - 1] = 0; - WARN_LOG(SCENET, "UNTESTED sceNetAdhocctlGetPeerList(%s, [%08x]=%i, %08x)", nickName, sizeAddr, buflen ? *buflen : -1, bufAddr); + WARN_LOG(SCENET, "UNTESTED sceNetAdhocctlGetAddrByName(%s, [%08x]=%i/%i, %08x)", nckName, sizeAddr, buflen ? *buflen : -1, sizeof(SceNetAdhocctlPeerInfoEmu), bufAddr); // Library initialized if (netAdhocctlInited) @@ -3727,7 +4163,7 @@ static int sceNetAdhocctlGetAddrByName(const char *nickName, u32 sizeAddr, u32 b } // Link List - int i = 0; for (; i < discovered - 1; i++) + for (int i = 0; i < discovered - 1; i++) { // Link Network buf[i].next = bufAddr + (sizeof(SceNetAdhocctlPeerInfoEmu)*i) + sizeof(SceNetAdhocctlPeerInfoEmu); @@ -3806,7 +4242,7 @@ int sceNetAdhocDiscoverGetStatus() { return 0; } -int sceNetAdhocDiscoverRequestSuspend(void) +int sceNetAdhocDiscoverRequestSuspend() { ERROR_LOG(SCENET, "UNIMPL sceNetAdhocDiscoverRequestSuspend()"); return 0; @@ -3854,8 +4290,11 @@ void broadcastHelloMessage(SceNetAdhocMatchingContext * context) // Allocate Hello Message Buffer, reuse when necessary if ((int32_t)context->hellolen > len) { - hello = (uint8_t *)realloc(hello, 5 + context->hellolen); - len = context->hellolen; + uint8_t* tmp = (uint8_t *)realloc(hello, 5 + context->hellolen); + if (tmp != NULL) { + hello = tmp; + len = context->hellolen; + } } // Allocated Hello Message Buffer @@ -3905,7 +4344,7 @@ void sendAcceptPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * ma int siblingcount = siblingbuflen / sizeof(SceNetEtherAddr); // Allocate Accept Message Buffer - uint8_t * accept = (uint8_t *)malloc(9 + optlen + siblingbuflen); + uint8_t * accept = (uint8_t *)malloc(9 + (uint32_t)optlen + siblingbuflen); // Allocated Accept Message Buffer if (accept != NULL) @@ -3932,7 +4371,8 @@ void sendAcceptPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * ma int i = 0; // Iterate Peer List - SceNetAdhocMatchingMemberInternal * item = context->peerlist; for (; item != NULL; item = item->next) + SceNetAdhocMatchingMemberInternal * item = context->peerlist; + for (; item != NULL; item = item->next) { // Ignore Target if (item == peer) continue; @@ -4127,7 +4567,8 @@ void sendBirthPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * mac memcpy(packet + 1, mac, sizeof(SceNetEtherAddr)); // Iterate Peers - SceNetAdhocMatchingMemberInternal * peer = context->peerlist; for (; peer != NULL; peer = peer->next) + SceNetAdhocMatchingMemberInternal * peer = context->peerlist; + for (; peer != NULL; peer = peer->next) { // Skip Newborn Child if (peer == newborn) continue; @@ -4141,10 +4582,11 @@ void sendBirthPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * mac context->socketlock->unlock(); // Log Send Success + char tmpmac[18]; if (sent >= 0) - INFO_LOG(SCENET, "InputLoop: Sending BIRTH to %02X:%02X:%02X:%02X:%02X:%02X", peer->mac.data[0], peer->mac.data[1], peer->mac.data[2], peer->mac.data[3], peer->mac.data[4], peer->mac.data[5]); + INFO_LOG(SCENET, "InputLoop: Sending BIRTH to %s", mac2str(&peer->mac, tmpmac)); else - WARN_LOG(SCENET, "InputLoop: Failed to Send BIRTH to %02X:%02X:%02X:%02X:%02X:%02X", peer->mac.data[0], peer->mac.data[1], peer->mac.data[2], peer->mac.data[3], peer->mac.data[4], peer->mac.data[5]); + WARN_LOG(SCENET, "InputLoop: Failed to Send BIRTH to %s", mac2str(&peer->mac, tmpmac)); } } } @@ -4173,7 +4615,8 @@ void sendDeathPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * mac memcpy(packet + 1, mac, sizeof(SceNetEtherAddr)); // Iterate Peers - SceNetAdhocMatchingMemberInternal * peer = context->peerlist; for (; peer != NULL; peer = peer->next) + SceNetAdhocMatchingMemberInternal * peer = context->peerlist; + for (; peer != NULL; peer = peer->next) { // Skip dead Child if (peer == deadkid) continue; @@ -4197,7 +4640,8 @@ void sendDeathPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * mac void sendByePacket(SceNetAdhocMatchingContext * context) { // Iterate Peers - SceNetAdhocMatchingMemberInternal * peer = context->peerlist; for (; peer != NULL; peer = peer->next) + SceNetAdhocMatchingMemberInternal * peer = context->peerlist; + for (; peer != NULL; peer = peer->next) { // Peer of Interest if (peer->state == PSP_ADHOC_MATCHING_PEER_PARENT || peer->state == PSP_ADHOC_MATCHING_PEER_CHILD || peer->state == PSP_ADHOC_MATCHING_PEER_P2P) @@ -4519,7 +4963,8 @@ void actOnCancelPacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * s else if (parent == peer) { // Iterate Peers - SceNetAdhocMatchingMemberInternal * item = context->peerlist; for (; item != NULL; item = item->next) + SceNetAdhocMatchingMemberInternal * item = context->peerlist; + for (; item != NULL; item = item->next) { // Established Peer if (item->state == PSP_ADHOC_MATCHING_PEER_CHILD || item->state == PSP_ADHOC_MATCHING_PEER_PARENT) @@ -4746,13 +5191,16 @@ void actOnByePacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * send // Delete Peer deletePeer(context, peer); + // Instead of removing peer immediately, We should give a little time before removing the peer and let it timed out? just in case the game is in the middle of communicating with the peer on another thread so it won't recognize it as Unknown peer + //peer->lastping = CoreTiming::GetGlobalTimeUsScaled(); } // Parent Bye else if (context->mode == PSP_ADHOC_MATCHING_MODE_CHILD && peer->state == PSP_ADHOC_MATCHING_PEER_PARENT) { // Iterate Peers - SceNetAdhocMatchingMemberInternal * item = context->peerlist; for (; item != NULL; item = item->next) + SceNetAdhocMatchingMemberInternal * item = context->peerlist; + for (; item != NULL; item = item->next) { // Established Peer if (item->state == PSP_ADHOC_MATCHING_PEER_CHILD || item->state == PSP_ADHOC_MATCHING_PEER_PARENT) @@ -4777,6 +5225,7 @@ void actOnByePacket(SceNetAdhocMatchingContext * context, SceNetEtherAddr * send */ int matchingEventThread(int matchingId) { + setCurrentThreadName("MatchingEvent"); // Multithreading Lock peerlock.lock(); // Cast Context @@ -4790,26 +5239,34 @@ int matchingEventThread(int matchingId) // Run while needed... if (context != NULL) { u32 bufLen = context->rxbuflen; //0; - u32 bufAddr = 0; //userMemory.Alloc(bufLen); + u32 bufAddr = 0; //= userMemory.Alloc(bufLen); //context->rxbuf; //static u32_le args[5] = { 0, 0, 0, 0, 0 }; // Need to be global/static so it can be accessed from a different thread - u32_le * args = context->handlerArgs; + u32_le * args = context->handlerArgs; //MatchingArgs - while (context->eventRunning) // (context->eventThread.get_id() != NULL) + while (contexts != NULL && context->eventRunning) // (context->eventThread.get_id() != NULL) { + // Multithreading Lock + peerlock.lock(); + // Cast Context + context = findMatchingContext(matchingId); + // Multithreading Unlock + peerlock.unlock(); + // Messages on Stack ready for processing - if (context->event_stack != NULL) + if (context != NULL && context->event_stack != NULL) { // Claim Stack context->eventlock->lock(); // Iterate Message List - ThreadMessage * msg = context->event_stack; for (; msg != NULL; msg = msg->next) + ThreadMessage * msg = context->event_stack; + /*for (; msg != NULL; msg = msg->next) { // Default Optional Data - void * opt = NULL; + void* opt = NULL; // Grab Optional Data - if (msg->optlen > 0) opt = ((u8 *)msg) + sizeof(ThreadMessage); //&msg[1] + if (msg->optlen > 0) opt = ((u8*)msg) + sizeof(ThreadMessage); //&msg[1] // Log Matching Events INFO_LOG(SCENET, "EventLoop[%d]: Matching Event [%d=%s] OptSize=%d", matchingId, msg->opcode, getMatchingEventStr(msg->opcode), msg->optlen); @@ -4820,30 +5277,73 @@ int matchingEventThread(int matchingId) // Notify Event Handlers notifyMatchingHandler(context, msg, opt, bufAddr, bufLen, args); context->eventlock->lock(); // Lock again + + // Give some time before executing the next mipscall to prevent event ACCEPT(6)->ESTABLISH(7) getting reversed After Action ESTABLISH(7)->ACCEPT(6) + sleep_ms(adhocEventPollDelayMS); // If we're using shared Buffer & Args for all Events We should wait for the Mipscall to be fully executed before processing the next event } // Clear Event Message Stack clearStack(context, PSP_ADHOC_MATCHING_EVENT_STACK); + */ + if (msg != NULL) + { + // Default Optional Data + void* opt = NULL; + + // Grab Optional Data + if (msg->optlen > 0) opt = ((u8*)msg) + sizeof(ThreadMessage); //&msg[1] + + // Log Matching Events + INFO_LOG(SCENET, "EventLoop[%d]: Matching Event [%d=%s] OptSize=%d", matchingId, msg->opcode, getMatchingEventStr(msg->opcode), msg->optlen); + + // Unlock to prevent race-condition with other threads due to recursive lock + context->eventlock->unlock(); + // Call Event Handler + //context->handler(context->id, msg->opcode, &msg->mac, msg->optlen, opt); + // Notify Event Handlers + notifyMatchingHandler(context, msg, opt, bufAddr, bufLen, args); // If we're using shared Buffer & Args for All Events We should wait for the Mipscall to be fully executed before processing the next event. GTA VCS need this delay/sleep. + + // Give some time before executing the next mipscall to prevent event ACCEPT(6)->ESTABLISH(7) getting reversed After Action ESTABLISH(7)->ACCEPT(6) + //do { + // //sceKernelDelayThread(adhocEventPollDelayMS * 1000); //adhocMatchingEventDelayMS * 1000 + // sleep_ms(1); + //} while (IsMatchingInCallback(context)); + + // Must Not be delayed too long to prevent desync/disconnect. Not longer than the delays on callback's HLE? + //sleep_ms(adhocEventPollDelayMS); //adhocMatchingEventDelayMS //adhocEventPollDelayMS //sceKernelDelayThread(10000); + + // Lock again + context->eventlock->lock(); + + // Pop event stack from front (this should be queue instead of stack?) + context->event_stack = msg->next; + free(msg); + msg = NULL; + } + + // Clear Event Message Stack + //clearStack(context, PSP_ADHOC_MATCHING_EVENT_STACK); // Free Stack context->eventlock->unlock(); } // Share CPU Time - sleep_ms(1); //10 //sceKernelDelayThread(10000); + sleep_ms(1); // Don't do anything if it's paused, otherwise the log will be flooded - while (Core_IsStepping() && context->eventRunning) sleep_ms(1); + while (Core_IsStepping() && coreState != CORE_POWERDOWN && contexts != NULL && context->eventRunning) sleep_ms(1); } // Process Last Messages - if (context->event_stack != NULL) + if (contexts != NULL && context->event_stack != NULL) { // Claim Stack context->eventlock->lock(); // Iterate Message List - ThreadMessage * msg = context->event_stack; for (; msg != NULL; msg = msg->next) + ThreadMessage * msg = context->event_stack; + for (; msg != NULL; msg = msg->next) { // Default Optional Data void * opt = NULL; @@ -4888,8 +5388,9 @@ int matchingEventThread(int matchingId) * @param argp SceNetAdhocMatchingContext * * @return Exit Point is never reached... */ -int matchingInputThread(int matchingId) +int matchingInputThread(int matchingId) // TODO: The MatchingInput thread is using sceNetAdhocPdpRecv & sceNetAdhocPdpSend functions so it might be better to run this on PSP thread instead of real thread { + setCurrentThreadName("MatchingInput"); // Multithreading Lock peerlock.lock(); // Cast Context @@ -4910,157 +5411,171 @@ int matchingInputThread(int matchingId) // Run while needed... if (context != NULL) { - while (context->inputRunning) + while (contexts != NULL && context->inputRunning) { - now = CoreTiming::GetGlobalTimeUsScaled(); //real_time_now()*1000000.0; + // Multithreading Lock + peerlock.lock(); + // Cast Context + context = findMatchingContext(matchingId); + // Multithreading Unlock + peerlock.unlock(); - // Hello Message Sending Context with unoccupied Slots - if ((context->mode == PSP_ADHOC_MATCHING_MODE_PARENT && (countChildren(context) < (context->maxpeers - 1))) || (context->mode == PSP_ADHOC_MATCHING_MODE_P2P && findP2P(context) == NULL)) - { - // Hello Message Broadcast necessary because of Hello Interval - if (context->hello_int > 0) - if ((now - lasthello) >= context->hello_int) + if (context != NULL) { + now = CoreTiming::GetGlobalTimeUsScaled(); //real_time_now()*1000000.0; + + // Hello Message Sending Context with unoccupied Slots + if ((context->mode == PSP_ADHOC_MATCHING_MODE_PARENT && (countChildren(context) < (context->maxpeers - 1))) || (context->mode == PSP_ADHOC_MATCHING_MODE_P2P && findP2P(context) == NULL)) { - // Broadcast Hello Message - broadcastHelloMessage(context); + // Hello Message Broadcast necessary because of Hello Interval + if (context->hello_int > 0) + if ((now - lasthello) >= context->hello_int) + { + // Broadcast Hello Message + broadcastHelloMessage(context); - // Update Hello Timer - lasthello = now; + // Update Hello Timer + lasthello = now; + } } - } - // Ping Required - if (context->keepalive_int > 0) - if ((now - lastping) >= context->keepalive_int) - { - // Broadcast Ping Message - broadcastPingMessage(context); + // Ping Required + if (context->keepalive_int > 0) + if ((now - lastping) >= context->keepalive_int) + { + // Broadcast Ping Message + broadcastPingMessage(context); - // Update Ping Timer - lastping = now; - } + // Update Ping Timer + lastping = now; + } - // Messages on Stack ready for processing - if (context->input_stack != NULL) - { - // Claim Stack - context->inputlock->lock(); - - // Iterate Message List - ThreadMessage * msg = context->input_stack; - for (; msg != NULL; msg = msg->next) + // Messages on Stack ready for processing + if (context->input_stack != NULL) { - // Default Optional Data - void * opt = NULL; + // Claim Stack + context->inputlock->lock(); - // Grab Optional Data - if (msg->optlen > 0) opt = ((u8 *)msg) + sizeof(ThreadMessage); + // Iterate Message List + ThreadMessage* msg = context->input_stack; + for (; msg != NULL; msg = msg->next) + { + // Default Optional Data + void* opt = NULL; - context->inputlock->unlock(); // Unlock to prevent race condition when locking peerlock + // 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); + context->inputlock->unlock(); // Unlock to prevent race condition when locking peerlock - // Send Join Packet - else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_JOIN) sendJoinPacket(context, &msg->mac, msg->optlen, opt); + // Send Accept Packet + if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_ACCEPT) sendAcceptPacket(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 Join Packet + else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_JOIN) sendJoinPacket(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 Cancel Packet + else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_CANCEL) sendCancelPacket(context, &msg->mac, msg->optlen, opt); - // Send Birth Packet - else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_BIRTH) sendBirthPacket(context, &msg->mac); + // Send Bulk Data Packet + else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_BULK) sendBulkDataPacket(context, &msg->mac, msg->optlen, opt); - // Cancel Bulk Data Transfer (does nothing as of now as we fire and forget anyway) // Do we need to check DeathPacket and BytePacket here? - // else if(msg->opcode == PSP_ADHOC_MATCHING_PACKET_BULK_ABORT) blabla; + // Send Birth Packet + else if (msg->opcode == PSP_ADHOC_MATCHING_PACKET_BIRTH) sendBirthPacket(context, &msg->mac); - context->inputlock->lock(); // Lock again + // 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); + + context->inputlock->lock(); // Lock again + } + + // Clear IO Message Stack + clearStack(context, PSP_ADHOC_MATCHING_INPUT_STACK); + + // Free Stack + context->inputlock->unlock(); } - // Clear IO Message Stack - clearStack(context, PSP_ADHOC_MATCHING_INPUT_STACK); + // Receive PDP Datagram + SceNetEtherAddr sendermac; + uint16_t senderport; + int rxbuflen = context->rxbuflen; + context->socketlock->lock(); + int recvresult = sceNetAdhocPdpRecv(context->socket, &sendermac, &senderport, context->rxbuf, &rxbuflen, 0, ADHOC_F_NONBLOCK); + context->socketlock->unlock(); - // Free Stack - context->inputlock->unlock(); + // Received Data from a Sender that interests us + if (recvresult == 0 && rxbuflen > 0 && context->port == senderport) + { + // Log Receive Success + if (context->rxbuf[0] > 1) { + INFO_LOG(SCENET, "InputLoop[%d]: Received %d Bytes (Opcode[%d]=%s)", matchingId, rxbuflen, context->rxbuf[0], getMatchingOpcodeStr(context->rxbuf[0])); + } + + // Update Peer Timestamp + peerlock.lock(); + SceNetAdhocctlPeerInfo* peer = findFriend(&sendermac); + if (peer != NULL) { + now = CoreTiming::GetGlobalTimeUsScaled(); + u64_le delta = now - peer->last_recv; + char tmpmac[18]; + DEBUG_LOG(SCENET, "Timestamp Delta: %llu (%llu - %llu) from %s", delta, now, peer->last_recv, mac2str(&sendermac, tmpmac)); + if (/*context->rxbuf[0] > 0 &&*/ peer->last_recv != 0) peer->last_recv = now; // - context->keepalive_int; // May need to deduce by ping interval to prevent Dissidia 012 unable to see other players (ie. disappearing issue) + } + peerlock.unlock(); + + // Ping Packet + if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_PING) actOnPingPacket(context, &sendermac); + + // Hello Packet + else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_HELLO) actOnHelloPacket(context, &sendermac, rxbuflen); + + // Join Packet + else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_JOIN) actOnJoinPacket(context, &sendermac, rxbuflen); + + // Accept Packet + else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_ACCEPT) actOnAcceptPacket(context, &sendermac, rxbuflen); + + // Cancel Packet + else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_CANCEL) actOnCancelPacket(context, &sendermac, rxbuflen); + + // Bulk Data Packet + else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_BULK) actOnBulkDataPacket(context, &sendermac, rxbuflen); + + // Abort Bulk Data Packet + //else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_BULK_ABORT) actOnAbortBulkDataPacket(context, &sendermac, rxbuflen); + + // Birth Packet + else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_BIRTH) actOnBirthPacket(context, &sendermac, rxbuflen); + + // Death Packet + else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_DEATH) actOnDeathPacket(context, &sendermac, rxbuflen); + + // Bye Packet + else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_BYE) actOnByePacket(context, &sendermac); + + // Ignore Incoming Trash Data + } + + // Handle Peer Timeouts + handleTimeout(context); } - - // Receive PDP Datagram - SceNetEtherAddr sendermac; - uint16_t senderport; - int rxbuflen = context->rxbuflen; - context->socketlock->lock(); - int recvresult = sceNetAdhocPdpRecv(context->socket, &sendermac, &senderport, context->rxbuf, &rxbuflen, 0, ADHOC_F_NONBLOCK); - context->socketlock->unlock(); - - // Received Data from a Sender that interests us - if (recvresult == 0 && rxbuflen > 0 && context->port == senderport) - { - // Log Receive Success - if (context->rxbuf[0] > 1) { - INFO_LOG(SCENET, "InputLoop[%d]: Received %d Bytes (Opcode[%d]=%s)", matchingId, rxbuflen, context->rxbuf[0], getMatchingOpcodeStr(context->rxbuf[0])); - } - - // Update Peer Timestamp - peerlock.lock(); - SceNetAdhocctlPeerInfo * peer = findFriend(&sendermac); - if (peer != NULL) { - now = CoreTiming::GetGlobalTimeUsScaled(); - u64_le delta = now - peer->last_recv; - DEBUG_LOG(SCENET, "Timestamp Delta: %llu (%llu - %llu)", delta, now, peer->last_recv); - if (/*context->rxbuf[0] > 0 &&*/ peer->last_recv != 0) peer->last_recv = now; // - context->keepalive_int; // May need to deduce by ping interval to prevent Dissidia 012 unable to see other players (ie. disappearing issue) - } - peerlock.unlock(); - - // Ping Packet - if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_PING) actOnPingPacket(context, &sendermac); - - // Hello Packet - else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_HELLO) actOnHelloPacket(context, &sendermac, rxbuflen); - - // Join Packet - else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_JOIN) actOnJoinPacket(context, &sendermac, rxbuflen); - - // Accept Packet - else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_ACCEPT) actOnAcceptPacket(context, &sendermac, rxbuflen); - - // Cancel Packet - else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_CANCEL) actOnCancelPacket(context, &sendermac, rxbuflen); - - // Bulk Data Packet - else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_BULK) actOnBulkDataPacket(context, &sendermac, rxbuflen); - - // Birth Packet - else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_BIRTH) actOnBirthPacket(context, &sendermac, rxbuflen); - - // Death Packet - else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_DEATH) actOnDeathPacket(context, &sendermac, rxbuflen); - - // Bye Packet - else if (context->rxbuf[0] == PSP_ADHOC_MATCHING_PACKET_BYE) actOnByePacket(context, &sendermac); - - // Ignore Incoming Trash Data - } - - // Handle Peer Timeouts - handleTimeout(context); - // Share CPU Time sleep_ms(1); //10 //sceKernelDelayThread(10000); // Don't do anything if it's paused, otherwise the log will be flooded - while (Core_IsStepping() && context->inputRunning) sleep_ms(1); + while (Core_IsStepping() && coreState != CORE_POWERDOWN && contexts != NULL && context->inputRunning) sleep_ms(1); } - // Send Bye Messages - sendByePacket(context); + if (contexts != NULL) { + // Send Bye Messages + sendByePacket(context); - // Free Peer List Buffer - clearPeerList(context); //deleteAllMembers(context); + // Free Peer List Buffer + clearPeerList(context); //deleteAllMembers(context); - // Delete Pointer Reference (and notify caller about finished cleanup) - //context->inputThread = NULL; + // Delete Pointer Reference (and notify caller about finished cleanup) + //context->inputThread = NULL; + } } // Log Shutdown diff --git a/Core/HLE/sceNetAdhoc.h b/Core/HLE/sceNetAdhoc.h index 0f30ebe6b8..73720e15a3 100644 --- a/Core/HLE/sceNetAdhoc.h +++ b/Core/HLE/sceNetAdhoc.h @@ -17,6 +17,8 @@ #pragma once +#include "Core/HLE/proAdhoc.h" + typedef struct MatchingArgs { u32_le data[6]; //ContextID, Opcode, bufAddr[ to MAC], OptLen, OptAddr[, EntryPoint] } PACK; @@ -25,6 +27,7 @@ class PointerWrap; void Register_sceNetAdhoc(); +u32_le __CreateHLELoop(u32_le* loopAddr, const char* sceFuncName, const char* hleFuncName, const char* tagName = NULL); void __NetAdhocInit(); void __NetAdhocShutdown(); void __NetAdhocDoState(PointerWrap &p); @@ -37,5 +40,12 @@ int sceNetAdhocctlCreate(const char * groupName); // May need to use these from sceNet.cpp extern bool netAdhocInited; extern bool netAdhocctlInited; +extern int adhocDefaultTimeout; +extern int adhocEventPollDelayMS; +extern int adhocMatchingEventDelayMS; +extern int adhocEventDelayMS; +extern std::recursive_mutex adhocEvtMtx; +extern int IsAdhocctlInCB; + int sceNetAdhocctlTerm(); int sceNetAdhocTerm();