From 6a9cf0f575422b45a9c5426e4c3f2bbd14842b7f Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Tue, 18 Aug 2020 00:39:46 +0700 Subject: [PATCH 1/7] An attempt to differentiate adhoc connect, create, and join. --- Core/Dialog/PSPNetconfDialog.cpp | 169 +++++++++++++++++++++++++++---- Core/Dialog/PSPNetconfDialog.h | 7 +- Core/HLE/proAdhoc.cpp | 13 +-- Core/HLE/proAdhoc.h | 10 +- Core/HLE/sceNet.cpp | 2 +- Core/HLE/sceNetAdhoc.cpp | 127 ++++++++++++++--------- Core/HLE/sceNetAdhoc.h | 15 ++- 7 files changed, 265 insertions(+), 78 deletions(-) diff --git a/Core/Dialog/PSPNetconfDialog.cpp b/Core/Dialog/PSPNetconfDialog.cpp index f4436d5564..a218382d68 100644 --- a/Core/Dialog/PSPNetconfDialog.cpp +++ b/Core/Dialog/PSPNetconfDialog.cpp @@ -18,17 +18,20 @@ #if defined(_WIN32) #include "Common/CommonWindows.h" #endif +#include #include "i18n/i18n.h" #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" #include "Core/Config.h" #include "Core/MemMapHelpers.h" #include "Core/Util/PPGeDraw.h" +#include "Core/HLE/sceKernelMemory.h" #include "Core/HLE/sceCtrl.h" #include "Core/HLE/sceUtility.h" -#include +#include "Core/HLE/sceNet.h" +#include "Core/HLE/sceNetAdhoc.h" #include "Core/Dialog/PSPNetconfDialog.h" -#include +#include "ext/native/util/text/utf8.h" #define NETCONF_CONNECT_APNET 0 @@ -44,6 +47,13 @@ static const float FONT_SCALE = 0.65f; const static int NET_INIT_DELAY_US = 300000; const static int NET_SHUTDOWN_DELAY_US = 26000; const static int NET_RUNNING_DELAY_US = 1000000; // KHBBS is showing adhoc dialog for about 3-4 seconds, but feels too long, so we're faking it to 1 sec instead to let players read the text +const static int NET_CONNECT_TIMEOUT = 5000000; + +struct ScanInfos { + s32_le sz; + SceNetAdhocctlScanInfoEmu si; +} PACK; + PSPNetconfDialog::PSPNetconfDialog() { } @@ -78,6 +88,11 @@ int PSPNetconfDialog::Init(u32 paramAddr) { cancelButtonFlag = CTRL_CIRCLE; } + connResult = -1; + scanInfosAddr = 0; + scanStep = 0; + startTime = (u64)(real_time_now() * 1000000.0); + StartFade(true); return 0; } @@ -114,7 +129,7 @@ void PSPNetconfDialog::DisplayMessage(std::string text1, std::string text2a, std // Without the scrollbar, we have 350 total pixels. float WRAP_WIDTH = 300.0f; - if (UTF8StringNonASCIICount(text1.c_str()) >= text1.size() / 4) { + if (UTF8StringNonASCIICount(text1.c_str()) >= (int)text1.size() / 4) { WRAP_WIDTH = 336.0f; if (text1.size() > 12) { messageStyle.scale = 0.6f; @@ -182,12 +197,20 @@ void PSPNetconfDialog::DisplayMessage(std::string text1, std::string text2a, std PPGeScissor(0, (int)(centerY - h2 - 2), 480, (int)(centerY + h2 + 2)); PPGeDrawTextWrapped(text1.c_str(), 240.0f, centerY - h2 - scrollPos_, WRAP_WIDTH, 0, messageStyle); - if (text2a != "") - PPGeDrawTextWrapped(text2a.c_str(), 240.0f - 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + marginTop, WRAP_WIDTH, 0, messageStyleRight); + if (text2a != "") { + if (text2b != "") + PPGeDrawTextWrapped(text2a.c_str(), 240.0f - 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + marginTop, WRAP_WIDTH, 0, messageStyleRight); + else + PPGeDrawTextWrapped(text2a.c_str(), 240.0f, centerY - h2 - scrollPos_ + totalHeight1 + marginTop, WRAP_WIDTH, 0, messageStyle); + } if (text2b != "") PPGeDrawTextWrapped(text2b.c_str(), 240.0f + 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + marginTop, WRAP_WIDTH, 0, messageStyleLeft); - if (text3a != "") - PPGeDrawTextWrapped(text3a.c_str(), 240.0f - 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + totalHeight2 + marginTop, WRAP_WIDTH, 0, messageStyleRight); + if (text3a != "") { + if (text3b != "") + PPGeDrawTextWrapped(text3a.c_str(), 240.0f - 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + totalHeight2 + marginTop, WRAP_WIDTH, 0, messageStyleRight); + else + PPGeDrawTextWrapped(text3a.c_str(), 240.0f, centerY - h2 - scrollPos_ + totalHeight1 + totalHeight2 + marginTop, WRAP_WIDTH, 0, messageStyle); + } if (text3b != "") PPGeDrawTextWrapped(text3b.c_str(), 240.0f + 5.0f, centerY - h2 - scrollPos_ + totalHeight1 + totalHeight2 + marginTop, WRAP_WIDTH, 0, messageStyleLeft); PPGeScissorReset(); @@ -229,6 +252,7 @@ int PSPNetconfDialog::Update(int animSpeed) { UpdateButtons(); auto di = GetI18NCategory("Dialog"); auto err = GetI18NCategory("Error"); + u64 now = (u64)(real_time_now() * 1000000.0); // It seems JPCSP doesn't check for NETCONF_STATUS_APNET if (request.netAction == NETCONF_CONNECT_APNET || request.netAction == NETCONF_STATUS_APNET || request.netAction == NETCONF_CONNECT_APNET_LAST) { @@ -310,6 +334,7 @@ int PSPNetconfDialog::Update(int animSpeed) { } else if (request.netAction == NETCONF_CONNECT_ADHOC || request.netAction == NETCONF_CREATE_ADHOC || request.netAction == NETCONF_JOIN_ADHOC) { int state = NetAdhocctl_GetState(); + bool timedout = (state == ADHOCCTL_STATE_DISCONNECTED && now - startTime > NET_CONNECT_TIMEOUT); UpdateFade(animSpeed); StartDraw(); @@ -317,18 +342,104 @@ int PSPNetconfDialog::Update(int animSpeed) { DrawBanner(); DrawIndicator(); - std::string channel = std::to_string(g_Config.iWlanAdhocChannel); - if (g_Config.iWlanAdhocChannel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC) - channel = "Automatic"; + if (timedout) { + // FIXME: Do we need to show error message? + DisplayMessage(di->T("InternalError", "An internal error has occurred.") + StringFromFormat("\n(%08X)", connResult)); + DisplayButtons(DS_BUTTON_CANCEL, di->T("Back")); + } + else { + std::string channel = std::to_string(g_Config.iWlanAdhocChannel); + if (g_Config.iWlanAdhocChannel == PSP_SYSTEMPARAM_ADHOC_CHANNEL_AUTOMATIC) + channel = "Automatic"; - DisplayMessage(di->T("ConnectingChannel", "Connecting.\nPlease wait...\n\nChannel")+std::string(" ")+di->T(channel)); + DisplayMessage(di->T("ConnectingPleaseWait", "Connecting.\nPlease wait..."), di->T("Channel") + std::string(" ") + di->T(channel)); - // Only Join mode is showing Cancel button on KHBBS and the button will fade out before the dialog is fading out, probably because it's already connected thus can't be canceled anymore - if (request.netAction == NETCONF_JOIN_ADHOC) - DisplayButtons(DS_BUTTON_CANCEL, di->T("Cancel")); + // Only Join mode is showing Cancel button on KHBBS and the button will fade out before the dialog is fading out, probably because it's already connected thus can't be canceled anymore + if (request.netAction == NETCONF_JOIN_ADHOC) + DisplayButtons(DS_BUTTON_CANCEL, di->T("Cancel")); - if (state == ADHOCCTL_STATE_DISCONNECTED && request.NetconfData.IsValid()) { - connResult = sceNetAdhocctlCreate(request.NetconfData->groupName); + // KHBBS will first enter the arena using NETCONF_CONNECT_ADHOC (auto-create group when not exist yet?), but when the event started the event's creator use NETCONF_CREATE_ADHOC while the joining players use NETCONF_JOIN_ADHOC + if (request.NetconfData.IsValid()) { + if (state == ADHOCCTL_STATE_DISCONNECTED) { + switch (request.netAction) + { + case NETCONF_CREATE_ADHOC: + if (connResult < 0) { + connResult = sceNetAdhocctlCreate(request.NetconfData->groupName); + } + break; + case NETCONF_JOIN_ADHOC: + // FIXME: Should we Scan for a matching group first before Joining a Group (like adhoc games normally do)? Or Is it really allowed to join non-existing group? + if (scanStep == 0) { + if (sceNetAdhocctlScan() >= 0) { + u32 structsz = sizeof(ScanInfos); + if (Memory::IsValidAddress(scanInfosAddr)) + userMemory.Free(scanInfosAddr); + scanInfosAddr = userMemory.Alloc(structsz, false, "NetconfScanInfo"); + Memory::Write_U32(sizeof(SceNetAdhocctlScanInfoEmu), scanInfosAddr); + scanStep = 1; + } + } + else if (scanStep == 1) { + s32 sz = Memory::Read_U32(scanInfosAddr); + // Get required buffer size + if (sceNetAdhocctlGetScanInfo(scanInfosAddr, 0) >= 0) { + s32 reqsz = Memory::Read_U32(scanInfosAddr); + if (reqsz > sz) { + sz = reqsz; + if (Memory::IsValidAddress(scanInfosAddr)) + userMemory.Free(scanInfosAddr); + u32 structsz = sz + sizeof(s32); + scanInfosAddr = userMemory.Alloc(structsz, false, "NetconfScanInfo"); + Memory::Write_U32(sz, scanInfosAddr); + } + if (reqsz > 0) { + if (sceNetAdhocctlGetScanInfo(scanInfosAddr, scanInfosAddr + sizeof(s32)) >= 0) { + ScanInfos* scanInfos = (ScanInfos*)Memory::GetPointer(scanInfosAddr); + int n = scanInfos->sz / sizeof(SceNetAdhocctlScanInfoEmu); + // Assuming returned SceNetAdhocctlScanInfoEmu(s) are contagious where next is pointing to current addr + sizeof(SceNetAdhocctlScanInfoEmu) + while (n > 0) { + SceNetAdhocctlScanInfoEmu* si = (SceNetAdhocctlScanInfoEmu*)Memory::GetPointer(scanInfosAddr + sizeof(s32) + sizeof(SceNetAdhocctlScanInfoEmu) * (n - 1LL)); + if (memcmp(si->group_name.data, request.NetconfData->groupName, ADHOCCTL_GROUPNAME_LEN) == 0) { + // Moving found group info to the front so we can use it on sceNetAdhocctlJoin easily + memcpy((char*)scanInfos + sizeof(s32), si, sizeof(SceNetAdhocctlScanInfoEmu)); + scanStep = 2; + break; + } + n--; + } + // Target group not found, try to scan again later + if (n <= 0) { + scanStep = 0; + } + } + } + // No group found, try to scan again later + else { + scanStep = 0; + } + } + } + else if (scanStep == 2) { + if (connResult < 0) { + connResult = sceNetAdhocctlJoin(scanInfosAddr + sizeof(s32)); + if (connResult >= 0) { + // We are done! + if (Memory::IsValidAddress(scanInfosAddr)) + userMemory.Free(scanInfosAddr); + scanInfosAddr = 0; + } + } + } + break; + default: + if (connResult < 0) { + connResult = sceNetAdhocctlConnect(request.NetconfData->groupName); + } + break; + } + } + } } // The Netconf dialog stays visible until the network reaches @@ -342,12 +453,20 @@ int PSPNetconfDialog::Update(int animSpeed) { else if (GetStatus() == SCE_UTILITY_STATUS_FINISHED) { StartFade(false); } + // Let's not leaks any memory + if (Memory::IsValidAddress(scanInfosAddr)) + userMemory.Free(scanInfosAddr); + scanInfosAddr = 0; } - if (request.netAction == NETCONF_JOIN_ADHOC && IsButtonPressed(cancelButtonFlag)) { + if ((request.netAction == NETCONF_JOIN_ADHOC || timedout) && IsButtonPressed(cancelButtonFlag)) { StartFade(false); ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NET_SHUTDOWN_DELAY_US); request.common.result = SCE_UTILITY_DIALOG_RESULT_ABORT; + // Let's not leaks any memory + if (Memory::IsValidAddress(scanInfosAddr)) + userMemory.Free(scanInfosAddr); + scanInfosAddr = 0; } EndDraw(); @@ -374,11 +493,25 @@ int PSPNetconfDialog::Shutdown(bool force) { void PSPNetconfDialog::DoState(PointerWrap &p) { PSPDialog::DoState(p); - auto s = p.Section("PSPNetconfigDialog", 0, 1); + auto s = p.Section("PSPNetconfigDialog", 0, 2); if (!s) return; Do(p, request); + if (s >= 2) { + Do(p, scanInfosAddr); + Do(p, scanStep); + Do(p, connResult); + } + else { + scanInfosAddr = 0; + scanStep = 0; + connResult = -1; + } + + if (p.mode == p.MODE_READ) { + startTime = 0; + } } pspUtilityDialogCommon* PSPNetconfDialog::GetCommonParam() diff --git a/Core/Dialog/PSPNetconfDialog.h b/Core/Dialog/PSPNetconfDialog.h index ea10471cc7..e02ebae5a0 100644 --- a/Core/Dialog/PSPNetconfDialog.h +++ b/Core/Dialog/PSPNetconfDialog.h @@ -34,6 +34,7 @@ struct SceUtilityNetconfParam { int netWifiSpot; // Flag to allow WIFI connections }; + class PSPNetconfDialog: public PSPDialog { public: PSPNetconfDialog(); @@ -57,11 +58,15 @@ private: SceUtilityNetconfParam request = {}; u32 requestAddr = 0; - int connResult = 0; + int connResult = -1; bool hideNotice = false; int yesnoChoice = 0; float scrollPos_ = 0.0f; int framesUpHeld_ = 0; int framesDownHeld_ = 0; + + u32 scanInfosAddr = 0; + int scanStep = 0; + u64 startTime = 0; }; diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index eb016f8040..b8f57cca83 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -55,7 +55,8 @@ bool friendFinderRunning = false; SceNetAdhocctlPeerInfo * friends = NULL; SceNetAdhocctlScanInfo * networks = NULL; SceNetAdhocctlScanInfo * newnetworks = NULL; -int threadStatus = ADHOCCTL_STATE_DISCONNECTED; +int adhocctlState = ADHOCCTL_STATE_DISCONNECTED; +int adhocConnectionType = ADHOC_CONNECT; int actionAfterAdhocMipsCall; int actionAfterMatchingMipsCall; @@ -1293,9 +1294,9 @@ int friendFinder(){ // Update User BSSID parameter.bssid.mac_addr = packet->mac; // This packet seems to contains Adhoc Group Creator's BSSID (similar to AP's BSSID) so it shouldn't get mixed up with local MAC address // Notify Event Handlers - //notifyAdhocctlHandlers(ADHOCCTL_EVENT_CONNECT, 0); + notifyAdhocctlHandlers(ADHOCCTL_EVENT_CONNECT, 0); // Change State - threadStatus = ADHOCCTL_STATE_CONNECTED; + //threadStatus = ADHOCCTL_STATE_CONNECTED; // Give time a little time //sceKernelDelayThread(adhocEventDelayMS * 1000); //sleep_ms(adhocEventDelayMS); @@ -1492,14 +1493,14 @@ int friendFinder(){ peerlock.unlock(); // 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; + //threadStatus = ADHOCCTL_STATE_DISCONNECTED; // Give time a little time //sceKernelDelayThread(adhocEventDelayMS * 1000); //sleep_ms(adhocEventDelayMS); @@ -1522,7 +1523,7 @@ int friendFinder(){ // Groups/Networks should be deallocated isn't? // Prevent the games from having trouble to reInitiate Adhoc (the next NetInit -> PdpCreate after NetTerm) - threadStatus = ADHOCCTL_STATE_DISCONNECTED; + adhocctlState = ADHOCCTL_STATE_DISCONNECTED; // Log Shutdown INFO_LOG(SCENET, "FriendFinder: End of Friend Finder Thread"); diff --git a/Core/HLE/proAdhoc.h b/Core/HLE/proAdhoc.h index 88e7f5d312..c3607e4d36 100644 --- a/Core/HLE/proAdhoc.h +++ b/Core/HLE/proAdhoc.h @@ -847,9 +847,17 @@ extern int one; extern bool friendFinderRunning; extern SceNetAdhocctlPeerInfo * friends; extern SceNetAdhocctlScanInfo * networks; -extern int threadStatus; +extern int adhocctlState; +extern int adhocConnectionType; // End of Aux vars +enum AdhocConnectionType : int +{ + ADHOC_CONNECT = 0, + ADHOC_CREATE = 1, + ADHOC_JOIN = 2, +}; + // Check if Matching callback is running bool IsMatchingInCallback(SceNetAdhocMatchingContext * context); bool SetMatchingInCallback(SceNetAdhocMatchingContext* context, bool IsInCB); diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index aa4ab0c2ab..a7d073ee1c 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -469,7 +469,7 @@ void __NetApctlCallbacks() // Must be delayed long enough whenever there is a pending callback. sceKernelDelayThread(delayus); - hleSkipDeadbeef();; + hleSkipDeadbeef(); } static inline u32 AllocUser(u32 size, bool fromTop, const char *name) { diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 24a62fade5..5aaa6aa0c4 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -113,7 +113,7 @@ void __NetAdhocShutdown() { } void __NetAdhocDoState(PointerWrap &p) { - auto s = p.Section("sceNetAdhoc", 1, 4); + auto s = p.Section("sceNetAdhoc", 1, 5); if (!s) return; @@ -156,6 +156,14 @@ void __NetAdhocDoState(PointerWrap &p) { it = 0; } } + if (s >= 5) { + Do(p, adhocConnectionType); + Do(p, adhocctlState); + } + else { + adhocConnectionType = ADHOC_CONNECT; + adhocctlState = ADHOCCTL_STATE_DISCONNECTED; + } if (p.mode == p.MODE_READ) { // Previously, this wasn't being saved. It needs its own space. @@ -276,10 +284,10 @@ static u32 sceNetAdhocctlInit(int stackSize, int prio, u32 productAddr) { } int NetAdhocctl_GetState() { - return threadStatus; + return adhocctlState; } -static int sceNetAdhocctlGetState(u32 ptrToStatus) { +int sceNetAdhocctlGetState(u32 ptrToStatus) { // Library uninitialized if (!netAdhocctlInited) return ERROR_NET_ADHOCCTL_NOT_INITIALIZED; @@ -1044,7 +1052,7 @@ static int sceNetAdhocctlGetAdhocId(u32 productStructAddr) { return ERROR_NET_ADHOCCTL_NOT_INITIALIZED; } -static int sceNetAdhocctlScan() { +int sceNetAdhocctlScan() { INFO_LOG(SCENET, "sceNetAdhocctlScan() at %08x", currentMIPS->pc); // Library initialized @@ -1052,14 +1060,14 @@ static int sceNetAdhocctlScan() { // Wait until Not connected if (friendFinderRunning) { int cnt = 0; - while ((threadStatus != ADHOCCTL_STATE_DISCONNECTED) && (cnt < adhocDefaultTimeout)) { + while ((adhocctlState != ADHOCCTL_STATE_DISCONNECTED) && (cnt < adhocDefaultTimeout)) { sleep_ms(1); cnt++; } } - if (threadStatus == ADHOCCTL_STATE_DISCONNECTED) { - threadStatus = ADHOCCTL_STATE_SCANNING; + if (adhocctlState == ADHOCCTL_STATE_DISCONNECTED) { + adhocctlState = ADHOCCTL_STATE_SCANNING; // Reset Networks/Group list to prevent other threads from using these soon to be replaced networks peerlock.lock(); @@ -1075,24 +1083,24 @@ static int sceNetAdhocctlScan() { if (iResult == SOCKET_ERROR) { int error = errno; ERROR_LOG(SCENET, "Socket error (%i) when sending", error); - threadStatus = ADHOCCTL_STATE_DISCONNECTED; + adhocctlState = ADHOCCTL_STATE_DISCONNECTED; //if (error == ECONNABORTED || error == ECONNRESET || error == ENOTCONN) return ERROR_NET_ADHOCCTL_NOT_INITIALIZED; // A case where it need to reconnect to AdhocServer return ERROR_NET_ADHOCCTL_DISCONNECTED; // ERROR_NET_ADHOCCTL_BUSY } // Does Connected Event's mipscall need be executed after returning from sceNetAdhocctlScan ? - notifyAdhocctlHandlers(ADHOCCTL_EVENT_SCAN, 0); - hleCheckCurrentCallbacks(); + //notifyAdhocctlHandlers(ADHOCCTL_EVENT_SCAN, 0); + //hleCheckCurrentCallbacks(); // 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) { + /*if (friendFinderRunning) { int cnt = 0; - while ((threadStatus == ADHOCCTL_STATE_SCANNING) && (cnt < adhocDefaultTimeout)) { + while ((adhocctlState == ADHOCCTL_STATE_SCANNING) && (cnt < adhocDefaultTimeout)) { sleep_ms(1); cnt++; } - } + }*/ //sceKernelDelayThread(adhocEventPollDelayMS * 1000); //hleDelayResult(0, "give time to init/cleanup", adhocEventPollDelayMS * 1000); @@ -1109,7 +1117,7 @@ static int sceNetAdhocctlScan() { return ERROR_NET_ADHOCCTL_NOT_INITIALIZED; } -static int sceNetAdhocctlGetScanInfo(u32 sizeAddr, u32 bufAddr) { +int sceNetAdhocctlGetScanInfo(u32 sizeAddr, u32 bufAddr) { s32_le *buflen = NULL; if (Memory::IsValidAddress(sizeAddr)) buflen = (s32_le *)Memory::GetPointer(sizeAddr); SceNetAdhocctlScanInfoEmu *buf = NULL; @@ -1245,7 +1253,7 @@ u32 NetAdhocctl_Disconnect() { // Library initialized if (netAdhocctlInited) { // Connected State (Adhoc Mode) - if (threadStatus != ADHOCCTL_STATE_DISCONNECTED) { // (threadStatus == ADHOCCTL_STATE_CONNECTED) + if (adhocctlState != ADHOCCTL_STATE_DISCONNECTED) { // (threadStatus == ADHOCCTL_STATE_CONNECTED) // Clear Network Name memset(¶meter.group_name, 0, sizeof(parameter.group_name)); @@ -1265,7 +1273,7 @@ u32 NetAdhocctl_Disconnect() { } // Set Disconnected State - threadStatus = ADHOCCTL_STATE_DISCONNECTED; + adhocctlState = ADHOCCTL_STATE_DISCONNECTED; // Free Network Lock //_freeNetworkLock(); @@ -1324,7 +1332,7 @@ static u32 sceNetAdhocctlDelHandler(u32 handlerID) { int NetAdhocctl_Term() { if (netAdhocctlInited) { - if (threadStatus != ADHOCCTL_STATE_DISCONNECTED) + if (adhocctlState != ADHOCCTL_STATE_DISCONNECTED) NetAdhocctl_Disconnect(); // Terminate Adhoc Threads @@ -1512,14 +1520,14 @@ int NetAdhocctl_Create(const char* groupName) { // Wait until Not connected if (friendFinderRunning) { int cnt = 0; - while ((threadStatus != ADHOCCTL_STATE_DISCONNECTED && threadStatus != ADHOCCTL_STATE_SCANNING) && (cnt < adhocDefaultTimeout)) { + while ((adhocctlState != ADHOCCTL_STATE_DISCONNECTED && adhocctlState != 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)) { + if ((adhocctlState == ADHOCCTL_STATE_DISCONNECTED) || (adhocctlState == ADHOCCTL_STATE_SCANNING)) { // Set Network Name if (groupNameStruct != NULL) parameter.group_name = *groupNameStruct; @@ -1546,7 +1554,7 @@ int NetAdhocctl_Create(const char* groupName) { ERROR_LOG(SCENET, "Socket error (%i) when sending", errno); //return ERROR_NET_ADHOCCTL_NOT_INITIALIZED; // ERROR_NET_ADHOCCTL_DISCONNECTED; // ERROR_NET_ADHOCCTL_BUSY; //Faking success, to prevent Full Auto 2 from freezing while Initializing Network - threadStatus = ADHOCCTL_STATE_CONNECTED; + adhocctlState = ADHOCCTL_STATE_CONNECTED; // Notify Event Handlers, Needed for the Nickname to be shown on the screen when success is faked // Might be better not to notify the game when faking success (failed to connect to adhoc server), at least the player will know that it failed to connect //__UpdateAdhocctlHandlers(ADHOCCTL_EVENT_CONNECT, 0); //CoreTiming::ScheduleEvent_Threadsafe_Immediate(eventAdhocctlHandlerUpdate, join32(ADHOCCTL_EVENT_CONNECT, 0)); @@ -1558,17 +1566,17 @@ int NetAdhocctl_Create(const char* groupName) { //setConnectionStatus(1); // Connected Event's mipscall need be executed before returning from sceNetAdhocctlCreate (or before the next sceNet function?) - notifyAdhocctlHandlers(ADHOCCTL_EVENT_CONNECT, 0); + //notifyAdhocctlHandlers(ADHOCCTL_EVENT_CONNECT, 0); // Wait for Status to be connected to prevent Ford Street Racing from Failed to create game session // TODO: Do this async while Delaying HLE Result - if (friendFinderRunning) { // This is thread-unsafe + /*if (friendFinderRunning) { // This is thread-unsafe int cnt = 0; - while ((threadStatus != ADHOCCTL_STATE_CONNECTED) && (cnt < adhocDefaultTimeout)) { // This is thread-unsafe + while ((adhocctlState != ADHOCCTL_STATE_CONNECTED) && (cnt < adhocDefaultTimeout)) { // This is thread-unsafe sleep_ms(1); cnt++; } - } + }*/ //sceKernelDelayThreadCB(adhocEventDelayMS * 1000); //hleCheckCurrentCallbacks(); @@ -1602,24 +1610,24 @@ int sceNetAdhocctlCreate(const char *groupName) { return -1; } + adhocConnectionType = ADHOC_CREATE; return NetAdhocctl_Create(groupName); } -static int sceNetAdhocctlConnect(u32 ptrToGroupName) { - if (Memory::IsValidAddress(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 +int sceNetAdhocctlConnect(const char* groupName) { + 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(%s) at %08x", grpName, currentMIPS->pc); + if (!g_Config.bEnableWlan) { + return -1; + } - INFO_LOG(SCENET, "sceNetAdhocctlConnect(groupName=%s) at %08x", grpName, currentMIPS->pc); - return NetAdhocctl_Create(groupName); - } - - return ERROR_NET_ADHOC_INVALID_ARG; // ERROR_NET_ADHOC_INVALID_ADDR; + adhocConnectionType = ADHOC_CONNECT; + return NetAdhocctl_Create(groupName); } -static int sceNetAdhocctlJoin(u32 scanInfoAddr) { - WARN_LOG(SCENET, "UNTESTED sceNetAdhocctlJoin(%08x) at %08x", scanInfoAddr, currentMIPS->pc); +int sceNetAdhocctlJoin(u32 scanInfoAddr) { + INFO_LOG(SCENET, "sceNetAdhocctlJoin(%08x) at %08x", scanInfoAddr, currentMIPS->pc); if (!g_Config.bEnableWlan) { return -1; } @@ -1631,11 +1639,14 @@ static int sceNetAdhocctlJoin(u32 scanInfoAddr) { if (Memory::IsValidAddress(scanInfoAddr)) { SceNetAdhocctlScanInfoEmu* sinfo = (SceNetAdhocctlScanInfoEmu*)Memory::GetPointer(scanInfoAddr); - //while (true) sleep_ms(1); + char grpName[9] = { 0 }; + memcpy(grpName, sinfo->group_name.data, ADHOCCTL_GROUPNAME_LEN); // Copied to null-terminated var to prevent unexpected behaviour on Logs + DEBUG_LOG(SCENET, "sceNetAdhocctlJoin - Group: %s", grpName); // We can ignore minor connection process differences here // TODO: Adhoc Server may need to be changed to differentiate between Host/Create and Join, otherwise it can't support multiple Host using the same Group name, thus causing one of the Host to be confused being treated as Join. - return NetAdhocctl_Create((const char*)&sinfo->group_name); + adhocConnectionType = ADHOC_JOIN; + return NetAdhocctl_Create(grpName); } // Invalid Argument @@ -4055,26 +4066,48 @@ void __NetTriggerCallbacks() args[0] = flags; args[1] = error; - //if (/*__KernelGetCurThread() == threadAdhocID &&*/ (!__IsInInterrupt() && __KernelIsDispatchEnabled() && !__KernelInCallback()) && IsAdhocctlInCallback() == 0) + // FIXME: When Joining a group, Do we need to wait for group creator's peer data before triggering the callback to make sure the game not to thinks we're the group creator? + if (flags != ADHOCCTL_EVENT_CONNECT || adhocConnectionType != ADHOC_JOIN || getActivePeerCount() > 0) { // Since 0 is a valid index to types_ we use -1 to detects if it was loaded from an old save state if (actionAfterAdhocMipsCall < 0) { actionAfterAdhocMipsCall = __KernelRegisterActionType(AfterAdhocMipsCall::Create); } + + delayus = (adhocEventPollDelayMS + 2 * adhocExtraPollDelayMS) * 1000; // Added an extra delay to prevent I/O Timing method from causing disconnection + switch (flags) { + case ADHOCCTL_EVENT_CONNECT: + adhocctlState = ADHOCCTL_STATE_CONNECTED; + delayus = (adhocEventDelayMS + 2 * adhocExtraPollDelayMS) * 1000; // May affects Dissidia 012 and GTA VCS + break; + case ADHOCCTL_EVENT_SCAN: // notified only when scan completed? + adhocctlState = ADHOCCTL_STATE_DISCONNECTED; + break; + case ADHOCCTL_EVENT_DISCONNECT: + adhocctlState = ADHOCCTL_STATE_DISCONNECTED; + break; + case ADHOCCTL_EVENT_GAME: + adhocctlState = ADHOCCTL_STATE_GAMEMODE; + break; + case ADHOCCTL_EVENT_DISCOVER: + adhocctlState = ADHOCCTL_STATE_DISCOVER; + break; + case ADHOCCTL_EVENT_WOL_INTERRUPT: + adhocctlState = ADHOCCTL_STATE_WOL; + break; + case ADHOCCTL_EVENT_ERROR: + adhocctlState = ADHOCCTL_STATE_DISCONNECTED; + break; + } + for (std::map::iterator it = adhocctlHandlers.begin(); it != adhocctlHandlers.end(); ++it) { DEBUG_LOG(SCENET, "AdhocctlCallback: [ID=%i][EVENT=%i]", it->first, flags); args[2] = it->second.argument; AfterAdhocMipsCall* after = (AfterAdhocMipsCall*)__KernelCreateAction(actionAfterAdhocMipsCall); after->SetData(it->first, flags, args[2]); - //SetAdhocctlInCallback(true); - //__KernelDirectMipsCall(it->second.entryPoint, after, args, 3, true); hleEnqueueCall(it->second.entryPoint, 3, args, after); } adhocctlEvents.pop_front(); - if (flags == ADHOCCTL_EVENT_CONNECT) - delayus = (adhocEventDelayMS + 2*adhocExtraPollDelayMS) * 1000; // May affects Dissidia 012 and GTA VCS - else - delayus = (adhocEventPollDelayMS + 2*adhocExtraPollDelayMS) * 1000; // Added an extra delay to prevent I/O Timing method from causing disconnection } } @@ -4118,7 +4151,7 @@ void __NetMatchingCallbacks() //(int matchingId) const HLEFunction sceNetAdhoc[] = { {0XE1D621D7, &WrapU_V, "sceNetAdhocInit", 'x', "" }, {0XA62C6F57, &WrapI_V, "sceNetAdhocTerm", 'i', "" }, - {0X0AD043ED, &WrapI_U, "sceNetAdhocctlConnect", 'i', "x" }, + {0X0AD043ED, &WrapI_C, "sceNetAdhocctlConnect", 'i', "s" }, {0X6F92741B, &WrapI_CIIU, "sceNetAdhocPdpCreate", 'i', "siix" }, {0XABED3790, &WrapI_ICUVIII, "sceNetAdhocPdpSend", 'i', "isxpiii" }, {0XDFE53E03, &WrapI_IVVVVUI, "sceNetAdhocPdpRecv", 'i', "ippppxi" }, @@ -4386,7 +4419,7 @@ const HLEFunction sceNetAdhocctl[] = { {0X20B317A0, &WrapU_UU, "sceNetAdhocctlAddHandler", 'x', "xx" }, {0X6402490B, &WrapU_U, "sceNetAdhocctlDelHandler", 'x', "x" }, {0X34401D65, &WrapU_V, "sceNetAdhocctlDisconnect", 'x', "" }, - {0X0AD043ED, &WrapI_U, "sceNetAdhocctlConnect", 'i', "x" }, + {0X0AD043ED, &WrapI_C, "sceNetAdhocctlConnect", 'i', "s" }, {0X08FFF7A0, &WrapI_V, "sceNetAdhocctlScan", 'i', "" }, {0X75ECD386, &WrapI_U, "sceNetAdhocctlGetState", 'i', "x" }, {0X8916C003, &WrapI_CU, "sceNetAdhocctlGetNameByAddr", 'i', "sx" }, diff --git a/Core/HLE/sceNetAdhoc.h b/Core/HLE/sceNetAdhoc.h index 98a859411d..bcd3ad6c73 100644 --- a/Core/HLE/sceNetAdhoc.h +++ b/Core/HLE/sceNetAdhoc.h @@ -41,7 +41,18 @@ void __UpdateAdhocctlHandlers(u32 flags, u32 error); void __UpdateMatchingHandler(MatchingArgs params); // I have to call this from netdialog +int sceNetAdhocctlGetState(u32 ptrToStatus); int sceNetAdhocctlCreate(const char * groupName); +int sceNetAdhocctlConnect(const char* groupName); +int sceNetAdhocctlJoin(u32 scanInfoAddr); +int sceNetAdhocctlScan(); +int sceNetAdhocctlGetScanInfo(u32 sizeAddr, u32 bufAddr); + +int NetAdhocMatching_Term(); +int NetAdhocctl_Term(); +int NetAdhocctl_GetState(); +int NetAdhocctl_Create(const char* groupName); +int NetAdhoc_Term(); // May need to use these from sceNet.cpp extern bool netAdhocInited; @@ -60,7 +71,3 @@ extern u32_le dummyThreadCode[3]; extern u32 matchingThreadHackAddr; extern u32_le matchingThreadCode[3]; -int NetAdhocMatching_Term(); -int NetAdhocctl_Term(); -int NetAdhocctl_GetState(); -int NetAdhoc_Term(); From 7d2236db2f9ac43da0c164ff33c2c377cec9de2b Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Mon, 31 Aug 2020 11:00:04 +0700 Subject: [PATCH 2/7] Increasing the shutdown delay so that there will be at least one "4=sceUtilityNetconfGetStatus()" after "0=sceUtilityNetconfShutdownStart()" instead of directly to "0=sceUtilityNetconfGetStatus()" (just like what happen when using utility.prx file with KHBBS on JPCSP) --- Core/Dialog/PSPNetconfDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/Dialog/PSPNetconfDialog.cpp b/Core/Dialog/PSPNetconfDialog.cpp index a218382d68..93571ce2ff 100644 --- a/Core/Dialog/PSPNetconfDialog.cpp +++ b/Core/Dialog/PSPNetconfDialog.cpp @@ -45,7 +45,7 @@ static const float FONT_SCALE = 0.65f; // Needs testing. const static int NET_INIT_DELAY_US = 300000; -const static int NET_SHUTDOWN_DELAY_US = 26000; +const static int NET_SHUTDOWN_DELAY_US = 260000; const static int NET_RUNNING_DELAY_US = 1000000; // KHBBS is showing adhoc dialog for about 3-4 seconds, but feels too long, so we're faking it to 1 sec instead to let players read the text const static int NET_CONNECT_TIMEOUT = 5000000; From 439171fc563f0161c15934d678fd503a58e2e2f6 Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Tue, 18 Aug 2020 02:01:30 +0700 Subject: [PATCH 3/7] An attempt to reduce the lags/stutters during multiplayer caused by improper blocking behavior. --- Core/HLE/proAdhoc.cpp | 26 +- Core/HLE/proAdhoc.h | 11 +- Core/HLE/sceKernelThread.cpp | 1 + Core/HLE/sceKernelThread.h | 1 + Core/HLE/sceNet.cpp | 6 +- Core/HLE/sceNetAdhoc.cpp | 1195 +++++++++++++++++++++++----------- Core/HLE/sceNetAdhoc.h | 34 + 7 files changed, 898 insertions(+), 376 deletions(-) diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index b8f57cca83..8458b2c9f1 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -55,6 +55,7 @@ bool friendFinderRunning = false; SceNetAdhocctlPeerInfo * friends = NULL; SceNetAdhocctlScanInfo * networks = NULL; SceNetAdhocctlScanInfo * newnetworks = NULL; +u64 adhocctlStartTime = 0; int adhocctlState = ADHOCCTL_STATE_DISCONNECTED; int adhocConnectionType = ADHOC_CONNECT; @@ -71,6 +72,7 @@ std::thread friendFinderThread; std::recursive_mutex peerlock; SceNetAdhocPdpStat * pdp[255]; SceNetAdhocPtpStat * ptp[255]; +std::map ptpConnectCount; std::vector chatLog; std::string name = ""; std::string incoming = ""; @@ -210,6 +212,15 @@ SceNetAdhocctlPeerInfo * findFriend(SceNetEtherAddr * MAC) { return peer; } +int getNonBlockingFlag(int fd) { +#ifdef _WIN32 + return 0; +#else + int sockflag = fcntl(fd, F_GETFL, O_NONBLOCK); + return sockflag & O_NONBLOCK; +#endif +} + void changeBlockingMode(int fd, int nonblocking) { unsigned long on = 1; unsigned long off = 0; @@ -321,6 +332,7 @@ void deleteAllPTP() { // Delete Reference ptp[i] = NULL; + ptpConnectCount.erase(i); } } } @@ -1174,7 +1186,9 @@ void sendChat(std::string chatString) { message = chatString.substr(0, 60); // 64 return chat variable corrupted is it out of memory? strcpy(chat.message, message.c_str()); //Send Chat Messages + changeBlockingMode(metasocket, 0); int chatResult = send(metasocket, (const char *)&chat, sizeof(chat), 0); + changeBlockingMode(metasocket, 1); NOTICE_LOG(SCENET, "Send Chat %s to Adhoc Server", chat.message); name = g_Config.sNickName.c_str(); chatLog.push_back(name.substr(0, 8) + ": " + chat.message); @@ -1245,7 +1259,9 @@ int friendFinder(){ uint8_t opcode = OPCODE_PING; // Send Ping to Server, may failed with socket error 10054/10053 if someone else with the same IP already connected to AdHoc Server (the server might need to be modified to differentiate MAC instead of IP) + changeBlockingMode(metasocket, 0); int iResult = send(metasocket, (const char*)&opcode, 1, 0); + changeBlockingMode(metasocket, 1); if (iResult == SOCKET_ERROR) { ERROR_LOG(SCENET, "FriendFinder: Socket Error (%i) when sending OPCODE_PING", errno); networkInited = false; @@ -1560,7 +1576,10 @@ int getLocalIp(sockaddr_in* SocketAddress) { struct sockaddr_in localAddr; localAddr.sin_addr.s_addr = INADDR_ANY; socklen_t addrLen = sizeof(localAddr); - if (SOCKET_ERROR != getsockname(metasocket, (struct sockaddr*) & localAddr, &addrLen)) { + changeBlockingMode(metasocket, 0); + int ret = getsockname(metasocket, (struct sockaddr*)&localAddr, &addrLen); + changeBlockingMode(metasocket, 1); + if (SOCKET_ERROR != ret) { if (isLocalServer) { localAddr.sin_addr = g_localhostIP.in.sin_addr; } @@ -1905,8 +1924,6 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ sleep_ms(1); cnt++; } - // Switch back to Blocking Behaviour - changeBlockingMode(metasocket, 0); if (iResult == SOCKET_ERROR && errorcode != EISCONN) { char buffer[512]; snprintf(buffer, sizeof(buffer), "Socket error (%i) when connecting to AdhocServer [%s/%s:%u]", errorcode, g_Config.proAdhocServer.c_str(), inet_ntoa(server_addr.sin_addr), ntohs(server_addr.sin_port)); @@ -1924,8 +1941,9 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ strncpy((char *)&packet.name.data, g_Config.sNickName.c_str(), ADHOCCTL_NICKNAME_LEN); packet.name.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; memcpy(packet.game.data, adhoc_id->data, ADHOCCTL_ADHOCID_LEN); + changeBlockingMode(metasocket, 0); int sent = send(metasocket, (char*)&packet, sizeof(packet), 0); - changeBlockingMode(metasocket, 1); // Change to non-blocking + changeBlockingMode(metasocket, 1); if (sent > 0) { socklen_t addrLen = sizeof(LocalIP); memset(&LocalIP, 0, addrLen); diff --git a/Core/HLE/proAdhoc.h b/Core/HLE/proAdhoc.h index c3607e4d36..fa5194f17e 100644 --- a/Core/HLE/proAdhoc.h +++ b/Core/HLE/proAdhoc.h @@ -827,6 +827,7 @@ extern std::thread friendFinderThread; extern std::recursive_mutex peerlock; extern SceNetAdhocPdpStat * pdp[255]; extern SceNetAdhocPtpStat * ptp[255]; +extern std::map ptpConnectCount; union SockAddrIN4 { sockaddr addr; @@ -846,7 +847,8 @@ extern SceNetAdhocMatchingContext * contexts; extern int one; extern bool friendFinderRunning; extern SceNetAdhocctlPeerInfo * friends; -extern SceNetAdhocctlScanInfo * networks; +extern SceNetAdhocctlScanInfo * networks; +extern u64 adhocctlStartTime; extern int adhocctlState; extern int adhocConnectionType; // End of Aux vars @@ -925,6 +927,13 @@ extern int newChat; */ SceNetAdhocctlPeerInfo * findFriend(SceNetEtherAddr * MAC); +/** + * Get the Non-Blocking Mode of the socket + * @param fd File Descriptor of the socket + * @return 1 for non-blocking, 0 for blocking + */ +int getNonBlockingFlag(int fd); + /** * Changes the Blocking Mode of the socket * @param fd File Descriptor of the socket diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 59b3ec3c7e..0e096111c0 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -81,6 +81,7 @@ const WaitTypeNames waitTypeNames[] = { { WAITTYPE_VMEM, "Volatile Mem" }, { WAITTYPE_ASYNCIO, "AsyncIO" }, { WAITTYPE_MICINPUT, "Microphone input"}, + { WAITTYPE_NET, "Network"}, }; const char *getWaitTypeName(WaitType type) diff --git a/Core/HLE/sceKernelThread.h b/Core/HLE/sceKernelThread.h index d36e12c986..bb08bb3b66 100644 --- a/Core/HLE/sceKernelThread.h +++ b/Core/HLE/sceKernelThread.h @@ -107,6 +107,7 @@ enum WaitType : int WAITTYPE_VMEM = 22, WAITTYPE_ASYNCIO = 23, WAITTYPE_MICINPUT = 24, // fake + WAITTYPE_NET = 25, // fake NUM_WAITTYPES }; diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index a7d073ee1c..a34167f275 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -162,7 +162,7 @@ void __NetInit() { SceNetEtherAddr mac; getLocalMac(&mac); - INFO_LOG(SCENET, "LocalHost IP will be %s [%s]", inet_ntoa(g_localhostIP.in.sin_addr), mac2str(&mac).c_str()); + NOTICE_LOG(SCENET, "LocalHost IP will be %s [%s]", inet_ntoa(g_localhostIP.in.sin_addr), mac2str(&mac).c_str()); // TODO: May be we should initialize & cleanup somewhere else than here for PortManager to be used as general purpose for whatever port forwarding PPSSPP needed __UPnPInit(); @@ -250,6 +250,9 @@ void __NetDoState(PointerWrap &p) { netInetInited = cur_netInetInited; netInited = cur_netInited; + // Discard leftover events + apctlEvents.clear(); + // Previously, this wasn't being saved. It needs its own space. if (!apctlThreadHackAddr || (apctlThreadHackAddr && strcmp("apctlThreadHack", kernelMemory.GetBlockTag(apctlThreadHackAddr)) != 0)) { u32 blockSize = sizeof(apctlThreadCode); @@ -582,6 +585,7 @@ static int sceNetInit(u32 poolSize, u32 calloutPri, u32 calloutStack, u32 netini // Clear Socket Translator Memory memset(&pdp, 0, sizeof(pdp)); memset(&ptp, 0, sizeof(ptp)); + ptpConnectCount.clear(); return hleLogSuccessI(SCENET, 0); } diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 5aaa6aa0c4..9d62792037 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -15,6 +15,10 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#if defined(_WIN32) +#include "Common/CommonWindows.h" +#endif + #if !defined(_WIN32) #include #endif @@ -46,6 +50,7 @@ #include "Core/HLE/sceNetAdhoc.h" #include "Core/HLE/sceNet.h" #include "Core/HLE/proAdhocServer.h" +#include "Core/HLE/KernelWaitHelpers.h" #include "i18n/i18n.h" // shared in sceNetAdhoc.h since it need to be used from sceNet.cpp also @@ -56,11 +61,11 @@ bool networkInited = false; static bool netAdhocMatchingInited; int netAdhocMatchingStarted = 0; -int adhocDefaultTimeout = 2000; //5000 +int adhocDefaultTimeout = 2000; //5000 ms int adhocExtraPollDelayMS = 10; //10 int adhocEventPollDelayMS = 100; //100 int adhocMatchingEventDelayMS = 30; //30 -int adhocEventDelayMS = 500; //500; This will affect the duration of "Connecting..." dialog/message box in .Hack//Link and Naruto Ultimate Ninja Heroes 3 +int adhocEventDelayMS = 300; //500; This will affect the duration of "Connecting..." dialog/message box in .Hack//Link and Naruto Ultimate Ninja Heroes 3 SceUID threadAdhocID; @@ -71,6 +76,11 @@ std::map adhocctlHandlers; std::vector matchingThreads; int IsAdhocctlInCB = 0; +int adhocctlNotifyEvent = -1; +int adhocSocketNotifyEvent = -1; +std::map adhocSocketRequests; +std::map sendTargetPeers; + u32 dummyThreadHackAddr = 0; u32_le dummyThreadCode[3]; u32 matchingThreadHackAddr = 0; @@ -78,11 +88,9 @@ u32_le matchingThreadCode[3]; int matchingEventThread(int matchingId); int matchingInputThread(int matchingId); +int AcceptPtpSocket(int ptpId, int newsocket, sockaddr_in& peeraddr, SceNetEtherAddr* addr, u16_le* port); +int PollAdhocSocket(SceNetAdhocPollSd* sds, int count, int timeout); -int sceNetAdhocTerm(); -int sceNetAdhocctlTerm(); -int sceNetAdhocMatchingTerm(); -int sceNetAdhocMatchingSetHelloOpt(int matchingId, int optLenAddr, u32 optDataAddr); void __NetAdhocShutdown() { // Kill AdhocServer Thread @@ -112,6 +120,455 @@ void __NetAdhocShutdown() { } } +static void __AdhocctlNotify(u64 userdata, int cyclesLate) { + SceUID threadID = userdata >> 32; + int uid = (int)(userdata & 0xFFFFFFFF); + + s64 result = 0; + u32 error = 0; + + SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_NET, error); + if (waitID == 0 || error != 0) + return; // FIXME: Is it safe to exit here like this without re-scheduling the event? Will this event be triggered again? What will happen to the result i might want to change if exited here? + + int waitVal = __KernelGetWaitValue(threadID, error); // FIXME: Is this value going to be a valid value if waitID == 0? or it's a value belonged to other event? + if (adhocctlState != waitVal && error == 0) { + // Detecting Adhocctl Initialization using waitVal < 0 + if (waitVal >= 0 || (waitVal < 0 && (g_Config.bEnableWlan && !networkInited))) { + u64 now = (u64)(real_time_now() * 1000.0); + if (now - adhocctlStartTime <= adhocDefaultTimeout) { + // Try again in another 0.5ms until state matched or timedout. + CoreTiming::ScheduleEvent(usToCycles(500) - cyclesLate, adhocctlNotifyEvent, userdata); + return; + } + else + result = 0; // ERROR_NET_ADHOCCTL_BUSY + } + else + result = 0; // Faking successfully connected to adhoc server + } + + //HLEKernel::ResumeFromWait(threadID, WAITTYPE_NET, uid, result); // FIXME: This won't do anything if waitID == 0, not sure what kind of value returned from the HLE which i might want to change here. + __KernelResumeThreadFromWait(threadID, result); // FIXME: Forcing to change the result, will it cause an issue if waitID == 0? + DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %d) Result (%08x) of sceNetAdhocctl - State: %d", waitID, error, (int)result, adhocctlState); +} + +void WaitAdhocctlState(int state, int usec, const char* reason) { + if (adhocctlNotifyEvent < 0) + adhocctlNotifyEvent = CoreTiming::RegisterEvent("__AdhocctlNotify", __AdhocctlNotify); + + int uid = (state < 0)? 1: metasocket; + u64 param = ((u64)__KernelGetCurThread()) << 32 | uid; + adhocctlStartTime = (u64)(real_time_now() * 1000.0); + CoreTiming::ScheduleEvent(usToCycles(usec), adhocctlNotifyEvent, param); + __KernelWaitCurThread(WAITTYPE_NET, uid, state, 0, false, reason); +} + +int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) { + sockaddr_in sin; + memset(&sin, 0, sizeof(sin)); + socklen_t sinlen = sizeof(sin); + + int ret = recvfrom(uid, (char*)req.buffer, *req.length, MSG_PEEK, (sockaddr*)&sin, &sinlen); + int sockerr = errno; + + // Note: UDP must not be received partially, otherwise leftover data in socket's buffer will be discarded + if (ret >= 0 && ret <= *req.length) { + ret = recvfrom(uid, (char*)req.buffer, *req.length, 0, (sockaddr*)&sin, &sinlen); + // UDP can also receives 0 data, while on TCP receiving 0 data = connection gracefully closed, but not sure whether PDP can send/recv 0 data or not tho + if (ret > 0) { + DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %u bytes from %s:%u\n", req.id, getLocalPort(uid), ret, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); + + // Peer MAC + SceNetEtherAddr mac; + + // Find Peer MAC + if (resolveIP(sin.sin_addr.s_addr, &mac)) { + // Provide Sender Information + *req.remoteMAC = mac; + *req.remotePort = ntohs(sin.sin_port) - portOffset; + + // Save Length + *req.length = ret; + + // Update last recv timestamp + peerlock.lock(); + auto peer = findFriend(&mac); + if (peer != NULL) peer->last_recv = CoreTiming::GetGlobalTimeUsScaled(); + peerlock.unlock(); + } + // Unknown Peer + else { + *req.length = ret; + *req.remotePort = ntohs(sin.sin_port) - portOffset; + + WARN_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %i bytes from Unknown Peer %s:%u", req.id, getLocalPort(uid), ret, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); + } + } + result = 0; + } + // On Windows: recvfrom on UDP can get error WSAECONNRESET when previous sendto's destination is unreachable (or destination port is not bound yet), may need to disable SIO_UDP_CONNRESET error + else if (sockerr == EAGAIN || sockerr == EWOULDBLOCK || sockerr == ECONNRESET || sockerr == ETIMEDOUT) { + u64 now = (u64)(real_time_now() * 1000000.0); + if (req.timeout == 0 || now - req.startTime <= req.timeout) { + + return -1; + } + else + result = ERROR_NET_ADHOC_TIMEOUT; + } + else + result = ERROR_NET_ADHOC_INVALID_ARG; // ERROR_NET_ADHOC_DISCONNECTED + + if (ret == SOCKET_ERROR) + DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv[%i]: Socket Error (%i)", req.id, sockerr); + + return 0; +} + +int DoBlockingPdpSend(int uid, AdhocSocketRequest& req, s64& result, AdhocSendTargets& targetPeers) { + SceNetAdhocPdpStat* pdpsocket = pdp[req.id - 1]; + + result = 0; + bool retry = false; + for (auto peer = targetPeers.peers.begin(); peer != targetPeers.peers.end(); ) { + // Fill in Target Structure + sockaddr_in target; + target.sin_family = AF_INET; + target.sin_addr.s_addr = peer->ip; + target.sin_port = htons(peer->port + ((isOriPort && !isPrivateIP(peer->ip)) ? 0 : portOffset)); + + int ret = sendto(pdpsocket->id, (const char*)req.buffer, targetPeers.length, 0, (sockaddr*)&target, sizeof(target)); + int sockerr = errno; + + if (ret >= 0) { + DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u](B): Sent %u bytes to %s:%u\n", uid, getLocalPort(pdpsocket->id), ret, inet_ntoa(target.sin_addr), ntohs(target.sin_port)); + // Remove successfully sent to peer to prevent sending the same data again during a retry + peer = targetPeers.peers.erase(peer); + } + else { + if (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK || sockerr == ETIMEDOUT)) { + u64 now = (u64)(real_time_now() * 1000000.0); + if (req.timeout == 0 || now - req.startTime <= req.timeout) { + retry = true; + } + else + // FIXME: Does Broadcast always success? even with timeout/blocking? + result = ERROR_NET_ADHOC_TIMEOUT; + } + ++peer; + } + + if (ret == SOCKET_ERROR) + DEBUG_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPdpSend[%i:%u->%u](B) [size=%i]", sockerr, uid, getLocalPort(pdpsocket->id), ntohs(target.sin_port), targetPeers.length); + } + + if (retry) + return -1; + + return 0; +} + +int DoBlockingPtpSend(int uid, AdhocSocketRequest& req, s64& result) { + SceNetAdhocPtpStat* ptpsocket = ptp[req.id - 1]; + + // Send Data + int ret = send(uid, (const char*)req.buffer, *req.length, 0); + int sockerr = errno; + + // Success + if (ret > 0) { + // Save Length + *req.length = ret; + + DEBUG_LOG(SCENET, "sceNetAdhocPtpSend[%i:%u]: Sent %u bytes to %s:%u\n", req.id, ptpsocket->lport, ret, mac2str(&ptpsocket->paddr).c_str(), ptpsocket->pport); + + // Return Success + result = 0; + } + + // Non-Critical Error + else if (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK || sockerr == ETIMEDOUT)) { + u64 now = (u64)(real_time_now() * 1000000.0); + if (req.timeout == 0 || now - req.startTime <= req.timeout) { + + return -1; + } + else + result = ERROR_NET_ADHOC_TIMEOUT; + } + + // Change Socket State + ptpsocket->state = ADHOC_PTP_STATE_CLOSED; + + // Disconnected + result = ERROR_NET_ADHOC_DISCONNECTED; + + if (ret == SOCKET_ERROR) + DEBUG_LOG(SCENET, "sceNetAdhocPtpSend[%i]: Socket Error (%i)", req.id, sockerr); + + return 0; +} + +int DoBlockingPtpRecv(int uid, AdhocSocketRequest& req, s64& result) { + SceNetAdhocPtpStat* ptpsocket = ptp[req.id - 1]; + + int ret = recv(uid, (char*)req.buffer, *req.length, 0); + int sockerr = errno; + + // Received Data + if (ret > 0) { + DEBUG_LOG(SCENET, "sceNetAdhocPtpRecv[%i:%u]: Received %u bytes from %s:%u\n", req.id, ptpsocket->lport, ret, mac2str(&ptpsocket->paddr).c_str(), ptpsocket->pport); + // Save Length + *req.length = ret; + + // Update last recv timestamp + peerlock.lock(); + auto peer = findFriend(&ptpsocket->paddr); + if (peer != NULL) peer->last_recv = CoreTiming::GetGlobalTimeUsScaled(); + peerlock.unlock(); + + result = 0; + } + // Non-Critical Error + else if (ret == SOCKET_ERROR && (sockerr == EAGAIN || sockerr == EWOULDBLOCK || sockerr == ETIMEDOUT)) { + u64 now = (u64)(real_time_now() * 1000000.0); + if (req.timeout == 0 || now - req.startTime <= req.timeout) { + return -1; + } + else + result = ERROR_NET_ADHOC_TIMEOUT; + } + else { + // Change Socket State + ptpsocket->state = ADHOC_PTP_STATE_CLOSED; + + // Disconnected + result = ERROR_NET_ADHOC_DISCONNECTED; // ERROR_NET_ADHOC_INVALID_ARG + } + + if (ret == SOCKET_ERROR) + DEBUG_LOG(SCENET, "sceNetAdhocPtpRecv[%i]: Socket Error (%i)", req.id, sockerr); + + return 0; +} + +int DoBlockingPtpAccept(int uid, AdhocSocketRequest& req, s64& result) { + sockaddr_in sin; + memset(&sin, 0, sizeof(sin)); + socklen_t sinlen = sizeof(sin); + + // Accept Connection + int ret = accept(uid, (sockaddr*)&sin, &sinlen); + int sockerr = errno; + + // Accepted New Connection + if (ret > 0) { + int newid = AcceptPtpSocket(req.id, ret, sin, req.remoteMAC, req.remotePort); + if (newid > 0) + result = newid; + } + else if (ret == SOCKET_ERROR && connectInProgress(sockerr)) { + u64 now = (u64)(real_time_now() * 1000000.0); + if (req.timeout == 0 || now - req.startTime <= req.timeout) { + return -1; + } + else + result = ERROR_NET_ADHOC_TIMEOUT; + } + else + result = ERROR_NET_ADHOC_INVALID_ARG; //ERROR_NET_ADHOC_TIMEOUT + + if (ret == SOCKET_ERROR) + DEBUG_LOG(SCENET, "sceNetAdhocPtpAccept[%i]: Socket Error (%i)", req.id, sockerr); + + return 0; +} + +int DoBlockingPtpConnect(int uid, AdhocSocketRequest& req, s64& result) { + fd_set readfds, writefds; + timeval tval; + FD_ZERO(&readfds); + FD_SET(uid, &readfds); + writefds = readfds; + tval.tv_sec = 0; + tval.tv_usec = 0; + + // Wait for Connection (assuming "connect" has been called before) + int ret = select(uid + 1, &readfds, &writefds, NULL, &tval); + int sockerr = errno; + + // Connection is ready + if (ret > 0) { + SceNetAdhocPtpStat* ptpsocket = ptp[req.id - 1]; + sockaddr_in sin; + memset(&sin, 0, sizeof(sin)); + socklen_t sinlen = sizeof(sin); + getpeername(uid, (sockaddr*)&sin, &sinlen); + + // Set Connected State + ptpsocket->state = ADHOC_PTP_STATE_ESTABLISHED; + + INFO_LOG(SCENET, "sceNetAdhocPtpConnect[%i:%u]: Established (%s:%u)", req.id, ptpsocket->lport, inet_ntoa(sin.sin_addr), ptpsocket->pport); + + // Success + result = 0; + } + // Timeout + else if (ret == 0) { + u64 now = (u64)(real_time_now() * 1000000.0); + if (req.timeout == 0 || now - req.startTime <= req.timeout) { + return -1; + } + else + result = ERROR_NET_ADHOC_TIMEOUT; + } + else + result = ERROR_NET_ADHOC_CONNECTION_REFUSED; // ERROR_NET_ADHOC_TIMEOUT; + + if (ret == SOCKET_ERROR) + DEBUG_LOG(SCENET, "sceNetAdhocPtpConnect[%i]: Socket Error (%i)", req.id, sockerr); + + return 0; +} + +int DoBlockingAdhocPollSocket(int uid, AdhocSocketRequest& req, s64& result) { + SceNetAdhocPollSd* sds = (SceNetAdhocPollSd*)req.buffer; + int ret = PollAdhocSocket(sds, req.id, 0); + if (ret <= 0) { + u64 now = (u64)(real_time_now() * 1000000.0); + if (req.timeout == 0 || now - req.startTime <= req.timeout) { + return -1; + } + else if (ret == 0) + result = ERROR_NET_ADHOC_TIMEOUT; + else + result = ERROR_NET_ADHOC_EXCEPTION_EVENT; + } + result = ret; + + return 0; +} + +static void __AdhocSocketNotify(u64 userdata, int cyclesLate) { + SceUID threadID = userdata >> 32; + int uid = (int)(userdata & 0xFFFFFFFF); // fd/socket id + + s64 result = -1; + u32 error = 0; + int delayUS = 500; + + SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_NET, error); + if (waitID == 0 || error != 0) + return; + + // Socket not found?! Should never happened! but if it ever happen should we just exit here or need to wake the thread first? + if (adhocSocketRequests.find(uid) == adhocSocketRequests.end()) { + WARN_LOG(SCENET, "sceNetAdhoc Socket WaitID(%i) not found!", uid); + //__KernelResumeThreadFromWait(threadID, ERROR_NET_ADHOC_TIMEOUT); + return; + } + + AdhocSocketRequest req = adhocSocketRequests[uid]; + + switch (req.type) { + case PDP_SEND: + if (sendTargetPeers.find(uid) == sendTargetPeers.end()) { + // No destination peers? + result = 0; + break; + } + if (DoBlockingPdpSend(uid, req, result, sendTargetPeers[uid])) { + // Try again in another 0.5ms until data available or timedout. + CoreTiming::ScheduleEvent(usToCycles(delayUS) - cyclesLate, adhocSocketNotifyEvent, userdata); + return; + } + sendTargetPeers.erase(uid); + break; + + case PDP_RECV: + if (DoBlockingPdpRecv(uid, req, result)) { + // Try again in another 0.5ms until data available or timedout. + CoreTiming::ScheduleEvent(usToCycles(delayUS) - cyclesLate, adhocSocketNotifyEvent, userdata); + return; + } + break; + + case PTP_SEND: + if (DoBlockingPtpSend(uid, req, result)) { + // Try again in another 0.5ms until data available or timedout. + CoreTiming::ScheduleEvent(usToCycles(delayUS) - cyclesLate, adhocSocketNotifyEvent, userdata); + return; + } + break; + + case PTP_RECV: + if (DoBlockingPtpRecv(uid, req, result)) { + // Try again in another 0.5ms until data available or timedout. + CoreTiming::ScheduleEvent(usToCycles(delayUS) - cyclesLate, adhocSocketNotifyEvent, userdata); + return; + } + break; + + case PTP_ACCEPT: + if (DoBlockingPtpAccept(uid, req, result)) { + // Try again in another 0.5ms until data available or timedout. + CoreTiming::ScheduleEvent(usToCycles(delayUS) - cyclesLate, adhocSocketNotifyEvent, userdata); + return; + } + break; + + case PTP_CONNECT: + if (DoBlockingPtpConnect(uid, req, result)) { + // Try again in another 0.5ms until data available or timedout. + CoreTiming::ScheduleEvent(usToCycles(delayUS) - cyclesLate, adhocSocketNotifyEvent, userdata); + return; + } + break; + + case ADHOC_POLL_SOCKET: + if (DoBlockingAdhocPollSocket(uid, req, result)) { + // Try again in another 0.5ms until data available or timedout. + CoreTiming::ScheduleEvent(usToCycles(delayUS) - cyclesLate, adhocSocketNotifyEvent, userdata); + return; + } + break; + } + + // We are done with this socket + adhocSocketRequests.erase(uid); + + __KernelResumeThreadFromWait(threadID, result); + DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %d) Result (%08x) of sceNetAdhoc - SocketID: %d", waitID, error, (int)result, req.id); +} + +int WaitBlockingAdhocSocket(int socketId, int type, int pspSocketId, void* buffer, s32_le* len, u32 timeoutUS, SceNetEtherAddr* remoteMAC, u16_le* remotePort, const char* reason) { + if (adhocSocketRequests.find(socketId) != adhocSocketRequests.end()) { + WARN_LOG(SCENET, "sceNetAdhoc - WaitID[%d] already existed, Socket[%d] is busy!", socketId, pspSocketId); + return ERROR_NET_ADHOC_BUSY; + } + + if (adhocSocketNotifyEvent < 0) + adhocSocketNotifyEvent = CoreTiming::RegisterEvent("__AdhocSocketNotify", __AdhocSocketNotify); + + if (getNonBlockingFlag(socketId) == 0) + changeBlockingMode(socketId, 1); + + u32 tmout = timeoutUS; + if (tmout > 0) + tmout = std::max(tmout, minSocketTimeoutUS); + + u64 param = ((u64)__KernelGetCurThread()) << 32 | socketId; + u64 startTime = (u64)(real_time_now() * 1000000.0); + adhocSocketRequests[socketId] = { type, pspSocketId, buffer, len, tmout, startTime, remoteMAC, remotePort }; + // Some games (ie. Power Stone Collection) are using as small as 100 usec timeout + CoreTiming::ScheduleEvent(usToCycles(100), adhocSocketNotifyEvent, param); + __KernelWaitCurThread(WAITTYPE_NET, socketId, 0, 0, false, reason); + + // Fallback return value + return ERROR_NET_ADHOC_TIMEOUT; +} + void __NetAdhocDoState(PointerWrap &p) { auto s = p.Section("sceNetAdhoc", 1, 5); if (!s) @@ -159,10 +616,16 @@ void __NetAdhocDoState(PointerWrap &p) { if (s >= 5) { Do(p, adhocConnectionType); Do(p, adhocctlState); + Do(p, adhocctlNotifyEvent); + CoreTiming::RestoreRegisterEvent(adhocctlNotifyEvent, "__AdhocctlNotify", __AdhocctlNotify); + Do(p, adhocSocketNotifyEvent); + CoreTiming::RestoreRegisterEvent(adhocSocketNotifyEvent, "__AdhocSocketNotify", __AdhocSocketNotify); } else { adhocConnectionType = ADHOC_CONNECT; adhocctlState = ADHOCCTL_STATE_DISCONNECTED; + adhocctlNotifyEvent = -1; + adhocSocketNotifyEvent = -1; } if (p.mode == p.MODE_READ) { @@ -179,6 +642,12 @@ void __NetAdhocDoState(PointerWrap &p) { // 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)); + + // Discard leftover events + adhocctlEvents.clear(); + matchingEvents.clear(); + adhocSocketRequests.clear(); + sendTargetPeers.clear(); // Let's not change "Inited" value when Loading SaveState to prevent memory & port leaks netAdhocMatchingInited = cur_netAdhocMatchingInited; @@ -197,15 +666,6 @@ void __UpdateMatchingHandler(MatchingArgs ArgsPtr) { matchingEvents.push_back(ArgsPtr); } -static int getBlockingFlag(int id) { -#ifdef _WIN32 - return 0; -#else - int sockflag = fcntl(id, F_GETFL, O_NONBLOCK); - return sockflag & O_NONBLOCK; -#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; @@ -219,12 +679,21 @@ u32_le __CreateHLELoop(u32_le *loopAddr, const char *sceFuncName, const char *hl return dummyThreadHackAddr; } +void __AdhocNotifInit() { + adhocctlNotifyEvent = CoreTiming::RegisterEvent("__AdhocctlNotify", __AdhocctlNotify); + adhocSocketNotifyEvent = CoreTiming::RegisterEvent("__AdhocSocketNotify", __AdhocSocketNotify); + + adhocSocketRequests.clear(); + sendTargetPeers.clear(); +} + void __NetAdhocInit() { friendFinderRunning = false; netAdhocInited = false; netAdhocctlInited = false; netAdhocMatchingInited = false; adhocctlHandlers.clear(); + __AdhocNotifInit(); __AdhocServerInit(); // Create built-in AdhocServer Thread @@ -273,13 +742,14 @@ static u32 sceNetAdhocctlInit(int stackSize, int prio, u32 productAddr) { } // Need to make sure to be connected to adhoc server before returning to prevent GTA VCS failed to create/join a group and unable to see any game room - int cnt = 0; - while (g_Config.bEnableWlan && !networkInited && (cnt < adhocDefaultTimeout)) { - sleep_ms(1); - cnt++; + int us = adhocExtraPollDelayMS * 1000; + if (g_Config.bEnableWlan && !networkInited) { + WaitAdhocctlState(-1, us, "adhoc init"); } + // Give a little time for friendFinder thread to be ready before the game use the next sceNet functions, should've checked for friendFinderRunning status instead of guessing the time? + else + hleDelayResult(0, "give some time", us); - //hleDelayResult(0, "give some time", adhocEventPollDelayMS * 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? return 0; } @@ -539,13 +1009,27 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int //_acquireNetworkLock(); // Send Data. UDP are guaranteed to be sent as a whole or nothing(failed if len > SO_MAX_MSG_SIZE), and never be partially sent/recv - changeBlockingMode(socket->id, flag); + changeBlockingMode(socket->id, 1); int sent = sendto(socket->id, (const char *)data, len, 0, (sockaddr *)&target, sizeof(target)); int error = errno; + if (sent == SOCKET_ERROR) { + // Simulate blocking behaviour with non-blocking socket + if (!flag && (error == EAGAIN || error == EWOULDBLOCK || error == ETIMEDOUT)) { + if (sendTargetPeers.find(socket->id) != sendTargetPeers.end()) { + DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u]: Socket(%d) is Busy!", id, getLocalPort(socket->id), socket->id); + return ERROR_NET_ADHOC_BUSY; + } + + AdhocSendTargets dest = { len, {}, false }; + dest.peers.push_back({ target.sin_addr.s_addr, dport }); + sendTargetPeers[socket->id] = dest; + return WaitBlockingAdhocSocket(socket->id, PDP_SEND, id, data, nullptr, timeout, nullptr, nullptr, "pdp send"); + } + 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(); @@ -558,13 +1042,12 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int return 0; // sent; // MotorStorm will try to resend if return value is not 0 } - // Blocking Situation - if (flag) return ERROR_NET_ADHOC_WOULD_BLOCK; + // Non-Blocking + if (flag) + return ERROR_NET_ADHOC_WOULD_BLOCK; - // Timeout - /*if (timeout > 0) - return ERROR_NET_ADHOC_TIMEOUT;*/ - return ERROR_NET_ADHOC_INVALID_ADDR; // There is no concept of Timeout when sending UDP due to no ACK, not sure about PDP since some games did use the timeout arg + // Does PDP can Timeout? There is no concept of Timeout when sending UDP due to no ACK, but might happen if the socket buffer is full, not sure about PDP since some games did use the timeout arg + return ERROR_NET_ADHOC_TIMEOUT; // ERROR_NET_ADHOC_INVALID_ADDR; } //VERBOSE_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u]: Unknown Target Peer %s:%u\n", id, getLocalPort(socket->id), mac2str(daddr, tmpmac), ntohs(target.sin_port)); } @@ -590,38 +1073,55 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int // Acquire Peer Lock peerlock.lock(); - - // Send Data - changeBlockingMode(socket->id, flag); // Do we need to switched to blocking-mode to make sure the data are fully sent? - + AdhocSendTargets dest = { len, {}, true }; // Iterate Peers - SceNetAdhocctlPeerInfo * peer = friends; + SceNetAdhocctlPeerInfo* peer = friends; for (; peer != NULL; peer = peer->next) { // Does Skipping sending to timed out friends could cause desync when players moving group at the time MP game started? - if (peer->last_recv == 0) + if (peer->last_recv == 0) continue; - // Fill in Target Structure - sockaddr_in target; - target.sin_family = AF_INET; - target.sin_addr.s_addr = peer->ip_addr; - target.sin_port = htons(dport + ((isOriPort && !isPrivateIP(peer->ip_addr)) ? 0 : portOffset)); - - int sent = sendto(socket->id, (const char *)data, len, 0, (sockaddr *)&target, sizeof(target)); - int error = errno; - if (sent == SOCKET_ERROR) { - DEBUG_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPdpSend[%i:%u->%u](BC) [size=%i]", error, id, getLocalPort(socket->id), ntohs(target.sin_port), len); + dest.peers.push_back({ peer->ip_addr, dport }); + } + // Free Peer Lock + peerlock.unlock(); + + // Send Data + changeBlockingMode(socket->id, 1); // Do we need to switched to blocking-mode to make sure the data are fully sent? + + // Simulate blocking behaviour with non-blocking socket + if (!flag) { + if (sendTargetPeers.find(socket->id) != sendTargetPeers.end()) { + DEBUG_LOG(SCENET, "sceNetAdhocPdpSend[%i:%u](BC): Socket(%d) is Busy!", id, getLocalPort(socket->id), socket->id); + return ERROR_NET_ADHOC_BUSY; } - - if (sent >= 0) { - 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)); + + sendTargetPeers[socket->id] = dest; + return WaitBlockingAdhocSocket(socket->id, PDP_SEND, id, data, nullptr, timeout, nullptr, nullptr, "pdp send broadcast"); + } + // Non-blocking + else { + // Iterate Peers + for (auto peer : dest.peers) { + // Fill in Target Structure + sockaddr_in target; + target.sin_family = AF_INET; + target.sin_addr.s_addr = peer.ip; + target.sin_port = htons(dport + ((isOriPort && !isPrivateIP(peer.ip)) ? 0 : portOffset)); + + int sent = sendto(socket->id, (const char*)data, len, 0, (sockaddr*)&target, sizeof(target)); + int error = errno; + if (sent == SOCKET_ERROR) { + DEBUG_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPdpSend[%i:%u->%u](BC) [size=%i]", error, id, getLocalPort(socket->id), ntohs(target.sin_port), len); + } + + if (sent >= 0) { + 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)); + } } } - changeBlockingMode(socket->id, 0); - - // Free Peer Lock - peerlock.unlock(); + //changeBlockingMode(socket->id, 0); // Free Network Lock //_freeNetworkLock(); @@ -764,23 +1264,18 @@ static int sceNetAdhocPdpRecv(int id, void *addr, void * port, void *buf, void * 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() * 1000000.0); - // Wait for Connection. On Windows: recvfrom on UDP can get error WSAECONNRESET when previous sendto's destination is unreachable (or destination port is not bound), may need to disable SIO_UDP_CONNRESET - while ((timeout == 0 || ((uint32_t)(real_time_now() * 1000000.0) - starttime) < (uint32_t)timeout) && (received == SOCKET_ERROR) && (connectInProgress(error) || error == ECONNRESET)) { - received = recvfrom(socket->id, (char*)buf, *len, 0, (sockaddr*)&sin, &sinlen); - error = errno; - // Wait 1ms - sleep_ms(1); - } - } - if (received == SOCKET_ERROR) { + if (flag == 0) { + // Simulate blocking behaviour with non-blocking socket + return WaitBlockingAdhocSocket(socket->id, PDP_RECV, id, buf, len, timeout, saddr, sport, "pdp recv"); + } + VERBOSE_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPdpRecv[%i:%u] [size=%i]", error, id, socket->lport, *len); } changeBlockingMode(socket->id, 0); + // Should we set output length to 0 on Error? + *len = 0; // Received Data. UDP can also receives 0 data, while on TCP 0 data = connection gracefully closed, but not sure about PDP tho if (received > 0) { @@ -864,6 +1359,57 @@ int sceNetAdhocSetSocketAlert(int id, int flag) { return 0; //Dummy Result } +int PollAdhocSocket(SceNetAdhocPollSd* sds, int count, int timeout) { + //WSAPoll only available for Vista or newer, so we'll use an alternative way for XP since Windows doesn't have poll function like *NIX + fd_set readfds, writefds, exceptfds; + int fd; + int maxfd = 0; + FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds); + // TODO: PDP and PTP should share the same indexing to prevent identical PDP & PTP socket id + for (int i = 0; i < count; i++) { + sds[i].revents = 0; + // Fill in Socket ID + if (sds[i].id <= 255 && ptp[sds[i].id - 1] != NULL) { + fd = ptp[sds[i].id - 1]->id; + if (ptp[sds[i].id - 1]->state == ADHOC_PTP_STATE_LISTEN) sds[i].revents |= ADHOC_EV_ACCEPT; + else + if (ptp[sds[i].id - 1]->state == ADHOC_PTP_STATE_CLOSED) sds[i].revents |= ADHOC_EV_CONNECT; + } + else { + fd = pdp[sds[i].id - 256]->id; + } + if (fd > maxfd) maxfd = fd; + if (sds[i].events & ADHOC_EV_RECV) FD_SET(fd, &readfds); + if (sds[i].events & ADHOC_EV_SEND) FD_SET(fd, &writefds); // Does Data can always be sent regardless of events bitmask? + //if (sds[i].events & ADHOC_EV_ALERT) + FD_SET(fd, &exceptfds); // Does Alert can be raised on revents regardless of events bitmask? + } + timeval tmout; + tmout.tv_sec = timeout / 1000000; // seconds + tmout.tv_usec = (timeout % 1000000); // microseconds + int affectedsockets = select(maxfd + 1, &readfds, &writefds, &exceptfds, &tmout); + if (affectedsockets > 0) { + affectedsockets = 0; + for (int i = 0; i < count; i++) { + if (sds[i].id <= 255 && ptp[sds[i].id - 1] != NULL) { + fd = ptp[sds[i].id - 1]->id; + } + else { + fd = pdp[sds[i].id - 256]->id; + } + if (FD_ISSET(fd, &readfds)) + sds[i].revents |= ADHOC_EV_RECV; + if (FD_ISSET(fd, &writefds)) + sds[i].revents |= ADHOC_EV_SEND; // Does Data can always be sent regardless of events bitmask? + sds[i].revents &= sds[i].events; + if (FD_ISSET(fd, &exceptfds)) + sds[i].revents |= ADHOC_EV_ALERT; // Does Alert can be raised on revents regardless of events bitmask? + if (sds[i].revents) affectedsockets++; + } + } + return affectedsockets; +} + int sceNetAdhocPollSocket(u32 socketStructAddr, int count, int timeout, int nonblock) { // timeout in microseconds // Library is initialized if (netAdhocInited) @@ -883,68 +1429,31 @@ int sceNetAdhocPollSocket(u32 socketStructAddr, int count, int timeout, int nonb } // Nonblocking Mode - if (nonblock) timeout = 0; + if (nonblock) + timeout = 0; - // Prevent Nonblocking Mode - else - if (timeout == 0) timeout = 1; + // Blocking Mode + else + // Does timeout = 0 means undefinite on PSP? + if (timeout == 0) + timeout = adhocDefaultTimeout * 1000; // minSocketTimeoutUS; - int affectedsockets = 0; - if (count > (int)FD_SETSIZE) count = FD_SETSIZE; // return affectedsockets; + if (count > (int)FD_SETSIZE) + count = FD_SETSIZE; // return 0; //ERROR_NET_ADHOC_INVALID_ARG // Acquire Network Lock //acquireNetworkLock(); // Poll Sockets //int affectedsockets = sceNetInetPoll(isds, count, timeout); - - //WSAPoll only available for Vista or newer, so we'll use an alternative way for XP since Windows doesn't have poll function like *NIX - fd_set readfds, writefds, exceptfds; - int fd; - int maxfd = 0; - FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds); - // TODO: PDP and PTP should share the same indexing to prevent identical PDP & PTP socket id - for (int i = 0; i < count; i++) { - sds[i].revents = 0; - // Fill in Socket ID - if (sds[i].id <= 255 && ptp[sds[i].id - 1] != NULL) { - fd = ptp[sds[i].id - 1]->id; - if (ptp[sds[i].id - 1]->state == ADHOC_PTP_STATE_LISTEN) sds[i].revents |= ADHOC_EV_ACCEPT; - else - if (ptp[sds[i].id - 1]->state == ADHOC_PTP_STATE_CLOSED) sds[i].revents |= ADHOC_EV_CONNECT; - } - else { - fd = pdp[sds[i].id - 256]->id; - } - if (fd > maxfd) maxfd = fd; - if (sds[i].events & ADHOC_EV_RECV) FD_SET(fd, &readfds); - if (sds[i].events & ADHOC_EV_SEND) FD_SET(fd, &writefds); // Does Data can always be sent regardless of events bitmask? - //if (sds[i].events & ADHOC_EV_ALERT) - FD_SET(fd, &exceptfds); // Does Alert can be raised on revents regardless of events bitmask? - } - timeval tmout; - tmout.tv_sec = timeout / 1000000; // seconds - tmout.tv_usec = (timeout % 1000000); // microseconds - affectedsockets = select(maxfd + 1, &readfds, &writefds, &exceptfds, &tmout); - if (affectedsockets > 0) { - affectedsockets = 0; - for (int i = 0; i < count; i++) { - if (sds[i].id <= 255 && ptp[sds[i].id - 1] != NULL) { - fd = ptp[sds[i].id - 1]->id; - } - else { - fd = pdp[sds[i].id - 256]->id; - } - if (FD_ISSET(fd, &readfds)) - sds[i].revents |= ADHOC_EV_RECV; - if (FD_ISSET(fd, &writefds)) - sds[i].revents |= ADHOC_EV_SEND; // Does Data can always be sent regardless of events bitmask? - sds[i].revents &= sds[i].events; - if (FD_ISSET(fd, &exceptfds)) - sds[i].revents |= ADHOC_EV_ALERT; // Does Alert can be raised on revents regardless of events bitmask? - if (sds[i].revents) affectedsockets++; - } - } + int affectedsockets = 0; + if (nonblock) + affectedsockets = PollAdhocSocket(sds, count, timeout); + else + // Simulate blocking behaviour with non-blocking socket + // Borrowing some arguments to pass some parameters. The dummy WaitID(count+1) might not be unique thus have duplicate possibilities if there are multiple thread trying to poll the same numbers of socket at the same time + return WaitBlockingAdhocSocket(count+1, ADHOC_POLL_SOCKET, count, sds, nullptr, timeout, nullptr, nullptr, "adhoc pollsocket"); + // Free Network Lock //freeNetworkLock(); @@ -980,6 +1489,7 @@ int NetAdhocPdp_Delete(int id, int unknown) { // Valid Socket if (sock != NULL) { // Close Connection + shutdown(sock->id, SD_BOTH); closesocket(sock->id); // Remove Port Forward from Router @@ -1052,20 +1562,13 @@ static int sceNetAdhocctlGetAdhocId(u32 productStructAddr) { return ERROR_NET_ADHOCCTL_NOT_INITIALIZED; } +// FIXME: Scan probably not a blocking function since there is ADHOCCTL_STATE_SCANNING state that can be polled by the game, right? int sceNetAdhocctlScan() { INFO_LOG(SCENET, "sceNetAdhocctlScan() at %08x", currentMIPS->pc); // Library initialized if (netAdhocctlInited) { - // Wait until Not connected - if (friendFinderRunning) { - int cnt = 0; - while ((adhocctlState != ADHOCCTL_STATE_DISCONNECTED) && (cnt < adhocDefaultTimeout)) { - sleep_ms(1); - cnt++; - } - } - + if (adhocctlState == ADHOCCTL_STATE_DISCONNECTED) { adhocctlState = ADHOCCTL_STATE_SCANNING; @@ -1079,32 +1582,18 @@ int sceNetAdhocctlScan() { uint8_t opcode = OPCODE_SCAN; // Send Scan Request Packet, may failed with socket error 10054/10053 if someone else with the same IP already connected to AdHoc Server (the server might need to be modified to differentiate MAC instead of IP) + changeBlockingMode(metasocket, 0); int iResult = send(metasocket, (char *)&opcode, 1, 0); + int error = errno; + changeBlockingMode(metasocket, 1); if (iResult == SOCKET_ERROR) { - int error = errno; ERROR_LOG(SCENET, "Socket error (%i) when sending", error); adhocctlState = ADHOCCTL_STATE_DISCONNECTED; + //notifyAdhocctlHandlers(ADHOCCTL_EVENT_SCAN, 0); //if (error == ECONNABORTED || error == ECONNRESET || error == ENOTCONN) return ERROR_NET_ADHOCCTL_NOT_INITIALIZED; // A case where it need to reconnect to AdhocServer return ERROR_NET_ADHOCCTL_DISCONNECTED; // ERROR_NET_ADHOCCTL_BUSY } - // Does Connected Event's mipscall need be executed after returning from sceNetAdhocctlScan ? - //notifyAdhocctlHandlers(ADHOCCTL_EVENT_SCAN, 0); - //hleCheckCurrentCallbacks(); - - // 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 ((adhocctlState == ADHOCCTL_STATE_SCANNING) && (cnt < adhocDefaultTimeout)) { - sleep_ms(1); - cnt++; - } - }*/ - - //sceKernelDelayThread(adhocEventPollDelayMS * 1000); - //hleDelayResult(0, "give time to init/cleanup", adhocEventPollDelayMS * 1000); - // Return Success return 0; } @@ -1267,14 +1756,16 @@ u32 NetAdhocctl_Disconnect() { //_acquireNetworkLock(); // Send Disconnect Request Packet + changeBlockingMode(metasocket, 0); int iResult = send(metasocket, (const char*)&opcode, 1, 0); + int error = errno; + changeBlockingMode(metasocket, 1); if (iResult == SOCKET_ERROR) { - ERROR_LOG(SCENET, "Socket error (%i) when sending", errno); + ERROR_LOG(SCENET, "Socket error (%i) when sending", error); + // Set Disconnected State + adhocctlState = ADHOCCTL_STATE_DISCONNECTED; } - // Set Disconnected State - adhocctlState = ADHOCCTL_STATE_DISCONNECTED; - // Free Network Lock //_freeNetworkLock(); } @@ -1300,9 +1791,16 @@ u32 NetAdhocctl_Disconnect() { // Notify Event Handlers (even if we weren't connected, not doing this will freeze games like God Eater, which expect this behaviour) notifyAdhocctlHandlers(ADHOCCTL_EVENT_DISCONNECT, 0); - hleCheckCurrentCallbacks(); + //hleCheckCurrentCallbacks(); + int us = adhocEventPollDelayMS * 1000; + if (adhocctlState != ADHOCCTL_STATE_DISCONNECTED && friendFinderRunning) { + WaitAdhocctlState(ADHOCCTL_STATE_DISCONNECTED, us, "adhoc disconnect"); + } // Return Success, some games might ignore returned value and always treat it as success, otherwise repeatedly calling this function + else if (adhocctlState == ADHOCCTL_STATE_DISCONNECTED) + hleDelayResult(0, "give time to init/cleanup", us); + return 0; } @@ -1517,15 +2015,6 @@ int NetAdhocctl_Create(const char* groupName) { if (netAdhocctlInited) { // Valid Argument if (validNetworkName(groupNameStruct)) { - // Wait until Not connected - if (friendFinderRunning) { - int cnt = 0; - while ((adhocctlState != ADHOCCTL_STATE_DISCONNECTED && adhocctlState != 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 ((adhocctlState == ADHOCCTL_STATE_DISCONNECTED) || (adhocctlState == ADHOCCTL_STATE_SCANNING)) { // Set Network Name @@ -1549,15 +2038,18 @@ int NetAdhocctl_Create(const char* groupName) { // Acquire Network Lock // Send Packet + changeBlockingMode(metasocket, 0); int iResult = send(metasocket, (const char*)&packet, sizeof(packet), 0); + int error = errno; + changeBlockingMode(metasocket, 1); if (iResult == SOCKET_ERROR) { - ERROR_LOG(SCENET, "Socket error (%i) when sending", errno); + ERROR_LOG(SCENET, "Socket error (%i) when sending", error); //return ERROR_NET_ADHOCCTL_NOT_INITIALIZED; // ERROR_NET_ADHOCCTL_DISCONNECTED; // ERROR_NET_ADHOCCTL_BUSY; //Faking success, to prevent Full Auto 2 from freezing while Initializing Network adhocctlState = ADHOCCTL_STATE_CONNECTED; // Notify Event Handlers, Needed for the Nickname to be shown on the screen when success is faked - // Might be better not to notify the game when faking success (failed to connect to adhoc server), at least the player will know that it failed to connect - //__UpdateAdhocctlHandlers(ADHOCCTL_EVENT_CONNECT, 0); //CoreTiming::ScheduleEvent_Threadsafe_Immediate(eventAdhocctlHandlerUpdate, join32(ADHOCCTL_EVENT_CONNECT, 0)); + // Connected Event's mipscall need be executed before returning from sceNetAdhocctlCreate (or before the next sceNet function?) + __UpdateAdhocctlHandlers(ADHOCCTL_EVENT_CONNECT, 0); //CoreTiming::ScheduleEvent_Threadsafe_Immediate(eventAdhocctlHandlerUpdate, join32(ADHOCCTL_EVENT_CONNECT, 0)); } // Free Network Lock @@ -1565,22 +2057,14 @@ int NetAdhocctl_Create(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 - // TODO: Do this async while Delaying HLE Result - /*if (friendFinderRunning) { // This is thread-unsafe - int cnt = 0; - while ((adhocctlState != 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); + int us = adhocEventDelayMS * 1000; + if (adhocctlState != ADHOCCTL_STATE_CONNECTED && friendFinderRunning) { + WaitAdhocctlState(ADHOCCTL_STATE_CONNECTED, us, "adhoc connect"); + } + // Giving time for Naruto Shippuden Ninja Heroes 3 to close down the "Connecting..." dialog, otherwise the dialog will stuck there. + else if (adhocctlState == ADHOCCTL_STATE_CONNECTED) + hleDelayResult(0, "give time to init/cleanup", us); // Return Success return 0; @@ -2006,6 +2490,7 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, // Link PTP Socket ptp[i] = internal; + ptpConnectCount[i] = 0; // Add Port Forward to Router. We may not even need to forward this local port, since PtpOpen usually have port 0 (any port) as source port and followed by PtpConnect (which mean acting as Client), right? //sceNetPortOpen("TCP", sport); @@ -2049,6 +2534,93 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, return 0; } +int AcceptPtpSocket(int ptpId, int newsocket, sockaddr_in& peeraddr, SceNetEtherAddr* addr, u16_le* port) { + // Cast Socket + SceNetAdhocPtpStat* socket = ptp[ptpId - 1]; + + // Enable Port Re-use + setsockopt(newsocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&one, sizeof(one)); + + // Disable Nagle Algo to send immediately. Or may be we shouldn't disable Nagle since there is PtpFlush function? + if (g_Config.bTCPNoDelay) setSockNoDelay(newsocket, 1); + + // Local Address Information + sockaddr_in local; + memset(&local, 0, sizeof(local)); + socklen_t locallen = sizeof(local); + + // Grab Local Address + if (getsockname(newsocket, (sockaddr*)&local, &locallen) == 0) { + // Peer MAC + SceNetEtherAddr mac; + + // Find Peer MAC + if (resolveIP(peeraddr.sin_addr.s_addr, &mac)) { + // Allocate Memory + SceNetAdhocPtpStat* internal = (SceNetAdhocPtpStat*)malloc(sizeof(SceNetAdhocPtpStat)); + + // Allocated Memory + if (internal != NULL) { + // Find Free Translator ID + int i = 0; + for (; i < 255; i++) if (ptp[i] == NULL) break; + + // Found Free Translator ID + if (i < 255) { + // Clear Memory + memset(internal, 0, sizeof(SceNetAdhocPtpStat)); + + // Copy Socket Descriptor to Structure + internal->id = newsocket; + + // Set Buffer Size + if (getSockBufferSize(newsocket, SO_RCVBUF) < socket->rcv_sb_cc) setSockBufferSize(newsocket, SO_RCVBUF, socket->rcv_sb_cc); + if (getSockBufferSize(newsocket, SO_SNDBUF) < socket->snd_sb_cc) setSockBufferSize(newsocket, SO_SNDBUF, socket->snd_sb_cc); + internal->rcv_sb_cc = socket->rcv_sb_cc; + internal->snd_sb_cc = socket->snd_sb_cc; + + // Copy Local Address Data to Structure + getLocalMac(&internal->laddr); + internal->lport = ntohs(local.sin_port) - portOffset; + + // Copy Peer Address Data to Structure + internal->paddr = mac; + internal->pport = ntohs(peeraddr.sin_port) - portOffset; + + // Set Connected State + internal->state = ADHOC_PTP_STATE_ESTABLISHED; + + // Return Peer Address Information + *addr = internal->paddr; + if (port != NULL) *port = internal->pport; + + // Link PTP Socket + ptp[i] = internal; + ptpConnectCount[i] = 0; + + // Add Port Forward to Router. Or may be doesn't need to be forwarded since local port already accessible from outside if others were able to connect & get accepted at this point, right? + //sceNetPortOpen("TCP", internal->lport); + //g_PortManager.Add(IP_PROTOCOL_TCP, internal->lport + portOffset); + + INFO_LOG(SCENET, "sceNetAdhocPtpAccept[%i->%i:%u]: Established (%s:%u)", ptpId, i + 1, internal->lport, inet_ntoa(peeraddr.sin_addr), internal->pport); + + // Return Socket + return i + 1; + } + + // Free Memory + free(internal); + } + } + } + + // Close Socket + closesocket(newsocket); + + ERROR_LOG(SCENET, "sceNetAdhocPtpAccept[%i]: Failed (Socket Closed)", ptpId); + return -1; +} + /** * Adhoc Emulator PTP Connection Acceptor * @param id Socket File Descriptor @@ -2092,13 +2664,9 @@ static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int sockaddr_in peeraddr; memset(&peeraddr, 0, sizeof(peeraddr)); socklen_t peeraddrlen = sizeof(peeraddr); - // Local Address Information - sockaddr_in local; - memset(&local, 0, sizeof(local)); - socklen_t locallen = sizeof(local); // Grab Nonblocking Flag - uint32_t nbio = getBlockingFlag(socket->id); + uint32_t nbio = getNonBlockingFlag(socket->id); // Switch to Nonblocking Behaviour if (nbio == 0) { // Overwrite Socket Option @@ -2109,27 +2677,13 @@ static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int // Accept Connection int newsocket = accept(socket->id, (sockaddr *)&peeraddr, &peeraddrlen); int error = errno; - - // Blocking Behaviour - if (!flag && newsocket == SOCKET_ERROR) { - // Get Start Time - uint32_t starttime = (uint32_t)(real_time_now()*1000000.0); - - // Retry until Timeout hits - while ((timeout == 0 || ((uint32_t)(real_time_now()*1000000.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); - } - } if (newsocket == SOCKET_ERROR) { - if (flag == 0) { // Prevent spamming Debug Log with retries of non-bocking socket - DEBUG_LOG(SCENET, "sceNetAdhocPtpAccept[%i]: Socket Error (%i)", id, error); + if (flag == 0) { + // Simulate blocking behaviour with non-blocking socket + return WaitBlockingAdhocSocket(socket->id, PTP_ACCEPT, id, nullptr, nullptr, timeout, addr, port, "ptp accept"); } + // Prevent spamming Debug Log with retries of non-bocking socket else { VERBOSE_LOG(SCENET, "sceNetAdhocPtpAccept[%i]: Socket Error (%i)", id, error); } @@ -2143,80 +2697,9 @@ static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int // Accepted New Connection if (newsocket > 0) { - // Enable Port Re-use - setsockopt(newsocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&one, sizeof(one)); - - // Disable Nagle Algo to send immediately. Or may be we shouldn't disable Nagle since there is PtpFlush function? - if (g_Config.bTCPNoDelay) setSockNoDelay(newsocket, 1); - - // Grab Local Address - if (getsockname(newsocket, (sockaddr *)&local, &locallen) == 0) { - // Peer MAC - SceNetEtherAddr mac; - - // Find Peer MAC - if (resolveIP(peeraddr.sin_addr.s_addr, &mac)) { - // Allocate Memory - SceNetAdhocPtpStat * internal = (SceNetAdhocPtpStat *)malloc(sizeof(SceNetAdhocPtpStat)); - - // Allocated Memory - if (internal != NULL) { - // Find Free Translator ID - int i = 0; - for (; i < 255; i++) if (ptp[i] == NULL) break; - - // Found Free Translator ID - if (i < 255) { - // Clear Memory - memset(internal, 0, sizeof(SceNetAdhocPtpStat)); - - // Copy Socket Descriptor to Structure - internal->id = newsocket; - - // Set Buffer Size - if (getSockBufferSize(newsocket, SO_RCVBUF) < socket->rcv_sb_cc) setSockBufferSize(newsocket, SO_RCVBUF, socket->rcv_sb_cc); - if (getSockBufferSize(newsocket, SO_SNDBUF) < socket->snd_sb_cc) setSockBufferSize(newsocket, SO_SNDBUF, socket->snd_sb_cc); - internal->rcv_sb_cc = socket->rcv_sb_cc; - internal->snd_sb_cc = socket->snd_sb_cc; - - // Copy Local Address Data to Structure - getLocalMac(&internal->laddr); - internal->lport = ntohs(local.sin_port) - portOffset; - - // Copy Peer Address Data to Structure - internal->paddr = mac; - internal->pport = ntohs(peeraddr.sin_port) - portOffset; - - // Set Connected State - internal->state = ADHOC_PTP_STATE_ESTABLISHED; - - // Return Peer Address Information - *addr = internal->paddr; - if (port != NULL) *port = internal->pport; - - // Link PTP Socket - ptp[i] = internal; - - // Add Port Forward to Router. Or may be doesn't need to be forwarded since local port already accessible from outside if others were able to connect & get accepted at this point, right? - //sceNetPortOpen("TCP", internal->lport); - //g_PortManager.Add(IP_PROTOCOL_TCP, internal->lport + portOffset); - - 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; - } - - // Free Memory - free(internal); - } - } - } - - // Close Socket - closesocket(newsocket); - - ERROR_LOG(SCENET, "sceNetAdhocPtpAccept[%i]: Failed (Socket Closed)", id); + int newid = AcceptPtpSocket(id, newsocket, peeraddr, addr, port); + if (newid >= 0) + return newid; } // Action would block @@ -2284,17 +2767,15 @@ static int sceNetAdhocPtpConnect(int id, int timeout, int flag) { sin.sin_port = htons(socket->pport + ((isOriPort && !isPrivateIP(sin.sin_addr.s_addr)) ? 0 : portOffset)); // Grab Nonblocking Flag - uint32_t nbio = getBlockingFlag(socket->id); + //uint32_t nbio = getNonBlockingFlag(socket->id); // Switch to Nonblocking Behaviour. Forcing blocking behaviour on the first connect may fix connection issue on GvG Next Plus, But i don't like using blocking socket with infinite timeout if the game it self were asking for non-blocking behaviour :( - /*if (nbio == 0) - { - // Overwrite Socket Option - changeBlockingMode(socket->id, 1); - }*/ + // We are using non-blocking to simulate blocking + changeBlockingMode(socket->id, 1); - // Connect Socket to Peer (Nonblocking) + // Connect Socket to Peer // NOTE: Based on what i read at stackoverflow, The First Non-blocking POSIX connect will always returns EAGAIN/EWOULDBLOCK because it returns without waiting for ACK/handshake, But GvG Next Plus is treating non-blocking PtpConnect just like blocking connect, May be on a real PSP the first non-blocking sceNetAdhocPtpConnect can be successfull? + // TODO: Keep track number of Connect attempts so we can simulate blocking on first attempt (getNonBlockingFlag can't be used to get non-blocking flag on Windows thus can't be used to keep track) int connectresult = connect(socket->id, (sockaddr *)&sin, sizeof(sin)); // Grab Error Code @@ -2303,16 +2784,10 @@ static int sceNetAdhocPtpConnect(int id, int timeout, int flag) { if (connectresult == SOCKET_ERROR) { ERROR_LOG(SCENET, "sceNetAdhocPtpConnect[%i]: Socket Error (%i) to %s:%u", id, errorcode, inet_ntoa(sin.sin_addr), socket->pport); } - - // Restore Blocking Behaviour - if (nbio == 0) - { - // Restore Socket Option - changeBlockingMode(socket->id, 0); - } - + // Instant Connection (Lucky!) - if (connectresult == 0 || (connectresult == SOCKET_ERROR && (errorcode == EISCONN))) { + if (connectresult != SOCKET_ERROR || errorcode == EISCONN) { + ptpConnectCount[id - 1]++; // Set Connected State socket->state = ADHOC_PTP_STATE_ESTABLISHED; @@ -2323,39 +2798,16 @@ static int sceNetAdhocPtpConnect(int id, int timeout, int flag) { // Connection in Progress else if (connectresult == SOCKET_ERROR && connectInProgress(errorcode)) { - // Nonblocking Mode - if (flag) { + ptpConnectCount[id - 1]++; + // Nonblocking Mode. First attempt need to be blocking for GvG Next Plus to work, even though it used non-blocking flag but only try to connect once per socket, which mean treating it just like blocking socket instead of non-blocking :( + if (flag && ptpConnectCount[id - 1] > 1) { //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); - - // 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) { - // Wait 1ms - sleep_ms(1); - } - - // Connected in Time - if (sin.sin_addr.s_addr == peer.sin_addr.s_addr/* && sin.sin_port == peer.sin_port*/) { - // Set Connected State - socket->state = ADHOC_PTP_STATE_ESTABLISHED; - - INFO_LOG(SCENET, "sceNetAdhocPtpConnect[%i:%u]: Established (%s:%u)", id, socket->lport, inet_ntoa(peer.sin_addr), socket->pport); - - // Success - return 0; - } - - // Timeout occured - return ERROR_NET_ADHOC_CONNECTION_REFUSED; // ERROR_NET_ADHOC_TIMEOUT; + // Simulate blocking behaviour with non-blocking socket + return WaitBlockingAdhocSocket(socket->id, PTP_CONNECT, id, nullptr, nullptr, timeout, nullptr, nullptr, "ptp connect"); } } } @@ -2387,6 +2839,7 @@ int NetAdhocPtp_Close(int id, int unknown) { SceNetAdhocPtpStat* socket = ptp[id - 1]; // Close Connection + shutdown(socket->id, SD_BOTH); closesocket(socket->id); // Remove Port Forward from Router @@ -2398,6 +2851,7 @@ int NetAdhocPtp_Close(int id, int unknown) { // Free Reference ptp[id - 1] = NULL; + ptpConnectCount.erase(id - 1); // Success return 0; @@ -2538,6 +2992,7 @@ static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int // Link PTP Socket ptp[i] = internal; + ptpConnectCount[i] = 0; // Add Port Forward to Router //sceNetPortOpen("TCP", sport); @@ -2627,10 +3082,9 @@ static int sceNetAdhocPtpSend(int id, u32 dataAddr, u32 dataSizeAddr, int timeou // _acquireNetworkLock(); // Send Data - //changeBlockingMode(socket->id, flag); + changeBlockingMode(socket->id, 1); int sent = send(socket->id, data, *len, 0); int error = errno; - changeBlockingMode(socket->id, 0); // Free Network Lock // _freeNetworkLock(); @@ -2640,7 +3094,7 @@ static int sceNetAdhocPtpSend(int id, u32 dataAddr, u32 dataSizeAddr, int timeou // Save Length *len = sent; - DEBUG_LOG(SCENET, "sceNetAdhocPtpSend[%i:%u]: Sent %u bytes to %s:%u", id, socket->lport, sent, mac2str(&socket->paddr).c_str(), socket->pport); + DEBUG_LOG(SCENET, "sceNetAdhocPtpSend[%i:%u]: Sent %u bytes to %s:%u\n", id, socket->lport, sent, mac2str(&socket->paddr).c_str(), socket->pport); // Return Success return 0; @@ -2648,11 +3102,12 @@ static int sceNetAdhocPtpSend(int id, u32 dataAddr, u32 dataSizeAddr, int timeou // Non-Critical Error else if (sent == SOCKET_ERROR && (error == EAGAIN || error == EWOULDBLOCK || error == ETIMEDOUT)) { - // Blocking Situation - if (flag) return ERROR_NET_ADHOC_WOULD_BLOCK; + // Non-Blocking + if (flag) + return ERROR_NET_ADHOC_WOULD_BLOCK; - // Timeout - return ERROR_NET_ADHOC_TIMEOUT; + // Simulate blocking behaviour with non-blocking socket + return WaitBlockingAdhocSocket(socket->id, PTP_SEND, id, (void*)data, len, timeout, nullptr, nullptr, "ptp send"); } // Change Socket State @@ -2722,25 +3177,21 @@ static int sceNetAdhocPtpRecv(int id, u32 dataAddr, u32 dataSizeAddr, int timeou 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() * 1000000.0); - // Wait for Connection - while ((timeout == 0 || ((uint32_t)(real_time_now() * 1000000.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) { + if (flag == 0) { + // Simulate blocking behaviour with non-blocking socket + return WaitBlockingAdhocSocket(socket->id, PTP_RECV, id, buf, len, timeout, nullptr, nullptr, "ptp recv"); + } + VERBOSE_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPtpRecv[%i:%u] [size=%i]", error, id, socket->lport, *len); } changeBlockingMode(socket->id, 0); // Free Network Lock // _freeNetworkLock(); + + // Should we set output length to 0 on Error? + *len = 0; // Received Data if (received > 0) { @@ -2753,7 +3204,7 @@ static int sceNetAdhocPtpRecv(int id, u32 dataAddr, u32 dataSizeAddr, int timeou if (peer != NULL) peer->last_recv = CoreTiming::GetGlobalTimeUsScaled(); peerlock.unlock(); - DEBUG_LOG(SCENET, "sceNetAdhocPtpRecv[%i:%u]: Received %u bytes from %s:%u", id, socket->lport, received, mac2str(&socket->paddr).c_str(), socket->pport); + DEBUG_LOG(SCENET, "sceNetAdhocPtpRecv[%i:%u]: Received %u bytes from %s:%u\n", id, socket->lport, received, mac2str(&socket->paddr).c_str(), socket->pport); // Return Success return 0; @@ -2765,14 +3216,15 @@ static int sceNetAdhocPtpRecv(int id, u32 dataAddr, u32 dataSizeAddr, int timeou if (flag) return ERROR_NET_ADHOC_WOULD_BLOCK; // Timeout - return ERROR_NET_ADHOC_TIMEOUT; + return hleLogError(SCENET, ERROR_NET_ADHOC_TIMEOUT, "ptp recv timeout"); } - + DEBUG_LOG(SCENET, "sceNetAdhocPtpRecv[%i:%u]: Result:%i (Error:%i)", id, socket->lport, received, error); + // Change Socket State socket->state = ADHOC_PTP_STATE_CLOSED; // Disconnected - return ERROR_NET_ADHOC_DISCONNECTED; + return hleLogError(SCENET, ERROR_NET_ADHOC_DISCONNECTED, "ptp recv disconnected"); } // Invalid Arguments @@ -2820,10 +3272,10 @@ static int sceNetAdhocPtpFlush(int id, int timeout, int nonblock) { setSockNoDelay(socket->id, 1); // 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); + //changeBlockingMode(socket->id, nonblock); int sent = send(socket->id, 0, 0, 0); int error = errno; - changeBlockingMode(socket->id, 0); + //changeBlockingMode(socket->id, 1); // Restore/Enable Nagle Algo setSockNoDelay(socket->id, n); @@ -2982,6 +3434,14 @@ int NetAdhocMatching_Delete(int matchingId) { free(item); item = NULL; + // Making sure there are no leftover matching events from this session which could cause a crash on the next session + for (auto it = matchingEvents.begin(); it != matchingEvents.end(); ) { + if (it->data[0] == matchingId) + it = matchingEvents.erase(it); + else + ++it; + } + // Stop Search break; } @@ -3045,7 +3505,7 @@ int NetAdhocMatching_Term() { } int sceNetAdhocMatchingTerm() { - WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingTerm()"); + WARN_LOG(SCENET, "UNTESTED sceNetAdhocMatchingTerm() at %08x", currentMIPS->pc); // Should we cleanup all created matching contexts first? just in case there are games that doesn't delete them before calling this NetAdhocMatching_Term(); @@ -3072,14 +3532,6 @@ static int sceNetAdhocMatchingCreate(int mode, int maxnum, int port, int rxbufle 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; @@ -3248,8 +3700,9 @@ int NetAdhocMatching_Start(int matchingId, int evthPri, int evthPartitionId, int // Multithreading Unlock peerlock.unlock(); - sleep_ms(adhocMatchingEventDelayMS); - //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? + // Give a little time to make sure matching Threads are ready before the game use the next sceNet functions, should've checked for status instead of guessing the time? + //sleep_ms(adhocMatchingEventDelayMS); + hleDelayResult(0, "give some time", adhocMatchingEventDelayMS * 1000); return 0; } @@ -4127,20 +4580,16 @@ void __NetMatchingCallbacks() //(int matchingId) u32_le* args = (u32_le*)&(*params); //auto context = findMatchingContext(args[0]); - //if (/*__KernelGetCurThread() == context->matching_thid &&*/ (!__IsInInterrupt() && __KernelIsDispatchEnabled() && !__KernelInCallback()) /*&& !IsMatchingInCallback(context)*/) - { - if (actionAfterMatchingMipsCall < 0) { - actionAfterMatchingMipsCall = __KernelRegisterActionType(AfterMatchingMipsCall::Create); - } - DEBUG_LOG(SCENET, "AdhocMatchingCallback: [ID=%i][EVENT=%i][%s]", args[0], args[1], mac2str((SceNetEtherAddr*)Memory::GetPointer(args[2])).c_str()); - AfterMatchingMipsCall* after = (AfterMatchingMipsCall*)__KernelCreateAction(actionAfterMatchingMipsCall); - after->SetData(args[0], args[1], args[2]); - //SetMatchingInCallback(context, true); - //__KernelDirectMipsCall(args[5], after, args, 5, true); - hleEnqueueCall(args[5], 5, args, after); - matchingEvents.pop_front(); - delayus = (adhocMatchingEventDelayMS + 2*adhocExtraPollDelayMS) * 1000; // Added an extra delay to prevent I/O Timing method from causing disconnection + if (actionAfterMatchingMipsCall < 0) { + actionAfterMatchingMipsCall = __KernelRegisterActionType(AfterMatchingMipsCall::Create); } + + DEBUG_LOG(SCENET, "AdhocMatchingCallback: [ID=%i][EVENT=%i][%s]", args[0], args[1], mac2str((SceNetEtherAddr*)Memory::GetPointer(args[2])).c_str()); + AfterMatchingMipsCall* after = (AfterMatchingMipsCall*)__KernelCreateAction(actionAfterMatchingMipsCall); + after->SetData(args[0], args[1], args[2]); + hleEnqueueCall(args[5], 5, args, after); + matchingEvents.pop_front(); + delayus = (adhocMatchingEventDelayMS + 2 * adhocExtraPollDelayMS) * 1000; // Added an extra delay to prevent I/O Timing method from causing disconnection } // 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? @@ -5586,7 +6035,7 @@ int matchingInputThread(int matchingId) // TODO: The MatchingInput thread is usi // Multithreading Lock peerlock.lock(); // Cast Context - SceNetAdhocMatchingContext * context = findMatchingContext(matchingId); + SceNetAdhocMatchingContext* context = findMatchingContext(matchingId); // Multithreading Unlock peerlock.unlock(); @@ -5598,6 +6047,10 @@ int matchingInputThread(int matchingId) // TODO: The MatchingInput thread is usi u64_le now; + static SceNetEtherAddr sendermac; + static uint16_t senderport; + static int rxbuflen; + // Log Startup INFO_LOG(SCENET, "InputLoop: Begin of InputLoop[%i] Thread", matchingId); @@ -5690,9 +6143,8 @@ int matchingInputThread(int matchingId) // TODO: The MatchingInput thread is usi } // Receive PDP Datagram - SceNetEtherAddr sendermac; - uint16_t senderport; - int rxbuflen = context->rxbuflen; + rxbuflen = context->rxbuflen; + senderport = 0; context->socketlock->lock(); int recvresult = sceNetAdhocPdpRecv(context->socket, &sendermac, &senderport, context->rxbuf, &rxbuflen, 0, ADHOC_F_NONBLOCK); context->socketlock->unlock(); @@ -5714,6 +6166,9 @@ int matchingInputThread(int matchingId) // TODO: The MatchingInput thread is usi DEBUG_LOG(SCENET, "Timestamp Delta: %llu (%llu - %llu) from %s", delta, now, peer->last_recv, mac2str(&sendermac).c_str()); if (/*context->rxbuf[0] > 0 &&*/ peer->last_recv != 0) peer->last_recv = now - 1; // - context->keepalive_int; // May need to deduce by ping interval to prevent Dissidia 012 unable to see other players (ie. disappearing issue) } + else { + WARN_LOG(SCENET, "InputLoop[%d]: Unknown Peer[%s:%u] (Recved=%i, Length=%i)", matchingId, mac2str(&sendermac).c_str(), senderport, recvresult, rxbuflen); + } peerlock.unlock(); // Ping Packet diff --git a/Core/HLE/sceNetAdhoc.h b/Core/HLE/sceNetAdhoc.h index bcd3ad6c73..08583433de 100644 --- a/Core/HLE/sceNetAdhoc.h +++ b/Core/HLE/sceNetAdhoc.h @@ -17,6 +17,7 @@ #pragma once +#include #include "Core/HLE/proAdhoc.h" #ifdef _MSC_VER @@ -29,6 +30,39 @@ typedef struct MatchingArgs { #pragma pack(pop) #endif +struct AdhocSendTarget { + u32 ip; + u16 port; // original port +}; + +struct AdhocSendTargets { + int length; + std::deque peers; + bool isBroadcast; +}; + +struct AdhocSocketRequest { + int type; + int id; // PDP/PTP socket id + void* buffer; + s32_le* length; + u32 timeout; + u64 startTime; + struct SceNetEtherAddr* remoteMAC; + u16_le* remotePort; +}; + +enum AdhocSocketRequestType : int +{ + PTP_CONNECT = 0, + PTP_ACCEPT = 1, + PTP_SEND = 2, + PTP_RECV = 3, + PDP_SEND = 4, + PDP_RECV = 5, + ADHOC_POLL_SOCKET = 6, +}; + class PointerWrap; void Register_sceNetAdhoc(); From 97904d7149e135da7c04176223c7ef562a66bff7 Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Sun, 23 Aug 2020 01:35:43 +0700 Subject: [PATCH 4/7] Fix possible stalled thread when PPSSPP closed down --- Core/HLE/proAdhocServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/HLE/proAdhocServer.cpp b/Core/HLE/proAdhocServer.cpp index 58e5fa7380..0d83b705b7 100644 --- a/Core/HLE/proAdhocServer.cpp +++ b/Core/HLE/proAdhocServer.cpp @@ -2075,7 +2075,7 @@ int server_loop(int server) sleep_ms(1); // Don't do anything if it's paused, otherwise the log will be flooded - while (adhocServerRunning && Core_IsStepping()) sleep_ms(1); + while (adhocServerRunning && Core_IsStepping() && coreState != CORE_POWERDOWN) sleep_ms(1); } // Free User Database Memory From a05da1a8c516a1bc6809e40f947d38633be7fdc8 Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Wed, 26 Aug 2020 17:17:31 +0700 Subject: [PATCH 5/7] Fix communication issue with Adhoc Server where ping to the Adhoc Server sometimes getting socket error 10053 and disconnected from Adhoc Server --- Core/HLE/proAdhoc.cpp | 153 ++++++++++++++++++------------- Core/HLE/proAdhoc.h | 8 +- Core/HLE/sceNetAdhoc.cpp | 192 ++++++++++++++++++++++++--------------- Core/HLE/sceNetAdhoc.h | 7 +- 4 files changed, 219 insertions(+), 141 deletions(-) diff --git a/Core/HLE/proAdhoc.cpp b/Core/HLE/proAdhoc.cpp index 8458b2c9f1..a96fce131f 100644 --- a/Core/HLE/proAdhoc.cpp +++ b/Core/HLE/proAdhoc.cpp @@ -43,6 +43,7 @@ #include "Core/HLE/sceKernelInterrupt.h" #include "Core/HLE/sceKernelThread.h" #include "Core/HLE/sceKernelMemory.h" +#include "Core/HLE/sceNetAdhoc.h" #include "Core/Instance.h" #include "proAdhoc.h" @@ -212,13 +213,26 @@ SceNetAdhocctlPeerInfo * findFriend(SceNetEtherAddr * MAC) { return peer; } -int getNonBlockingFlag(int fd) { -#ifdef _WIN32 - return 0; -#else - int sockflag = fcntl(fd, F_GETFL, O_NONBLOCK); - return sockflag & O_NONBLOCK; -#endif +int IsSocketReady(int fd, bool readfd, bool writefd, int* errorcode, int timeoutUS) { + fd_set readfds, writefds; + timeval tval; + + FD_ZERO(&readfds); + writefds = readfds; + if (readfd) { + FD_SET(fd, &readfds); + } + if (writefd) { + FD_SET(fd, &writefds); + } + tval.tv_sec = timeoutUS / 1000000; + tval.tv_usec = timeoutUS % 1000000; + + int ret = select(fd + 1, &readfds, &writefds, nullptr, &tval); + if (errorcode != nullptr) + *errorcode = errno; + + return ret; } void changeBlockingMode(int fd, int nonblocking) { @@ -1186,14 +1200,14 @@ void sendChat(std::string chatString) { message = chatString.substr(0, 60); // 64 return chat variable corrupted is it out of memory? strcpy(chat.message, message.c_str()); //Send Chat Messages - changeBlockingMode(metasocket, 0); - int chatResult = send(metasocket, (const char *)&chat, sizeof(chat), 0); - changeBlockingMode(metasocket, 1); - NOTICE_LOG(SCENET, "Send Chat %s to Adhoc Server", chat.message); - name = g_Config.sNickName.c_str(); - chatLog.push_back(name.substr(0, 8) + ": " + chat.message); - if (chatScreenVisible) { - updateChatScreen = true; + if (IsSocketReady(metasocket, false, true) > 0) { + int chatResult = send(metasocket, (const char*)&chat, sizeof(chat), 0); + NOTICE_LOG(SCENET, "Send Chat %s to Adhoc Server", chat.message); + name = g_Config.sNickName.c_str(); + chatLog.push_back(name.substr(0, 8) + ": " + chat.message); + if (chatScreenVisible) { + updateChatScreen = true; + } } } } @@ -1242,49 +1256,66 @@ int friendFinder(){ // Reconnect when disconnected while Adhocctl is still inited if (metasocket == (int)INVALID_SOCKET && netAdhocctlInited) { - if (g_Config.bEnableWlan && initNetwork(&product_code) == 0) { - networkInited = true; - } - } - - if (networkInited) { - // Ping Server - now = real_time_now() * 1000000.0; // Use real_time_now()*1000000.0 instead of CoreTiming::GetGlobalTimeUsScaled() if the game gets disconnected from AdhocServer too soon when FPS wasn't stable - if (now - lastping >= PSP_ADHOCCTL_PING_TIMEOUT) { // We may 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 - lastping = now; - - // Prepare Packet - uint8_t opcode = OPCODE_PING; - - // Send Ping to Server, may failed with socket error 10054/10053 if someone else with the same IP already connected to AdHoc Server (the server might need to be modified to differentiate MAC instead of IP) - changeBlockingMode(metasocket, 0); - int iResult = send(metasocket, (const char*)&opcode, 1, 0); - changeBlockingMode(metasocket, 1); - if (iResult == SOCKET_ERROR) { - ERROR_LOG(SCENET, "FriendFinder: Socket Error (%i) when sending OPCODE_PING", errno); + if (g_Config.bEnableWlan) { + if (initNetwork(&product_code) == 0) { + networkInited = true; + INFO_LOG(SCENET, "FriendFinder: Network [RE]Initialized"); + } + else { networkInited = false; shutdown(metasocket, SD_BOTH); closesocket(metasocket); metasocket = (int)INVALID_SOCKET; } } + } - // Wait for Incoming Data - int received = recv(metasocket, (char*)(rx + rxpos), sizeof(rx) - rxpos, 0); + if (networkInited) { + // Ping Server + now = real_time_now() * 1000000.0; // Use real_time_now()*1000000.0 instead of CoreTiming::GetGlobalTimeUsScaled() if the game gets disconnected from AdhocServer too soon when FPS wasn't stable + // original code : ((sceKernelGetSystemTimeWide() - lastping) >= ADHOCCTL_PING_TIMEOUT) + if (now - lastping >= PSP_ADHOCCTL_PING_TIMEOUT) { // We may need to use lower interval to prevent getting timeout at Pro Adhoc Server through internet + // Prepare Packet + uint8_t opcode = OPCODE_PING; - // Free Network Lock - //_freeNetworkLock(); + // Send Ping to Server, may failed with socket error 10054/10053 if someone else with the same IP already connected to AdHoc Server (the server might need to be modified to differentiate MAC instead of IP) + if (IsSocketReady(metasocket, false, true) > 0) { + int iResult = send(metasocket, (const char*)&opcode, 1, 0); + int error = errno; + // KHBBS seems to be getting error 10053 often + if (iResult == SOCKET_ERROR) { + ERROR_LOG(SCENET, "FriendFinder: Socket Error (%i) when sending OPCODE_PING", error); + if (error != EAGAIN && error != EWOULDBLOCK) { + networkInited = false; + shutdown(metasocket, SD_BOTH); + closesocket(metasocket); + metasocket = (int)INVALID_SOCKET; + } + } + else { + // Update Ping Time + lastping = now; + DEBUG_LOG(SCENET, "FriendFinder: Sending OPCODE_PING (%llu)", now); + } + } + } - // Received Data - if (received > 0) { - // Fix Position - rxpos += received; + // Check for Incoming Data + if (IsSocketReady(metasocket, true, false) > 0) { + int received = recv(metasocket, (char*)(rx + rxpos), sizeof(rx) - rxpos, 0); - // Log Incoming Traffic - //printf("Received %d Bytes of Data from Server\n", received); - INFO_LOG(SCENET, "Received %d Bytes of Data from Adhoc Server", received); + // Free Network Lock + //_freeNetworkLock(); + + // Received Data + if (received > 0) { + // Fix Position + rxpos += received; + + // Log Incoming Traffic + //printf("Received %d Bytes of Data from Server\n", received); + INFO_LOG(SCENET, "Received %d Bytes of Data from Adhoc Server", received); + } } // Handle Packets @@ -1297,6 +1328,9 @@ int friendFinder(){ SceNetAdhocctlConnectBSSIDPacketS2C* packet = (SceNetAdhocctlConnectBSSIDPacketS2C*)rx; INFO_LOG(SCENET, "FriendFinder: Incoming OPCODE_CONNECT_BSSID [%s]", mac2str(&packet->mac).c_str()); + // Update User BSSID + parameter.bssid.mac_addr = packet->mac; // This packet seems to contains Adhoc Group Creator's BSSID (similar to AP's BSSID) so it shouldn't get mixed up with local MAC address + // 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); @@ -1307,8 +1341,6 @@ int friendFinder(){ notifyAdhocctlHandlers(ADHOCCTL_EVENT_CONNECT, 0); }*/ - // Update User BSSID - parameter.bssid.mac_addr = packet->mac; // This packet seems to contains Adhoc Group Creator's BSSID (similar to AP's BSSID) so it shouldn't get mixed up with local MAC address // Notify Event Handlers notifyAdhocctlHandlers(ADHOCCTL_EVENT_CONNECT, 0); // Change State @@ -1576,9 +1608,7 @@ int getLocalIp(sockaddr_in* SocketAddress) { struct sockaddr_in localAddr; localAddr.sin_addr.s_addr = INADDR_ANY; socklen_t addrLen = sizeof(localAddr); - changeBlockingMode(metasocket, 0); int ret = getsockname(metasocket, (struct sockaddr*)&localAddr, &addrLen); - changeBlockingMode(metasocket, 1); if (SOCKET_ERROR != ret) { if (isLocalServer) { localAddr.sin_addr = g_localhostIP.in.sin_addr; @@ -1858,6 +1888,10 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ return SOCKET_ERROR; } setSockKeepAlive(metasocket, true); + // Disable Nagle Algo to prevent delaying small packets + setSockNoDelay(metasocket, 1); + // Switch to Nonblocking Behaviour + changeBlockingMode(metasocket, 1); struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; @@ -1914,17 +1948,14 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ product_code.type = adhoc_id->type; memcpy(product_code.data, adhoc_id->data, ADHOCCTL_ADHOCID_LEN); - // Switch to Nonblocking Behaviour - changeBlockingMode(metasocket, 1); // Connect to Adhoc Server server_addr.sin_addr = serverIp; int errorcode = 0; int cnt = 0; - while ((iResult = connect(metasocket, (sockaddr*)&server_addr, sizeof(server_addr))) == SOCKET_ERROR && (errorcode = errno) != EISCONN && cnt < adhocDefaultTimeout) { - sleep_ms(1); - cnt++; - } - if (iResult == SOCKET_ERROR && errorcode != EISCONN) { + iResult = connect(metasocket, (sockaddr*)&server_addr, sizeof(server_addr)); + errorcode = errno; + + if (iResult == SOCKET_ERROR && errorcode != EISCONN && (IsSocketReady(metasocket, true, true, nullptr, adhocDefaultTimeout * 1000) <= 0)) { char buffer[512]; snprintf(buffer, sizeof(buffer), "Socket error (%i) when connecting to AdhocServer [%s/%s:%u]", errorcode, g_Config.proAdhocServer.c_str(), inet_ntoa(server_addr.sin_addr), ntohs(server_addr.sin_port)); ERROR_LOG(SCENET, "%s", buffer); @@ -1941,9 +1972,9 @@ int initNetwork(SceNetAdhocctlAdhocId *adhoc_id){ strncpy((char *)&packet.name.data, g_Config.sNickName.c_str(), ADHOCCTL_NICKNAME_LEN); packet.name.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; memcpy(packet.game.data, adhoc_id->data, ADHOCCTL_ADHOCID_LEN); - changeBlockingMode(metasocket, 0); + + IsSocketReady(metasocket, false, true, nullptr, adhocDefaultTimeout * 1000); int sent = send(metasocket, (char*)&packet, sizeof(packet), 0); - changeBlockingMode(metasocket, 1); if (sent > 0) { socklen_t addrLen = sizeof(LocalIP); memset(&LocalIP, 0, addrLen); diff --git a/Core/HLE/proAdhoc.h b/Core/HLE/proAdhoc.h index fa5194f17e..8c88d471fc 100644 --- a/Core/HLE/proAdhoc.h +++ b/Core/HLE/proAdhoc.h @@ -49,7 +49,6 @@ #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" #include "Core/HLE/sceKernelMutex.h" @@ -928,11 +927,12 @@ extern int newChat; SceNetAdhocctlPeerInfo * findFriend(SceNetEtherAddr * MAC); /** - * Get the Non-Blocking Mode of the socket + * Get the Readability(ie. recv) and/or Writability(ie. send) of a socket * @param fd File Descriptor of the socket - * @return 1 for non-blocking, 0 for blocking + * @param timeout in usec (micro seconds), 0 = non-blocking + * @return > 0 = ready, 0 = timeout, -1 = error (errorcode only represent error of select and doesn't represent error of the socket) */ -int getNonBlockingFlag(int fd); +int IsSocketReady(int fd, bool readfd, bool writefd, int* errorcode = nullptr, int timeoutUS = 0); /** * Changes the Blocking Mode of the socket diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 9d62792037..7b9644e4e6 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -46,7 +46,6 @@ #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" @@ -63,7 +62,7 @@ static bool netAdhocMatchingInited; int netAdhocMatchingStarted = 0; int adhocDefaultTimeout = 2000; //5000 ms int adhocExtraPollDelayMS = 10; //10 -int adhocEventPollDelayMS = 100; //100 +int adhocEventPollDelayMS = 100; //100; Seems to be the same with PSP_ADHOCCTL_RECV_TIMEOUT int adhocMatchingEventDelayMS = 30; //30 int adhocEventDelayMS = 300; //500; This will affect the duration of "Connecting..." dialog/message box in .Hack//Link and Naruto Ultimate Ninja Heroes 3 @@ -78,6 +77,7 @@ int IsAdhocctlInCB = 0; int adhocctlNotifyEvent = -1; int adhocSocketNotifyEvent = -1; +std::map adhocctlRequests; std::map adhocSocketRequests; std::map sendTargetPeers; @@ -129,9 +129,51 @@ static void __AdhocctlNotify(u64 userdata, int cyclesLate) { SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_NET, error); if (waitID == 0 || error != 0) - return; // FIXME: Is it safe to exit here like this without re-scheduling the event? Will this event be triggered again? What will happen to the result i might want to change if exited here? + return; - int waitVal = __KernelGetWaitValue(threadID, error); // FIXME: Is this value going to be a valid value if waitID == 0? or it's a value belonged to other event? + // Socket not found?! Should never happened! but if it ever happen should we just exit here or need to wake the thread first? + if (adhocctlRequests.find(uid) == adhocctlRequests.end()) { + WARN_LOG(SCENET, "sceNetAdhocctl Socket WaitID(%i) not found!", uid); + //__KernelResumeThreadFromWait(threadID, ERROR_NET_ADHOCCTL_BUSY); + return; + } + + AdhocctlRequest& req = adhocctlRequests[uid]; + int len = 0; + + SceNetAdhocctlConnectPacketC2S packet; + memset(&packet, 0, sizeof(packet)); + packet.base.opcode = req.opcode; + packet.group = req.group; + + switch (req.opcode) + { + case OPCODE_CONNECT: + len = sizeof(packet); + break; + case OPCODE_SCAN: + case OPCODE_DISCONNECT: + len = 1; + break; + } + + // Send Packet if it wasn't succesfully sent before + if (len > 0) { + if (IsSocketReady(metasocket, false, true) > 0) { + int ret = send(metasocket, (const char*)&packet, len, 0); + int sockerr = errno; + + if (ret > 0 || (ret == SOCKET_ERROR && sockerr != EAGAIN && sockerr != EWOULDBLOCK)) { + // Prevent from sending again + req.opcode = 0; + if (ret == SOCKET_ERROR) + DEBUG_LOG(SCENET, "sceNetAdhocctl[%i]: Socket Error (%i)", uid, sockerr); + } + } + } + + // Now we just need to wait for replies from adhoc server and the change of state + int waitVal = __KernelGetWaitValue(threadID, error); if (adhocctlState != waitVal && error == 0) { // Detecting Adhocctl Initialization using waitVal < 0 if (waitVal >= 0 || (waitVal < 0 && (g_Config.bEnableWlan && !networkInited))) { @@ -148,20 +190,32 @@ static void __AdhocctlNotify(u64 userdata, int cyclesLate) { result = 0; // Faking successfully connected to adhoc server } - //HLEKernel::ResumeFromWait(threadID, WAITTYPE_NET, uid, result); // FIXME: This won't do anything if waitID == 0, not sure what kind of value returned from the HLE which i might want to change here. - __KernelResumeThreadFromWait(threadID, result); // FIXME: Forcing to change the result, will it cause an issue if waitID == 0? + __KernelResumeThreadFromWait(threadID, result); DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %d) Result (%08x) of sceNetAdhocctl - State: %d", waitID, error, (int)result, adhocctlState); + + // We are done with this request + adhocctlRequests.erase(uid); } -void WaitAdhocctlState(int state, int usec, const char* reason) { +int WaitAdhocctlState(AdhocctlRequest request, int state, int usec, const char* reason) { + int uid = (state < 0) ? 1 : metasocket; + + if (adhocctlRequests.find(uid) != adhocctlRequests.end()) { + WARN_LOG(SCENET, "sceNetAdhocctl - WaitID[%d] already existed, Socket is busy!", uid); + return ERROR_NET_ADHOCCTL_BUSY; + } + if (adhocctlNotifyEvent < 0) adhocctlNotifyEvent = CoreTiming::RegisterEvent("__AdhocctlNotify", __AdhocctlNotify); - int uid = (state < 0)? 1: metasocket; u64 param = ((u64)__KernelGetCurThread()) << 32 | uid; adhocctlStartTime = (u64)(real_time_now() * 1000.0); + adhocctlRequests[uid] = request; CoreTiming::ScheduleEvent(usToCycles(usec), adhocctlNotifyEvent, param); __KernelWaitCurThread(WAITTYPE_NET, uid, state, 0, false, reason); + + // faked success + return 0; } int DoBlockingPdpRecv(int uid, AdhocSocketRequest& req, s64& result) { @@ -386,17 +440,10 @@ int DoBlockingPtpAccept(int uid, AdhocSocketRequest& req, s64& result) { } int DoBlockingPtpConnect(int uid, AdhocSocketRequest& req, s64& result) { - fd_set readfds, writefds; - timeval tval; - FD_ZERO(&readfds); - FD_SET(uid, &readfds); - writefds = readfds; - tval.tv_sec = 0; - tval.tv_usec = 0; + int sockerr; // Wait for Connection (assuming "connect" has been called before) - int ret = select(uid + 1, &readfds, &writefds, NULL, &tval); - int sockerr = errno; + int ret = IsSocketReady(uid, true, true, &sockerr); // Connection is ready if (ret > 0) { @@ -535,11 +582,11 @@ static void __AdhocSocketNotify(u64 userdata, int cyclesLate) { break; } - // We are done with this socket - adhocSocketRequests.erase(uid); - __KernelResumeThreadFromWait(threadID, result); DEBUG_LOG(SCENET, "Returning (WaitID: %d, error: %d) Result (%08x) of sceNetAdhoc - SocketID: %d", waitID, error, (int)result, req.id); + + // We are done with this socket + adhocSocketRequests.erase(uid); } int WaitBlockingAdhocSocket(int socketId, int type, int pspSocketId, void* buffer, s32_le* len, u32 timeoutUS, SceNetEtherAddr* remoteMAC, u16_le* remotePort, const char* reason) { @@ -551,8 +598,7 @@ int WaitBlockingAdhocSocket(int socketId, int type, int pspSocketId, void* buffe if (adhocSocketNotifyEvent < 0) adhocSocketNotifyEvent = CoreTiming::RegisterEvent("__AdhocSocketNotify", __AdhocSocketNotify); - if (getNonBlockingFlag(socketId) == 0) - changeBlockingMode(socketId, 1); + //changeBlockingMode(socketId, 1); u32 tmout = timeoutUS; if (tmout > 0) @@ -646,6 +692,7 @@ void __NetAdhocDoState(PointerWrap &p) { // Discard leftover events adhocctlEvents.clear(); matchingEvents.clear(); + adhocctlRequests.clear(); adhocSocketRequests.clear(); sendTargetPeers.clear(); @@ -683,6 +730,7 @@ void __AdhocNotifInit() { adhocctlNotifyEvent = CoreTiming::RegisterEvent("__AdhocctlNotify", __AdhocctlNotify); adhocSocketNotifyEvent = CoreTiming::RegisterEvent("__AdhocSocketNotify", __AdhocSocketNotify); + adhocctlRequests.clear(); adhocSocketRequests.clear(); sendTargetPeers.clear(); } @@ -744,7 +792,8 @@ static u32 sceNetAdhocctlInit(int stackSize, int prio, u32 productAddr) { // Need to make sure to be connected to adhoc server before returning to prevent GTA VCS failed to create/join a group and unable to see any game room int us = adhocExtraPollDelayMS * 1000; if (g_Config.bEnableWlan && !networkInited) { - WaitAdhocctlState(-1, us, "adhoc init"); + AdhocctlRequest dummyreq = { OPCODE_LOGIN, {0} }; + return WaitAdhocctlState(dummyreq, -1, us, "adhocctl init"); } // Give a little time for friendFinder thread to be ready before the game use the next sceNet functions, should've checked for friendFinderRunning status instead of guessing the time? else @@ -871,6 +920,9 @@ static int sceNetAdhocPdpCreate(const char *mac, int port, int bufferSize, u32 u //sceNetPortOpen("UDP", port); UPnP_Add(IP_PROTOCOL_UDP, isOriPort ? port : port + portOffset, port + portOffset); // g_PortManager.Add(IP_PROTOCOL_UDP, isOriPort ? port : port + portOffset, port + portOffset); + // Switch to non-blocking for futher usage + changeBlockingMode(usocket, 1); + // Success return i + 256; } @@ -1009,7 +1061,6 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int //_acquireNetworkLock(); // Send Data. UDP are guaranteed to be sent as a whole or nothing(failed if len > SO_MAX_MSG_SIZE), and never be partially sent/recv - changeBlockingMode(socket->id, 1); int sent = sendto(socket->id, (const char *)data, len, 0, (sockaddr *)&target, sizeof(target)); int error = errno; @@ -1087,8 +1138,6 @@ static int sceNetAdhocPdpSend(int id, const char *mac, u32 port, void *data, int peerlock.unlock(); // Send Data - changeBlockingMode(socket->id, 1); // Do we need to switched to blocking-mode to make sure the data are fully sent? - // Simulate blocking behaviour with non-blocking socket if (!flag) { if (sendTargetPeers.find(socket->id) != sendTargetPeers.end()) { @@ -1232,11 +1281,9 @@ static int sceNetAdhocPdpRecv(int id, void *addr, void * port, void *buf, void * // Receive Data. PDP always sent in full size or nothing(failed), recvfrom will always receive in full size as requested (blocking) or failed (non-blocking). If available UDP data is larger than buffer, excess data is lost. - changeBlockingMode(socket->id, 1); // Should peek first for the available data size if it's more than len return ERROR_NET_ADHOC_NOT_ENOUGH_SPACE along with required size in len to prevent losing excess data received = recvfrom(socket->id, (char*)buf, *len, MSG_PEEK, (sockaddr*)&sin, &sinlen); if (received != SOCKET_ERROR && *len < received) { - changeBlockingMode(socket->id, 0); WARN_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Peeked %u/%u bytes from %s:%u\n", id, getLocalPort(socket->id), received, *len, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); *len = received; @@ -1272,7 +1319,6 @@ static int sceNetAdhocPdpRecv(int id, void *addr, void * port, void *buf, void * VERBOSE_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPdpRecv[%i:%u] [size=%i]", error, id, socket->lport, *len); } - changeBlockingMode(socket->id, 0); // Should we set output length to 0 on Error? *len = 0; @@ -1571,6 +1617,7 @@ int sceNetAdhocctlScan() { if (adhocctlState == ADHOCCTL_STATE_DISCONNECTED) { adhocctlState = ADHOCCTL_STATE_SCANNING; + int us = adhocEventPollDelayMS * 1000; // Reset Networks/Group list to prevent other threads from using these soon to be replaced networks peerlock.lock(); @@ -1582,17 +1629,23 @@ int sceNetAdhocctlScan() { uint8_t opcode = OPCODE_SCAN; // Send Scan Request Packet, may failed with socket error 10054/10053 if someone else with the same IP already connected to AdHoc Server (the server might need to be modified to differentiate MAC instead of IP) - changeBlockingMode(metasocket, 0); int iResult = send(metasocket, (char *)&opcode, 1, 0); int error = errno; - changeBlockingMode(metasocket, 1); + if (iResult == SOCKET_ERROR) { - ERROR_LOG(SCENET, "Socket error (%i) when sending", error); - adhocctlState = ADHOCCTL_STATE_DISCONNECTED; - //notifyAdhocctlHandlers(ADHOCCTL_EVENT_SCAN, 0); - //if (error == ECONNABORTED || error == ECONNRESET || error == ENOTCONN) return ERROR_NET_ADHOCCTL_NOT_INITIALIZED; // A case where it need to reconnect to AdhocServer - return ERROR_NET_ADHOCCTL_DISCONNECTED; // ERROR_NET_ADHOCCTL_BUSY + if (error != EAGAIN && error != EWOULDBLOCK) { + ERROR_LOG(SCENET, "Socket error (%i) when sending", error); + adhocctlState = ADHOCCTL_STATE_DISCONNECTED; + //if (error == ECONNABORTED || error == ECONNRESET || error == ENOTCONN) return ERROR_NET_ADHOCCTL_NOT_INITIALIZED; // A case where it need to reconnect to AdhocServer + return ERROR_NET_ADHOCCTL_BUSY; + } + else if (friendFinderRunning) { + AdhocctlRequest req = { OPCODE_SCAN, {0} }; + return WaitAdhocctlState(req, ADHOCCTL_STATE_DISCONNECTED, us, "adhocctl scan"); + } } + else + hleDelayResult(0, "give a little time to be ready to receive the callback", us); // Not delaying here may cause Naruto Shippuden Ultimate Ninja Heroes 3 to get disconnected when the mission started // Return Success return 0; @@ -1741,6 +1794,8 @@ static u32 sceNetAdhocctlAddHandler(u32 handlerPtr, u32 handlerArg) { u32 NetAdhocctl_Disconnect() { // Library initialized if (netAdhocctlInited) { + int iResult, error; + int us = adhocEventPollDelayMS * 1000; // Connected State (Adhoc Mode) if (adhocctlState != ADHOCCTL_STATE_DISCONNECTED) { // (threadStatus == ADHOCCTL_STATE_CONNECTED) // Clear Network Name @@ -1756,14 +1811,20 @@ u32 NetAdhocctl_Disconnect() { //_acquireNetworkLock(); // Send Disconnect Request Packet - changeBlockingMode(metasocket, 0); - int iResult = send(metasocket, (const char*)&opcode, 1, 0); - int error = errno; - changeBlockingMode(metasocket, 1); + iResult = send(metasocket, (const char*)&opcode, 1, 0); + error = errno; + + // Sending may get socket error 10053 if the AdhocServer is already shutted down if (iResult == SOCKET_ERROR) { - ERROR_LOG(SCENET, "Socket error (%i) when sending", error); - // Set Disconnected State - adhocctlState = ADHOCCTL_STATE_DISCONNECTED; + if (error != EAGAIN && error != EWOULDBLOCK) { + ERROR_LOG(SCENET, "Socket error (%i) when sending", error); + // Set Disconnected State + adhocctlState = ADHOCCTL_STATE_DISCONNECTED; + } + else if (friendFinderRunning) { + AdhocctlRequest req = { OPCODE_DISCONNECT, {0} }; + WaitAdhocctlState(req, ADHOCCTL_STATE_DISCONNECTED, us, "adhocctl disconnect"); + } } // Free Network Lock @@ -1793,12 +1854,8 @@ u32 NetAdhocctl_Disconnect() { notifyAdhocctlHandlers(ADHOCCTL_EVENT_DISCONNECT, 0); //hleCheckCurrentCallbacks(); - int us = adhocEventPollDelayMS * 1000; - if (adhocctlState != ADHOCCTL_STATE_DISCONNECTED && friendFinderRunning) { - WaitAdhocctlState(ADHOCCTL_STATE_DISCONNECTED, us, "adhoc disconnect"); - } // Return Success, some games might ignore returned value and always treat it as success, otherwise repeatedly calling this function - else if (adhocctlState == ADHOCCTL_STATE_DISCONNECTED) + if (adhocctlState == ADHOCCTL_STATE_DISCONNECTED || iResult != SOCKET_ERROR) hleDelayResult(0, "give time to init/cleanup", us); return 0; @@ -2038,10 +2095,9 @@ int NetAdhocctl_Create(const char* groupName) { // Acquire Network Lock // Send Packet - changeBlockingMode(metasocket, 0); int iResult = send(metasocket, (const char*)&packet, sizeof(packet), 0); int error = errno; - changeBlockingMode(metasocket, 1); + if (iResult == SOCKET_ERROR) { ERROR_LOG(SCENET, "Socket error (%i) when sending", error); //return ERROR_NET_ADHOCCTL_NOT_INITIALIZED; // ERROR_NET_ADHOCCTL_DISCONNECTED; // ERROR_NET_ADHOCCTL_BUSY; @@ -2059,11 +2115,13 @@ int NetAdhocctl_Create(const char* groupName) { // Wait for Status to be connected to prevent Ford Street Racing from Failed to create game session int us = adhocEventDelayMS * 1000; - if (adhocctlState != ADHOCCTL_STATE_CONNECTED && friendFinderRunning) { - WaitAdhocctlState(ADHOCCTL_STATE_CONNECTED, us, "adhoc connect"); + if (adhocctlState != ADHOCCTL_STATE_CONNECTED && iResult == SOCKET_ERROR && friendFinderRunning) { + AdhocctlRequest req = { OPCODE_CONNECT, {0} }; + if (groupNameStruct != NULL) req.group = *groupNameStruct; + return WaitAdhocctlState(req, ADHOCCTL_STATE_CONNECTED, us, "adhocctl connect"); } // Giving time for Naruto Shippuden Ninja Heroes 3 to close down the "Connecting..." dialog, otherwise the dialog will stuck there. - else if (adhocctlState == ADHOCCTL_STATE_CONNECTED) + else if (adhocctlState == ADHOCCTL_STATE_CONNECTED || iResult != SOCKET_ERROR) hleDelayResult(0, "give time to init/cleanup", us); // Return Success @@ -2497,6 +2555,9 @@ static int sceNetAdhocPtpOpen(const char *srcmac, int sport, const char *dstmac, if (!isClient) UPnP_Add(IP_PROTOCOL_TCP, isOriPort ? sport : sport + portOffset, sport + portOffset); // g_PortManager.Add(IP_PROTOCOL_TCP, isOriPort ? sport : sport + portOffset, sport + portOffset); + // Switch to non-blocking for futher usage + changeBlockingMode(tcpsocket, 1); + // Return PTP Socket Pointer return i + 1; } @@ -2665,15 +2726,6 @@ static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int memset(&peeraddr, 0, sizeof(peeraddr)); socklen_t peeraddrlen = sizeof(peeraddr); - // Grab Nonblocking Flag - uint32_t nbio = getNonBlockingFlag(socket->id); - // Switch to Nonblocking Behaviour - if (nbio == 0) { - // Overwrite Socket Option - 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; @@ -2689,12 +2741,6 @@ static int sceNetAdhocPtpAccept(int id, u32 peerMacAddrPtr, u32 peerPortPtr, int } } - // Restore Blocking Behaviour - if (nbio == 0) { - // Restore Socket Option - changeBlockingMode(socket->id, 0); - } - // Accepted New Connection if (newsocket > 0) { int newid = AcceptPtpSocket(id, newsocket, peeraddr, addr, port); @@ -2769,10 +2815,6 @@ static int sceNetAdhocPtpConnect(int id, int timeout, int flag) { // Grab Nonblocking Flag //uint32_t nbio = getNonBlockingFlag(socket->id); - // Switch to Nonblocking Behaviour. Forcing blocking behaviour on the first connect may fix connection issue on GvG Next Plus, But i don't like using blocking socket with infinite timeout if the game it self were asking for non-blocking behaviour :( - // We are using non-blocking to simulate blocking - changeBlockingMode(socket->id, 1); - // Connect Socket to Peer // NOTE: Based on what i read at stackoverflow, The First Non-blocking POSIX connect will always returns EAGAIN/EWOULDBLOCK because it returns without waiting for ACK/handshake, But GvG Next Plus is treating non-blocking PtpConnect just like blocking connect, May be on a real PSP the first non-blocking sceNetAdhocPtpConnect can be successfull? // TODO: Keep track number of Connect attempts so we can simulate blocking on first attempt (getNonBlockingFlag can't be used to get non-blocking flag on Windows thus can't be used to keep track) @@ -2998,6 +3040,9 @@ static int sceNetAdhocPtpListen(const char *srcmac, int sport, int bufsize, int //sceNetPortOpen("TCP", sport); UPnP_Add(IP_PROTOCOL_TCP, isOriPort ? sport : sport + portOffset, sport + portOffset); // g_PortManager.Add(IP_PROTOCOL_TCP, isOriPort ? sport : sport + portOffset, sport + portOffset); + // Switch to non-blocking for futher usage + changeBlockingMode(tcpsocket, 1); + // Return PTP Socket Pointer return i + 1; } @@ -3082,7 +3127,6 @@ static int sceNetAdhocPtpSend(int id, u32 dataAddr, u32 dataSizeAddr, int timeou // _acquireNetworkLock(); // Send Data - changeBlockingMode(socket->id, 1); int sent = send(socket->id, data, *len, 0); int error = errno; @@ -3173,7 +3217,6 @@ static int sceNetAdhocPtpRecv(int id, u32 dataAddr, u32 dataSizeAddr, int timeou int error = 0; // Receive Data - changeBlockingMode(socket->id, 1); received = recv(socket->id, (char*)buf, *len, 0); error = errno; @@ -3185,7 +3228,6 @@ static int sceNetAdhocPtpRecv(int id, u32 dataAddr, u32 dataSizeAddr, int timeou VERBOSE_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPtpRecv[%i:%u] [size=%i]", error, id, socket->lport, *len); } - changeBlockingMode(socket->id, 0); // Free Network Lock // _freeNetworkLock(); diff --git a/Core/HLE/sceNetAdhoc.h b/Core/HLE/sceNetAdhoc.h index 08583433de..6a12e19c97 100644 --- a/Core/HLE/sceNetAdhoc.h +++ b/Core/HLE/sceNetAdhoc.h @@ -30,6 +30,11 @@ typedef struct MatchingArgs { #pragma pack(pop) #endif +struct AdhocctlRequest { + u8 opcode; + SceNetAdhocctlGroupName group; +}; + struct AdhocSendTarget { u32 ip; u16 port; // original port @@ -48,7 +53,7 @@ struct AdhocSocketRequest { s32_le* length; u32 timeout; u64 startTime; - struct SceNetEtherAddr* remoteMAC; + SceNetEtherAddr* remoteMAC; u16_le* remotePort; }; From d3157205dd87efd66323263adf5bcb71902d322a Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Sun, 30 Aug 2020 23:50:31 +0700 Subject: [PATCH 6/7] Don't allocate PSP's Memory during DoState --- Core/HLE/sceNet.cpp | 21 ++++++++++++--------- Core/HLE/sceNetAdhoc.cpp | 30 ++++++++++++++++-------------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index a34167f275..6187a3e1ad 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -200,6 +200,15 @@ void notifyApctlHandlers(int oldState, int newState, int flag, int error) { __UpdateApctlHandlers(oldState, newState, flag, error); } +void netValidateLoopMemory() { + // Allocate Memory if it wasn't valid/allocated after loaded from old SaveState + if (!apctlThreadHackAddr || (apctlThreadHackAddr && strcmp("apctlThreadHack", kernelMemory.GetBlockTag(apctlThreadHackAddr)) != 0)) { + u32 blockSize = sizeof(apctlThreadCode); + apctlThreadHackAddr = kernelMemory.Alloc(blockSize, false, "apctlThreadHack"); + if (apctlThreadHackAddr) Memory::Memcpy(apctlThreadHackAddr, apctlThreadCode, sizeof(apctlThreadCode)); + } +} + // This feels like a dubious proposition, mostly... void __NetDoState(PointerWrap &p) { auto s = p.Section("sceNet", 1, 4); @@ -244,22 +253,15 @@ void __NetDoState(PointerWrap &p) { apctlThreadHackAddr = 0; apctlThreadID = 0; } - // 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) { + // Let's not change "Inited" value when Loading SaveState in the middle of multiplayer to prevent memory & port leaks netApctlInited = cur_netApctlInited; netInetInited = cur_netInetInited; netInited = cur_netInited; // Discard leftover events apctlEvents.clear(); - - // Previously, this wasn't being saved. It needs its own space. - if (!apctlThreadHackAddr || (apctlThreadHackAddr && strcmp("apctlThreadHack", kernelMemory.GetBlockTag(apctlThreadHackAddr)) != 0)) { - u32 blockSize = sizeof(apctlThreadCode); - apctlThreadHackAddr = kernelMemory.Alloc(blockSize, false, "apctlThreadHack"); - } - // Restore Apctl Loop MIPS code to prevent crashes after loading from SaveState - if (apctlThreadHackAddr) Memory::Memcpy(apctlThreadHackAddr, apctlThreadCode, sizeof(apctlThreadCode)); } } @@ -780,6 +782,7 @@ static int sceNetApctlInit(int stackSize, int initPriority) { truncate_cpy(netApctlInfo.subNetMask, sizeof(netApctlInfo.subNetMask), "0.0.0.0"); // Create APctl fake-Thread + netValidateLoopMemory(); apctlThreadID = __KernelCreateThread("ApctlThread", __KernelGetCurThreadModuleId(), apctlThreadHackAddr, initPriority, stackSize, PSP_THREAD_ATTR_USER, 0, true); if (apctlThreadID > 0) { __KernelStartThread(apctlThreadID, 0, 0); diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 7b9644e4e6..03c5c579d0 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -615,6 +615,20 @@ int WaitBlockingAdhocSocket(int socketId, int type, int pspSocketId, void* buffe return ERROR_NET_ADHOC_TIMEOUT; } +void netAdhocValidateLoopMemory() { + // Allocate Memory if it wasn't valid/allocated after loaded from old SaveState + if (!dummyThreadHackAddr || (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 || (matchingThreadHackAddr && strcmp("matchingThreadHack", kernelMemory.GetBlockTag(matchingThreadHackAddr)) != 0)) { + u32 blockSize = sizeof(matchingThreadCode); + matchingThreadHackAddr = kernelMemory.Alloc(blockSize, false, "matchingThreadHack"); + if (matchingThreadHackAddr) Memory::Memcpy(matchingThreadHackAddr, matchingThreadCode, sizeof(matchingThreadCode)); + } +} + void __NetAdhocDoState(PointerWrap &p) { auto s = p.Section("sceNetAdhoc", 1, 5); if (!s) @@ -675,20 +689,6 @@ void __NetAdhocDoState(PointerWrap &p) { } if (p.mode == p.MODE_READ) { - // Previously, this wasn't being saved. It needs its own space. - if (!dummyThreadHackAddr || (dummyThreadHackAddr && strcmp("dummythreadhack", kernelMemory.GetBlockTag(dummyThreadHackAddr)) != 0)) { - u32 blockSize = sizeof(dummyThreadCode); - dummyThreadHackAddr = kernelMemory.Alloc(blockSize, false, "dummythreadhack"); - } - if (!matchingThreadHackAddr || (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)); - // Discard leftover events adhocctlEvents.clear(); matchingEvents.clear(); @@ -778,6 +778,7 @@ 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 + netAdhocValidateLoopMemory(); threadAdhocID = __KernelCreateThread("AdhocThread", __KernelGetCurThreadModuleId(), dummyThreadHackAddr, prio, stackSize, PSP_THREAD_ATTR_USER, 0, true); if (threadAdhocID > 0) { __KernelStartThread(threadAdhocID, 0, 0); @@ -3716,6 +3717,7 @@ int NetAdhocMatching_Start(int matchingId, int evthPri, int evthPartitionId, int }*/ // Create & Start the Fake PSP Thread ("matching_ev%d" and "matching_io%d") + netAdhocValidateLoopMemory(); std::string thrname = std::string("MatchingThr") + std::to_string(matchingId); matchingThreads[item->matching_thid] = sceKernelCreateThread(thrname.c_str(), matchingThreadHackAddr, evthPri, evthStack, 0, 0); //item->matchingThread = new HLEHelperThread(thrname.c_str(), "sceNetAdhocMatching", "__NetMatchingCallbacks", inthPri, inthStack); From 170d313fa7ba7766f9d8ad6ac391e8b2e40e045d Mon Sep 17 00:00:00 2001 From: ANR2ME Date: Mon, 31 Aug 2020 09:20:46 +0700 Subject: [PATCH 7/7] Minor changes to make sure the output struct/arg have similar value to the one from prx file (based on Kingdom Hearts BBS) --- Core/HLE/proAdhoc.h | 12 +++++++----- Core/HLE/sceNetAdhoc.cpp | 30 ++++++++++-------------------- 2 files changed, 17 insertions(+), 25 deletions(-) diff --git a/Core/HLE/proAdhoc.h b/Core/HLE/proAdhoc.h index 8c88d471fc..304657acdb 100644 --- a/Core/HLE/proAdhoc.h +++ b/Core/HLE/proAdhoc.h @@ -252,14 +252,16 @@ typedef struct SceNetAdhocctlParameter { SceNetAdhocctlNickname nickname; } PACK SceNetAdhocctlParameter; -// Peer Information +// Peer Information (internal use only) typedef struct SceNetAdhocctlPeerInfo { SceNetAdhocctlPeerInfo * next; SceNetAdhocctlNickname nickname; SceNetEtherAddr mac_addr; - u32_le ip_addr; - uint8_t padding[2]; + u16_le padding; + u32_le flags; u64_le last_recv; // Need to use the same method with sceKernelGetSystemTimeWide (ie. CoreTiming::GetGlobalTimeUsScaled) to prevent timing issue (ie. in game timeout) + + u32_le ip_addr; // internal use only } PACK SceNetAdhocctlPeerInfo; // Peer Information with u32 pointers @@ -267,8 +269,8 @@ typedef struct SceNetAdhocctlPeerInfoEmu { u32_le next; // Changed the pointer to u32 SceNetAdhocctlNickname nickname; SceNetEtherAddr mac_addr; - u32_le ip_addr; //jpcsp wrote 6bytes of 0x11 for this & padding - u16 padding; // Changed the padding to u16 + u16_le padding; //00 00 + u32_le flags; //00 04 00 00 // State of the peer? Or related to sceNetAdhocAuth_CF4D9BED ? u64_le last_recv; // Need to use the same method with sceKernelGetSystemTimeWide (ie. CoreTiming::GetGlobalTimeUsScaled) to prevent timing issue (ie. in game timeout) } PACK SceNetAdhocctlPeerInfoEmu; diff --git a/Core/HLE/sceNetAdhoc.cpp b/Core/HLE/sceNetAdhoc.cpp index 03c5c579d0..8af2249f3d 100644 --- a/Core/HLE/sceNetAdhoc.cpp +++ b/Core/HLE/sceNetAdhoc.cpp @@ -1321,9 +1321,6 @@ static int sceNetAdhocPdpRecv(int id, void *addr, void * port, void *buf, void * VERBOSE_LOG(SCENET, "Socket Error (%i) on sceNetAdhocPdpRecv[%i:%u] [size=%i]", error, id, socket->lport, *len); } - // Should we set output length to 0 on Error? - *len = 0; - // Received Data. UDP can also receives 0 data, while on TCP 0 data = connection gracefully closed, but not sure about PDP tho if (received > 0) { DEBUG_LOG(SCENET, "sceNetAdhocPdpRecv[%i:%u]: Received %u bytes from %s:%u\n", id, getLocalPort(socket->id), received, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); @@ -1666,7 +1663,7 @@ int sceNetAdhocctlGetScanInfo(u32 sizeAddr, u32 bufAddr) { SceNetAdhocctlScanInfoEmu *buf = NULL; if (Memory::IsValidAddress(bufAddr)) buf = (SceNetAdhocctlScanInfoEmu *)Memory::GetPointer(bufAddr); - INFO_LOG(SCENET, "sceNetAdhocctlGetScanInfo([%08x]=%i, %08x)", sizeAddr, /*buflen ? *buflen : -1*/Memory::Read_U32(sizeAddr), bufAddr); + INFO_LOG(SCENET, "sceNetAdhocctlGetScanInfo([%08x]=%i, %08x)", sizeAddr, Memory::Read_U32(sizeAddr), bufAddr); if (!g_Config.bEnableWlan) { return 0; } @@ -2017,17 +2014,14 @@ int sceNetAdhocctlGetPeerInfo(const char *mac, int size, u32 peerInfoAddr) { // Local MAC if (isLocalMAC(maddr)) { - sockaddr_in addr; SceNetAdhocctlNickname nickname; - getLocalIp(&addr); - strncpy((char*)&nickname.data, g_Config.sNickName.c_str(), ADHOCCTL_NICKNAME_LEN); - nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; // making sure to be null-terminated since strncpy doesn't implicitly append null + truncate_cpy((char*)&nickname.data, ADHOCCTL_NICKNAME_LEN, g_Config.sNickName.c_str()); //buf->next = 0; buf->nickname = nickname; 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->flags = 0x0400; buf->padding = 0; buf->last_recv = CoreTiming::GetGlobalTimeUsScaled(); @@ -2049,7 +2043,7 @@ int sceNetAdhocctlGetPeerInfo(const char *mac, int size, u32 peerInfoAddr) { buf->nickname = peer->nickname; 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->flags = 0x0400; //peer->ip_addr; 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 @@ -3233,9 +3227,6 @@ static int sceNetAdhocPtpRecv(int id, u32 dataAddr, u32 dataSizeAddr, int timeou // Free Network Lock // _freeNetworkLock(); - // Should we set output length to 0 on Error? - *len = 0; - // Received Data if (received > 0) { // Save Length @@ -3618,7 +3609,7 @@ static int sceNetAdhocMatchingCreate(int mode, int maxnum, int port, int rxbufle if (keepalive_int < 1) context->keepalive_int = PSP_ADHOCCTL_PING_TIMEOUT; else context->keepalive_int = keepalive_int; // client might set this to 0 context->keepalivecounter = init_count; // used to multiply keepalive_int as timeout context->timeout = ((u64_le)keepalive_int * (u64_le)init_count); - if (context->timeout < 5000000) context->timeout = 5000000; // For internet play we need higher timeout than what the game wanted + context->timeout = std::max(context->timeout, adhocDefaultTimeout * 1000ULL); // For internet play we need higher timeout than what the game wanted context->handler = handler; // Fill in Selfpeer @@ -4761,7 +4752,7 @@ static int sceNetAdhocctlGetPeerList(u32 sizeAddr, u32 bufAddr) { // Copy Peer Info buf[discovered].nickname = peer->nickname; buf[discovered].mac_addr = peer->mac_addr; - buf[discovered].ip_addr = peer->ip_addr; + buf[discovered].flags = 0x0400; //peer->ip_addr; buf[discovered].last_recv = peer->last_recv; discovered++; @@ -4846,12 +4837,10 @@ static int sceNetAdhocctlGetAddrByName(const char *nickName, u32 sizeAddr, u32 b sockaddr_in addr; getLocalIp(&addr); - //buf->next = 0; buf[discovered].nickname = parameter.nickname; buf[discovered].nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; // last char need to be null-terminated char getLocalMac(&buf[discovered].mac_addr); - buf[discovered].ip_addr = addr.sin_addr.s_addr; // 0x11111111; - //buf->padding = 0x1111; //0; + buf[discovered].flags = 0x0400; //addr.sin_addr.s_addr; buf[discovered++].last_recv = CoreTiming::GetGlobalTimeUsScaled(); } @@ -4865,13 +4854,14 @@ static int sceNetAdhocctlGetAddrByName(const char *nickName, u32 sizeAddr, u32 b if (strncmp((char *)&peer->nickname.data, nickName, ADHOCCTL_NICKNAME_LEN) == 0) { // Fake Receive Time - if (peer->last_recv != 0) peer->last_recv = CoreTiming::GetGlobalTimeUsScaled(); //sceKernelGetSystemTimeWide(); + if (peer->last_recv != 0) + peer->last_recv = CoreTiming::GetGlobalTimeUsScaled(); //sceKernelGetSystemTimeWide(); // Copy Peer Info buf[discovered].nickname = peer->nickname; buf[discovered].nickname.data[ADHOCCTL_NICKNAME_LEN - 1] = 0; // last char need to be null-terminated char buf[discovered].mac_addr = peer->mac_addr; - buf[discovered].ip_addr = peer->ip_addr; + buf[discovered].flags = 0x0400; //peer->ip_addr; buf[discovered++].last_recv = peer->last_recv; } }