Fix server list refresh and add cancellable non-blocking connection

Server list: edits and deletions now update the UI immediately by
calling SearchForGames() in ForceFriendsSessionRefresh() and
UpdateGamesList() on nav-back to LoadOrJoinMenu.

Connection: moved WinsockNetLayer::JoinGame() to a background thread
with non-blocking sockets (5s timeout, 3 retries). Users can cancel
with B/Escape during the attempt. Failed connections always show an
error dialog.
This commit is contained in:
itsRevela
2026-03-24 11:30:14 -05:00
parent d4d5ffe403
commit 5dad6c24f7
10 changed files with 297 additions and 11 deletions
@@ -772,8 +772,32 @@ void CGameNetworkManager::CancelJoinGame(LPVOID lpParam)
#ifdef _XBOX_ONE
s_pPlatformNetworkManager->CancelJoinGame();
#endif
#ifdef _WINDOWS64
static_cast<CPlatformNetworkManagerStub*>(s_pPlatformNetworkManager)->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();
@@ -106,6 +106,10 @@ 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);
@@ -546,6 +546,61 @@ int CPlatformNetworkManagerStub::JoinGame(FriendSessionInfo* searchResult, int l
#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;
@@ -955,6 +1010,13 @@ void CPlatformNetworkManagerStub::ForceFriendsSessionRefresh()
delete m_pSearchResults[i];
m_pSearchResults[i] = nullptr;
}
#ifdef _WINDOWS64
// Immediately rebuild the session list from servers.db so that
// edits/deletions are visible as soon as the UI regains focus,
// rather than waiting for the next TickSearch() cycle.
SearchForGames();
#endif
}
INetworkPlayer *CPlatformNetworkManagerStub::addNetworkPlayer(IQNetPlayer *pQNetPlayer)
@@ -42,6 +42,11 @@ 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);