Only call Engine.EndGame right before closing

Since #7786, the GUI tick completed normally after calling
closePageCallback instead of being cancelled like previously. Since
Engine.EndGame deletes the internal g_Game pointer, every Engine call
after it that tried to access g_Game caused a segfault.
This patch solves it by moving Engine.EndGame to the very end, right
before closing the session page.
This commit is contained in:
Vantha 2026-06-11 21:06:33 +02:00
parent 705901daae
commit d0a03df2a6
6 changed files with 27 additions and 29 deletions

View file

@ -11,7 +11,7 @@ class NetworkStatusOverlay
Engine.GetGUIObjectByName("disconnectedExitButton").onPress = () => Engine.GetGUIObjectByName("disconnectedExitButton").onPress = () =>
{ {
closePageCallback({ [Engine.openRequest]: endGame(true) }); closePageCallback({ [Engine.openRequest]: getNextPageOpenRequest(true) });
}; };
registerNetworkStatusChangeHandler(this.onNetStatusMessage.bind(this)); registerNetworkStatusChangeHandler(this.onNetStatusMessage.bind(this));

View file

@ -18,12 +18,12 @@ QuitConfirmation.prototype.Buttons =
{ {
// Translation: Shown in the Dialog that shows up when the game finishes // Translation: Shown in the Dialog that shows up when the game finishes
"caption": translate("Quit and View Summary"), "caption": translate("Quit and View Summary"),
"onPress": () => endGame(true) "onPress": () => getNextPageOpenRequest(true)
}, },
{ {
// Translation: Shown in the Dialog that shows up when the game finishes // Translation: Shown in the Dialog that shows up when the game finishes
"caption": translate("Quit"), "caption": translate("Quit"),
"onPress": () => endGame(false) "onPress": () => getNextPageOpenRequest(false)
} }
]; ];

View file

@ -6,7 +6,7 @@ ReturnQuestion.prototype.Caption = translate("Do you want to resign or will you
ReturnQuestion.prototype.Buttons = [ ReturnQuestion.prototype.Buttons = [
{ {
"caption": translate("I will return"), "caption": translate("I will return"),
"onPress": () => endGame(false) "onPress": () => getNextPageOpenRequest(false)
}, },
{ {
"caption": translate("I resign"), "caption": translate("I resign"),

View file

@ -26,11 +26,11 @@ QuitConfirmationReplay.prototype.Buttons =
{ {
// Translation: Shown in the Dialog that shows up when a replay finishes // Translation: Shown in the Dialog that shows up when a replay finishes
"caption": translate("Quit and View Summary"), "caption": translate("Quit and View Summary"),
"onPress": () => endGame(true) "onPress": () => getNextPageOpenRequest(true)
}, },
{ {
// Translation: Shown in the Dialog that shows up when a replay finishes // Translation: Shown in the Dialog that shows up when a replay finishes
"caption": translate("Quit"), "caption": translate("Quit"),
"onPress": () => endGame(false) "onPress": () => getNextPageOpenRequest(false)
} }
]; ];

View file

@ -368,7 +368,17 @@ async function init(initData, hotloadData)
setTimeout(displayGamestateNotifications, 1000); setTimeout(displayGamestateNotifications, 1000);
return promise; const closeResult = await promise;
Engine.EndGame();
// After the replay file was closed in EndGame
// Done here to keep EndGame small
if (!g_IsReplay)
Engine.AddReplayToCache(Engine.GetCurrentReplayDirectory());
if (g_IsController && Engine.HasXmppClient())
Engine.SendUnregisterGame();
return closeResult;
} }
function registerPlayersInitHandler(handler) function registerPlayersInitHandler(handler)
@ -528,31 +538,20 @@ function closeOpenDialogs()
g_TradeDialog.close(); g_TradeDialog.close();
} }
function endGame(showSummary) /**
* Put together an open request for the next GUI page to show after closing the session.
* This could be, depending on the situation, the the summary screen, the main menu, the lobby, etc.
*/
function getNextPageOpenRequest(showSummary)
{ {
// Before ending the game
const replayDirectory = Engine.GetCurrentReplayDirectory();
const simData = Engine.GuiInterfaceCall("GetReplayMetadata");
const playerID = Engine.GetPlayerID();
Engine.EndGame();
// After the replay file was closed in EndGame
// Done here to keep EndGame small
if (!g_IsReplay)
Engine.AddReplayToCache(replayDirectory);
if (g_IsController && Engine.HasXmppClient())
Engine.SendUnregisterGame();
const summaryData = { const summaryData = {
"sim": simData, "sim": Engine.GuiInterfaceCall("GetReplayMetadata"),
"gui": { "gui": {
"dialog": false, "dialog": false,
"assignedPlayer": playerID, "assignedPlayer": Engine.GetPlayerID(),
"disconnected": g_Disconnected, "disconnected": g_Disconnected,
"isReplay": g_IsReplay, "isReplay": g_IsReplay,
"replayDirectory": !g_HasRejoined && replayDirectory, "replayDirectory": !g_HasRejoined && Engine.GetCurrentReplayDirectory(),
"replaySelectionData": g_ReplaySelectionData "replaySelectionData": g_ReplaySelectionData
} }
}; };
@ -561,9 +560,8 @@ function endGame(showSummary)
{ {
const menu = g_CampaignSession.getMenu(); const menu = g_CampaignSession.getMenu();
if (g_InitAttributes.campaignData.skipSummary) if (g_InitAttributes.campaignData.skipSummary)
{
return { "page": menu }; return { "page": menu };
}
summaryData.campaignData = { "filename": g_InitAttributes.campaignData.run }; summaryData.campaignData = { "filename": g_InitAttributes.campaignData.run };
summaryData.nextPage = menu; summaryData.nextPage = menu;
} }

View file

@ -140,7 +140,7 @@ class TutorialManager
continue() continue()
{ {
if (this.isFinished) if (this.isFinished)
this.closePageCallback({ [Engine.openRequest]: endGame(true) }); this.closePageCallback({ [Engine.openRequest]: getNextPageOpenRequest(true) });
else if (this.pendingSteps.length) else if (this.pendingSteps.length)
this.displayNextPendingStep(); this.displayNextPendingStep();
else else