Add asynchronous server joining (#1408)

This commit is contained in:
Sylvessa
2026-03-26 09:15:11 -05:00
committed by GitHub
parent dee559bd16
commit 1a50770647
8 changed files with 480 additions and 21 deletions

View File

@@ -47,7 +47,8 @@ public:
{
JOINGAME_SUCCESS,
JOINGAME_FAIL_GENERAL,
JOINGAME_FAIL_SERVER_FULL
JOINGAME_FAIL_SERVER_FULL,
JOINGAME_PENDING
} eJoinGameResult;
void Initialise();

View File

@@ -173,6 +173,11 @@ bool CPlatformNetworkManagerStub::Initialise(CGameNetworkManager *pGameNetworkMa
m_bSearchPending = false;
m_bIsOfflineGame = false;
#ifdef _WINDOWS64
m_bJoinPending = false;
m_joinLocalUsersMask = 0;
m_joinHostName[0] = 0;
#endif
m_pSearchParam = nullptr;
m_SessionsUpdatedCallback = nullptr;
@@ -282,6 +287,38 @@ void CPlatformNetworkManagerStub::DoWork()
m_bLeaveGameOnTick = false;
}
}
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
}
@@ -511,36 +548,22 @@ int CPlatformNetworkManagerStub::JoinGame(FriendSessionInfo* searchResult, int l
IQNet::m_player[0].m_smallId = 0;
IQNet::m_player[0].m_isRemote = true;
IQNet::m_player[0].m_isHostPlayer = true;
// Remote host still maps to legacy host XUID in mixed old/new sessions.
IQNet::m_player[0].m_resolvedXuid = Win64Xuid::GetLegacyEmbeddedHostXuid();
wcsncpy_s(IQNet::m_player[0].m_gamertag, 32, searchResult->data.hostName, _TRUNCATE);
WinsockNetLayer::StopDiscovery();
if (!WinsockNetLayer::JoinGame(hostIP, hostPort))
wcsncpy_s(m_joinHostName, 32, searchResult->data.hostName, _TRUNCATE);
m_joinLocalUsersMask = localUsersMask;
if (!WinsockNetLayer::BeginJoinGame(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;
// 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

View File

@@ -77,6 +77,12 @@ private:
bool m_bIsPrivateGame;
int m_flagIndexSize;
#ifdef _WINDOWS64
bool m_bJoinPending;
int m_joinLocalUsersMask;
wchar_t m_joinHostName[32];
#endif
// This is only maintained by the host, and is not valid on client machines
GameSessionData m_hostGameSessionData;
CGameNetworkManager *m_pGameNetworkManager;

View File

@@ -2,6 +2,16 @@
#include "UI.h"
#include "UIScene_ConnectingProgress.h"
#include "..\..\Minecraft.h"
#ifdef _WINDOWS64
#include "..\..\Windows64\Network\WinsockNetLayer.h"
#include "..\..\..\Minecraft.World\DisconnectPacket.h"
static int ConnectingProgress_OnRejectedDialogOK(LPVOID, int iPad, const C4JStorage::EMessageResult)
{
ui.NavigateBack(iPad);
return 0;
}
#endif
UIScene_ConnectingProgress::UIScene_ConnectingProgress(int iPad, void *_initData, UILayer *parentLayer) : UIScene(iPad, parentLayer)
{
@@ -43,6 +53,12 @@ UIScene_ConnectingProgress::UIScene_ConnectingProgress(int iPad, void *_initData
m_cancelFuncParam = param->cancelFuncParam;
m_removeLocalPlayer = false;
m_showingButton = false;
#ifdef _WINDOWS64
WinsockNetLayer::eJoinState initState = WinsockNetLayer::GetJoinState();
m_asyncJoinActive = (initState != WinsockNetLayer::eJoinState_Idle && initState != WinsockNetLayer::eJoinState_Cancelled);
m_asyncJoinFailed = false;
#endif
}
UIScene_ConnectingProgress::~UIScene_ConnectingProgress()
@@ -53,6 +69,18 @@ UIScene_ConnectingProgress::~UIScene_ConnectingProgress()
void UIScene_ConnectingProgress::updateTooltips()
{
#ifdef _WINDOWS64
if (m_asyncJoinActive)
{
ui.SetTooltips( m_iPad, -1, IDS_TOOLTIPS_BACK);
return;
}
if (m_asyncJoinFailed)
{
ui.SetTooltips( m_iPad, IDS_TOOLTIPS_SELECT, -1);
return;
}
#endif
// 4J-PB - removing the option of cancel join, since it didn't work anyway
//ui.SetTooltips( m_iPad, -1, m_showTooltips?IDS_TOOLTIPS_CANCEL_JOIN:-1);
ui.SetTooltips( m_iPad, -1, -1);
@@ -62,6 +90,85 @@ void UIScene_ConnectingProgress::tick()
{
UIScene::tick();
#ifdef _WINDOWS64
if (m_asyncJoinActive)
{
WinsockNetLayer::eJoinState state = WinsockNetLayer::GetJoinState();
if (state == WinsockNetLayer::eJoinState_Connecting)
{
// connecting.............
int attempt = WinsockNetLayer::GetJoinAttempt();
int maxAttempts = WinsockNetLayer::GetJoinMaxAttempts();
char buf[128];
if (attempt <= 1)
sprintf_s(buf, "Connecting...");
else
sprintf_s(buf, "Connecting failed, trying again (%d/%d)", attempt, maxAttempts);
wchar_t wbuf[128];
mbstowcs(wbuf, buf, 128);
m_labelTitle.setLabel(wstring(wbuf));
}
else if (state == WinsockNetLayer::eJoinState_Success)
{
m_asyncJoinActive = false;
// go go go
}
else if (state == WinsockNetLayer::eJoinState_Cancelled)
{
// cancel
m_asyncJoinActive = false;
navigateBack();
}
else if (state == WinsockNetLayer::eJoinState_Rejected)
{
// server full and banned are passed differently compared to other disconnects it seems
m_asyncJoinActive = false;
DisconnectPacket::eDisconnectReason reason = WinsockNetLayer::GetJoinRejectReason();
int exitReasonStringId;
switch (reason)
{
case DisconnectPacket::eDisconnect_ServerFull:
exitReasonStringId = IDS_DISCONNECTED_SERVER_FULL;
break;
case DisconnectPacket::eDisconnect_Banned:
exitReasonStringId = IDS_DISCONNECTED_KICKED;
break;
default:
exitReasonStringId = IDS_CONNECTION_LOST_SERVER;
break;
}
UINT uiIDA[1];
uiIDA[0] = IDS_CONFIRM_OK;
ui.RequestErrorMessage(IDS_CONNECTION_FAILED, exitReasonStringId, uiIDA, 1, ProfileManager.GetPrimaryPad(), ConnectingProgress_OnRejectedDialogOK, nullptr, nullptr);
}
else if (state == WinsockNetLayer::eJoinState_Failed)
{
// FAIL
m_asyncJoinActive = false;
m_asyncJoinFailed = true;
int maxAttempts = WinsockNetLayer::GetJoinMaxAttempts();
char buf[256];
sprintf_s(buf, "Failed to connect after %d attempts. The server may be unavailable.", maxAttempts);
wchar_t wbuf[256];
mbstowcs(wbuf, buf, 256);
// TIL that these exist
// not going to use a actual popup due to it requiring messing with strings which can really mess things up
// i dont trust myself with that
// these need to be touched up later as teh button is a bit offset
m_labelTitle.setLabel(L"Unable to connect to server");
m_progressBar.setLabel(wstring(wbuf));
m_progressBar.showBar(false);
m_progressBar.setVisible(true);
m_buttonConfirm.setVisible(true);
m_showingButton = true;
m_controlTimer.setVisible(false);
}
return;
}
#endif
if( m_removeLocalPlayer )
{
m_removeLocalPlayer = false;
@@ -94,6 +201,8 @@ void UIScene_ConnectingProgress::handleGainFocus(bool navBack)
void UIScene_ConnectingProgress::handleLoseFocus()
{
if (!m_runFailTimer) return;
int millisecsLeft = getTimer(0)->targetTime - System::currentTimeMillis();
int millisecsTaken = getTimer(0)->duration - millisecsLeft;
app.DebugPrintf("\n");
@@ -208,6 +317,17 @@ void UIScene_ConnectingProgress::handleInput(int iPad, int key, bool repeat, boo
switch(key)
{
// 4J-PB - Removed the option to cancel join - it didn't work anyway
#ifdef _WINDOWS64
case ACTION_MENU_CANCEL:
if (pressed && m_asyncJoinActive)
{
m_asyncJoinActive = false;
WinsockNetLayer::CancelJoinGame();
navigateBack();
handled = true;
}
break;
#endif
// case ACTION_MENU_CANCEL:
// {
// if(m_cancelFunc != nullptr)
@@ -250,6 +370,13 @@ void UIScene_ConnectingProgress::handlePress(F64 controlId, F64 childId)
case eControl_Confirm:
if(m_showingButton)
{
#ifdef _WINDOWS64
if (m_asyncJoinFailed)
{
navigateBack();
}
else
#endif
if( m_iPad != ProfileManager.GetPrimaryPad() && g_NetworkManager.IsInSession() )
{
// The connection failed if we see the button, so the temp player should be removed and the viewports updated again

View File

@@ -13,6 +13,11 @@ private:
void (*m_cancelFunc)(LPVOID param);
LPVOID m_cancelFuncParam;
#ifdef _WINDOWS64
bool m_asyncJoinActive;
bool m_asyncJoinFailed;
#endif
enum EControls
{
eControl_Confirm

View File

@@ -583,6 +583,24 @@ void UIScene_JoinMenu::JoinGame(UIScene_JoinMenu* pClass)
// Alert the app the we no longer want to be informed of ethernet connections
app.SetLiveLinkRequired( false );
#ifdef _WINDOWS64
if (result == CGameNetworkManager::JOINGAME_PENDING)
{
pClass->m_bIgnoreInput = false;
ConnectionProgressParams *param = new ConnectionProgressParams();
param->iPad = ProfileManager.GetPrimaryPad();
param->stringId = -1;
param->showTooltips = true;
param->setFailTimer = false;
param->timerTime = 0;
param->cancelFunc = nullptr;
param->cancelFuncParam = nullptr;
ui.NavigateToScene(ProfileManager.GetPrimaryPad(), eUIScene_ConnectingProgress, param);
return;
}
#endif
if( result != CGameNetworkManager::JOINGAME_SUCCESS )
{
int exitReasonStringId = -1;