mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-17 23:08:36 +02:00
ELECTRON: Use steamworks.js to integrate with Steamworks SDK (#1563)
This commit is contained in:
@@ -169,7 +169,7 @@ Saving a file will reload the game automatically.
|
|||||||
|
|
||||||
### How to build the electron app
|
### How to build the electron app
|
||||||
|
|
||||||
Tested on Node v16.13.1 (LTS) on Windows.
|
Tested on Node v20.11.1 (LTS) on Windows.
|
||||||
These steps only work in a Bash-like environment, like MinGW for Windows.
|
These steps only work in a Bash-like environment, like MinGW for Windows.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -180,10 +180,6 @@ $ npm run build:dev
|
|||||||
# Use electron-packager to build the app to the .build/ folder.
|
# Use electron-packager to build the app to the .build/ folder.
|
||||||
$ npm run electron
|
$ npm run electron
|
||||||
|
|
||||||
# When launching the .exe directly, you'll need the steam_appid.txt file in the root.
|
|
||||||
# If not using Windows, change this line accordingly.
|
|
||||||
$ cp .build/bitburner-win32-x64/resources/app/steam_appid.txt .build/bitburner-win32-x64/steam_appid.txt
|
|
||||||
|
|
||||||
# And run the game...
|
# And run the game...
|
||||||
$ .build/bitburner-win32-x64/bitburner.exe
|
$ .build/bitburner-win32-x64/bitburner.exe
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,26 +1,39 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
const greenworks = require("./greenworks");
|
const { steamworksClient } = require("./steamworksUtils");
|
||||||
const log = require("electron-log");
|
const log = require("electron-log");
|
||||||
|
|
||||||
async function enableAchievementsInterval(window) {
|
function enableAchievementsInterval(window) {
|
||||||
// If the Steam API could not be initialized on game start, we'll abort this.
|
// If the Steam API could not be initialized on game start, we'll abort this.
|
||||||
if (global.greenworksError) return;
|
if (!steamworksClient) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// This is backward but the game fills in an array called `document.achievements` and we retrieve it from
|
// This is backward but the game fills in an array called `document.achievements` and we retrieve it from
|
||||||
// here. Hey if it works it works.
|
// here. Hey if it works it works.
|
||||||
const steamAchievements = greenworks.getAchievementNames();
|
const allSteamAchievements = steamworksClient.achievement.names();
|
||||||
log.silly(`All Steam achievements ${JSON.stringify(steamAchievements)}`);
|
log.silly(`All Steam achievements ${JSON.stringify(allSteamAchievements)}`);
|
||||||
const playerAchieved = (await Promise.all(steamAchievements.map(checkSteamAchievement))).filter((name) => !!name);
|
const steamAchievements = allSteamAchievements.filter((achievement) =>
|
||||||
log.debug(`Player has Steam achievements ${JSON.stringify(playerAchieved)}`);
|
steamworksClient.achievement.isActivated(achievement),
|
||||||
|
);
|
||||||
|
log.debug(`Player has Steam achievements ${JSON.stringify(steamAchievements)}`);
|
||||||
const intervalID = setInterval(async () => {
|
const intervalID = setInterval(async () => {
|
||||||
try {
|
try {
|
||||||
const playerAchievements = await window.webContents.executeJavaScript("document.achievements");
|
const playerAchievements = await window.webContents.executeJavaScript("document.achievements");
|
||||||
for (const ach of playerAchievements) {
|
for (const achievement of playerAchievements) {
|
||||||
if (!steamAchievements.includes(ach)) continue; // Don't try activating achievements that don't exist Steam-side
|
// Don't try activating achievements that don't exist Steam-side
|
||||||
if (playerAchieved.includes(ach)) continue; // Don't spam achievements that have already been recorded
|
if (!allSteamAchievements.includes(achievement)) {
|
||||||
log.info(`Granting Steam achievement ${ach}`);
|
continue;
|
||||||
greenworks.activateAchievement(ach, () => undefined);
|
}
|
||||||
playerAchieved.push(ach);
|
// Don't spam achievements that have already been recorded
|
||||||
|
if (steamAchievements.includes(achievement)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
log.info(`Granting Steam achievement ${achievement}`);
|
||||||
|
if (steamworksClient.achievement.activate(achievement)) {
|
||||||
|
steamAchievements.push(achievement);
|
||||||
|
} else {
|
||||||
|
log.warn(`Cannot grant Steam achievement ${achievement}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(error);
|
log.error(error);
|
||||||
@@ -28,25 +41,11 @@ async function enableAchievementsInterval(window) {
|
|||||||
// The interval probably did not get cleared after a window kill
|
// The interval probably did not get cleared after a window kill
|
||||||
log.warn("Clearing achievements timer");
|
log.warn("Clearing achievements timer");
|
||||||
clearInterval(intervalID);
|
clearInterval(intervalID);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
window.achievementsIntervalID = intervalID;
|
window.achievementsIntervalID = intervalID;
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkSteamAchievement(name) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
greenworks.getAchievement(
|
|
||||||
name,
|
|
||||||
(playerHas) => resolve(playerHas ? name : ""),
|
|
||||||
(err) => {
|
|
||||||
log.warn(`Failed to get Steam achievement ${name} status: ${err}`);
|
|
||||||
resolve("");
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function disableAchievementsInterval(window) {
|
function disableAchievementsInterval(window) {
|
||||||
if (window.achievementsIntervalID) {
|
if (window.achievementsIntervalID) {
|
||||||
clearInterval(window.achievementsIntervalID);
|
clearInterval(window.achievementsIntervalID);
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ async function createWindow(killall) {
|
|||||||
utils.attachUnresponsiveAppHandler(window);
|
utils.attachUnresponsiveAppHandler(window);
|
||||||
|
|
||||||
menu.refreshMenu(window);
|
menu.refreshMenu(window);
|
||||||
setStopProcessHandler(app, window);
|
setStopProcessHandler(window);
|
||||||
|
|
||||||
return window;
|
return window;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,298 +0,0 @@
|
|||||||
// Copyright (c) 2015 Greenheart Games Pty. Ltd. All rights reserved.
|
|
||||||
// Use of this source code is governed by the MIT license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
// The source code can be found in https://github.com/greenheartgames/greenworks
|
|
||||||
var fs = require("fs");
|
|
||||||
|
|
||||||
var greenworks;
|
|
||||||
|
|
||||||
if (process.arch === "x64") {
|
|
||||||
if (process.platform === "darwin") greenworks = require("./lib/greenworks-osx64");
|
|
||||||
else if (process.platform === "win32") greenworks = require("./lib/greenworks-win64");
|
|
||||||
else if (process.platform === "linux") greenworks = require("./lib/greenworks-linux64");
|
|
||||||
}
|
|
||||||
|
|
||||||
function error_process(err, error_callback) {
|
|
||||||
if (err && error_callback) error_callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (greenworks) {
|
|
||||||
greenworks.ugcGetItems = function (options, ugc_matching_type, ugc_query_type, success_callback, error_callback) {
|
|
||||||
if (typeof options !== "object") {
|
|
||||||
error_callback = success_callback;
|
|
||||||
success_callback = ugc_query_type;
|
|
||||||
ugc_query_type = ugc_matching_type;
|
|
||||||
ugc_matching_type = options;
|
|
||||||
options = {
|
|
||||||
app_id: greenworks.getAppId(),
|
|
||||||
page_num: 1,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
greenworks._ugcGetItems(options, ugc_matching_type, ugc_query_type, success_callback, error_callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
greenworks.ugcGetUserItems = function (
|
|
||||||
options,
|
|
||||||
ugc_matching_type,
|
|
||||||
ugc_list_sort_order,
|
|
||||||
ugc_list,
|
|
||||||
success_callback,
|
|
||||||
error_callback,
|
|
||||||
) {
|
|
||||||
if (typeof options !== "object") {
|
|
||||||
error_callback = success_callback;
|
|
||||||
success_callback = ugc_list;
|
|
||||||
ugc_list = ugc_list_sort_order;
|
|
||||||
ugc_list_sort_order = ugc_matching_type;
|
|
||||||
ugc_matching_type = options;
|
|
||||||
options = {
|
|
||||||
app_id: greenworks.getAppId(),
|
|
||||||
page_num: 1,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
greenworks._ugcGetUserItems(
|
|
||||||
options,
|
|
||||||
ugc_matching_type,
|
|
||||||
ugc_list_sort_order,
|
|
||||||
ugc_list,
|
|
||||||
success_callback,
|
|
||||||
error_callback,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
greenworks.ugcSynchronizeItems = function (options, sync_dir, success_callback, error_callback) {
|
|
||||||
if (typeof options !== "object") {
|
|
||||||
error_callback = success_callback;
|
|
||||||
success_callback = sync_dir;
|
|
||||||
sync_dir = options;
|
|
||||||
options = {
|
|
||||||
app_id: greenworks.getAppId(),
|
|
||||||
page_num: 1,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
greenworks._ugcSynchronizeItems(options, sync_dir, success_callback, error_callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
greenworks.publishWorkshopFile = function (
|
|
||||||
options,
|
|
||||||
file_path,
|
|
||||||
image_path,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
success_callback,
|
|
||||||
error_callback,
|
|
||||||
) {
|
|
||||||
if (typeof options !== "object") {
|
|
||||||
error_callback = success_callback;
|
|
||||||
success_callback = description;
|
|
||||||
description = title;
|
|
||||||
title = image_path;
|
|
||||||
image_path = file_path;
|
|
||||||
file_path = options;
|
|
||||||
options = {
|
|
||||||
app_id: greenworks.getAppId(),
|
|
||||||
tags: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
greenworks._publishWorkshopFile(
|
|
||||||
options,
|
|
||||||
file_path,
|
|
||||||
image_path,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
success_callback,
|
|
||||||
error_callback,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
greenworks.updatePublishedWorkshopFile = function (
|
|
||||||
options,
|
|
||||||
published_file_handle,
|
|
||||||
file_path,
|
|
||||||
image_path,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
success_callback,
|
|
||||||
error_callback,
|
|
||||||
) {
|
|
||||||
if (typeof options !== "object") {
|
|
||||||
error_callback = success_callback;
|
|
||||||
success_callback = description;
|
|
||||||
description = title;
|
|
||||||
title = image_path;
|
|
||||||
image_path = file_path;
|
|
||||||
file_path = published_file_handle;
|
|
||||||
published_file_handle = options;
|
|
||||||
options = {
|
|
||||||
tags: [], // No tags are set
|
|
||||||
};
|
|
||||||
}
|
|
||||||
greenworks._updatePublishedWorkshopFile(
|
|
||||||
options,
|
|
||||||
published_file_handle,
|
|
||||||
file_path,
|
|
||||||
image_path,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
success_callback,
|
|
||||||
error_callback,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// An utility function for publish related APIs.
|
|
||||||
// It processes remains steps after saving files to Steam Cloud.
|
|
||||||
function file_share_process(file_name, image_name, next_process_func, error_callback, progress_callback) {
|
|
||||||
if (progress_callback) progress_callback("Completed on saving files on Steam Cloud.");
|
|
||||||
greenworks.fileShare(
|
|
||||||
file_name,
|
|
||||||
function () {
|
|
||||||
greenworks.fileShare(
|
|
||||||
image_name,
|
|
||||||
function () {
|
|
||||||
next_process_func();
|
|
||||||
},
|
|
||||||
function (err) {
|
|
||||||
error_process(err, error_callback);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
function (err) {
|
|
||||||
error_process(err, error_callback);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Publishing user generated content(ugc) to Steam contains following steps:
|
|
||||||
// 1. Save file and image to Steam Cloud.
|
|
||||||
// 2. Share the file and image.
|
|
||||||
// 3. publish the file to workshop.
|
|
||||||
greenworks.ugcPublish = function (
|
|
||||||
file_name,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
image_name,
|
|
||||||
success_callback,
|
|
||||||
error_callback,
|
|
||||||
progress_callback,
|
|
||||||
) {
|
|
||||||
var publish_file_process = function () {
|
|
||||||
if (progress_callback) progress_callback("Completed on sharing files.");
|
|
||||||
greenworks.publishWorkshopFile(
|
|
||||||
file_name,
|
|
||||||
image_name,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
function (publish_file_id) {
|
|
||||||
success_callback(publish_file_id);
|
|
||||||
},
|
|
||||||
function (err) {
|
|
||||||
error_process(err, error_callback);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
};
|
|
||||||
greenworks.saveFilesToCloud(
|
|
||||||
[file_name, image_name],
|
|
||||||
function () {
|
|
||||||
file_share_process(file_name, image_name, publish_file_process, error_callback, progress_callback);
|
|
||||||
},
|
|
||||||
function (err) {
|
|
||||||
error_process(err, error_callback);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Update publish ugc steps:
|
|
||||||
// 1. Save new file and image to Steam Cloud.
|
|
||||||
// 2. Share file and images.
|
|
||||||
// 3. Update published file.
|
|
||||||
greenworks.ugcPublishUpdate = function (
|
|
||||||
published_file_id,
|
|
||||||
file_name,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
image_name,
|
|
||||||
success_callback,
|
|
||||||
error_callback,
|
|
||||||
progress_callback,
|
|
||||||
) {
|
|
||||||
var update_published_file_process = function () {
|
|
||||||
if (progress_callback) progress_callback("Completed on sharing files.");
|
|
||||||
greenworks.updatePublishedWorkshopFile(
|
|
||||||
published_file_id,
|
|
||||||
file_name,
|
|
||||||
image_name,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
function () {
|
|
||||||
success_callback();
|
|
||||||
},
|
|
||||||
function (err) {
|
|
||||||
error_process(err, error_callback);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
greenworks.saveFilesToCloud(
|
|
||||||
[file_name, image_name],
|
|
||||||
function () {
|
|
||||||
file_share_process(file_name, image_name, update_published_file_process, error_callback, progress_callback);
|
|
||||||
},
|
|
||||||
function (err) {
|
|
||||||
error_process(err, error_callback);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Greenworks Utils APIs implmentation.
|
|
||||||
greenworks.Utils.move = function (source_dir, target_dir, success_callback, error_callback) {
|
|
||||||
fs.rename(source_dir, target_dir, function (err) {
|
|
||||||
if (err) {
|
|
||||||
if (error_callback) error_callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (success_callback) success_callback();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
greenworks.init = function () {
|
|
||||||
if (this.initAPI()) return true;
|
|
||||||
if (!this.isSteamRunning()) throw new Error("Steam initialization failed. Steam is not running.");
|
|
||||||
var appId;
|
|
||||||
try {
|
|
||||||
appId = fs.readFileSync("steam_appid.txt", "utf8");
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error(
|
|
||||||
"Steam initialization failed. Steam is running," +
|
|
||||||
"but steam_appid.txt is missing. Expected to find it in: " +
|
|
||||||
require("path").resolve("steam_appid.txt"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (!/^\d+ *\r?\n?$/.test(appId)) {
|
|
||||||
throw new Error(
|
|
||||||
"Steam initialization failed. " +
|
|
||||||
"steam_appid.txt appears to be invalid; " +
|
|
||||||
"it should contain a numeric ID: " +
|
|
||||||
appId,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
throw new Error(
|
|
||||||
"Steam initialization failed, but Steam is running, " +
|
|
||||||
"and steam_appid.txt is present and valid." +
|
|
||||||
"Maybe that's not really YOUR app ID? " +
|
|
||||||
appId.trim(),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
var EventEmitter = require("events").EventEmitter;
|
|
||||||
greenworks.__proto__ = EventEmitter.prototype;
|
|
||||||
EventEmitter.call(greenworks);
|
|
||||||
|
|
||||||
greenworks._steam_events.on = function () {
|
|
||||||
greenworks.emit.apply(greenworks, arguments);
|
|
||||||
};
|
|
||||||
|
|
||||||
process.versions["greenworks"] = greenworks._version;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = greenworks;
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
|||||||
1812820
|
|
||||||
@@ -19,7 +19,7 @@ app.on("window-all-closed", () => {
|
|||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
const greenworks = require("./greenworks");
|
require("./steamworksUtils");
|
||||||
const gameWindow = require("./gameWindow");
|
const gameWindow = require("./gameWindow");
|
||||||
const achievements = require("./achievements");
|
const achievements = require("./achievements");
|
||||||
const utils = require("./utils");
|
const utils = require("./utils");
|
||||||
@@ -36,23 +36,9 @@ log.transports.console.level = store.get("console-log-level", "debug");
|
|||||||
|
|
||||||
log.info(`Started app: ${JSON.stringify(process.argv)}`);
|
log.info(`Started app: ${JSON.stringify(process.argv)}`);
|
||||||
|
|
||||||
// We want to fail gracefully if we cannot connect to Steam
|
|
||||||
try {
|
|
||||||
if (greenworks && greenworks.init()) {
|
|
||||||
log.info("Steam API has been initialized.");
|
|
||||||
} else {
|
|
||||||
const error = "Steam API has failed to initialize.";
|
|
||||||
log.warn(error);
|
|
||||||
global.greenworksError = error;
|
|
||||||
}
|
|
||||||
} catch (ex) {
|
|
||||||
log.warn(ex.message);
|
|
||||||
global.greenworksError = ex.message;
|
|
||||||
}
|
|
||||||
|
|
||||||
let isRestoreDisabled = false;
|
let isRestoreDisabled = false;
|
||||||
|
|
||||||
function setStopProcessHandler(app, window) {
|
function setStopProcessHandler(window) {
|
||||||
const closingWindowHandler = async (e) => {
|
const closingWindowHandler = async (e) => {
|
||||||
// We need to prevent the default closing event to add custom logic
|
// We need to prevent the default closing event to add custom logic
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -115,7 +101,7 @@ function setStopProcessHandler(app, window) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const receivedDisableRestoreHandler = async (event, arg) => {
|
const receivedDisableRestoreHandler = (event, arg) => {
|
||||||
if (!window) return log.warn("Window was undefined in disable import handler");
|
if (!window) return log.warn("Window was undefined in disable import handler");
|
||||||
|
|
||||||
log.debug(`Disabling auto-restore for ${arg.duration}ms.`);
|
log.debug(`Disabling auto-restore for ${arg.duration}ms.`);
|
||||||
@@ -126,7 +112,7 @@ function setStopProcessHandler(app, window) {
|
|||||||
}, arg.duration);
|
}, arg.duration);
|
||||||
};
|
};
|
||||||
|
|
||||||
const receivedGameSavedHandler = async (event, arg) => {
|
const receivedGameSavedHandler = (event, arg) => {
|
||||||
if (!window) return log.warn("Window was undefined in game saved handler");
|
if (!window) return log.warn("Window was undefined in game saved handler");
|
||||||
|
|
||||||
const { save, ...other } = arg;
|
const { save, ...other } = arg;
|
||||||
@@ -229,15 +215,15 @@ app.on("ready", async () => {
|
|||||||
const window = new BrowserWindow({ show: false });
|
const window = new BrowserWindow({ show: false });
|
||||||
await window.loadFile("export.html");
|
await window.loadFile("export.html");
|
||||||
window.show();
|
window.show();
|
||||||
setStopProcessHandler(app, window);
|
setStopProcessHandler(window);
|
||||||
await utils.exportSave(window);
|
await utils.exportSave(window);
|
||||||
} else {
|
} else {
|
||||||
const window = await startWindow(process.argv.includes("--no-scripts"));
|
const window = await startWindow(process.argv.includes("--no-scripts"));
|
||||||
if (global.greenworksError) {
|
if (global.steamworksError) {
|
||||||
await dialog.showMessageBox(window, {
|
await dialog.showMessageBox(window, {
|
||||||
title: "Bitburner",
|
title: "Bitburner",
|
||||||
message: "Could not connect to Steam",
|
message: "Could not connect to Steam",
|
||||||
detail: `${global.greenworksError}\n\nYou won't be able to receive achievements until this is resolved and you restart the game.`,
|
detail: `${global.steamworksError.message}\n\nYou won't be able to receive achievements until this is resolved and you restart the game.`,
|
||||||
type: "warning",
|
type: "warning",
|
||||||
buttons: ["OK"],
|
buttons: ["OK"],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ const Store = require("electron-store");
|
|||||||
const utils = require("./utils");
|
const utils = require("./utils");
|
||||||
const storage = require("./storage");
|
const storage = require("./storage");
|
||||||
const store = new Store();
|
const store = new Store();
|
||||||
|
const { steamworksClient } = require("./steamworksUtils");
|
||||||
|
|
||||||
function getMenu(window) {
|
function getMenu(window) {
|
||||||
const canZoomIn = utils.getZoomFactor() <= 2;
|
const canZoomIn = utils.getZoomFactor() <= 2;
|
||||||
@@ -54,7 +55,7 @@ function getMenu(window) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Export Scripts",
|
label: "Export Scripts",
|
||||||
click: async () => window.webContents.send("trigger-scripts-export"),
|
click: () => window.webContents.send("trigger-scripts-export"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "separator",
|
type: "separator",
|
||||||
@@ -74,7 +75,7 @@ function getMenu(window) {
|
|||||||
{
|
{
|
||||||
label: "Load From File",
|
label: "Load From File",
|
||||||
click: async () => {
|
click: async () => {
|
||||||
const defaultPath = await storage.getSaveFolder(window);
|
const defaultPath = storage.getSaveFolder(window);
|
||||||
const result = await dialog.showOpenDialog(window, {
|
const result = await dialog.showOpenDialog(window, {
|
||||||
title: "Load From File",
|
title: "Load From File",
|
||||||
defaultPath: defaultPath,
|
defaultPath: defaultPath,
|
||||||
@@ -103,7 +104,7 @@ function getMenu(window) {
|
|||||||
click: async () => {
|
click: async () => {
|
||||||
try {
|
try {
|
||||||
const saveData = await storage.getSteamCloudSaveData();
|
const saveData = await storage.getSteamCloudSaveData();
|
||||||
await storage.pushSaveGameForImport(window, saveData, false);
|
storage.pushSaveGameForImport(window, saveData, false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(error);
|
log.error(error);
|
||||||
utils.writeToast(window, "Could not load from Steam Cloud", "error", 5000);
|
utils.writeToast(window, "Could not load from Steam Cloud", "error", 5000);
|
||||||
@@ -126,7 +127,7 @@ function getMenu(window) {
|
|||||||
{
|
{
|
||||||
label: "Auto-Save to Steam Cloud",
|
label: "Auto-Save to Steam Cloud",
|
||||||
type: "checkbox",
|
type: "checkbox",
|
||||||
enabled: !global.greenworksError,
|
enabled: steamworksClient !== undefined,
|
||||||
checked: storage.isCloudEnabled(),
|
checked: storage.isCloudEnabled(),
|
||||||
click: (menuItem) => {
|
click: (menuItem) => {
|
||||||
storage.setCloudEnabledConfig(menuItem.checked);
|
storage.setCloudEnabledConfig(menuItem.checked);
|
||||||
@@ -166,8 +167,8 @@ function getMenu(window) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Open Saves Directory",
|
label: "Open Saves Directory",
|
||||||
click: async () => {
|
click: () => {
|
||||||
const path = await storage.getSaveFolder(window);
|
const path = storage.getSaveFolder(window);
|
||||||
shell.openPath(path);
|
shell.openPath(path);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -289,10 +290,15 @@ function getMenu(window) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Delete Steam Cloud Data",
|
label: "Delete Steam Cloud Data",
|
||||||
enabled: !global.greenworksError,
|
enabled: steamworksClient !== undefined,
|
||||||
click: async () => {
|
click: () => {
|
||||||
|
if (steamworksClient.cloud.listFiles().length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
await storage.deleteCloudFile();
|
if (!storage.deleteCloudFile()) {
|
||||||
|
log.warn("Cannot delete Steam Cloud data");
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(error);
|
log.error(error);
|
||||||
}
|
}
|
||||||
|
|||||||
28
electron/package-lock.json
generated
28
electron/package-lock.json
generated
@@ -8,11 +8,33 @@
|
|||||||
"name": "bitburner",
|
"name": "bitburner",
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@catloversg/steamworks.js": "0.0.1",
|
||||||
"electron-log": "^4.4.8",
|
"electron-log": "^4.4.8",
|
||||||
"electron-store": "^8.1.0",
|
"electron-store": "^8.1.0",
|
||||||
"lodash": "^4.17.21"
|
"lodash": "^4.17.21"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@catloversg/steamworks.js": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@catloversg/steamworks.js/-/steamworks.js-0.0.1.tgz",
|
||||||
|
"integrity": "sha512-Kj3JZUVqYuLsF34g/N/Ap/aWsJHJoWKbvn/R19fzlZTJY+XMv5myRcngUuydwzvJzHR+BhVk7FzQVV8D8w5x1Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "22.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz",
|
||||||
|
"integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~6.21.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ajv": {
|
"node_modules/ajv": {
|
||||||
"version": "8.12.0",
|
"version": "8.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
||||||
@@ -313,6 +335,12 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "6.21.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||||
|
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/uri-js": {
|
"node_modules/uri-js": {
|
||||||
"version": "4.4.1",
|
"version": "4.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||||
|
|||||||
@@ -24,8 +24,9 @@
|
|||||||
"buildResources": "public"
|
"buildResources": "public"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"electron-store": "^8.1.0",
|
"@catloversg/steamworks.js": "0.0.1",
|
||||||
"electron-log": "^4.4.8",
|
"electron-log": "^4.4.8",
|
||||||
|
"electron-store": "^8.1.0",
|
||||||
"lodash": "^4.17.21"
|
"lodash": "^4.17.21"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
1812820
|
|
||||||
20
electron/steamworksUtils.js
Normal file
20
electron/steamworksUtils.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
|
const steamworks = require("@catloversg/steamworks.js");
|
||||||
|
const log = require("electron-log");
|
||||||
|
|
||||||
|
let steamworksClient;
|
||||||
|
try {
|
||||||
|
// 1812820 is our Steam App ID.
|
||||||
|
steamworksClient = steamworks.init(1812820);
|
||||||
|
} catch (error) {
|
||||||
|
if (error.message?.includes("Steam is probably not running")) {
|
||||||
|
log.warn(error.message);
|
||||||
|
} else {
|
||||||
|
log.warn(error);
|
||||||
|
}
|
||||||
|
global.steamworksError = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
steamworksClient,
|
||||||
|
};
|
||||||
@@ -3,12 +3,12 @@ const { app, ipcMain } = require("electron");
|
|||||||
const path = require("path");
|
const path = require("path");
|
||||||
const fs = require("fs/promises");
|
const fs = require("fs/promises");
|
||||||
|
|
||||||
const greenworks = require("./greenworks");
|
|
||||||
const log = require("electron-log");
|
const log = require("electron-log");
|
||||||
const flatten = require("lodash/flatten");
|
const flatten = require("lodash/flatten");
|
||||||
const Store = require("electron-store");
|
const Store = require("electron-store");
|
||||||
const { isBinaryFormat } = require("./saveDataBinaryFormat");
|
const { isBinaryFormat } = require("./saveDataBinaryFormat");
|
||||||
const store = new Store();
|
const store = new Store();
|
||||||
|
const { steamworksClient } = require("./steamworksUtils");
|
||||||
|
|
||||||
// https://stackoverflow.com/a/69418940
|
// https://stackoverflow.com/a/69418940
|
||||||
const dirSize = async (directory) => {
|
const dirSize = async (directory) => {
|
||||||
@@ -33,7 +33,7 @@ const getNewestFile = async (directory) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getAllSaves = async (window) => {
|
const getAllSaves = async (window) => {
|
||||||
const rootDirectory = await getSaveFolder(window, true);
|
const rootDirectory = getSaveFolder(window, true);
|
||||||
const data = await fs.readdir(rootDirectory, { withFileTypes: true });
|
const data = await fs.readdir(rootDirectory, { withFileTypes: true });
|
||||||
const savesPromises = data
|
const savesPromises = data
|
||||||
.filter((e) => e.isDirectory())
|
.filter((e) => e.isDirectory())
|
||||||
@@ -45,8 +45,8 @@ const getAllSaves = async (window) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
async function prepareSaveFolders(window) {
|
async function prepareSaveFolders(window) {
|
||||||
const rootFolder = await getSaveFolder(window, true);
|
const rootFolder = getSaveFolder(window, true);
|
||||||
const currentFolder = await getSaveFolder(window);
|
const currentFolder = getSaveFolder(window);
|
||||||
const backupsFolder = path.join(rootFolder, "/_backups");
|
const backupsFolder = path.join(rootFolder, "/_backups");
|
||||||
await prepareFolders(rootFolder, currentFolder, backupsFolder);
|
await prepareFolders(rootFolder, currentFolder, backupsFolder);
|
||||||
}
|
}
|
||||||
@@ -87,69 +87,67 @@ function setCloudEnabledConfig(value) {
|
|||||||
store.set("cloud-enabled", value);
|
store.set("cloud-enabled", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getSaveFolder(window, root = false) {
|
function getSaveFolder(window, root = false) {
|
||||||
if (root) return path.join(app.getPath("userData"), "/saves");
|
if (root) {
|
||||||
|
return path.join(app.getPath("userData"), "/saves");
|
||||||
|
}
|
||||||
const identifier = window.gameInfo?.player?.identifier ?? "";
|
const identifier = window.gameInfo?.player?.identifier ?? "";
|
||||||
return path.join(app.getPath("userData"), "/saves", `/${identifier}`);
|
return path.join(app.getPath("userData"), "/saves", `/${identifier}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isCloudEnabled() {
|
function isCloudEnabled() {
|
||||||
// If the Steam API could not be initialized on game start, we'll abort this.
|
// If the Steam API could not be initialized on game start, we'll abort this.
|
||||||
if (global.greenworksError) return false;
|
if (!steamworksClient) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// If the user disables it in Steam there's nothing we can do
|
// If the user disables it in Steam there's nothing we can do
|
||||||
if (!greenworks.isCloudEnabledForUser()) return false;
|
if (!steamworksClient.cloud.isEnabledForAccount()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Let's check the config file to see if it's been overriden
|
// Let's check the config file to see if it's been overridden
|
||||||
const enabledInConf = store.get("cloud-enabled", true);
|
if (!store.get("cloud-enabled", true)) {
|
||||||
if (!enabledInConf) return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const isAppEnabled = greenworks.isCloudEnabled();
|
if (!steamworksClient.cloud.isEnabledForApp()) {
|
||||||
if (!isAppEnabled) greenworks.enableCloud(true);
|
steamworksClient.cloud.setEnabledForApp(true);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveCloudFile(name, content) {
|
function saveCloudFile(name, content) {
|
||||||
return new Promise((resolve, reject) => {
|
steamworksClient.cloud.writeFile(name, content);
|
||||||
greenworks.saveTextToFile(name, content, resolve, reject);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFirstCloudFile() {
|
function getFilenameOfFirstCloudFile() {
|
||||||
const nbFiles = greenworks.getFileCount();
|
const files = steamworksClient.cloud.listFiles();
|
||||||
if (nbFiles === 0) throw new Error("No files in cloud");
|
if (files.length === 0) {
|
||||||
const file = greenworks.getFileNameAndSize(0);
|
throw new Error("No files in cloud");
|
||||||
log.silly(`Found ${nbFiles} files.`);
|
}
|
||||||
|
const file = files[0];
|
||||||
|
log.silly(`Found ${files.length} files.`);
|
||||||
log.silly(`First File: ${file.name} (${file.size} bytes)`);
|
log.silly(`First File: ${file.name} (${file.size} bytes)`);
|
||||||
return file.name;
|
return file.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCloudFile() {
|
function getCloudFile() {
|
||||||
const file = getFirstCloudFile();
|
return steamworksClient.cloud.readFile(getFilenameOfFirstCloudFile());
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
greenworks.readTextFromFile(file, resolve, reject);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteCloudFile() {
|
function deleteCloudFile() {
|
||||||
const file = getFirstCloudFile();
|
return steamworksClient.cloud.deleteFile(getFilenameOfFirstCloudFile());
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
greenworks.deleteFile(file, resolve, reject);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getSteamCloudQuota() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
greenworks.getCloudQuota(resolve, reject);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function backupSteamDataToDisk(currentPlayerId) {
|
async function backupSteamDataToDisk(currentPlayerId) {
|
||||||
const nbFiles = greenworks.getFileCount();
|
const files = steamworksClient.cloud.listFiles();
|
||||||
if (nbFiles === 0) return;
|
if (files.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const file = greenworks.getFileNameAndSize(0);
|
const file = files[0];
|
||||||
const previousPlayerId = file.name.replace(".json.gz", "");
|
const previousPlayerId = file.name.replace(".json.gz", "");
|
||||||
if (previousPlayerId !== currentPlayerId) {
|
if (previousPlayerId !== currentPlayerId) {
|
||||||
const backupSaveData = await getSteamCloudSaveData();
|
const backupSaveData = await getSteamCloudSaveData();
|
||||||
@@ -170,7 +168,7 @@ async function pushSaveDataToSteamCloud(saveData, currentPlayerId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
backupSteamDataToDisk(currentPlayerId);
|
await backupSteamDataToDisk(currentPlayerId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(error);
|
log.error(error);
|
||||||
}
|
}
|
||||||
@@ -178,8 +176,8 @@ async function pushSaveDataToSteamCloud(saveData, currentPlayerId) {
|
|||||||
const steamSaveName = `${currentPlayerId}.json.gz`;
|
const steamSaveName = `${currentPlayerId}.json.gz`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When we push save file to Steam Cloud, we use greenworks.saveTextToFile. It seems that this method expects a string
|
* When we push save file to Steam Cloud, we use steamworksClient.cloud.writeFile. This function requires a string as
|
||||||
* as the file content. That is why saveData is encoded in base64 and pushed to Steam Cloud as a text file.
|
* the file content. That is why saveData is encoded in base64 and pushed to Steam Cloud as a text file.
|
||||||
*
|
*
|
||||||
* Encoding saveData in UTF-8 (with buffer.toString("utf8")) is not the proper way to convert binary data to string.
|
* Encoding saveData in UTF-8 (with buffer.toString("utf8")) is not the proper way to convert binary data to string.
|
||||||
* Quote from buffer's documentation: "If encoding is 'utf8' and a byte sequence in the input is not valid UTF-8, then
|
* Quote from buffer's documentation: "If encoding is 'utf8' and a byte sequence in the input is not valid UTF-8, then
|
||||||
@@ -194,7 +192,7 @@ async function pushSaveDataToSteamCloud(saveData, currentPlayerId) {
|
|||||||
log.debug(`Saving to Steam Cloud as ${steamSaveName}`);
|
log.debug(`Saving to Steam Cloud as ${steamSaveName}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await saveCloudFile(steamSaveName, content);
|
saveCloudFile(steamSaveName, content);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(error);
|
log.error(error);
|
||||||
}
|
}
|
||||||
@@ -208,7 +206,7 @@ async function getSteamCloudSaveData() {
|
|||||||
return Promise.reject("Steam Cloud is not Enabled");
|
return Promise.reject("Steam Cloud is not Enabled");
|
||||||
}
|
}
|
||||||
log.debug(`Fetching Save in Steam Cloud`);
|
log.debug(`Fetching Save in Steam Cloud`);
|
||||||
const cloudString = await getCloudFile();
|
const cloudString = getCloudFile();
|
||||||
// Decode cloudString to get save data back.
|
// Decode cloudString to get save data back.
|
||||||
const saveData = Buffer.from(cloudString, "base64");
|
const saveData = Buffer.from(cloudString, "base64");
|
||||||
log.debug(`SaveData: ${saveData.length} bytes`);
|
log.debug(`SaveData: ${saveData.length} bytes`);
|
||||||
@@ -216,7 +214,7 @@ async function getSteamCloudSaveData() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function saveGameToDisk(window, electronGameData) {
|
async function saveGameToDisk(window, electronGameData) {
|
||||||
const currentFolder = await getSaveFolder(window);
|
const currentFolder = getSaveFolder(window);
|
||||||
let saveFolderSizeBytes = await getFolderSizeInBytes(currentFolder);
|
let saveFolderSizeBytes = await getFolderSizeInBytes(currentFolder);
|
||||||
const maxFolderSizeBytes = store.get("autosave-quota", 1e8); // 100Mb per playerIndentifier
|
const maxFolderSizeBytes = store.get("autosave-quota", 1e8); // 100Mb per playerIndentifier
|
||||||
const remainingSpaceBytes = maxFolderSizeBytes - saveFolderSizeBytes;
|
const remainingSpaceBytes = maxFolderSizeBytes - saveFolderSizeBytes;
|
||||||
@@ -264,7 +262,7 @@ async function saveGameToDisk(window, electronGameData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadLastFromDisk(window) {
|
async function loadLastFromDisk(window) {
|
||||||
const folder = await getSaveFolder(window);
|
const folder = getSaveFolder(window);
|
||||||
const last = await getNewestFile(folder);
|
const last = await getNewestFile(folder);
|
||||||
log.debug(`Last modified file: "${last.file}" (${last.stat.mtime.toLocaleString()})`);
|
log.debug(`Last modified file: "${last.file}" (${last.stat.mtime.toLocaleString()})`);
|
||||||
return loadFileFromDisk(last.file);
|
return loadFileFromDisk(last.file);
|
||||||
@@ -286,7 +284,7 @@ async function loadFileFromDisk(path) {
|
|||||||
|
|
||||||
function getSaveInformation(window, save) {
|
function getSaveInformation(window, save) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
ipcMain.once("get-save-info-response", async (event, data) => {
|
ipcMain.once("get-save-info-response", (event, data) => {
|
||||||
resolve(data);
|
resolve(data);
|
||||||
});
|
});
|
||||||
window.webContents.send("get-save-info-request", save);
|
window.webContents.send("get-save-info-request", save);
|
||||||
@@ -303,7 +301,7 @@ function getCurrentSave(window) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function pushSaveGameForImport(window, save, automatic) {
|
function pushSaveGameForImport(window, save, automatic) {
|
||||||
ipcMain.once("push-import-result", async (event, arg) => {
|
ipcMain.once("push-import-result", (event, arg) => {
|
||||||
log.debug(`Was save imported? ${arg.wasImported ? "Yes" : "No"}`);
|
log.debug(`Was save imported? ${arg.wasImported ? "Yes" : "No"}`);
|
||||||
});
|
});
|
||||||
window.webContents.send("push-save-request", { save, automatic });
|
window.webContents.send("push-save-request", { save, automatic });
|
||||||
@@ -379,7 +377,6 @@ module.exports = {
|
|||||||
pushSaveGameForImport,
|
pushSaveGameForImport,
|
||||||
pushSaveDataToSteamCloud,
|
pushSaveDataToSteamCloud,
|
||||||
getSteamCloudSaveData,
|
getSteamCloudSaveData,
|
||||||
getSteamCloudQuota,
|
|
||||||
deleteCloudFile,
|
deleteCloudFile,
|
||||||
saveGameToDisk,
|
saveGameToDisk,
|
||||||
loadLastFromDisk,
|
loadLastFromDisk,
|
||||||
|
|||||||
29
package-lock.json
generated
29
package-lock.json
generated
@@ -79,7 +79,7 @@
|
|||||||
"copy-webpack-plugin": "^12.0.2",
|
"copy-webpack-plugin": "^12.0.2",
|
||||||
"css-loader": "^7.1.2",
|
"css-loader": "^7.1.2",
|
||||||
"csstype": "3.1.2",
|
"csstype": "3.1.2",
|
||||||
"electron": "^29.4.5",
|
"electron": "^35.1.5",
|
||||||
"electron-packager": "^17.1.2",
|
"electron-packager": "^17.1.2",
|
||||||
"eslint": "^8.52.0",
|
"eslint": "^8.52.0",
|
||||||
"eslint-plugin-react": "^7.33.2",
|
"eslint-plugin-react": "^7.33.2",
|
||||||
@@ -5129,12 +5129,13 @@
|
|||||||
"integrity": "sha512-xPSg0jm4mqgEkNhowKgZFBNtwoEwF6gJ4Dhww+GFpm3IgtNseHQZ5IqdNwnquZEoANxyDAKDRAdVo4Z72VvD/g=="
|
"integrity": "sha512-xPSg0jm4mqgEkNhowKgZFBNtwoEwF6gJ4Dhww+GFpm3IgtNseHQZ5IqdNwnquZEoANxyDAKDRAdVo4Z72VvD/g=="
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "20.14.14",
|
"version": "22.13.14",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.14.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.14.tgz",
|
||||||
"integrity": "sha512-d64f00982fS9YoOgJkAMolK7MN8Iq3TDdVjchbYHdEmjth/DHowx82GnoA+tVUAN+7vxfYUgAzi+JXbKNd2SDQ==",
|
"integrity": "sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~5.26.4"
|
"undici-types": "~6.20.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/node-forge": {
|
"node_modules/@types/node-forge": {
|
||||||
@@ -8175,14 +8176,15 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/electron": {
|
"node_modules/electron": {
|
||||||
"version": "29.4.5",
|
"version": "35.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/electron/-/electron-29.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/electron/-/electron-35.1.5.tgz",
|
||||||
"integrity": "sha512-DlEuzGbWBYl1Qr0qUYgNZdoixJg4YGHy2HC6fkRjSXSlb01UrQ5ORi8hNLzelzyYx8rNQyyE3zDUuk9EnZwYuA==",
|
"integrity": "sha512-LolvbKKQUSCGvEwbEQNt1cxD1t+YYClDNwBIjn4d28KM8FSqUn9zJuf6AbqNA7tVs9OFl/EQpmg/m4lZV1hH8g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron/get": "^2.0.0",
|
"@electron/get": "^2.0.0",
|
||||||
"@types/node": "^20.9.0",
|
"@types/node": "^22.7.7",
|
||||||
"extract-zip": "^2.0.1"
|
"extract-zip": "^2.0.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -18193,10 +18195,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/undici-types": {
|
"node_modules/undici-types": {
|
||||||
"version": "5.26.5",
|
"version": "6.20.0",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
|
||||||
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/unicode-canonical-property-names-ecmascript": {
|
"node_modules/unicode-canonical-property-names-ecmascript": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
|
|||||||
@@ -80,7 +80,7 @@
|
|||||||
"copy-webpack-plugin": "^12.0.2",
|
"copy-webpack-plugin": "^12.0.2",
|
||||||
"css-loader": "^7.1.2",
|
"css-loader": "^7.1.2",
|
||||||
"csstype": "3.1.2",
|
"csstype": "3.1.2",
|
||||||
"electron": "^29.4.5",
|
"electron": "^35.1.5",
|
||||||
"electron-packager": "^17.1.2",
|
"electron-packager": "^17.1.2",
|
||||||
"eslint": "^8.52.0",
|
"eslint": "^8.52.0",
|
||||||
"eslint-plugin-react": "^7.33.2",
|
"eslint-plugin-react": "^7.33.2",
|
||||||
|
|||||||
@@ -15,11 +15,6 @@ cd ..
|
|||||||
cp -r .app/* .package
|
cp -r .app/* .package
|
||||||
cp -r electron/* .package
|
cp -r electron/* .package
|
||||||
|
|
||||||
# steam_appid.txt would end up in the resource dir
|
|
||||||
rm .package/steam_appid.txt
|
|
||||||
|
|
||||||
BUILD_PLATFORM="${1:-"all"}"
|
BUILD_PLATFORM="${1:-"all"}"
|
||||||
# And finally build the app.
|
# And finally build the app.
|
||||||
npm run electron:packager-$BUILD_PLATFORM
|
npm run electron:packager-$BUILD_PLATFORM
|
||||||
|
|
||||||
echo .build/* | xargs -n 1 cp electron/steam_appid.txt
|
|
||||||
|
|||||||
Reference in New Issue
Block a user