From a95579a0465886e040567cf0966d31af13a06839 Mon Sep 17 00:00:00 2001 From: phosit Date: Wed, 29 Apr 2026 21:44:55 +0200 Subject: [PATCH] Fix UI when connecting to a server fails When the connection fails, it wasn't possible to close the progress window. Now `PollNetworkClient` also resolves the previous promise. --- .../gui/gamesetup/NetMessages/NetMessages.js | 2 ++ .../mods/public/gui/gamesetup/gamesetup.js | 6 +++- .../public/gui/gamesetup_mp/gamesetup_mp.js | 12 ++++--- .../data/mods/public/gui/session/messages.js | 2 ++ .../data/mods/public/gui/session/session.js | 31 ++++++++++--------- source/gui/CGUI.cpp | 2 +- source/network/NetClient.cpp | 16 ++++++++-- source/network/NetClient.h | 2 +- 8 files changed, 49 insertions(+), 24 deletions(-) diff --git a/binaries/data/mods/public/gui/gamesetup/NetMessages/NetMessages.js b/binaries/data/mods/public/gui/gamesetup/NetMessages/NetMessages.js index 9d276c2331..e8fe944edb 100644 --- a/binaries/data/mods/public/gui/gamesetup/NetMessages/NetMessages.js +++ b/binaries/data/mods/public/gui/gamesetup/NetMessages/NetMessages.js @@ -32,6 +32,8 @@ class NetMessages while (true) { const message = await Engine.PollNetworkClient(); + if (!message) + return; log("Net message: " + uneval(message)); diff --git a/binaries/data/mods/public/gui/gamesetup/gamesetup.js b/binaries/data/mods/public/gui/gamesetup/gamesetup.js index af1f86e869..d3a2120dff 100644 --- a/binaries/data/mods/public/gui/gamesetup/gamesetup.js +++ b/binaries/data/mods/public/gui/gamesetup/gamesetup.js @@ -47,7 +47,11 @@ function init(initData, hotloadData) return Promise.race([new Promise(closePageCallback => { g_SetupWindow = new SetupWindow(initData, hotloadData, closePageCallback); - }), ...(g_IsNetworked ? [g_SetupWindow.controls.netMessages.pollPendingMessages()] : [])]); + }), new Promise((_, reject) => + { + if (g_IsNetworked) + g_SetupWindow.controls.netMessages.pollPendingMessages().catch(reject); + })]); } function getHotloadData() diff --git a/binaries/data/mods/public/gui/gamesetup_mp/gamesetup_mp.js b/binaries/data/mods/public/gui/gamesetup_mp/gamesetup_mp.js index 1e5c23a3bc..8e0fe428b4 100644 --- a/binaries/data/mods/public/gui/gamesetup_mp/gamesetup_mp.js +++ b/binaries/data/mods/public/gui/gamesetup_mp/gamesetup_mp.js @@ -72,7 +72,9 @@ async function waitOnEvent(loadSavedGame, joinFromLobby) })); if (tickResult === cancelTag) break; - const result = await onTick(loadSavedGame); + const result = await cancelOr(onTick(loadSavedGame)); + if (result === cancelTag) + break; if (typeof result === "object") return result; if (result) @@ -240,7 +242,7 @@ function getConnectionFailReason(reason) function reportConnectionFail(reason) { - messageBox( + return messageBox( 400, 200, (translate("Failed to connect to the server.") ) + "\n\n" + getConnectionFailReason(reason), @@ -253,6 +255,8 @@ async function pollAndHandleNetworkClient(loadSavedGame) while (true) { const message = await Engine.PollNetworkClient(); + if (!message) + return true; if (!g_IsConnecting) continue; @@ -268,7 +272,7 @@ async function pollAndHandleNetworkClient(loadSavedGame) switch (message.status) { case "failed": - reportConnectionFail(message.reason, false); + await reportConnectionFail(message.reason, false); return true; default: @@ -326,7 +330,7 @@ async function pollAndHandleNetworkClient(loadSavedGame) switch (message.status) { case "failed": - reportConnectionFail(message.reason, false); + await reportConnectionFail(message.reason, false); return true; default: diff --git a/binaries/data/mods/public/gui/session/messages.js b/binaries/data/mods/public/gui/session/messages.js index 7e23066702..77f2c28615 100644 --- a/binaries/data/mods/public/gui/session/messages.js +++ b/binaries/data/mods/public/gui/session/messages.js @@ -425,6 +425,8 @@ async function handleNetMessages() while (true) { const msg = await Engine.PollNetworkClient(); + if (!msg) + return; log("Net message: " + uneval(msg)); diff --git a/binaries/data/mods/public/gui/session/session.js b/binaries/data/mods/public/gui/session/session.js index 81c9c53f85..70c69f6ef2 100644 --- a/binaries/data/mods/public/gui/session/session.js +++ b/binaries/data/mods/public/gui/session/session.js @@ -331,20 +331,23 @@ async function init(initData, hotloadData) resumeGame(); }); - const promise = Promise.race([g_IsNetworked ? handleNetMessages() : new Promise(() => {}), - new Promise(closePageCallback => - { - g_PlayerViewControl.registerViewedPlayerChangeHandler(resetTemplates.bind(undefined, - closePageCallback)); - g_Menu = new Menu(g_PauseControl, g_PlayerViewControl, g_Chat, closePageCallback); - g_NetworkStatusOverlay = new NetworkStatusOverlay(closePageCallback); - g_QuitConfirmationDefeat = new QuitConfirmationDefeat(closePageCallback); - g_QuitConfirmationReplay = new QuitConfirmationReplay(closePageCallback); - // TODO: use event instead - onSimulationUpdate(closePageCallback); - Engine.GetGUIObjectByName("session").onSimulationUpdate = - onSimulationUpdate.bind(undefined, closePageCallback); - })]); + const promise = Promise.race([new Promise((_, reject) => + { + if (g_IsNetworked) + handleNetMessages().catch(reject); + }), new Promise(closePageCallback => + { + g_PlayerViewControl.registerViewedPlayerChangeHandler(resetTemplates.bind(undefined, + closePageCallback)); + g_Menu = new Menu(g_PauseControl, g_PlayerViewControl, g_Chat, closePageCallback); + g_NetworkStatusOverlay = new NetworkStatusOverlay(closePageCallback); + g_QuitConfirmationDefeat = new QuitConfirmationDefeat(closePageCallback); + g_QuitConfirmationReplay = new QuitConfirmationReplay(closePageCallback); + // TODO: use event instead + onSimulationUpdate(closePageCallback); + Engine.GetGUIObjectByName("session").onSimulationUpdate = + onSimulationUpdate.bind(undefined, closePageCallback); + })]); for (const handler of g_PlayersInitHandlers) handler(); diff --git a/source/gui/CGUI.cpp b/source/gui/CGUI.cpp index 083434ffea..001a9750df 100644 --- a/source/gui/CGUI.cpp +++ b/source/gui/CGUI.cpp @@ -108,7 +108,7 @@ CGUI::CGUI(ScriptContext& context) CGUI::~CGUI() { if (g_NetClient) - g_NetClient->Unregister(*m_ScriptInterface); + g_NetClient->Unregister(m_ScriptInterface.get()); } InReaction CGUI::HandleEvent(const SDL_Event_* ev) diff --git a/source/network/NetClient.cpp b/source/network/NetClient.cpp index 0c2b52cdbb..0d98cf6700 100644 --- a/source/network/NetClient.cpp +++ b/source/network/NetClient.cpp @@ -159,6 +159,8 @@ CNetClient::CNetClient(CGame* game, const CStrW& username, const CStr& hostJID, CNetClient::~CNetClient() { + Unregister(nullptr); + // Try to flush messages before dying (probably fails). if (m_ClientTurnManager) m_ClientTurnManager->OnDestroyConnection(); @@ -364,6 +366,7 @@ void CNetClient::CheckServerConnection() JSObject* CNetClient::GetNextGUIMessage(const ScriptInterface& guiInterface) { + Unregister(nullptr); const ScriptRequest rq{guiInterface}; m_GuiMessagePoll.emplace(GuiPollData{guiInterface, {rq.cx, JS::NewPromiseObject(rq.cx, nullptr)}}); @@ -371,10 +374,17 @@ JSObject* CNetClient::GetNextGUIMessage(const ScriptInterface& guiInterface) return m_GuiMessagePoll.value().promise; } -void CNetClient::Unregister(const ScriptInterface& guiInterface) +void CNetClient::Unregister(const ScriptInterface* guiInterface) { - if (m_GuiMessagePoll.has_value() && &m_GuiMessagePoll.value().interface == &guiInterface) - m_GuiMessagePoll.reset(); + if (!m_GuiMessagePoll.has_value() || + (guiInterface && &m_GuiMessagePoll.value().interface != guiInterface)) + { + return; + } + auto& [interface, promise] = m_GuiMessagePoll.value(); + const ScriptRequest oldRq{interface}; + JS::ResolvePromise(oldRq.cx, promise, JS::UndefinedHandleValue); + m_GuiMessagePoll.reset(); } void CNetClient::FetchMessage() diff --git a/source/network/NetClient.h b/source/network/NetClient.h index 0e7b61d36e..730db8b61c 100644 --- a/source/network/NetClient.h +++ b/source/network/NetClient.h @@ -147,7 +147,7 @@ public: * Has to be called bevore the @c ScriptInterface gets destroied so that * no future messages are sent to it. */ - void Unregister(const ScriptInterface& guiInterface); + void Unregister(const ScriptInterface* guiInterface); /** * Add a message to the queue, to be read by GuiPoll.