diff --git a/electron/exit.html b/electron/exit.html
new file mode 100644
index 000000000..31b81c627
--- /dev/null
+++ b/electron/exit.html
@@ -0,0 +1,30 @@
+
+
+
+
+ Bitburner
+
+
+
+
+
+
Exiting ...
+
+
+
diff --git a/electron/main.js b/electron/main.js
index e7500b6af..c63406261 100644
--- a/electron/main.js
+++ b/electron/main.js
@@ -68,10 +68,17 @@ function createWindow(killall) {
// here. Hey if it works it works.
const achievements = greenworks.getAchievementNames();
const intervalID = setInterval(async () => {
- const achs = await win.webContents.executeJavaScript("document.achievements");
- for (const ach of achs) {
- if (!achievements.includes(ach)) continue;
- greenworks.activateAchievement(ach, () => undefined);
+ try {
+ const achs = await win.webContents.executeJavaScript("document.achievements");
+ for (const ach of achs) {
+ if (!achievements.includes(ach)) continue;
+ greenworks.activateAchievement(ach, () => undefined);
+ }
+ } catch (error) {
+ // The interval properly did not properly get cleared after a window kill
+ log.warn('Clearing achievements timer');
+ clearInterval(intervalID);
+ return;
}
}, 1000);
win.achievementsIntervalID = intervalID;
@@ -81,8 +88,13 @@ function createWindow(killall) {
setStopProcessHandler(app, win, false);
if (intervalID) clearInterval(intervalID);
win.webContents.forcefullyCrashRenderer();
+ win.on('closed', () => {
+ // Wait for window to be closed before opening the new one to prevent race conditions
+ log.debug('Opening new window');
+ const newWindow = createWindow(killScripts);
+ setStopProcessHandler(app, newWindow, true);
+ })
win.close();
- createWindow(killScripts);
};
const promptForReload = () => {
win.off('unresponsive', promptForReload);
@@ -170,10 +182,39 @@ function createWindow(killall) {
}
function setStopProcessHandler(app, window, enabled) {
- const clearWindowHandler = () => {
+ const closingWindowHandler = async (e) => {
+ // We need to prevent the default closing event to add custom logic
+ e.preventDefault();
+
+ // First we clear the achievement timer
if (window.achievementsIntervalID) {
clearInterval(window.achievementsIntervalID);
}
+
+ // We'll try to execute javascript on the page to see if we're stuck
+ let canRunJS = false;
+ win.webContents.executeJavaScript('window.stop(); document.close()', true)
+ .then(() => canRunJS = true);
+ setTimeout(() => {
+ // Wait a few milliseconds to prevent a race condition before loading the exit screen
+ win.webContents.stop();
+ win.loadFile("exit.html")
+ }, 20);
+
+ // Wait 200ms, if the promise has not yet resolved, let's crash the process since we're possibly in a stuck scenario
+ setTimeout(() => {
+ if (!canRunJS) {
+ // We're stuck, let's crash the process
+ log.warn('Forcefully crashing the renderer process');
+ win.webContents.forcefullyCrashRenderer();
+ }
+
+ log.debug('Destroying the window');
+ win.destroy();
+ }, 200);
+ }
+
+ const clearWindowHandler = () => {
window = null;
};
@@ -181,15 +222,18 @@ function setStopProcessHandler(app, window, enabled) {
log.info('Quitting the app...');
app.isQuiting = true;
app.quit();
- // eslint-disable-next-line no-process-exit
process.exit(0);
};
if (enabled) {
+ log.debug('Adding closing handlers');
window.on("closed", clearWindowHandler);
+ window.on("close", closingWindowHandler)
app.on("window-all-closed", stopProcessHandler);
} else {
+ log.debug('Removing closing handlers');
window.removeListener("closed", clearWindowHandler);
+ window.removeListener("close", closingWindowHandler);
app.removeListener("window-all-closed", stopProcessHandler);
}
}