mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-07-26 06:12:28 +00:00
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. In order to follow the convention that only functions directly closing the page are called `closePageCallback`, that function is in turn renamed to `closeSession`. Additionally, this allows only resolving the promise with a bool (`showSummary`) and then only calling `getNextPageOpenRequest` at the end of `init` for reasons of simplicity.
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
class Menu
|
||||
{
|
||||
constructor(pauseControl, playerViewControl, chat, closePageCallback)
|
||||
constructor(pauseControl, playerViewControl, chat, closeSession)
|
||||
{
|
||||
this.menuButton = Engine.GetGUIObjectByName("menuButton");
|
||||
this.menuButton.onPress = this.toggle.bind(this);
|
||||
@@ -26,7 +26,7 @@ class Menu
|
||||
this.buttons = handlerNames.map((handlerName, i) =>
|
||||
{
|
||||
const handler = new MenuButtons.prototype[handlerName](menuButtons[i], pauseControl, playerViewControl, chat);
|
||||
this.initButton(handler, menuButtons[i], i, closePageCallback);
|
||||
this.initButton(handler, menuButtons[i], i, closeSession);
|
||||
return handler;
|
||||
});
|
||||
|
||||
@@ -62,12 +62,12 @@ class Menu
|
||||
this.startAnimation();
|
||||
}
|
||||
|
||||
initButton(handler, button, i, closePageCallback)
|
||||
initButton(handler, button, i, closeSession)
|
||||
{
|
||||
button.onPress = () =>
|
||||
{
|
||||
this.close();
|
||||
handler.onPress(closePageCallback);
|
||||
handler.onPress(closeSession);
|
||||
};
|
||||
button.size.top = this.buttonHeight * (i + 1) + this.margin;
|
||||
button.size.bottom = this.buttonHeight * (i + 2);
|
||||
|
||||
@@ -283,13 +283,13 @@ MenuButtons.prototype.Exit = class
|
||||
this.pauseControl = pauseControl;
|
||||
}
|
||||
|
||||
onPress(closePageCallback)
|
||||
onPress(closeSession)
|
||||
{
|
||||
for (const name in QuitConfirmationMenu.prototype)
|
||||
{
|
||||
const quitConfirmation = new QuitConfirmationMenu.prototype[name]();
|
||||
if (quitConfirmation.enabled())
|
||||
quitConfirmation.display(closePageCallback);
|
||||
quitConfirmation.display(closeSession);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -4,15 +4,12 @@
|
||||
*/
|
||||
class NetworkStatusOverlay
|
||||
{
|
||||
constructor(closePageCallback)
|
||||
constructor(closeSession)
|
||||
{
|
||||
this.netStatus = Engine.GetGUIObjectByName("netStatus");
|
||||
this.loadingClientsText = Engine.GetGUIObjectByName("loadingClientsText");
|
||||
|
||||
Engine.GetGUIObjectByName("disconnectedExitButton").onPress = () =>
|
||||
{
|
||||
closePageCallback({ [Engine.openRequest]: endGame(true) });
|
||||
};
|
||||
Engine.GetGUIObjectByName("disconnectedExitButton").onPress = () => { closeSession(true); };
|
||||
|
||||
registerNetworkStatusChangeHandler(this.onNetStatusMessage.bind(this));
|
||||
registerClientsLoadingHandler(this.onClientsLoadingMessage.bind(this));
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
class SessionMessageBox
|
||||
{
|
||||
async display(closePageCallback)
|
||||
async display(closeSession)
|
||||
{
|
||||
closeOpenDialogs();
|
||||
g_PauseControl.implicitPause();
|
||||
@@ -19,12 +19,9 @@ class SessionMessageBox
|
||||
"buttonCaptions": this.Buttons ? this.Buttons.map(button => button.caption) : undefined,
|
||||
});
|
||||
|
||||
this.closeSession = closeSession;
|
||||
if (this.Buttons && this.Buttons[buttonId].onPress)
|
||||
{
|
||||
const ret = this.Buttons[buttonId].onPress.call(this);
|
||||
if (ret !== undefined)
|
||||
closePageCallback({ [Engine.openRequest]: ret });
|
||||
}
|
||||
this.Buttons[buttonId].onPress.call(this);
|
||||
|
||||
if (this.ResumeOnClose)
|
||||
resumeGame();
|
||||
|
||||
@@ -18,12 +18,12 @@ QuitConfirmation.prototype.Buttons =
|
||||
{
|
||||
// Translation: Shown in the Dialog that shows up when the game finishes
|
||||
"caption": translate("Quit and View Summary"),
|
||||
"onPress": () => endGame(true)
|
||||
"onPress": function() { this.closeSession(true); }
|
||||
},
|
||||
{
|
||||
// Translation: Shown in the Dialog that shows up when the game finishes
|
||||
"caption": translate("Quit"),
|
||||
"onPress": () => endGame(false)
|
||||
"onPress": function() { this.closeSession(false); }
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
class QuitConfirmationDefeat extends QuitConfirmation
|
||||
{
|
||||
constructor(closePageCallback)
|
||||
constructor(closeSession)
|
||||
{
|
||||
super();
|
||||
|
||||
@@ -11,10 +11,10 @@ class QuitConfirmationDefeat extends QuitConfirmation
|
||||
return;
|
||||
|
||||
this.confirmHandler = undefined;
|
||||
registerPlayersFinishedHandler(this.onPlayersFinished.bind(this, closePageCallback));
|
||||
registerPlayersFinishedHandler(this.onPlayersFinished.bind(this, closeSession));
|
||||
}
|
||||
|
||||
onPlayersFinished(closePageCallback, players, won)
|
||||
onPlayersFinished(closeSession, players, won)
|
||||
{
|
||||
if (players.indexOf(Engine.GetPlayerID()) == -1)
|
||||
return;
|
||||
@@ -22,11 +22,11 @@ class QuitConfirmationDefeat extends QuitConfirmation
|
||||
// Defer simulation result until
|
||||
// 1. the loading screen finished for all networked clients (g_IsNetworkedActive)
|
||||
// 2. all messages modifying g_Players victory state were processed (next turn)
|
||||
this.confirmHandler = this.confirmExit.bind(this, won, closePageCallback);
|
||||
this.confirmHandler = this.confirmExit.bind(this, won, closeSession);
|
||||
registerSimulationUpdateHandler(this.confirmHandler);
|
||||
}
|
||||
|
||||
confirmExit(won, closePageCallback)
|
||||
confirmExit(won, closeSession)
|
||||
{
|
||||
if (g_IsNetworked && !g_IsNetworkedActive)
|
||||
return;
|
||||
@@ -47,7 +47,7 @@ class QuitConfirmationDefeat extends QuitConfirmation
|
||||
|
||||
this.Buttons = askExit ? super.Buttons : undefined;
|
||||
|
||||
this.display(closePageCallback);
|
||||
this.display(closeSession);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ ReturnQuestion.prototype.Caption = translate("Do you want to resign or will you
|
||||
ReturnQuestion.prototype.Buttons = [
|
||||
{
|
||||
"caption": translate("I will return"),
|
||||
"onPress": () => endGame(false)
|
||||
"onPress": function() { this.closeSession(false); }
|
||||
},
|
||||
{
|
||||
"caption": translate("I resign"),
|
||||
@@ -74,9 +74,9 @@ QuitConfirmationMenu.prototype.MultiplayerClient.prototype.Buttons =
|
||||
},
|
||||
{
|
||||
"caption": translate("Yes"),
|
||||
"onPress": closePageCallback =>
|
||||
"onPress": closeSession =>
|
||||
{
|
||||
(new ReturnQuestion()).display(closePageCallback);
|
||||
(new ReturnQuestion()).display(closeSession);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
*/
|
||||
class QuitConfirmationReplay extends QuitConfirmation
|
||||
{
|
||||
constructor(closePageCallback)
|
||||
constructor(closeSession)
|
||||
{
|
||||
super();
|
||||
Engine.GetGUIObjectByName("session").onReplayFinished = this.display.bind(this, closePageCallback);
|
||||
Engine.GetGUIObjectByName("session").onReplayFinished = this.display.bind(this, closeSession);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,11 +26,11 @@ QuitConfirmationReplay.prototype.Buttons =
|
||||
{
|
||||
// Translation: Shown in the Dialog that shows up when a replay finishes
|
||||
"caption": translate("Quit and View Summary"),
|
||||
"onPress": () => endGame(true)
|
||||
"onPress": function() { this.closeSession(true); }
|
||||
},
|
||||
{
|
||||
// Translation: Shown in the Dialog that shows up when a replay finishes
|
||||
"caption": translate("Quit"),
|
||||
"onPress": () => endGame(false)
|
||||
"onPress": function() { this.closeSession(false); }
|
||||
}
|
||||
];
|
||||
|
||||
@@ -338,13 +338,13 @@ async function init(initData, hotloadData)
|
||||
{
|
||||
if (g_IsNetworked)
|
||||
handleNetMessages().catch(reject);
|
||||
}), new Promise(closePageCallback =>
|
||||
}), new Promise(closeSession =>
|
||||
{
|
||||
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);
|
||||
g_TutorialManager = new TutorialManager(closePageCallback, hotloadData?.tutorial);
|
||||
g_Menu = new Menu(g_PauseControl, g_PlayerViewControl, g_Chat, closeSession);
|
||||
g_NetworkStatusOverlay = new NetworkStatusOverlay(closeSession);
|
||||
g_QuitConfirmationDefeat = new QuitConfirmationDefeat(closeSession);
|
||||
g_QuitConfirmationReplay = new QuitConfirmationReplay(closeSession);
|
||||
g_TutorialManager = new TutorialManager(closeSession, hotloadData?.tutorial);
|
||||
})]);
|
||||
|
||||
// TODO: use event instead
|
||||
@@ -370,7 +370,19 @@ async function init(initData, hotloadData)
|
||||
|
||||
setTimeout(displayGamestateNotifications, 1000);
|
||||
|
||||
return promise;
|
||||
const showSummary = await promise;
|
||||
|
||||
// Depends on the simulation, so it has to be called before EndGame.
|
||||
const openRequest = getNextPageOpenRequest(showSummary);
|
||||
|
||||
Engine.EndGame();
|
||||
// After the replay file was closed in EndGame
|
||||
if (!g_IsReplay)
|
||||
Engine.AddReplayToCache(Engine.GetCurrentReplayDirectory());
|
||||
if (g_IsController && Engine.HasXmppClient())
|
||||
Engine.SendUnregisterGame();
|
||||
|
||||
return { [Engine.openRequest]: openRequest };
|
||||
}
|
||||
|
||||
function registerPlayersInitHandler(handler)
|
||||
@@ -530,31 +542,20 @@ function closeOpenDialogs()
|
||||
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 = {
|
||||
"sim": simData,
|
||||
"sim": Engine.GuiInterfaceCall("GetReplayMetadata"),
|
||||
"gui": {
|
||||
"dialog": false,
|
||||
"assignedPlayer": playerID,
|
||||
"assignedPlayer": Engine.GetPlayerID(),
|
||||
"disconnected": g_Disconnected,
|
||||
"isReplay": g_IsReplay,
|
||||
"replayDirectory": !g_HasRejoined && replayDirectory,
|
||||
"replayDirectory": !g_HasRejoined && Engine.GetCurrentReplayDirectory(),
|
||||
"replaySelectionData": g_ReplaySelectionData
|
||||
}
|
||||
};
|
||||
@@ -563,9 +564,8 @@ function endGame(showSummary)
|
||||
{
|
||||
const menu = g_CampaignSession.getMenu();
|
||||
if (g_InitAttributes.campaignData.skipSummary)
|
||||
{
|
||||
return { "page": menu };
|
||||
}
|
||||
|
||||
summaryData.campaignData = { "filename": g_InitAttributes.campaignData.run };
|
||||
summaryData.nextPage = menu;
|
||||
}
|
||||
|
||||
@@ -25,11 +25,11 @@ class TutorialManager
|
||||
activePanel;
|
||||
currentWarning = "";
|
||||
isFinished = false;
|
||||
closePageCallback;
|
||||
closeSession;
|
||||
|
||||
constructor(closePageCallback, hotloadData)
|
||||
constructor(closeSession, hotloadData)
|
||||
{
|
||||
this.closePageCallback = closePageCallback;
|
||||
this.closeSession = closeSession;
|
||||
|
||||
this.parentObj.hidden = true;
|
||||
|
||||
@@ -140,7 +140,7 @@ class TutorialManager
|
||||
continue()
|
||||
{
|
||||
if (this.isFinished)
|
||||
this.closePageCallback({ [Engine.openRequest]: endGame(true) });
|
||||
this.closeSession(true);
|
||||
else if (this.pendingSteps.length)
|
||||
this.displayNextPendingStep();
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user