From ec0146effac516320b6388552d46f80f192614a4 Mon Sep 17 00:00:00 2001 From: NOTPIES Date: Wed, 4 Mar 2026 21:47:45 -0300 Subject: [PATCH] fix: improve net code for player list etc --- Minecraft.Client/ClientConnection.cpp | 6 +- .../Network/PlatformNetworkManagerStub.cpp | 115 ++++++++++++------ Minecraft.Client/Common/Network/SessionInfo.h | 5 + .../Common/UI/UIScene_JoinMenu.cpp | 38 ++++++ Minecraft.Client/PlayerList.cpp | 1 + .../Windows64/Network/WinsockNetLayer.cpp | 73 +++++++++-- .../Windows64/Network/WinsockNetLayer.h | 3 + 7 files changed, 193 insertions(+), 48 deletions(-) diff --git a/Minecraft.Client/ClientConnection.cpp b/Minecraft.Client/ClientConnection.cpp index 628b330..66aead4 100644 --- a/Minecraft.Client/ClientConnection.cpp +++ b/Minecraft.Client/ClientConnection.cpp @@ -775,9 +775,13 @@ void ClientConnection::handleAddPlayer(shared_ptr packet) { NetworkPlayerXbox *npx = (NetworkPlayerXbox *)np; IQNetPlayer *qp = npx->GetQNetPlayer(); - if (qp != NULL && qp->m_gamertag[0] == 0) + if (qp != NULL) { wcsncpy_s(qp->m_gamertag, 32, packet->name.c_str(), _TRUNCATE); + if (g_NetworkManager.IsHost()) + { + g_NetworkManager.UpdateAndSetGameSessionData(); + } } } } diff --git a/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp b/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp index 932c0bb..3d9c900 100644 --- a/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp +++ b/Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp @@ -94,7 +94,7 @@ void CPlatformNetworkManagerStub::NotifyPlayerJoined(IQNetPlayer *pQNetPlayer ) if( m_pIQNet->IsHost() ) { // 4J-PB - only the host should do this -// g_NetworkManager.UpdateAndSetGameSessionData(); + g_NetworkManager.UpdateAndSetGameSessionData(); SystemFlagAddPlayer( networkPlayer ); } @@ -141,6 +141,11 @@ void CPlatformNetworkManagerStub::NotifyPlayerLeaving(IQNetPlayer *pQNetPlayer) g_NetworkManager.PlayerLeaving(networkPlayer); + if (m_pIQNet->IsHost()) + { + g_NetworkManager.UpdateAndSetGameSessionData(networkPlayer); + } + for (int idx = 0; idx < XUSER_MAX_COUNT; ++idx) { if (playerChangedCallback[idx] != NULL) @@ -503,44 +508,50 @@ bool CPlatformNetworkManagerStub::_RunNetworkGame() void CPlatformNetworkManagerStub::UpdateAndSetGameSessionData(INetworkPlayer *pNetworkPlayerLeaving /*= NULL*/) { -// DWORD playerCount = m_pIQNet->GetPlayerCount(); -// -// if( this->m_bLeavingGame ) -// return; -// -// if( GetHostPlayer() == NULL ) -// return; -// -// for(unsigned int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i) -// { -// if( i < playerCount ) -// { -// INetworkPlayer *pNetworkPlayer = GetPlayerByIndex(i); -// -// // We can call this from NotifyPlayerLeaving but at that point the player is still considered in the session -// if( pNetworkPlayer != pNetworkPlayerLeaving ) -// { -// m_hostGameSessionData.players[i] = ((NetworkPlayerXbox *)pNetworkPlayer)->GetUID(); -// -// char *temp; -// temp = (char *)wstringtofilename( pNetworkPlayer->GetOnlineName() ); -// memcpy(m_hostGameSessionData.szPlayers[i],temp,XUSER_NAME_SIZE); -// } -// else -// { -// m_hostGameSessionData.players[i] = NULL; -// memset(m_hostGameSessionData.szPlayers[i],0,XUSER_NAME_SIZE); -// } -// } -// else -// { -// m_hostGameSessionData.players[i] = NULL; -// memset(m_hostGameSessionData.szPlayers[i],0,XUSER_NAME_SIZE); -// } -// } -// -// m_hostGameSessionData.hostPlayerUID = ((NetworkPlayerXbox *)GetHostPlayer())->GetQNetPlayer()->GetXuid(); -// m_hostGameSessionData.m_uiGameHostSettings = app.GetGameHostOption(eGameHostOption_All); +#ifdef _WINDOWS64 + if (this->m_bLeavingGame) + return; + + if (!m_pIQNet->IsHost()) + return; + + DWORD playerCount = m_pIQNet->GetPlayerCount(); + + m_hostGameSessionData.m_uiGameHostSettings = app.GetGameHostOption(eGameHostOption_All); + m_hostGameSessionData.playerCount = (unsigned char)playerCount; + + for (unsigned int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i) + { + if (i < playerCount) + { + INetworkPlayer *pNetworkPlayer = GetPlayerByIndex(i); + + if (pNetworkPlayer != NULL && pNetworkPlayer != pNetworkPlayerLeaving) + { + m_hostGameSessionData.players[i] = (GameSessionUID)(i + 1); + + const wchar_t *name = pNetworkPlayer->GetOnlineName(); + char nameBuf[XUSER_NAME_SIZE]; + memset(nameBuf, 0, sizeof(nameBuf)); + WideCharToMultiByte(CP_ACP, 0, name, -1, nameBuf, XUSER_NAME_SIZE - 1, NULL, NULL); + memcpy(m_hostGameSessionData.szPlayers[i], nameBuf, XUSER_NAME_SIZE); + } + else + { + m_hostGameSessionData.players[i] = 0; + memset(m_hostGameSessionData.szPlayers[i], 0, XUSER_NAME_SIZE); + } + } + else + { + m_hostGameSessionData.players[i] = 0; + memset(m_hostGameSessionData.szPlayers[i], 0, XUSER_NAME_SIZE); + } + } + + // update the LAN advertiser with current player names and count + WinsockNetLayer::UpdateAdvertisePlayerNames(m_hostGameSessionData.playerCount, m_hostGameSessionData.szPlayers); +#endif } int CPlatformNetworkManagerStub::RemovePlayerOnSocketClosedThreadProc( void* lpParam ) @@ -757,6 +768,17 @@ void CPlatformNetworkManagerStub::SearchForGames() info->data.playerCount = lanSessions[i].playerCount; info->data.maxPlayers = lanSessions[i].maxPlayers; + memset(info->data.players, 0, sizeof(info->data.players)); + memset(info->data.szPlayers, 0, sizeof(info->data.szPlayers)); + for (int p = 0; p < MINECRAFT_NET_MAX_PLAYERS && p < lanSessions[i].playerCount; p++) + { + if (lanSessions[i].playerNames[p][0] != 0) + { + info->data.players[p] = (GameSessionUID)(p + 1); + memcpy(info->data.szPlayers[p], lanSessions[i].playerNames[p], XUSER_NAME_SIZE); + } + } + info->sessionId = (SessionID)((unsigned __int64)inet_addr(lanSessions[i].hostIP) | ((unsigned __int64)lanSessions[i].hostPort << 32)); friendsSessions[0].push_back(info); @@ -790,6 +812,21 @@ vector *CPlatformNetworkManagerStub::GetSessionList(int iPa bool CPlatformNetworkManagerStub::GetGameSessionInfo(int iPad, SessionID sessionId, FriendSessionInfo *foundSessionInfo) { +#ifdef _WINDOWS64 + for (size_t i = 0; i < friendsSessions[0].size(); i++) + { + if (friendsSessions[0][i]->sessionId == sessionId) + { + if (foundSessionInfo != NULL) + { + foundSessionInfo->sessionId = friendsSessions[0][i]->sessionId; + foundSessionInfo->data = friendsSessions[0][i]->data; + foundSessionInfo->hasPartyMember = friendsSessions[0][i]->hasPartyMember; + } + return true; + } + } +#endif return false; } diff --git a/Minecraft.Client/Common/Network/SessionInfo.h b/Minecraft.Client/Common/Network/SessionInfo.h index c4b6b9e..dc270f6 100644 --- a/Minecraft.Client/Common/Network/SessionInfo.h +++ b/Minecraft.Client/Common/Network/SessionInfo.h @@ -77,6 +77,9 @@ typedef struct _GameSessionData unsigned char playerCount; unsigned char maxPlayers; + GameSessionUID players[MINECRAFT_NET_MAX_PLAYERS]; + char szPlayers[MINECRAFT_NET_MAX_PLAYERS][XUSER_NAME_SIZE]; + _GameSessionData() { netVersion = 0; @@ -90,6 +93,8 @@ typedef struct _GameSessionData memset(hostName, 0, sizeof(hostName)); playerCount = 0; maxPlayers = MINECRAFT_NET_MAX_PLAYERS; + memset(players, 0, sizeof(players)); + memset(szPlayers, 0, sizeof(szPlayers)); } } GameSessionData; #endif diff --git a/Minecraft.Client/Common/UI/UIScene_JoinMenu.cpp b/Minecraft.Client/Common/UI/UIScene_JoinMenu.cpp index cad86dc..c7b941a 100644 --- a/Minecraft.Client/Common/UI/UIScene_JoinMenu.cpp +++ b/Minecraft.Client/Common/UI/UIScene_JoinMenu.cpp @@ -105,6 +105,21 @@ void UIScene_JoinMenu::tick() break; } } +#elif defined(_WINDOWS64) + for( int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; i++ ) + { + if( m_selectedSession->data.players[i] != 0 && m_selectedSession->data.szPlayers[i][0] != 0 ) + { + string playerName(m_selectedSession->data.szPlayers[i], XUSER_NAME_SIZE); + size_t end = playerName.find('\0'); + if (end != string::npos) playerName.resize(end); + m_buttonListPlayers.addItem(playerName); + } + else + { + break; + } + } #endif m_labelLabels[eLabel_Difficulty].init(app.GetString(IDS_LABEL_DIFFICULTY)); @@ -579,7 +594,30 @@ void UIScene_JoinMenu::handleTimerComplete(int id) } playersList.SetCurSel(selectedIndex); } +#elif defined(_WINDOWS64) + { + bool success = g_NetworkManager.GetGameSessionInfo(m_iPad, m_selectedSession->sessionId, m_selectedSession); + if (success) + { + m_buttonListPlayers.clearList(); + for (unsigned int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i) + { + if (m_selectedSession->data.players[i] != 0 && m_selectedSession->data.szPlayers[i][0] != 0) + { + string playerName(m_selectedSession->data.szPlayers[i], XUSER_NAME_SIZE); + size_t end = playerName.find('\0'); + if (end != string::npos) playerName.resize(end); + m_buttonListPlayers.addItem(playerName); + } + else + { + break; + } + } + } + } #endif + addTimer(UPDATE_PLAYERS_TIMER_ID, UPDATE_PLAYERS_TIMER_TIME); } break; }; diff --git a/Minecraft.Client/PlayerList.cpp b/Minecraft.Client/PlayerList.cpp index 4e5334a..1e5dba2 100644 --- a/Minecraft.Client/PlayerList.cpp +++ b/Minecraft.Client/PlayerList.cpp @@ -106,6 +106,7 @@ void PlayerList::placeNewPlayer(Connection *connection, shared_ptr NetworkPlayerXbox *nxp = (NetworkPlayerXbox *)networkPlayer; IQNetPlayer *qnp = nxp->GetQNetPlayer(); wcsncpy_s(qnp->m_gamertag, 32, player->name.c_str(), _TRUNCATE); + g_NetworkManager.UpdateAndSetGameSessionData(); } #endif diff --git a/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp b/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp index 8dd814a..4b177ce 100644 --- a/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp +++ b/Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp @@ -243,7 +243,8 @@ bool WinsockNetLayer::JoinGame(const char *ip, int port) bool connected = false; BYTE assignedSmallId = 0; - const int maxAttempts = 12; + const int maxAttempts = 3; + const int connectTimeoutMs = 3000; for (int attempt = 0; attempt < maxAttempts; ++attempt) { @@ -257,17 +258,60 @@ bool WinsockNetLayer::JoinGame(const char *ip, int port) int noDelay = 1; setsockopt(s_hostConnectionSocket, IPPROTO_TCP, TCP_NODELAY, (const char *)&noDelay, sizeof(noDelay)); + u_long nonBlocking = 1; + ioctlsocket(s_hostConnectionSocket, FIONBIO, &nonBlocking); + iResult = connect(s_hostConnectionSocket, result->ai_addr, (int)result->ai_addrlen); if (iResult == SOCKET_ERROR) { int err = WSAGetLastError(); - app.DebugPrintf("connect() to %s:%d failed (attempt %d/%d): %d\n", ip, port, attempt + 1, maxAttempts, err); - closesocket(s_hostConnectionSocket); - s_hostConnectionSocket = INVALID_SOCKET; - Sleep(200); - continue; + if (err == WSAEWOULDBLOCK) + { + fd_set writeFds, exceptFds; + FD_ZERO(&writeFds); + FD_ZERO(&exceptFds); + FD_SET(s_hostConnectionSocket, &writeFds); + FD_SET(s_hostConnectionSocket, &exceptFds); + + struct timeval tv; + tv.tv_sec = connectTimeoutMs / 1000; + tv.tv_usec = (connectTimeoutMs % 1000) * 1000; + + int selectResult = select(0, NULL, &writeFds, &exceptFds, &tv); + if (selectResult <= 0 || FD_ISSET(s_hostConnectionSocket, &exceptFds)) + { + app.DebugPrintf("connect() to %s:%d timed out or failed (attempt %d/%d)\n", ip, port, attempt + 1, maxAttempts); + closesocket(s_hostConnectionSocket); + s_hostConnectionSocket = INVALID_SOCKET; + continue; + } + + int sockErr = 0; + int sockErrLen = sizeof(sockErr); + getsockopt(s_hostConnectionSocket, SOL_SOCKET, SO_ERROR, (char *)&sockErr, &sockErrLen); + if (sockErr != 0) + { + app.DebugPrintf("connect() to %s:%d failed with SO_ERROR %d (attempt %d/%d)\n", ip, port, sockErr, attempt + 1, maxAttempts); + closesocket(s_hostConnectionSocket); + s_hostConnectionSocket = INVALID_SOCKET; + continue; + } + } + else + { + app.DebugPrintf("connect() to %s:%d failed (attempt %d/%d): %d\n", ip, port, attempt + 1, maxAttempts, err); + closesocket(s_hostConnectionSocket); + s_hostConnectionSocket = INVALID_SOCKET; + continue; + } } + u_long blocking = 0; + ioctlsocket(s_hostConnectionSocket, FIONBIO, &blocking); + + DWORD recvTimeout = 3000; + setsockopt(s_hostConnectionSocket, SOL_SOCKET, SO_RCVTIMEO, (const char *)&recvTimeout, sizeof(recvTimeout)); + BYTE assignBuf[1]; int bytesRecv = recv(s_hostConnectionSocket, (char *)assignBuf, 1, 0); if (bytesRecv != 1) @@ -275,7 +319,6 @@ bool WinsockNetLayer::JoinGame(const char *ip, int port) app.DebugPrintf("Failed to receive small ID assignment from host (attempt %d/%d)\n", attempt + 1, maxAttempts); closesocket(s_hostConnectionSocket); s_hostConnectionSocket = INVALID_SOCKET; - Sleep(200); continue; } @@ -714,6 +757,18 @@ void WinsockNetLayer::UpdateAdvertisePlayerCount(BYTE count) LeaveCriticalSection(&s_advertiseLock); } +void WinsockNetLayer::UpdateAdvertisePlayerNames(BYTE count, const char playerNames[][XUSER_NAME_SIZE]) +{ + EnterCriticalSection(&s_advertiseLock); + memset(s_advertiseData.playerNames, 0, sizeof(s_advertiseData.playerNames)); + s_advertiseData.playerCount = count; + for (int i = 0; i < count && i < 8; i++) + { + memcpy(s_advertiseData.playerNames[i], playerNames[i], XUSER_NAME_SIZE); + } + LeaveCriticalSection(&s_advertiseLock); +} + void WinsockNetLayer::UpdateAdvertiseJoinable(bool joinable) { EnterCriticalSection(&s_advertiseLock); @@ -821,7 +876,7 @@ std::vector WinsockNetLayer::GetDiscoveredSessions() DWORD WINAPI WinsockNetLayer::DiscoveryThreadProc(LPVOID param) { - char recvBuf[512]; + char recvBuf[1024]; while (s_discovering) { @@ -865,6 +920,7 @@ DWORD WINAPI WinsockNetLayer::DiscoveryThreadProc(LPVOID param) s_discoveredSessions[i].subTexturePackId = broadcast->subTexturePackId; s_discoveredSessions[i].isJoinable = (broadcast->isJoinable != 0); s_discoveredSessions[i].lastSeenTick = now; + memcpy(s_discoveredSessions[i].playerNames, broadcast->playerNames, sizeof(broadcast->playerNames)); found = true; break; } @@ -885,6 +941,7 @@ DWORD WINAPI WinsockNetLayer::DiscoveryThreadProc(LPVOID param) session.subTexturePackId = broadcast->subTexturePackId; session.isJoinable = (broadcast->isJoinable != 0); session.lastSeenTick = now; + memcpy(session.playerNames, broadcast->playerNames, sizeof(broadcast->playerNames)); s_discoveredSessions.push_back(session); app.DebugPrintf("Win64 LAN: Discovered game \"%ls\" at %s:%d\n", diff --git a/Minecraft.Client/Windows64/Network/WinsockNetLayer.h b/Minecraft.Client/Windows64/Network/WinsockNetLayer.h index e6ace42..ccd17cb 100644 --- a/Minecraft.Client/Windows64/Network/WinsockNetLayer.h +++ b/Minecraft.Client/Windows64/Network/WinsockNetLayer.h @@ -31,6 +31,7 @@ struct Win64LANBroadcast DWORD texturePackParentId; BYTE subTexturePackId; BYTE isJoinable; + char playerNames[8][XUSER_NAME_SIZE]; }; #pragma pack(pop) @@ -47,6 +48,7 @@ struct Win64LANSession unsigned char subTexturePackId; bool isJoinable; DWORD lastSeenTick; + char playerNames[8][XUSER_NAME_SIZE]; }; struct Win64RemoteConnection @@ -88,6 +90,7 @@ public: static void StopAdvertising(); static void UpdateAdvertisePlayerCount(BYTE count); static void UpdateAdvertiseJoinable(bool joinable); + static void UpdateAdvertisePlayerNames(BYTE count, const char playerNames[][XUSER_NAME_SIZE]); static bool StartDiscovery(); static void StopDiscovery();