From 1a50770647c582c5ce194e5741e3014bb1c1e8b2 Mon Sep 17 00:00:00 2001 From: Sylvessa <225480449+sylvessa@users.noreply.github.com> Date: Thu, 26 Mar 2026 09:15:11 -0500 Subject: [PATCH] Add asynchronous server joining (#1408) --- .../Common/Network/GameNetworkManager.h | 3 +- .../Network/PlatformNetworkManagerStub.cpp | 63 +++-- .../Network/PlatformNetworkManagerStub.h | 6 + .../Common/UI/UIScene_ConnectingProgress.cpp | 127 +++++++++ .../Common/UI/UIScene_ConnectingProgress.h | 5 + .../Common/UI/UIScene_JoinMenu.cpp | 18 ++ .../Windows64/Network/WinsockNetLayer.cpp | 249 ++++++++++++++++++ .../Windows64/Network/WinsockNetLayer.h | 30 +++ 8 files changed, 480 insertions(+), 21 deletions(-) diff --git a/Minecraft.Client/Common/Network/GameNetworkManager.h b/Minecraft.Client/Common/Network/GameNetworkManager.h index 3357b3cd..22d58807 100644 --- a/Minecraft.Client/Common/Network/GameNetworkManager.h +++ b/Minecraft.Client/Common/Network/GameNetworkManager.h @@ -47,7 +47,8 @@ public: { JOINGAME_SUCCESS, JOINGAME_FAIL_GENERAL, - JOINGAME_FAIL_SERVER_FULL + JOINGAME_FAIL_SERVER_FULL, + JOINGAME_PENDING } eJoinGameResult; void Initialise(); diff --git a/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp b/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp index 1e625098..430f2c11 100644 --- a/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp +++ b/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp @@ -173,6 +173,11 @@ bool CPlatformNetworkManagerStub::Initialise(CGameNetworkManager *pGameNetworkMa m_bSearchPending = false; m_bIsOfflineGame = false; +#ifdef _WINDOWS64 + m_bJoinPending = false; + m_joinLocalUsersMask = 0; + m_joinHostName[0] = 0; +#endif m_pSearchParam = nullptr; m_SessionsUpdatedCallback = nullptr; @@ -282,6 +287,38 @@ void CPlatformNetworkManagerStub::DoWork() m_bLeaveGameOnTick = false; } } + + if (m_bJoinPending) + { + WinsockNetLayer::eJoinState state = WinsockNetLayer::GetJoinState(); + if (state == WinsockNetLayer::eJoinState_Success) + { + WinsockNetLayer::FinalizeJoin(); + + BYTE localSmallId = WinsockNetLayer::GetLocalSmallId(); + + IQNet::m_player[localSmallId].m_smallId = localSmallId; + IQNet::m_player[localSmallId].m_isRemote = false; + IQNet::m_player[localSmallId].m_isHostPlayer = false; + IQNet::m_player[localSmallId].m_resolvedXuid = Win64Xuid::ResolvePersistentXuid(); + + Minecraft* pMinecraft = Minecraft::GetInstance(); + wcscpy_s(IQNet::m_player[localSmallId].m_gamertag, 32, pMinecraft->user->name.c_str()); + IQNet::s_playerCount = localSmallId + 1; + + NotifyPlayerJoined(&IQNet::m_player[0]); + NotifyPlayerJoined(&IQNet::m_player[localSmallId]); + + m_pGameNetworkManager->StateChange_AnyToStarting(); + m_bJoinPending = false; + } + else if (state == WinsockNetLayer::eJoinState_Failed || + state == WinsockNetLayer::eJoinState_Rejected || + state == WinsockNetLayer::eJoinState_Cancelled) + { + m_bJoinPending = false; + } + } #endif } @@ -511,36 +548,22 @@ int CPlatformNetworkManagerStub::JoinGame(FriendSessionInfo* searchResult, int l IQNet::m_player[0].m_smallId = 0; IQNet::m_player[0].m_isRemote = true; IQNet::m_player[0].m_isHostPlayer = true; - // Remote host still maps to legacy host XUID in mixed old/new sessions. IQNet::m_player[0].m_resolvedXuid = Win64Xuid::GetLegacyEmbeddedHostXuid(); wcsncpy_s(IQNet::m_player[0].m_gamertag, 32, searchResult->data.hostName, _TRUNCATE); WinsockNetLayer::StopDiscovery(); - if (!WinsockNetLayer::JoinGame(hostIP, hostPort)) + wcsncpy_s(m_joinHostName, 32, searchResult->data.hostName, _TRUNCATE); + m_joinLocalUsersMask = localUsersMask; + + if (!WinsockNetLayer::BeginJoinGame(hostIP, hostPort)) { app.DebugPrintf("Win64 LAN: Failed to connect to %s:%d\n", hostIP, hostPort); return CGameNetworkManager::JOINGAME_FAIL_GENERAL; } - BYTE localSmallId = WinsockNetLayer::GetLocalSmallId(); - - IQNet::m_player[localSmallId].m_smallId = localSmallId; - IQNet::m_player[localSmallId].m_isRemote = false; - IQNet::m_player[localSmallId].m_isHostPlayer = false; - // Local non-host identity is the persistent uid.dat XUID. - IQNet::m_player[localSmallId].m_resolvedXuid = Win64Xuid::ResolvePersistentXuid(); - - Minecraft* pMinecraft = Minecraft::GetInstance(); - wcscpy_s(IQNet::m_player[localSmallId].m_gamertag, 32, pMinecraft->user->name.c_str()); - IQNet::s_playerCount = localSmallId + 1; - - NotifyPlayerJoined(&IQNet::m_player[0]); - NotifyPlayerJoined(&IQNet::m_player[localSmallId]); - - m_pGameNetworkManager->StateChange_AnyToStarting(); - - return CGameNetworkManager::JOINGAME_SUCCESS; + m_bJoinPending = true; + return CGameNetworkManager::JOINGAME_PENDING; #else return CGameNetworkManager::JOINGAME_SUCCESS; #endif diff --git a/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.h b/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.h index 4a3f4068..dffa3953 100644 --- a/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.h +++ b/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.h @@ -77,6 +77,12 @@ private: bool m_bIsPrivateGame; int m_flagIndexSize; +#ifdef _WINDOWS64 + bool m_bJoinPending; + int m_joinLocalUsersMask; + wchar_t m_joinHostName[32]; +#endif + // This is only maintained by the host, and is not valid on client machines GameSessionData m_hostGameSessionData; CGameNetworkManager *m_pGameNetworkManager; diff --git a/Minecraft.Client/Common/UI/UIScene_ConnectingProgress.cpp b/Minecraft.Client/Common/UI/UIScene_ConnectingProgress.cpp index e10a5a62..40557cd5 100644 --- a/Minecraft.Client/Common/UI/UIScene_ConnectingProgress.cpp +++ b/Minecraft.Client/Common/UI/UIScene_ConnectingProgress.cpp @@ -2,6 +2,16 @@ #include "UI.h" #include "UIScene_ConnectingProgress.h" #include "..\..\Minecraft.h" +#ifdef _WINDOWS64 +#include "..\..\Windows64\Network\WinsockNetLayer.h" +#include "..\..\..\Minecraft.World\DisconnectPacket.h" + +static int ConnectingProgress_OnRejectedDialogOK(LPVOID, int iPad, const C4JStorage::EMessageResult) +{ + ui.NavigateBack(iPad); + return 0; +} +#endif UIScene_ConnectingProgress::UIScene_ConnectingProgress(int iPad, void *_initData, UILayer *parentLayer) : UIScene(iPad, parentLayer) { @@ -43,6 +53,12 @@ UIScene_ConnectingProgress::UIScene_ConnectingProgress(int iPad, void *_initData m_cancelFuncParam = param->cancelFuncParam; m_removeLocalPlayer = false; m_showingButton = false; + +#ifdef _WINDOWS64 + WinsockNetLayer::eJoinState initState = WinsockNetLayer::GetJoinState(); + m_asyncJoinActive = (initState != WinsockNetLayer::eJoinState_Idle && initState != WinsockNetLayer::eJoinState_Cancelled); + m_asyncJoinFailed = false; +#endif } UIScene_ConnectingProgress::~UIScene_ConnectingProgress() @@ -53,6 +69,18 @@ UIScene_ConnectingProgress::~UIScene_ConnectingProgress() void UIScene_ConnectingProgress::updateTooltips() { +#ifdef _WINDOWS64 + if (m_asyncJoinActive) + { + ui.SetTooltips( m_iPad, -1, IDS_TOOLTIPS_BACK); + return; + } + if (m_asyncJoinFailed) + { + ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT, -1); + return; + } +#endif // 4J-PB - removing the option of cancel join, since it didn't work anyway //ui.SetTooltips( m_iPad, -1, m_showTooltips?IDS_TOOLTIPS_CANCEL_JOIN:-1); ui.SetTooltips( m_iPad, -1, -1); @@ -62,6 +90,85 @@ void UIScene_ConnectingProgress::tick() { UIScene::tick(); +#ifdef _WINDOWS64 + if (m_asyncJoinActive) + { + WinsockNetLayer::eJoinState state = WinsockNetLayer::GetJoinState(); + if (state == WinsockNetLayer::eJoinState_Connecting) + { + // connecting............. + int attempt = WinsockNetLayer::GetJoinAttempt(); + int maxAttempts = WinsockNetLayer::GetJoinMaxAttempts(); + char buf[128]; + if (attempt <= 1) + sprintf_s(buf, "Connecting..."); + else + sprintf_s(buf, "Connecting failed, trying again (%d/%d)", attempt, maxAttempts); + wchar_t wbuf[128]; + mbstowcs(wbuf, buf, 128); + m_labelTitle.setLabel(wstring(wbuf)); + } + else if (state == WinsockNetLayer::eJoinState_Success) + { + m_asyncJoinActive = false; + // go go go + } + else if (state == WinsockNetLayer::eJoinState_Cancelled) + { + // cancel + m_asyncJoinActive = false; + navigateBack(); + } + else if (state == WinsockNetLayer::eJoinState_Rejected) + { + // server full and banned are passed differently compared to other disconnects it seems + m_asyncJoinActive = false; + DisconnectPacket::eDisconnectReason reason = WinsockNetLayer::GetJoinRejectReason(); + int exitReasonStringId; + switch (reason) + { + case DisconnectPacket::eDisconnect_ServerFull: + exitReasonStringId = IDS_DISCONNECTED_SERVER_FULL; + break; + case DisconnectPacket::eDisconnect_Banned: + exitReasonStringId = IDS_DISCONNECTED_KICKED; + break; + default: + exitReasonStringId = IDS_CONNECTION_LOST_SERVER; + break; + } + UINT uiIDA[1]; + uiIDA[0] = IDS_CONFIRM_OK; + ui.RequestErrorMessage(IDS_CONNECTION_FAILED, exitReasonStringId, uiIDA, 1, ProfileManager.GetPrimaryPad(), ConnectingProgress_OnRejectedDialogOK, nullptr, nullptr); + } + else if (state == WinsockNetLayer::eJoinState_Failed) + { + // FAIL + m_asyncJoinActive = false; + m_asyncJoinFailed = true; + + int maxAttempts = WinsockNetLayer::GetJoinMaxAttempts(); + char buf[256]; + sprintf_s(buf, "Failed to connect after %d attempts. The server may be unavailable.", maxAttempts); + wchar_t wbuf[256]; + mbstowcs(wbuf, buf, 256); + + // TIL that these exist + // not going to use a actual popup due to it requiring messing with strings which can really mess things up + // i dont trust myself with that + // these need to be touched up later as teh button is a bit offset + m_labelTitle.setLabel(L"Unable to connect to server"); + m_progressBar.setLabel(wstring(wbuf)); + m_progressBar.showBar(false); + m_progressBar.setVisible(true); + m_buttonConfirm.setVisible(true); + m_showingButton = true; + m_controlTimer.setVisible(false); + } + return; + } +#endif + if( m_removeLocalPlayer ) { m_removeLocalPlayer = false; @@ -94,6 +201,8 @@ void UIScene_ConnectingProgress::handleGainFocus(bool navBack) void UIScene_ConnectingProgress::handleLoseFocus() { + if (!m_runFailTimer) return; + int millisecsLeft = getTimer(0)->targetTime - System::currentTimeMillis(); int millisecsTaken = getTimer(0)->duration - millisecsLeft; app.DebugPrintf("\n"); @@ -208,6 +317,17 @@ void UIScene_ConnectingProgress::handleInput(int iPad, int key, bool repeat, boo switch(key) { // 4J-PB - Removed the option to cancel join - it didn't work anyway +#ifdef _WINDOWS64 + case ACTION_MENU_CANCEL: + if (pressed && m_asyncJoinActive) + { + m_asyncJoinActive = false; + WinsockNetLayer::CancelJoinGame(); + navigateBack(); + handled = true; + } + break; +#endif // case ACTION_MENU_CANCEL: // { // if(m_cancelFunc != nullptr) @@ -250,6 +370,13 @@ void UIScene_ConnectingProgress::handlePress(F64 controlId, F64 childId) case eControl_Confirm: if(m_showingButton) { +#ifdef _WINDOWS64 + if (m_asyncJoinFailed) + { + navigateBack(); + } + else +#endif if( m_iPad != ProfileManager.GetPrimaryPad() && g_NetworkManager.IsInSession() ) { // The connection failed if we see the button, so the temp player should be removed and the viewports updated again diff --git a/Minecraft.Client/Common/UI/UIScene_ConnectingProgress.h b/Minecraft.Client/Common/UI/UIScene_ConnectingProgress.h index 2c52284c..eaaea7f6 100644 --- a/Minecraft.Client/Common/UI/UIScene_ConnectingProgress.h +++ b/Minecraft.Client/Common/UI/UIScene_ConnectingProgress.h @@ -13,6 +13,11 @@ private: void (*m_cancelFunc)(LPVOID param); LPVOID m_cancelFuncParam; +#ifdef _WINDOWS64 + bool m_asyncJoinActive; + bool m_asyncJoinFailed; +#endif + enum EControls { eControl_Confirm diff --git a/Minecraft.Client/Common/UI/UIScene_JoinMenu.cpp b/Minecraft.Client/Common/UI/UIScene_JoinMenu.cpp index 417c1700..5b83ea7c 100644 --- a/Minecraft.Client/Common/UI/UIScene_JoinMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_JoinMenu.cpp @@ -583,6 +583,24 @@ void UIScene_JoinMenu::JoinGame(UIScene_JoinMenu* pClass) // Alert the app the we no longer want to be informed of ethernet connections app.SetLiveLinkRequired( false ); +#ifdef _WINDOWS64 + if (result == CGameNetworkManager::JOINGAME_PENDING) + { + pClass->m_bIgnoreInput = false; + + ConnectionProgressParams *param = new ConnectionProgressParams(); + param->iPad = ProfileManager.GetPrimaryPad(); + param->stringId = -1; + param->showTooltips = true; + param->setFailTimer = false; + param->timerTime = 0; + param->cancelFunc = nullptr; + param->cancelFuncParam = nullptr; + ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_ConnectingProgress, param); + return; + } +#endif + if( result != CGameNetworkManager::JOINGAME_SUCCESS ) { int exitReasonStringId = -1; diff --git a/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp b/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp index 981ab3ab..acc043e5 100644 --- a/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp +++ b/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp @@ -67,6 +67,16 @@ SOCKET WinsockNetLayer::s_splitScreenSocket[XUSER_MAX_COUNT] = { INVALID_SOCKET, BYTE WinsockNetLayer::s_splitScreenSmallId[XUSER_MAX_COUNT] = { 0xFF, 0xFF, 0xFF, 0xFF }; HANDLE WinsockNetLayer::s_splitScreenRecvThread[XUSER_MAX_COUNT] = {nullptr, nullptr, nullptr, nullptr}; +// async stuff +HANDLE WinsockNetLayer::s_joinThread = nullptr; +volatile WinsockNetLayer::eJoinState WinsockNetLayer::s_joinState = WinsockNetLayer::eJoinState_Idle; +volatile int WinsockNetLayer::s_joinAttempt = 0; +volatile bool WinsockNetLayer::s_joinCancel = false; +char WinsockNetLayer::s_joinIP[256] = {}; +int WinsockNetLayer::s_joinPort = 0; +BYTE WinsockNetLayer::s_joinAssignedSmallId = 0; +DisconnectPacket::eDisconnectReason WinsockNetLayer::s_joinRejectReason = DisconnectPacket::eDisconnect_Quitting; + bool g_Win64MultiplayerHost = false; bool g_Win64MultiplayerJoin = false; int g_Win64MultiplayerPort = WIN64_NET_DEFAULT_PORT; @@ -114,6 +124,15 @@ void WinsockNetLayer::Shutdown() StopAdvertising(); StopDiscovery(); + s_joinCancel = true; + if (s_joinThread != nullptr) + { + WaitForSingleObject(s_joinThread, 5000); + CloseHandle(s_joinThread); + s_joinThread = nullptr; + } + s_joinState = eJoinState_Idle; + s_active = false; s_connected = false; @@ -421,6 +440,215 @@ bool WinsockNetLayer::JoinGame(const char* ip, int port) return true; } +bool WinsockNetLayer::BeginJoinGame(const char* ip, int port) +{ + if (!s_initialized && !Initialize()) return false; + + // if there isnt any cleanup it sometime caused issues. Oops + CancelJoinGame(); + if (s_joinThread != nullptr) + { + WaitForSingleObject(s_joinThread, 5000); + CloseHandle(s_joinThread); + s_joinThread = nullptr; + } + + s_isHost = false; + s_hostSmallId = 0; + s_connected = false; + s_active = false; + + if (s_hostConnectionSocket != INVALID_SOCKET) + { + closesocket(s_hostConnectionSocket); + s_hostConnectionSocket = INVALID_SOCKET; + } + + if (s_clientRecvThread != nullptr) + { + WaitForSingleObject(s_clientRecvThread, 5000); + CloseHandle(s_clientRecvThread); + s_clientRecvThread = nullptr; + } + + strncpy_s(s_joinIP, sizeof(s_joinIP), ip, _TRUNCATE); + s_joinPort = port; + s_joinAttempt = 0; + s_joinCancel = false; + s_joinAssignedSmallId = 0; + s_joinRejectReason = DisconnectPacket::eDisconnect_Quitting; + s_joinState = eJoinState_Connecting; + + s_joinThread = CreateThread(nullptr, 0, JoinThreadProc, nullptr, 0, nullptr); + if (s_joinThread == nullptr) + { + s_joinState = eJoinState_Failed; + return false; + } + return true; +} + +DWORD WINAPI WinsockNetLayer::JoinThreadProc(LPVOID param) +{ + struct addrinfo hints = {}; + struct addrinfo* result = nullptr; + + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + char portStr[16]; + sprintf_s(portStr, "%d", s_joinPort); + + int iResult = getaddrinfo(s_joinIP, portStr, &hints, &result); + if (iResult != 0) + { + app.DebugPrintf("getaddrinfo failed for %s:%d - %d\n", s_joinIP, s_joinPort, iResult); + s_joinState = eJoinState_Failed; + return 0; + } + + bool connected = false; + BYTE assignedSmallId = 0; + SOCKET sock = INVALID_SOCKET; + + for (int attempt = 0; attempt < JOIN_MAX_ATTEMPTS; ++attempt) + { + if (s_joinCancel) + { + freeaddrinfo(result); + s_joinState = eJoinState_Cancelled; + return 0; + } + + s_joinAttempt = attempt + 1; + + sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol); + if (sock == INVALID_SOCKET) + { + app.DebugPrintf("socket() failed: %d\n", WSAGetLastError()); + break; + } + + int noDelay = 1; + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char*)&noDelay, sizeof(noDelay)); + + iResult = connect(sock, result->ai_addr, static_cast(result->ai_addrlen)); + if (iResult == SOCKET_ERROR) + { + int err = WSAGetLastError(); + app.DebugPrintf("connect() to %s:%d failed (attempt %d/%d): %d\n", s_joinIP, s_joinPort, attempt + 1, JOIN_MAX_ATTEMPTS, err); + closesocket(sock); + sock = INVALID_SOCKET; + for (int w = 0; w < 4 && !s_joinCancel; w++) + Sleep(50); + continue; + } + + BYTE assignBuf[1]; + int bytesRecv = recv(sock, (char*)assignBuf, 1, 0); + if (bytesRecv != 1) + { + app.DebugPrintf("failed to receive small id assignment from host (attempt %d/%d)\n", attempt + 1, JOIN_MAX_ATTEMPTS); + closesocket(sock); + sock = INVALID_SOCKET; + for (int w = 0; w < 4 && !s_joinCancel; w++) + Sleep(50); + continue; + } + + if (assignBuf[0] == WIN64_SMALLID_REJECT) + { + BYTE rejectBuf[5]; + if (!RecvExact(sock, rejectBuf, 5)) + { + app.DebugPrintf("failed to receive reject reason from host (?)\n"); + closesocket(sock); + sock = INVALID_SOCKET; + for (int w = 0; w < 4 && !s_joinCancel; w++) + Sleep(50); + continue; + } + int reason = ((rejectBuf[1] & 0xff) << 24) | ((rejectBuf[2] & 0xff) << 16) | + ((rejectBuf[3] & 0xff) << 8) | (rejectBuf[4] & 0xff); + s_joinRejectReason = (DisconnectPacket::eDisconnectReason)reason; + closesocket(sock); + freeaddrinfo(result); + s_joinState = eJoinState_Rejected; + return 0; + } + + assignedSmallId = assignBuf[0]; + connected = true; + break; + } + freeaddrinfo(result); + + if (s_joinCancel) + { + if (sock != INVALID_SOCKET) closesocket(sock); + s_joinState = eJoinState_Cancelled; + return 0; + } + + if (!connected) + { + s_joinState = eJoinState_Failed; + return 0; + } + + s_hostConnectionSocket = sock; + s_joinAssignedSmallId = assignedSmallId; + s_joinState = eJoinState_Success; + return 0; +} + +void WinsockNetLayer::CancelJoinGame() +{ + if (s_joinState == eJoinState_Connecting) + { + s_joinCancel = true; + } + else if (s_joinState == eJoinState_Success) + { + // fix a race cond + if (s_hostConnectionSocket != INVALID_SOCKET) + { + closesocket(s_hostConnectionSocket); + s_hostConnectionSocket = INVALID_SOCKET; + } + s_joinState = eJoinState_Cancelled; + } +} + +bool WinsockNetLayer::FinalizeJoin() +{ + if (s_joinState != eJoinState_Success) + return false; + + s_localSmallId = s_joinAssignedSmallId; + + strncpy_s(g_Win64MultiplayerIP, sizeof(g_Win64MultiplayerIP), s_joinIP, _TRUNCATE); + g_Win64MultiplayerPort = s_joinPort; + + app.DebugPrintf("connected to %s:%d, assigned smallId=%d\n", s_joinIP, s_joinPort, s_localSmallId); + + s_active = true; + s_connected = true; + + s_clientRecvThread = CreateThread(nullptr, 0, ClientRecvThreadProc, nullptr, 0, nullptr); + + if (s_joinThread != nullptr) + { + WaitForSingleObject(s_joinThread, 2000); + CloseHandle(s_joinThread); + s_joinThread = nullptr; + } + + s_joinState = eJoinState_Idle; + return true; +} + bool WinsockNetLayer::SendOnSocket(SOCKET sock, const void* data, int dataSize) { if (sock == INVALID_SOCKET || dataSize <= 0 || dataSize > WIN64_NET_MAX_PACKET_SIZE) return false; @@ -1334,4 +1562,25 @@ DWORD WINAPI WinsockNetLayer::DiscoveryThreadProc(LPVOID param) return 0; } +// some lazy helper funcs +WinsockNetLayer::eJoinState WinsockNetLayer::GetJoinState() +{ + return s_joinState; +} + +int WinsockNetLayer::GetJoinAttempt() +{ + return s_joinAttempt; +} + +int WinsockNetLayer::GetJoinMaxAttempts() +{ + return JOIN_MAX_ATTEMPTS; +} + +DisconnectPacket::eDisconnectReason WinsockNetLayer::GetJoinRejectReason() +{ + return s_joinRejectReason; +} + #endif diff --git a/Minecraft.Client/Windows64/Network/WinsockNetLayer.h b/Minecraft.Client/Windows64/Network/WinsockNetLayer.h index afccbd66..8a11e391 100644 --- a/Minecraft.Client/Windows64/Network/WinsockNetLayer.h +++ b/Minecraft.Client/Windows64/Network/WinsockNetLayer.h @@ -21,6 +21,8 @@ class Socket; +#include "..\..\..\Minecraft.World\DisconnectPacket.h" + #pragma pack(push, 1) struct Win64LANBroadcast { @@ -69,6 +71,23 @@ public: static bool HostGame(int port, const char* bindIp = nullptr); static bool JoinGame(const char* ip, int port); + enum eJoinState + { + eJoinState_Idle, + eJoinState_Connecting, + eJoinState_Success, + eJoinState_Failed, + eJoinState_Rejected, + eJoinState_Cancelled + }; + static bool BeginJoinGame(const char* ip, int port); + static void CancelJoinGame(); + static eJoinState GetJoinState(); + static int GetJoinAttempt(); + static int GetJoinMaxAttempts(); + static DisconnectPacket::eDisconnectReason GetJoinRejectReason(); + static bool FinalizeJoin(); + static bool SendToSmallId(BYTE targetSmallId, const void* data, int dataSize); static bool SendOnSocket(SOCKET sock, const void* data, int dataSize); @@ -112,6 +131,17 @@ private: static DWORD WINAPI SplitScreenRecvThreadProc(LPVOID param); static DWORD WINAPI AdvertiseThreadProc(LPVOID param); static DWORD WINAPI DiscoveryThreadProc(LPVOID param); + static DWORD WINAPI JoinThreadProc(LPVOID param); + + static HANDLE s_joinThread; + static volatile eJoinState s_joinState; + static volatile int s_joinAttempt; + static volatile bool s_joinCancel; + static char s_joinIP[256]; + static int s_joinPort; + static BYTE s_joinAssignedSmallId; + static DisconnectPacket::eDisconnectReason s_joinRejectReason; + static const int JOIN_MAX_ATTEMPTS = 4; static SOCKET s_listenSocket; static SOCKET s_hostConnectionSocket;