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:
itsRevela
2026-03-26 11:51:17 -05:00
parent 39b0ad1cb4
commit f1310abe08
10 changed files with 414 additions and 204 deletions
@@ -2,6 +2,15 @@
#include "UI.h"
#include "UIScene_ConnectingProgress.h"
#include "..\..\Minecraft.h"
#ifdef _WINDOWS64
#include "..\..\Windows64\Network\WinsockNetLayer.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 +52,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_Connecting ||
initState == WinsockNetLayer::eJoinState_Success);
m_asyncJoinFailed = false;
#endif
}
UIScene_ConnectingProgress::~UIScene_ConnectingProgress()
@@ -53,6 +68,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 +89,61 @@ void UIScene_ConnectingProgress::tick()
{
UIScene::tick();
#ifdef _WINDOWS64
if (m_asyncJoinActive)
{
WinsockNetLayer::eJoinState state = WinsockNetLayer::GetJoinState();
switch (state)
{
case WinsockNetLayer::eJoinState_Connecting:
{
int attempt = WinsockNetLayer::GetJoinAttempt();
int maxAttempts = WinsockNetLayer::GetJoinMaxAttempts();
wchar_t buf[128];
if (attempt > 1)
swprintf_s(buf, L"Connecting... (attempt %d/%d)", attempt, maxAttempts);
else
swprintf_s(buf, L"Connecting...");
m_labelTitle.setLabel(buf);
break;
}
case WinsockNetLayer::eJoinState_Success:
m_asyncJoinActive = false;
m_labelTitle.setLabel(L"Joining world...");
break;
case WinsockNetLayer::eJoinState_Cancelled:
m_asyncJoinActive = false;
navigateBack();
break;
case WinsockNetLayer::eJoinState_Rejected:
{
m_asyncJoinActive = false;
DisconnectPacket::eDisconnectReason reason = WinsockNetLayer::GetJoinRejectReason();
int reasonStringId = IDS_CONNECTION_LOST_SERVER;
if (reason == DisconnectPacket::eDisconnect_ServerFull)
reasonStringId = IDS_DISCONNECTED_SERVER_FULL;
else if (reason == DisconnectPacket::eDisconnect_Kicked)
reasonStringId = IDS_DISCONNECTED_KICKED;
UINT uiIDA[1];
uiIDA[0] = IDS_CONFIRM_OK;
ui.RequestErrorMessage(IDS_CONNECTION_FAILED, reasonStringId, uiIDA, 1, m_iPad, ConnectingProgress_OnRejectedDialogOK, nullptr);
break;
}
case WinsockNetLayer::eJoinState_Failed:
m_asyncJoinActive = false;
m_asyncJoinFailed = true;
m_labelTitle.setLabel(app.GetString(IDS_CONNECTION_FAILED));
m_buttonConfirm.setVisible(true);
m_showingButton = true;
updateTooltips();
break;
default:
break;
}
}
#endif
if( m_removeLocalPlayer )
{
m_removeLocalPlayer = false;
@@ -94,6 +176,7 @@ 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");
@@ -207,6 +290,18 @@ void UIScene_ConnectingProgress::handleInput(int iPad, int key, bool repeat, boo
switch(key)
{
#ifdef _WINDOWS64
case ACTION_MENU_CANCEL:
if (pressed && m_asyncJoinActive)
{
m_asyncJoinActive = false;
WinsockNetLayer::CancelJoinGame();
navigateBack();
handled = true;
return;
}
break;
#endif
// 4J-PB - Removed the option to cancel join - it didn't work anyway
// case ACTION_MENU_CANCEL:
// {
@@ -250,6 +345,13 @@ void UIScene_ConnectingProgress::handlePress(F64 controlId, F64 childId)
case eControl_Confirm:
if(m_showingButton)
{
#ifdef _WINDOWS64
if (m_asyncJoinFailed)
{
navigateBack();
break;
}
#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
@@ -12,6 +12,10 @@ private:
bool m_showingButton;
void (*m_cancelFunc)(LPVOID param);
LPVOID m_cancelFuncParam;
#ifdef _WINDOWS64
bool m_asyncJoinActive;
bool m_asyncJoinFailed;
#endif
enum EControls
{
+17 -56
View File
@@ -29,8 +29,6 @@ UIScene_JoinMenu::UIScene_JoinMenu(int iPad, void *_initData, UILayer *parentLay
m_editServerPhase = eEditServer_Idle;
m_editServerButtonIndex = -1;
m_deleteServerButtonIndex = -1;
m_asyncJoinInProgress = false;
m_joinLocalUsersMask = 0;
#endif
}
@@ -64,35 +62,6 @@ void UIScene_JoinMenu::updateTooltips()
void UIScene_JoinMenu::tick()
{
#ifdef _WINDOWS64
if (m_asyncJoinInProgress && WinsockNetLayer::IsJoinComplete())
{
m_asyncJoinInProgress = false;
if (WinsockNetLayer::GetJoinResult())
{
int result = g_NetworkManager.FinishJoinGame(m_selectedSession);
app.SetLiveLinkRequired(false);
if (result != CGameNetworkManager::JOINGAME_SUCCESS)
{
m_bIgnoreInput = false;
m_buttonJoinGame.setLabel(app.GetString(IDS_JOIN_GAME));
updateTooltips();
}
}
else
{
app.SetLiveLinkRequired(false);
m_bIgnoreInput = false;
m_buttonJoinGame.setLabel(app.GetString(IDS_JOIN_GAME));
updateTooltips();
UINT uiIDA[1];
uiIDA[0] = IDS_CONFIRM_OK;
ui.RequestErrorMessage(IDS_CONNECTION_FAILED, IDS_CONNECTION_LOST_SERVER, uiIDA, 1, ProfileManager.GetPrimaryPad());
}
}
#endif
if( !m_friendInfoRequestIssued )
{
ui.NavigateToScene(m_iPad, eUIScene_Timer);
@@ -316,19 +285,6 @@ wstring UIScene_JoinMenu::getMoviePath()
void UIScene_JoinMenu::handleInput(int iPad, int key, bool repeat, bool pressed, bool released, bool &handled)
{
#ifdef _WINDOWS64
if (m_asyncJoinInProgress && key == ACTION_MENU_CANCEL && pressed)
{
g_NetworkManager.CancelJoinGame(nullptr);
m_asyncJoinInProgress = false;
m_bIgnoreInput = false;
m_buttonJoinGame.setLabel(app.GetString(IDS_JOIN_GAME));
updateTooltips();
handled = true;
return;
}
#endif
if(m_bIgnoreInput) return;
ui.AnimateKeyPress(m_iPad, key, repeat, pressed, released);
@@ -625,23 +581,28 @@ void UIScene_JoinMenu::JoinGame(UIScene_JoinMenu* pClass)
ProfileManager.DisplaySystemMessage( SCE_MSG_DIALOG_SYSMSG_TYPE_TRC_PSN_CHAT_RESTRICTION, ProfileManager.GetPrimaryPad() );
}
#endif
#ifdef _WINDOWS64
if (g_NetworkManager.BeginJoinGameAsync(pClass->m_selectedSession, dwLocalUsersMask))
{
pClass->m_asyncJoinInProgress = true;
pClass->m_joinLocalUsersMask = dwLocalUsersMask;
pClass->m_buttonJoinGame.setLabel(app.GetString(IDS_PROGRESS_CONNECTING));
ui.SetTooltips(DEFAULT_XUI_MENU_USER, -1, IDS_TOOLTIPS_CANCEL_JOIN);
return;
}
CGameNetworkManager::eJoinGameResult result = CGameNetworkManager::JOINGAME_FAIL_GENERAL;
#else
CGameNetworkManager::eJoinGameResult result = g_NetworkManager.JoinGame( pClass->m_selectedSession, dwLocalUsersMask );
#endif
// 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 = IDS_PROGRESS_CONNECTING;
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;
@@ -70,8 +70,6 @@ private:
wstring m_editServerPort;
int m_editServerButtonIndex;
int m_deleteServerButtonIndex;
bool m_asyncJoinInProgress;
DWORD m_joinLocalUsersMask;
#endif
public: