Refactor async server joining with eJoinState enum and dedicated progress UI
Replace the boolean-flag-based async join system with a clean state machine (eJoinState enum) and move connection progress handling from UIScene_JoinMenu into UIScene_ConnectingProgress as a dedicated UI class. Combines the best of two approaches: non-blocking sockets with select() timeout and SO_RCVTIMEO clearing (prevents random disconnects) with the upstream's state enum, FinalizeJoin separation, and ConnectingProgress UI. JoinGame() now returns JOINGAME_PENDING on Win64, and PlatformNetworkManagerStub::DoWork() polls the join state to finalize the connection when the background thread succeeds.
This commit is contained in:
@@ -773,31 +773,10 @@ void CGameNetworkManager::CancelJoinGame(LPVOID lpParam)
|
||||
s_pPlatformNetworkManager->CancelJoinGame();
|
||||
#endif
|
||||
#ifdef _WINDOWS64
|
||||
static_cast<CPlatformNetworkManagerStub*>(s_pPlatformNetworkManager)->CancelJoinGame();
|
||||
WinsockNetLayer::CancelJoinGame();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
bool CGameNetworkManager::BeginJoinGameAsync(FriendSessionInfo *searchResult, int localUsersMask)
|
||||
{
|
||||
app.SetTutorialMode(false);
|
||||
g_NetworkManager.SetLocalGame(false);
|
||||
|
||||
int primaryUserIndex = ProfileManager.GetLockedProfile();
|
||||
|
||||
Minecraft::GetInstance()->clearConnectionFailed();
|
||||
|
||||
localUsersMask |= GetLocalPlayerMask(ProfileManager.GetPrimaryPad());
|
||||
|
||||
return static_cast<CPlatformNetworkManagerStub*>(s_pPlatformNetworkManager)->BeginJoinGameAsync(searchResult, localUsersMask, primaryUserIndex);
|
||||
}
|
||||
|
||||
int CGameNetworkManager::FinishJoinGame(FriendSessionInfo *searchResult)
|
||||
{
|
||||
return static_cast<CPlatformNetworkManagerStub*>(s_pPlatformNetworkManager)->FinishJoinGame(searchResult);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool CGameNetworkManager::LeaveGame(bool bMigrateHost)
|
||||
{
|
||||
Minecraft::GetInstance()->gui->clearMessages();
|
||||
|
||||
@@ -47,7 +47,8 @@ public:
|
||||
{
|
||||
JOINGAME_SUCCESS,
|
||||
JOINGAME_FAIL_GENERAL,
|
||||
JOINGAME_FAIL_SERVER_FULL
|
||||
JOINGAME_FAIL_SERVER_FULL,
|
||||
JOINGAME_PENDING
|
||||
} eJoinGameResult;
|
||||
|
||||
void Initialise();
|
||||
@@ -106,10 +107,6 @@ public:
|
||||
bool JoinGameFromInviteInfo( int userIndex, int userMask, const INVITE_INFO *pInviteInfo);
|
||||
eJoinGameResult JoinGame(FriendSessionInfo *searchResult, int localUsersMask);
|
||||
static void CancelJoinGame(LPVOID lpParam); // Not part of the shared interface
|
||||
#ifdef _WINDOWS64
|
||||
bool BeginJoinGameAsync(FriendSessionInfo *searchResult, int localUsersMask);
|
||||
int FinishJoinGame(FriendSessionInfo *searchResult);
|
||||
#endif
|
||||
bool LeaveGame(bool bMigrateHost);
|
||||
static int JoinFromInvite_SignInReturned(void *pParam,bool bContinue, int iPad);
|
||||
void UpdateAndSetGameSessionData(INetworkPlayer *pNetworkPlayerLeaving = nullptr);
|
||||
|
||||
@@ -173,6 +173,9 @@ bool CPlatformNetworkManagerStub::Initialise(CGameNetworkManager *pGameNetworkMa
|
||||
m_bSearchPending = false;
|
||||
|
||||
m_bIsOfflineGame = false;
|
||||
#ifdef _WINDOWS64
|
||||
m_bJoinPending = false;
|
||||
#endif
|
||||
m_pSearchParam = nullptr;
|
||||
m_SessionsUpdatedCallback = nullptr;
|
||||
|
||||
@@ -282,6 +285,39 @@ void CPlatformNetworkManagerStub::DoWork()
|
||||
m_bLeaveGameOnTick = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Async join finalization: when the background thread reports success,
|
||||
// register players and transition the session to starting state.
|
||||
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
|
||||
}
|
||||
|
||||
@@ -517,90 +553,19 @@ int CPlatformNetworkManagerStub::JoinGame(FriendSessionInfo* searchResult, int l
|
||||
|
||||
WinsockNetLayer::StopDiscovery();
|
||||
|
||||
if (!WinsockNetLayer::JoinGame(hostIP, hostPort))
|
||||
if (!WinsockNetLayer::BeginJoinGame(hostIP, hostPort))
|
||||
{
|
||||
app.DebugPrintf("Win64 LAN: Failed to connect to %s:%d\n", hostIP, hostPort);
|
||||
app.DebugPrintf("Win64 LAN: Failed to start async join 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
|
||||
}
|
||||
|
||||
#ifdef _WINDOWS64
|
||||
bool CPlatformNetworkManagerStub::BeginJoinGameAsync(FriendSessionInfo* searchResult, int localUsersMask, int primaryUserIndex)
|
||||
{
|
||||
if (searchResult == nullptr)
|
||||
return false;
|
||||
|
||||
const char* hostIP = searchResult->data.hostIP;
|
||||
int hostPort = searchResult->data.hostPort;
|
||||
|
||||
if (hostPort <= 0 || hostIP[0] == 0)
|
||||
return false;
|
||||
|
||||
m_bLeavingGame = false;
|
||||
m_bLeaveGameOnTick = 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;
|
||||
IQNet::m_player[0].m_resolvedXuid = Win64Xuid::GetLegacyEmbeddedHostXuid();
|
||||
wcsncpy_s(IQNet::m_player[0].m_gamertag, 32, searchResult->data.hostName, _TRUNCATE);
|
||||
|
||||
WinsockNetLayer::StopDiscovery();
|
||||
|
||||
return WinsockNetLayer::StartJoinGameAsync(hostIP, hostPort);
|
||||
}
|
||||
|
||||
int CPlatformNetworkManagerStub::FinishJoinGame(FriendSessionInfo* searchResult)
|
||||
{
|
||||
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();
|
||||
|
||||
return CGameNetworkManager::JOINGAME_SUCCESS;
|
||||
}
|
||||
|
||||
void CPlatformNetworkManagerStub::CancelJoinGame()
|
||||
{
|
||||
WinsockNetLayer::CancelJoinGame();
|
||||
}
|
||||
#endif
|
||||
|
||||
bool CPlatformNetworkManagerStub::SetLocalGame(bool isLocal)
|
||||
{
|
||||
m_bIsOfflineGame = isLocal;
|
||||
|
||||
@@ -42,11 +42,6 @@ public:
|
||||
|
||||
virtual void HostGame(int localUsersMask, bool bOnlineGame, bool bIsPrivate, unsigned char publicSlots = MINECRAFT_NET_MAX_PLAYERS, unsigned char privateSlots = 0);
|
||||
virtual int JoinGame(FriendSessionInfo *searchResult, int localUsersMask, int primaryUserIndex );
|
||||
#ifdef _WINDOWS64
|
||||
bool BeginJoinGameAsync(FriendSessionInfo *searchResult, int localUsersMask, int primaryUserIndex);
|
||||
int FinishJoinGame(FriendSessionInfo *searchResult);
|
||||
void CancelJoinGame();
|
||||
#endif
|
||||
virtual bool SetLocalGame(bool isLocal);
|
||||
virtual bool IsLocalGame() { return m_bIsOfflineGame; }
|
||||
virtual void SetPrivateGame(bool isPrivate);
|
||||
@@ -81,6 +76,9 @@ private:
|
||||
bool m_bIsOfflineGame;
|
||||
bool m_bIsPrivateGame;
|
||||
int m_flagIndexSize;
|
||||
#ifdef _WINDOWS64
|
||||
bool m_bJoinPending;
|
||||
#endif
|
||||
|
||||
// This is only maintained by the host, and is not valid on client machines
|
||||
GameSessionData m_hostGameSessionData;
|
||||
|
||||
Reference in New Issue
Block a user