#include "stdafx.h" #include "..\..\..\Minecraft.World\Socket.h" #include "..\..\..\Minecraft.World\StringHelpers.h" #include "PlatformNetworkManagerStub.h" #include "..\..\Xbox\Network\NetworkPlayerXbox.h" #ifdef _WINDOWS64 #include "..\..\Windows64\Network\WinsockNetLayer.h" #include "..\..\Minecraft.h" #include "..\..\User.h" extern bool g_Win64DedicatedServerMode; namespace { static unsigned long long GetNowMs64() { return (unsigned long long)GetTickCount64(); } } #endif CPlatformNetworkManagerStub *g_pPlatformNetworkManager; void CPlatformNetworkManagerStub::NotifyPlayerJoined(IQNetPlayer *pQNetPlayer ) { if (pQNetPlayer == NULL) { app.DebugPrintf("NotifyPlayerJoined called with NULL player\n"); return; } const char * pszDescription; // 4J Stu - We create a fake socket for every where that we need an INBOUND queue of game data. Outbound // is all handled by QNet so we don't need that. Therefore each client player has one, and the host has one // for each client player. bool createFakeSocket = false; bool localPlayer = false; NetworkPlayerXbox *networkPlayer = (NetworkPlayerXbox *)addNetworkPlayer(pQNetPlayer); if( pQNetPlayer->IsLocal() ) { localPlayer = true; if( pQNetPlayer->IsHost() ) { pszDescription = "local host"; // 4J Stu - No socket for the localhost as it uses a special loopback queue m_machineQNetPrimaryPlayers.push_back( pQNetPlayer ); } else { pszDescription = "local"; // We need an inbound queue on all local players to receive data from the host createFakeSocket = true; } } else { if( pQNetPlayer->IsHost() ) { pszDescription = "remote host"; } else { pszDescription = "remote"; // If we are the host, then create a fake socket for every remote player if( m_pIQNet->IsHost() ) { createFakeSocket = true; } } if( m_pIQNet->IsHost() && !m_bHostChanged ) { // Do we already have a primary player for this system? bool systemHasPrimaryPlayer = false; for(AUTO_VAR(it, m_machineQNetPrimaryPlayers.begin()); it < m_machineQNetPrimaryPlayers.end(); ++it) { IQNetPlayer *pQNetPrimaryPlayer = *it; if( pQNetPlayer->IsSameSystem(pQNetPrimaryPlayer) ) { systemHasPrimaryPlayer = true; break; } } if( !systemHasPrimaryPlayer ) m_machineQNetPrimaryPlayers.push_back( pQNetPlayer ); } } g_NetworkManager.PlayerJoining( networkPlayer ); if( createFakeSocket == true && !m_bHostChanged ) { g_NetworkManager.CreateSocket( networkPlayer, localPlayer ); } app.DebugPrintf( "Player 0x%p \"%ls\" joined; %s; voice %i; camera %i.\n", pQNetPlayer, pQNetPlayer->GetGamertag(), pszDescription, (int) pQNetPlayer->HasVoice(), (int) pQNetPlayer->HasCamera() ); if( m_pIQNet->IsHost() ) { // 4J-PB - only the host should do this g_NetworkManager.UpdateAndSetGameSessionData(); SystemFlagAddPlayer( networkPlayer ); } for( int idx = 0; idx < XUSER_MAX_COUNT; ++idx) { if(playerChangedCallback[idx] != NULL) playerChangedCallback[idx]( playerChangedCallbackParam[idx], networkPlayer, false ); } if(m_pIQNet->GetState() == QNET_STATE_GAME_PLAY) { int localPlayerCount = 0; for(unsigned int idx = 0; idx < XUSER_MAX_COUNT; ++idx) { if( m_pIQNet->GetLocalPlayerByUserIndex(idx) != NULL ) ++localPlayerCount; } float appTime = app.getAppTime(); // Only record stats for the primary player here m_lastPlayerEventTimeStart = appTime; } } void CPlatformNetworkManagerStub::NotifyPlayerLeaving(IQNetPlayer *pQNetPlayer) { if (pQNetPlayer == NULL) { app.DebugPrintf("NotifyPlayerLeaving called with NULL player\n"); return; } app.DebugPrintf("Player 0x%p \"%ls\" leaving.\n", pQNetPlayer, pQNetPlayer->GetGamertag()); INetworkPlayer *networkPlayer = getNetworkPlayer(pQNetPlayer); if (networkPlayer == NULL) return; Socket *socket = networkPlayer->GetSocket(); if (socket != NULL) { if (m_pIQNet->IsHost()) g_NetworkManager.CloseConnection(networkPlayer); } if (m_pIQNet->IsHost()) { SystemFlagRemovePlayer(networkPlayer); } g_NetworkManager.PlayerLeaving(networkPlayer); for (int idx = 0; idx < XUSER_MAX_COUNT; ++idx) { if (playerChangedCallback[idx] != NULL) playerChangedCallback[idx](playerChangedCallbackParam[idx], networkPlayer, true); } removeNetworkPlayer(pQNetPlayer); if (m_pIQNet->IsHost()) { // Exclude the leaving player from advertised count immediately. g_NetworkManager.UpdateAndSetGameSessionData(networkPlayer); } } bool CPlatformNetworkManagerStub::Initialise(CGameNetworkManager *pGameNetworkManager, int flagIndexSize) { m_pGameNetworkManager = pGameNetworkManager; m_flagIndexSize = flagIndexSize; m_pIQNet = new IQNet(); g_pPlatformNetworkManager = this; for( int i = 0; i < XUSER_MAX_COUNT; i++ ) { playerChangedCallback[ i ] = NULL; } m_bLeavingGame = false; m_bLeaveGameOnTick = false; m_bHostChanged = false; #ifdef _WINDOWS64 m_bTransferPending = false; m_bTransferLeaving = false; m_transferHostIp[0] = 0; m_transferHostPort = 0; m_transferSuppressErrorsUntilMs = 0; #endif m_bSearchResultsReady = false; m_bSearchPending = false; m_bIsOfflineGame = false; m_pSearchParam = NULL; m_SessionsUpdatedCallback = NULL; for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) { m_searchResultsCount[i] = 0; m_lastSearchStartTime[i] = 0; // The results that will be filled in with the current search m_pSearchResults[i] = NULL; m_pQoSResult[i] = NULL; m_pCurrentSearchResults[i] = NULL; m_pCurrentQoSResult[i] = NULL; m_currentSearchResultsCount[i] = 0; } // Success! return true; } void CPlatformNetworkManagerStub::Terminate() { } int CPlatformNetworkManagerStub::GetJoiningReadyPercentage() { return 100; } int CPlatformNetworkManagerStub::CorrectErrorIDS(int IDS) { return IDS; } bool CPlatformNetworkManagerStub::isSystemPrimaryPlayer(IQNetPlayer *pQNetPlayer) { return true; } // We call this twice a frame, either side of the render call so is a good place to "tick" things void CPlatformNetworkManagerStub::DoWork() { #ifdef _WINDOWS64 extern QNET_STATE _iQNetStubState; if (m_bTransferPending) { if (g_NetworkManager.IsInSession()) { if (!m_bTransferLeaving) { m_bTransferLeaving = true; app.DebugPrintf("Win64 LAN: Transfer requested to %s:%d, leaving current session\n", m_transferHostIp, m_transferHostPort); g_NetworkManager.LeaveGame(false); } return; } FriendSessionInfo targetSession; memset(&targetSession, 0, sizeof(targetSession)); strncpy_s(targetSession.data.hostIP, sizeof(targetSession.data.hostIP), m_transferHostIp, _TRUNCATE); targetSession.data.hostPort = m_transferHostPort; wcsncpy_s(targetSession.data.hostName, XUSER_NAME_SIZE, L"Server Transfer", _TRUNCATE); targetSession.data.netVersion = MINECRAFT_NET_VERSION; targetSession.data.isJoinable = true; targetSession.data.isReadyToJoin = true; int primaryPad = ProfileManager.GetPrimaryPad(); if (primaryPad < 0) { primaryPad = 0; } const int localUsersMask = GetLocalPlayerMask(primaryPad); const int joinResult = JoinGame(&targetSession, localUsersMask, ProfileManager.GetLockedProfile()); app.DebugPrintf("Win64 LAN: Transfer join result=%d for %s:%d\n", joinResult, m_transferHostIp, m_transferHostPort); m_bTransferPending = false; m_bTransferLeaving = false; m_transferHostIp[0] = 0; m_transferHostPort = 0; m_transferSuppressErrorsUntilMs = GetNowMs64() + 10000ULL; } if (_iQNetStubState == QNET_STATE_SESSION_STARTING && app.GetGameStarted()) { _iQNetStubState = QNET_STATE_GAME_PLAY; if (m_pIQNet->IsHost()) WinsockNetLayer::UpdateAdvertiseJoinable(true); } if (_iQNetStubState == QNET_STATE_IDLE) TickSearch(); if (_iQNetStubState == QNET_STATE_GAME_PLAY && m_pIQNet->IsHost()) { BYTE disconnectedSmallId; while (WinsockNetLayer::PopDisconnectedSmallId(&disconnectedSmallId)) { IQNetPlayer *qnetPlayer = m_pIQNet->GetPlayerBySmallId(disconnectedSmallId); if (qnetPlayer != NULL && qnetPlayer->m_smallId == disconnectedSmallId) { NotifyPlayerLeaving(qnetPlayer); qnetPlayer->m_smallId = 0; qnetPlayer->m_isRemote = false; qnetPlayer->m_isHostPlayer = false; qnetPlayer->m_gamertag[0] = 0; qnetPlayer->SetCustomDataValue(0); WinsockNetLayer::PushFreeSmallId(disconnectedSmallId); // Recompute active slot span instead of blindly decrementing. // A lower smallId can disconnect while higher smallIds are still active. DWORD highestActive = 1; for (DWORD idx = 1; idx < MINECRAFT_NET_MAX_PLAYERS; ++idx) { if (IQNet::m_player[idx].GetCustomDataValue() != 0) { highestActive = idx + 1; } } IQNet::s_playerCount = highestActive; app.DebugPrintf("Win64 LAN: Recomputed active player slot span to %u after disconnect smallId=%u\n", (unsigned int)IQNet::s_playerCount, (unsigned int)disconnectedSmallId); } } } #endif } int CPlatformNetworkManagerStub::GetPlayerCount() { return m_pIQNet->GetPlayerCount(); } bool CPlatformNetworkManagerStub::ShouldMessageForFullSession() { return false; } int CPlatformNetworkManagerStub::GetOnlinePlayerCount() { return 1; } int CPlatformNetworkManagerStub::GetLocalPlayerMask(int playerIndex) { return 1 << playerIndex; } bool CPlatformNetworkManagerStub::AddLocalPlayerByUserIndex( int userIndex ) { NotifyPlayerJoined(m_pIQNet->GetLocalPlayerByUserIndex(userIndex)); return ( m_pIQNet->AddLocalPlayerByUserIndex(userIndex) == S_OK ); } bool CPlatformNetworkManagerStub::RemoveLocalPlayerByUserIndex( int userIndex ) { return true; } bool CPlatformNetworkManagerStub::IsInStatsEnabledSession() { return true; } bool CPlatformNetworkManagerStub::SessionHasSpace(unsigned int spaceRequired /*= 1*/) { return true; } void CPlatformNetworkManagerStub::SendInviteGUI(int quadrant) { } bool CPlatformNetworkManagerStub::IsAddingPlayer() { return false; } bool CPlatformNetworkManagerStub::LeaveGame(bool bMigrateHost) { if( m_bLeavingGame ) return true; m_bLeavingGame = true; #ifdef _WINDOWS64 WinsockNetLayer::StopAdvertising(); #endif if(m_pIQNet->IsHost() && g_NetworkManager.ServerStoppedValid()) { m_pIQNet->EndGame(); g_NetworkManager.ServerStoppedWait(); g_NetworkManager.ServerStoppedDestroy(); } else { m_pIQNet->EndGame(); } for (AUTO_VAR(it, currentNetworkPlayers.begin()); it != currentNetworkPlayers.end(); it++) delete *it; currentNetworkPlayers.clear(); m_machineQNetPrimaryPlayers.clear(); SystemFlagReset(); #ifdef _WINDOWS64 // Clear all back-pointers from IQNet slots to deleted network players. for (int idx = 0; idx < MINECRAFT_NET_MAX_PLAYERS; ++idx) { IQNet::m_player[idx].SetCustomDataValue(0); } #endif #ifdef _WINDOWS64 WinsockNetLayer::Shutdown(); WinsockNetLayer::Initialize(); #endif return true; } bool CPlatformNetworkManagerStub::_LeaveGame(bool bMigrateHost, bool bLeaveRoom) { return true; } void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame, bool bIsPrivate, unsigned char publicSlots /*= MINECRAFT_NET_MAX_PLAYERS*/, unsigned char privateSlots /*= 0*/) { SetLocalGame( !bOnlineGame ); SetPrivateGame( bIsPrivate ); SystemFlagReset(); localUsersMask |= GetLocalPlayerMask( g_NetworkManager.GetPrimaryPad() ); m_bLeavingGame = false; m_pIQNet->HostGame(); #ifdef _WINDOWS64 IQNet::m_player[0].m_smallId = 0; IQNet::m_player[0].m_isRemote = false; IQNet::m_player[0].m_isHostPlayer = true; IQNet::s_playerCount = 1; #endif _HostGame( localUsersMask, publicSlots, privateSlots ); #ifdef _WINDOWS64 unsigned char advertiseMaxPlayers = publicSlots; if (advertiseMaxPlayers == 0 || advertiseMaxPlayers > MINECRAFT_NET_MAX_PLAYERS) { advertiseMaxPlayers = MINECRAFT_NET_MAX_PLAYERS; } g_Win64MultiplayerMaxPlayers = (int)advertiseMaxPlayers; int port = g_Win64MultiplayerPort; if (port <= 0) { port = WIN64_NET_DEFAULT_PORT; } if (!WinsockNetLayer::IsActive()) WinsockNetLayer::HostGame(port); const wchar_t *hostName = IQNet::m_player[0].m_gamertag; unsigned int settings = app.GetGameHostOption(eGameHostOption_All); WinsockNetLayer::StartAdvertising(port, hostName, settings, 0, 0, MINECRAFT_NET_VERSION, advertiseMaxPlayers); UpdateAndSetGameSessionData(); #endif } void CPlatformNetworkManagerStub::_HostGame(int usersMask, unsigned char publicSlots /*= MINECRAFT_NET_MAX_PLAYERS*/, unsigned char privateSlots /*= 0*/) { } bool CPlatformNetworkManagerStub::_StartGame() { return true; } int CPlatformNetworkManagerStub::JoinGame(FriendSessionInfo *searchResult, int localUsersMask, int primaryUserIndex) { #ifdef _WINDOWS64 if (searchResult == NULL) return CGameNetworkManager::JOINGAME_FAIL_GENERAL; const char *hostIP = searchResult->data.hostIP; int hostPort = searchResult->data.hostPort; if (hostPort <= 0 || hostIP[0] == 0) return CGameNetworkManager::JOINGAME_FAIL_GENERAL; m_bLeavingGame = false; IQNet::s_isHosting = false; m_pIQNet->ClientJoinGame(); IQNet::m_player[0].m_smallId = 0; IQNet::m_player[0].m_isRemote = true; IQNet::m_player[0].m_isHostPlayer = true; wcsncpy_s(IQNet::m_player[0].m_gamertag, 32, searchResult->data.hostName, _TRUNCATE); WinsockNetLayer::StopDiscovery(); if (!WinsockNetLayer::JoinGame(hostIP, hostPort)) { app.DebugPrintf("Win64 LAN: Failed to connect to %s:%d\n", hostIP, hostPort); // Reset transient join state so a failed attempt does not poison subsequent joins. m_pIQNet->EndGame(); WinsockNetLayer::StartDiscovery(); 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; 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; #else return CGameNetworkManager::JOINGAME_SUCCESS; #endif } bool CPlatformNetworkManagerStub::SetLocalGame(bool isLocal) { m_bIsOfflineGame = isLocal; return true; } void CPlatformNetworkManagerStub::SetPrivateGame(bool isPrivate) { app.DebugPrintf("Setting as private game: %s\n", isPrivate ? "yes" : "no" ); m_bIsPrivateGame = isPrivate; } void CPlatformNetworkManagerStub::RegisterPlayerChangedCallback(int iPad, void (*callback)(void *callbackParam, INetworkPlayer *pPlayer, bool leaving), void *callbackParam) { playerChangedCallback[iPad] = callback; playerChangedCallbackParam[iPad] = callbackParam; } void CPlatformNetworkManagerStub::UnRegisterPlayerChangedCallback(int iPad, void (*callback)(void *callbackParam, INetworkPlayer *pPlayer, bool leaving), void *callbackParam) { if(playerChangedCallbackParam[iPad] == callbackParam) { playerChangedCallback[iPad] = NULL; playerChangedCallbackParam[iPad] = NULL; } } void CPlatformNetworkManagerStub::HandleSignInChange() { return; } bool CPlatformNetworkManagerStub::_RunNetworkGame() { #ifdef _WINDOWS64 extern QNET_STATE _iQNetStubState; _iQNetStubState = QNET_STATE_GAME_PLAY; for (DWORD i = 0; i < IQNet::s_playerCount; i++) { if (IQNet::m_player[i].m_isRemote) { INetworkPlayer *pNetworkPlayer = getNetworkPlayer(&IQNet::m_player[i]); if (pNetworkPlayer != NULL && pNetworkPlayer->GetSocket() != NULL) { Socket::addIncomingSocket(pNetworkPlayer->GetSocket()); } } } #endif return true; } void CPlatformNetworkManagerStub::UpdateAndSetGameSessionData(INetworkPlayer *pNetworkPlayerLeaving /*= NULL*/) { #ifdef _WINDOWS64 if (this->m_bLeavingGame || !m_pIQNet->IsHost() || !WinsockNetLayer::IsHosting()) { return; } BYTE advertisedCount = (BYTE)m_pIQNet->GetPlayerCount(); if (g_Win64DedicatedServerMode && advertisedCount > 0) { // Dedicated host keeps slot 0 active internally for network scheduling, // but should not be shown as an in-game player in server browser counts. --advertisedCount; } if (pNetworkPlayerLeaving != NULL && advertisedCount > 0) { --advertisedCount; } if (advertisedCount == 0 && !g_Win64DedicatedServerMode) { advertisedCount = 1; } extern QNET_STATE _iQNetStubState; const BYTE maxPlayers = WinsockNetLayer::GetMaxPlayers(); const bool joinable = (_iQNetStubState == QNET_STATE_GAME_PLAY) && (advertisedCount < maxPlayers); const unsigned int advertisedSettings = app.GetGameHostOption(eGameHostOption_All); WinsockNetLayer::UpdateAdvertiseGameSettings(advertisedSettings); WinsockNetLayer::UpdateAdvertisePlayerCount(advertisedCount); WinsockNetLayer::UpdateAdvertiseJoinable(joinable); app.DebugPrintf("Win64 LAN: Updated advertised session data (players=%d, joinable=%d, settings=0x%08X)\n", (int)advertisedCount, joinable ? 1 : 0, advertisedSettings); #endif } int CPlatformNetworkManagerStub::RemovePlayerOnSocketClosedThreadProc( void* lpParam ) { INetworkPlayer *pNetworkPlayer = (INetworkPlayer *)lpParam; Socket *socket = pNetworkPlayer->GetSocket(); if( socket != NULL ) { //printf("Waiting for socket closed event\n"); socket->m_socketClosedEvent->WaitForSignal(INFINITE); //printf("Socket closed event has fired\n"); // 4J Stu - Clear our reference to this socket pNetworkPlayer->SetSocket( NULL ); delete socket; } return g_pPlatformNetworkManager->RemoveLocalPlayer( pNetworkPlayer ); } bool CPlatformNetworkManagerStub::RemoveLocalPlayer( INetworkPlayer *pNetworkPlayer ) { return true; } CPlatformNetworkManagerStub::PlayerFlags::PlayerFlags(INetworkPlayer *pNetworkPlayer, unsigned int count) { // 4J Stu - Don't assert, just make it a multiple of 8! This count is calculated from a load of separate values, // and makes tweaking world/render sizes a pain if we hit an assert here count = (count + 8 - 1) & ~(8 - 1); //assert( ( count % 8 ) == 0 ); this->m_pNetworkPlayer = pNetworkPlayer; this->flags = new unsigned char [ count / 8 ]; memset( this->flags, 0, count / 8 ); this->count = count; } CPlatformNetworkManagerStub::PlayerFlags::~PlayerFlags() { delete [] flags; } // Add a player to the per system flag storage - if we've already got a player from that system, copy its flags over void CPlatformNetworkManagerStub::SystemFlagAddPlayer(INetworkPlayer *pNetworkPlayer) { PlayerFlags *newPlayerFlags = new PlayerFlags( pNetworkPlayer, m_flagIndexSize); // If any of our existing players are on the same system, then copy over flags from that one for( unsigned int i = 0; i < m_playerFlags.size(); i++ ) { if( pNetworkPlayer->IsSameSystem(m_playerFlags[i]->m_pNetworkPlayer) ) { memcpy( newPlayerFlags->flags, m_playerFlags[i]->flags, m_playerFlags[i]->count / 8 ); break; } } m_playerFlags.push_back(newPlayerFlags); } // Remove a player from the per system flag storage - just maintains the m_playerFlags vector without any gaps in it void CPlatformNetworkManagerStub::SystemFlagRemovePlayer(INetworkPlayer *pNetworkPlayer) { for( unsigned int i = 0; i < m_playerFlags.size(); i++ ) { if( m_playerFlags[i]->m_pNetworkPlayer == pNetworkPlayer ) { delete m_playerFlags[i]; m_playerFlags[i] = m_playerFlags.back(); m_playerFlags.pop_back(); return; } } } void CPlatformNetworkManagerStub::SystemFlagReset() { for( unsigned int i = 0; i < m_playerFlags.size(); i++ ) { delete m_playerFlags[i]; } m_playerFlags.clear(); } // Set a per system flag - this is done by setting the flag on every player that shares that system void CPlatformNetworkManagerStub::SystemFlagSet(INetworkPlayer *pNetworkPlayer, int index) { if( ( index < 0 ) || ( index >= m_flagIndexSize ) ) return; if( pNetworkPlayer == NULL ) return; for( unsigned int i = 0; i < m_playerFlags.size(); i++ ) { if( pNetworkPlayer->IsSameSystem(m_playerFlags[i]->m_pNetworkPlayer) ) { m_playerFlags[i]->flags[ index / 8 ] |= ( 128 >> ( index % 8 ) ); } } } // Get value of a per system flag - can be read from the flags of the passed in player as anything else sent to that // system should also have been duplicated here bool CPlatformNetworkManagerStub::SystemFlagGet(INetworkPlayer *pNetworkPlayer, int index) { if( ( index < 0 ) || ( index >= m_flagIndexSize ) ) return false; if( pNetworkPlayer == NULL ) { return false; } for( unsigned int i = 0; i < m_playerFlags.size(); i++ ) { if( m_playerFlags[i]->m_pNetworkPlayer == pNetworkPlayer ) { return ( ( m_playerFlags[i]->flags[ index / 8 ] & ( 128 >> ( index % 8 ) ) ) != 0 ); } } return false; } wstring CPlatformNetworkManagerStub::GatherStats() { return L""; } wstring CPlatformNetworkManagerStub::GatherRTTStats() { wstring stats(L"Rtt: "); wchar_t stat[32]; for(unsigned int i = 0; i < GetPlayerCount(); ++i) { IQNetPlayer *pQNetPlayer = ((NetworkPlayerXbox *)GetPlayerByIndex( i ))->GetQNetPlayer(); if(!pQNetPlayer->IsLocal()) { ZeroMemory(stat,32*sizeof(WCHAR)); swprintf(stat, 32, L"%d: %d/", i, pQNetPlayer->GetCurrentRtt() ); stats.append(stat); } } return stats; } void CPlatformNetworkManagerStub::TickSearch() { #ifdef _WINDOWS64 if (m_SessionsUpdatedCallback == NULL) return; static DWORD lastSearchTime = 0; DWORD now = GetTickCount(); if (now - lastSearchTime < 2000) return; lastSearchTime = now; SearchForGames(); #endif } void CPlatformNetworkManagerStub::SearchForGames() { #ifdef _WINDOWS64 std::vector lanSessions = WinsockNetLayer::GetDiscoveredSessions(); for (size_t i = 0; i < friendsSessions[0].size(); i++) delete friendsSessions[0][i]; friendsSessions[0].clear(); for (size_t i = 0; i < lanSessions.size(); i++) { FriendSessionInfo *info = new FriendSessionInfo(); size_t nameLen = wcslen(lanSessions[i].hostName); info->displayLabel = new wchar_t[nameLen + 1]; wcscpy_s(info->displayLabel, nameLen + 1, lanSessions[i].hostName); info->displayLabelLength = (unsigned char)nameLen; info->displayLabelViewableStartIndex = 0; info->data.netVersion = lanSessions[i].netVersion; info->data.m_uiGameHostSettings = lanSessions[i].gameHostSettings; info->data.texturePackParentId = lanSessions[i].texturePackParentId; info->data.subTexturePackId = lanSessions[i].subTexturePackId; info->data.isReadyToJoin = lanSessions[i].isJoinable; info->data.isJoinable = lanSessions[i].isJoinable; strncpy_s(info->data.hostIP, sizeof(info->data.hostIP), lanSessions[i].hostIP, _TRUNCATE); info->data.hostPort = lanSessions[i].hostPort; wcsncpy_s(info->data.hostName, XUSER_NAME_SIZE, lanSessions[i].hostName, _TRUNCATE); info->data.playerCount = lanSessions[i].playerCount; info->data.maxPlayers = lanSessions[i].maxPlayers; info->sessionId = (SessionID)((unsigned __int64)inet_addr(lanSessions[i].hostIP) | ((unsigned __int64)lanSessions[i].hostPort << 32)); friendsSessions[0].push_back(info); } m_searchResultsCount[0] = (int)friendsSessions[0].size(); if (m_SessionsUpdatedCallback != NULL) m_SessionsUpdatedCallback(m_pSearchParam); #endif } int CPlatformNetworkManagerStub::SearchForGamesThreadProc( void* lpParameter ) { return 0; } void CPlatformNetworkManagerStub::SetSearchResultsReady(int resultCount) { m_bSearchResultsReady = true; m_searchResultsCount[m_lastSearchPad] = resultCount; } vector *CPlatformNetworkManagerStub::GetSessionList(int iPad, int localPlayers, bool partyOnly) { vector *filteredList = new vector(); for (size_t i = 0; i < friendsSessions[0].size(); i++) filteredList->push_back(friendsSessions[0][i]); return filteredList; } bool CPlatformNetworkManagerStub::GetGameSessionInfo(int iPad, SessionID sessionId, FriendSessionInfo *foundSessionInfo) { return false; } void CPlatformNetworkManagerStub::SetSessionsUpdatedCallback( void (*SessionsUpdatedCallback)(LPVOID pParam), LPVOID pSearchParam ) { m_SessionsUpdatedCallback = SessionsUpdatedCallback; m_pSearchParam = pSearchParam; } void CPlatformNetworkManagerStub::GetFullFriendSessionInfo( FriendSessionInfo *foundSession, void (* FriendSessionUpdatedFn)(bool success, void *pParam), void *pParam ) { FriendSessionUpdatedFn(true, pParam); } void CPlatformNetworkManagerStub::ForceFriendsSessionRefresh() { app.DebugPrintf("Resetting friends session search data\n"); for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) { m_searchResultsCount[i] = 0; m_lastSearchStartTime[i] = 0; delete m_pSearchResults[i]; m_pSearchResults[i] = NULL; } } INetworkPlayer *CPlatformNetworkManagerStub::addNetworkPlayer(IQNetPlayer *pQNetPlayer) { NetworkPlayerXbox *pNetworkPlayer = new NetworkPlayerXbox(pQNetPlayer); pQNetPlayer->SetCustomDataValue((ULONG_PTR)pNetworkPlayer); currentNetworkPlayers.push_back( pNetworkPlayer ); return pNetworkPlayer; } void CPlatformNetworkManagerStub::removeNetworkPlayer(IQNetPlayer *pQNetPlayer) { INetworkPlayer *pNetworkPlayer = getNetworkPlayer(pQNetPlayer); for( AUTO_VAR(it, currentNetworkPlayers.begin()); it != currentNetworkPlayers.end(); it++ ) { if( *it == pNetworkPlayer ) { currentNetworkPlayers.erase(it); return; } } } INetworkPlayer *CPlatformNetworkManagerStub::getNetworkPlayer(IQNetPlayer *pQNetPlayer) { return pQNetPlayer ? (INetworkPlayer *)(pQNetPlayer->GetCustomDataValue()) : NULL; } INetworkPlayer *CPlatformNetworkManagerStub::GetLocalPlayerByUserIndex(int userIndex ) { return getNetworkPlayer(m_pIQNet->GetLocalPlayerByUserIndex(userIndex)); } INetworkPlayer *CPlatformNetworkManagerStub::GetPlayerByIndex(int playerIndex) { return getNetworkPlayer(m_pIQNet->GetPlayerByIndex(playerIndex)); } INetworkPlayer * CPlatformNetworkManagerStub::GetPlayerByXuid(PlayerUID xuid) { IQNetPlayer *qnetPlayer = m_pIQNet->GetPlayerByXuid(xuid); #ifdef _WINDOWS64 if (qnetPlayer == NULL) { const unsigned __int64 kWin64StubXuidBase = 0xe000d45248242f2eULL; const unsigned __int64 uxuid = (unsigned __int64)xuid; if (uxuid >= kWin64StubXuidBase) { const unsigned __int64 delta = uxuid - kWin64StubXuidBase; if (delta < (unsigned __int64)MINECRAFT_NET_MAX_PLAYERS) { const BYTE smallId = (BYTE)delta; // Clients can legitimately resolve remote players by XUID before the // platform join callback has materialized the slot's NetworkPlayer. // Hosts keep the stricter check to avoid creating phantom entries. const bool slotActive = (!m_pIQNet->IsHost()) || (smallId == 0) || (IQNet::m_player[smallId].GetCustomDataValue() != 0); if (slotActive) { qnetPlayer = &IQNet::m_player[smallId]; qnetPlayer->m_smallId = smallId; if (smallId == 0) { qnetPlayer->m_isHostPlayer = true; qnetPlayer->m_isRemote = !m_pIQNet->IsHost(); } else { const BYTE localSmallId = WinsockNetLayer::GetLocalSmallId(); const bool isLocalSlot = (!m_pIQNet->IsHost() && (smallId == localSmallId)); qnetPlayer->m_isHostPlayer = false; qnetPlayer->m_isRemote = !isLocalSlot; } if (smallId >= IQNet::s_playerCount) { IQNet::s_playerCount = smallId + 1; } app.DebugPrintf("Win64 LAN: Materialized player slot from XUID (smallId=%u, isRemote=%d)\n", (unsigned int)smallId, qnetPlayer->m_isRemote ? 1 : 0); } } } } #endif if (qnetPlayer == NULL) { return NULL; } INetworkPlayer *networkPlayer = getNetworkPlayer(qnetPlayer); #ifdef _WINDOWS64 if (networkPlayer == NULL) { NotifyPlayerJoined(qnetPlayer); networkPlayer = getNetworkPlayer(qnetPlayer); app.DebugPrintf("Win64 LAN: Lazily created network player from XUID (smallId=%u)\n", (unsigned int)qnetPlayer->GetSmallId()); } #endif return networkPlayer; } INetworkPlayer * CPlatformNetworkManagerStub::GetPlayerBySmallId(unsigned char smallId) { IQNetPlayer *qnetPlayer = m_pIQNet->GetPlayerBySmallId(smallId); if (qnetPlayer == NULL) { return NULL; } INetworkPlayer *networkPlayer = getNetworkPlayer(qnetPlayer); #ifdef _WINDOWS64 if (networkPlayer == NULL && smallId != 0 && !m_pIQNet->IsHost()) { const BYTE localSmallId = WinsockNetLayer::GetLocalSmallId(); const bool isLocalSlot = (smallId == localSmallId); qnetPlayer->m_smallId = smallId; qnetPlayer->m_isHostPlayer = false; qnetPlayer->m_isRemote = !isLocalSlot; if (smallId >= IQNet::s_playerCount) { IQNet::s_playerCount = smallId + 1; } // On Win64 clients we may receive player/entity packets for remote users before a // platform-layer "player joined" callback has been issued for that slot. NotifyPlayerJoined(qnetPlayer); networkPlayer = getNetworkPlayer(qnetPlayer); app.DebugPrintf("Win64 LAN: Lazily created network player for smallId=%u\n", (unsigned int)smallId); } #endif return networkPlayer; } INetworkPlayer *CPlatformNetworkManagerStub::GetHostPlayer() { return getNetworkPlayer(m_pIQNet->GetHostPlayer()); } bool CPlatformNetworkManagerStub::IsHost() { return m_pIQNet->IsHost() && !m_bHostChanged; } bool CPlatformNetworkManagerStub::JoinGameFromInviteInfo( int userIndex, int userMask, const INVITE_INFO *pInviteInfo) { return ( m_pIQNet->JoinGameFromInviteInfo( userIndex, userMask, pInviteInfo ) == S_OK); } void CPlatformNetworkManagerStub::SetSessionTexturePackParentId( int id ) { m_hostGameSessionData.texturePackParentId = id; } void CPlatformNetworkManagerStub::SetSessionSubTexturePackId( int id ) { m_hostGameSessionData.subTexturePackId = id; } void CPlatformNetworkManagerStub::Notify(int ID, ULONG_PTR Param) { } bool CPlatformNetworkManagerStub::IsInSession() { return m_pIQNet->GetState() != QNET_STATE_IDLE; } bool CPlatformNetworkManagerStub::IsInGameplay() { return m_pIQNet->GetState() == QNET_STATE_GAME_PLAY; } bool CPlatformNetworkManagerStub::IsReadyToPlayOrIdle() { return true; } #ifdef _WINDOWS64 void CPlatformNetworkManagerStub::QueueServerTransfer(const char *hostIp, int hostPort) { if (hostIp == NULL || hostIp[0] == 0 || hostPort <= 0) { return; } strncpy_s(m_transferHostIp, sizeof(m_transferHostIp), hostIp, _TRUNCATE); m_transferHostPort = hostPort; m_bTransferPending = true; m_bTransferLeaving = false; m_transferSuppressErrorsUntilMs = GetNowMs64() + 10000ULL; app.DebugPrintf("Win64 LAN: Queued server transfer target %s:%d\n", m_transferHostIp, m_transferHostPort); } void CPlatformNetworkManagerStub::RequestServerTransfer(const char *hostIp, int hostPort) { if (g_pPlatformNetworkManager == NULL) { return; } g_pPlatformNetworkManager->QueueServerTransfer(hostIp, hostPort); } bool CPlatformNetworkManagerStub::IsServerTransferInProgress() { return (g_pPlatformNetworkManager != NULL) && (g_pPlatformNetworkManager->m_bTransferPending || g_pPlatformNetworkManager->m_bTransferLeaving || (GetNowMs64() < g_pPlatformNetworkManager->m_transferSuppressErrorsUntilMs)); } #endif