Fixes for PR #96 (#170)

* Implement basic multiplayer functionality

* Update README.md

---------

Co-authored-by: Slenderman <ssimulpong@outlook.com>
This commit is contained in:
lspepinho
2026-03-02 20:30:22 -03:00
committed by GitHub
parent 15129932da
commit 8b28c20d7a
20 changed files with 1705 additions and 266 deletions

View File

@@ -2,7 +2,12 @@
#include "..\..\..\Minecraft.World\Socket.h"
#include "..\..\..\Minecraft.World\StringHelpers.h"
#include "PlatformNetworkManagerStub.h"
#include "..\..\Xbox\Network\NetworkPlayerXbox.h" // TODO - stub version of this?
#include "..\..\Xbox\Network\NetworkPlayerXbox.h"
#ifdef _WINDOWS64
#include "..\..\Windows64\Network\WinsockNetLayer.h"
#include "..\..\Minecraft.h"
#include "..\..\User.h"
#endif
CPlatformNetworkManagerStub *g_pPlatformNetworkManager;
@@ -114,10 +119,43 @@ void CPlatformNetworkManagerStub::NotifyPlayerJoined(IQNetPlayer *pQNetPlayer )
}
}
void CPlatformNetworkManagerStub::NotifyPlayerLeaving(IQNetPlayer* pQNetPlayer)
{
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);
}
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++ )
{
@@ -174,6 +212,37 @@ bool CPlatformNetworkManagerStub::isSystemPrimaryPlayer(IQNetPlayer *pQNetPlayer
// 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 (_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);
if (IQNet::s_playerCount > 1)
IQNet::s_playerCount--;
}
}
}
#endif
}
int CPlatformNetworkManagerStub::GetPlayerCount()
@@ -232,6 +301,10 @@ bool CPlatformNetworkManagerStub::LeaveGame(bool bMigrateHost)
m_bLeavingGame = true;
#ifdef _WINDOWS64
WinsockNetLayer::StopAdvertising();
#endif
// If we are the host wait for the game server to end
if(m_pIQNet->IsHost() && g_NetworkManager.ServerStoppedValid())
{
@@ -239,6 +312,22 @@ bool CPlatformNetworkManagerStub::LeaveGame(bool bMigrateHost)
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
WinsockNetLayer::Shutdown();
WinsockNetLayer::Initialize();
#endif
return true;
}
@@ -262,7 +351,24 @@ void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame,
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
int 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);
#endif
//#endif
}
@@ -275,9 +381,54 @@ bool CPlatformNetworkManagerStub::_StartGame()
return true;
}
int CPlatformNetworkManagerStub::JoinGame(FriendSessionInfo *searchResult, int localUsersMask, int primaryUserIndex)
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);
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)
@@ -315,6 +466,22 @@ void CPlatformNetworkManagerStub::HandleSignInChange()
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;
}
@@ -503,10 +670,60 @@ wstring CPlatformNetworkManagerStub::GatherRTTStats()
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<Win64LANSession> 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 )
@@ -522,7 +739,9 @@ void CPlatformNetworkManagerStub::SetSearchResultsReady(int resultCount)
vector<FriendSessionInfo *> *CPlatformNetworkManagerStub::GetSessionList(int iPad, int localPlayers, bool partyOnly)
{
vector<FriendSessionInfo *> *filteredList = new vector<FriendSessionInfo *>();;
vector<FriendSessionInfo*>* filteredList = new vector<FriendSessionInfo*>();
for (size_t i = 0; i < friendsSessions[0].size(); i++)
filteredList->push_back(friendsSessions[0][i]);
return filteredList;
}