ELECTRON: Use steamworks.js to integrate with Steamworks SDK (#1563)

This commit is contained in:
catloversg
2025-04-15 06:19:17 +07:00
committed by GitHub
parent d592e07719
commit 44b8baa8ad
28 changed files with 162 additions and 431 deletions

View File

@@ -169,7 +169,7 @@ Saving a file will reload the game automatically.
### 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.
```sh
@@ -180,10 +180,6 @@ $ npm run build:dev
# Use electron-packager to build the app to the .build/ folder.
$ 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...
$ .build/bitburner-win32-x64/bitburner.exe
```

View File

@@ -1,26 +1,39 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const greenworks = require("./greenworks");
const { steamworksClient } = require("./steamworksUtils");
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 (global.greenworksError) return;
if (!steamworksClient) {
return;
}
// 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.
const steamAchievements = greenworks.getAchievementNames();
log.silly(`All Steam achievements ${JSON.stringify(steamAchievements)}`);
const playerAchieved = (await Promise.all(steamAchievements.map(checkSteamAchievement))).filter((name) => !!name);
log.debug(`Player has Steam achievements ${JSON.stringify(playerAchieved)}`);
const allSteamAchievements = steamworksClient.achievement.names();
log.silly(`All Steam achievements ${JSON.stringify(allSteamAchievements)}`);
const steamAchievements = allSteamAchievements.filter((achievement) =>
steamworksClient.achievement.isActivated(achievement),
);
log.debug(`Player has Steam achievements ${JSON.stringify(steamAchievements)}`);
const intervalID = setInterval(async () => {
try {
const playerAchievements = await window.webContents.executeJavaScript("document.achievements");
for (const ach of playerAchievements) {
if (!steamAchievements.includes(ach)) continue; // 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
log.info(`Granting Steam achievement ${ach}`);
greenworks.activateAchievement(ach, () => undefined);
playerAchieved.push(ach);
for (const achievement of playerAchievements) {
// Don't try activating achievements that don't exist Steam-side
if (!allSteamAchievements.includes(achievement)) {
continue;
}
// 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) {
log.error(error);
@@ -28,25 +41,11 @@ async function enableAchievementsInterval(window) {
// The interval probably did not get cleared after a window kill
log.warn("Clearing achievements timer");
clearInterval(intervalID);
return;
}
}, 1000);
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) {
if (window.achievementsIntervalID) {
clearInterval(window.achievementsIntervalID);

View File

@@ -63,7 +63,7 @@ async function createWindow(killall) {
utils.attachUnresponsiveAppHandler(window);
menu.refreshMenu(window);
setStopProcessHandler(app, window);
setStopProcessHandler(window);
return window;
}

View File

@@ -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.

View File

@@ -1 +0,0 @@
1812820

View File

@@ -19,7 +19,7 @@ app.on("window-all-closed", () => {
process.exit(0);
});
const greenworks = require("./greenworks");
require("./steamworksUtils");
const gameWindow = require("./gameWindow");
const achievements = require("./achievements");
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)}`);
// 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;
function setStopProcessHandler(app, window) {
function setStopProcessHandler(window) {
const closingWindowHandler = async (e) => {
// We need to prevent the default closing event to add custom logic
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");
log.debug(`Disabling auto-restore for ${arg.duration}ms.`);
@@ -126,7 +112,7 @@ function setStopProcessHandler(app, window) {
}, arg.duration);
};
const receivedGameSavedHandler = async (event, arg) => {
const receivedGameSavedHandler = (event, arg) => {
if (!window) return log.warn("Window was undefined in game saved handler");
const { save, ...other } = arg;
@@ -229,15 +215,15 @@ app.on("ready", async () => {
const window = new BrowserWindow({ show: false });
await window.loadFile("export.html");
window.show();
setStopProcessHandler(app, window);
setStopProcessHandler(window);
await utils.exportSave(window);
} else {
const window = await startWindow(process.argv.includes("--no-scripts"));
if (global.greenworksError) {
if (global.steamworksError) {
await dialog.showMessageBox(window, {
title: "Bitburner",
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",
buttons: ["OK"],
});

View File

@@ -5,6 +5,7 @@ const Store = require("electron-store");
const utils = require("./utils");
const storage = require("./storage");
const store = new Store();
const { steamworksClient } = require("./steamworksUtils");
function getMenu(window) {
const canZoomIn = utils.getZoomFactor() <= 2;
@@ -54,7 +55,7 @@ function getMenu(window) {
},
{
label: "Export Scripts",
click: async () => window.webContents.send("trigger-scripts-export"),
click: () => window.webContents.send("trigger-scripts-export"),
},
{
type: "separator",
@@ -74,7 +75,7 @@ function getMenu(window) {
{
label: "Load From File",
click: async () => {
const defaultPath = await storage.getSaveFolder(window);
const defaultPath = storage.getSaveFolder(window);
const result = await dialog.showOpenDialog(window, {
title: "Load From File",
defaultPath: defaultPath,
@@ -103,7 +104,7 @@ function getMenu(window) {
click: async () => {
try {
const saveData = await storage.getSteamCloudSaveData();
await storage.pushSaveGameForImport(window, saveData, false);
storage.pushSaveGameForImport(window, saveData, false);
} catch (error) {
log.error(error);
utils.writeToast(window, "Could not load from Steam Cloud", "error", 5000);
@@ -126,7 +127,7 @@ function getMenu(window) {
{
label: "Auto-Save to Steam Cloud",
type: "checkbox",
enabled: !global.greenworksError,
enabled: steamworksClient !== undefined,
checked: storage.isCloudEnabled(),
click: (menuItem) => {
storage.setCloudEnabledConfig(menuItem.checked);
@@ -166,8 +167,8 @@ function getMenu(window) {
},
{
label: "Open Saves Directory",
click: async () => {
const path = await storage.getSaveFolder(window);
click: () => {
const path = storage.getSaveFolder(window);
shell.openPath(path);
},
},
@@ -289,10 +290,15 @@ function getMenu(window) {
},
{
label: "Delete Steam Cloud Data",
enabled: !global.greenworksError,
click: async () => {
enabled: steamworksClient !== undefined,
click: () => {
if (steamworksClient.cloud.listFiles().length === 0) {
return;
}
try {
await storage.deleteCloudFile();
if (!storage.deleteCloudFile()) {
log.warn("Cannot delete Steam Cloud data");
}
} catch (error) {
log.error(error);
}

View File

@@ -8,11 +8,33 @@
"name": "bitburner",
"version": "3.0.0",
"dependencies": {
"@catloversg/steamworks.js": "0.0.1",
"electron-log": "^4.4.8",
"electron-store": "^8.1.0",
"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": {
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
@@ -313,6 +335,12 @@
"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": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",

View File

@@ -24,8 +24,9 @@
"buildResources": "public"
},
"dependencies": {
"electron-store": "^8.1.0",
"@catloversg/steamworks.js": "0.0.1",
"electron-log": "^4.4.8",
"electron-store": "^8.1.0",
"lodash": "^4.17.21"
}
}

View File

@@ -1 +0,0 @@
1812820

View 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,
};

View File

@@ -3,12 +3,12 @@ const { app, ipcMain } = require("electron");
const path = require("path");
const fs = require("fs/promises");
const greenworks = require("./greenworks");
const log = require("electron-log");
const flatten = require("lodash/flatten");
const Store = require("electron-store");
const { isBinaryFormat } = require("./saveDataBinaryFormat");
const store = new Store();
const { steamworksClient } = require("./steamworksUtils");
// https://stackoverflow.com/a/69418940
const dirSize = async (directory) => {
@@ -33,7 +33,7 @@ const getNewestFile = async (directory) => {
};
const getAllSaves = async (window) => {
const rootDirectory = await getSaveFolder(window, true);
const rootDirectory = getSaveFolder(window, true);
const data = await fs.readdir(rootDirectory, { withFileTypes: true });
const savesPromises = data
.filter((e) => e.isDirectory())
@@ -45,8 +45,8 @@ const getAllSaves = async (window) => {
};
async function prepareSaveFolders(window) {
const rootFolder = await getSaveFolder(window, true);
const currentFolder = await getSaveFolder(window);
const rootFolder = getSaveFolder(window, true);
const currentFolder = getSaveFolder(window);
const backupsFolder = path.join(rootFolder, "/_backups");
await prepareFolders(rootFolder, currentFolder, backupsFolder);
}
@@ -87,69 +87,67 @@ function setCloudEnabledConfig(value) {
store.set("cloud-enabled", value);
}
async function getSaveFolder(window, root = false) {
if (root) return path.join(app.getPath("userData"), "/saves");
function getSaveFolder(window, root = false) {
if (root) {
return path.join(app.getPath("userData"), "/saves");
}
const identifier = window.gameInfo?.player?.identifier ?? "";
return path.join(app.getPath("userData"), "/saves", `/${identifier}`);
}
function isCloudEnabled() {
// 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 (!greenworks.isCloudEnabledForUser()) return false;
if (!steamworksClient.cloud.isEnabledForAccount()) {
return false;
}
// Let's check the config file to see if it's been overriden
const enabledInConf = store.get("cloud-enabled", true);
if (!enabledInConf) return false;
// Let's check the config file to see if it's been overridden
if (!store.get("cloud-enabled", true)) {
return false;
}
const isAppEnabled = greenworks.isCloudEnabled();
if (!isAppEnabled) greenworks.enableCloud(true);
if (!steamworksClient.cloud.isEnabledForApp()) {
steamworksClient.cloud.setEnabledForApp(true);
}
return true;
}
function saveCloudFile(name, content) {
return new Promise((resolve, reject) => {
greenworks.saveTextToFile(name, content, resolve, reject);
});
steamworksClient.cloud.writeFile(name, content);
}
function getFirstCloudFile() {
const nbFiles = greenworks.getFileCount();
if (nbFiles === 0) throw new Error("No files in cloud");
const file = greenworks.getFileNameAndSize(0);
log.silly(`Found ${nbFiles} files.`);
function getFilenameOfFirstCloudFile() {
const files = steamworksClient.cloud.listFiles();
if (files.length === 0) {
throw new Error("No files in cloud");
}
const file = files[0];
log.silly(`Found ${files.length} files.`);
log.silly(`First File: ${file.name} (${file.size} bytes)`);
return file.name;
}
function getCloudFile() {
const file = getFirstCloudFile();
return new Promise((resolve, reject) => {
greenworks.readTextFromFile(file, resolve, reject);
});
return steamworksClient.cloud.readFile(getFilenameOfFirstCloudFile());
}
function deleteCloudFile() {
const file = getFirstCloudFile();
return new Promise((resolve, reject) => {
greenworks.deleteFile(file, resolve, reject);
});
}
async function getSteamCloudQuota() {
return new Promise((resolve, reject) => {
greenworks.getCloudQuota(resolve, reject);
});
return steamworksClient.cloud.deleteFile(getFilenameOfFirstCloudFile());
}
async function backupSteamDataToDisk(currentPlayerId) {
const nbFiles = greenworks.getFileCount();
if (nbFiles === 0) return;
const files = steamworksClient.cloud.listFiles();
if (files.length === 0) {
return;
}
const file = greenworks.getFileNameAndSize(0);
const file = files[0];
const previousPlayerId = file.name.replace(".json.gz", "");
if (previousPlayerId !== currentPlayerId) {
const backupSaveData = await getSteamCloudSaveData();
@@ -170,7 +168,7 @@ async function pushSaveDataToSteamCloud(saveData, currentPlayerId) {
}
try {
backupSteamDataToDisk(currentPlayerId);
await backupSteamDataToDisk(currentPlayerId);
} catch (error) {
log.error(error);
}
@@ -178,8 +176,8 @@ async function pushSaveDataToSteamCloud(saveData, currentPlayerId) {
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
* as the file content. That is why saveData is encoded in base64 and pushed to Steam Cloud as a text file.
* When we push save file to Steam Cloud, we use steamworksClient.cloud.writeFile. This function requires a string as
* 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.
* 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}`);
try {
await saveCloudFile(steamSaveName, content);
saveCloudFile(steamSaveName, content);
} catch (error) {
log.error(error);
}
@@ -208,7 +206,7 @@ async function getSteamCloudSaveData() {
return Promise.reject("Steam Cloud is not Enabled");
}
log.debug(`Fetching Save in Steam Cloud`);
const cloudString = await getCloudFile();
const cloudString = getCloudFile();
// Decode cloudString to get save data back.
const saveData = Buffer.from(cloudString, "base64");
log.debug(`SaveData: ${saveData.length} bytes`);
@@ -216,7 +214,7 @@ async function getSteamCloudSaveData() {
}
async function saveGameToDisk(window, electronGameData) {
const currentFolder = await getSaveFolder(window);
const currentFolder = getSaveFolder(window);
let saveFolderSizeBytes = await getFolderSizeInBytes(currentFolder);
const maxFolderSizeBytes = store.get("autosave-quota", 1e8); // 100Mb per playerIndentifier
const remainingSpaceBytes = maxFolderSizeBytes - saveFolderSizeBytes;
@@ -264,7 +262,7 @@ async function saveGameToDisk(window, electronGameData) {
}
async function loadLastFromDisk(window) {
const folder = await getSaveFolder(window);
const folder = getSaveFolder(window);
const last = await getNewestFile(folder);
log.debug(`Last modified file: "${last.file}" (${last.stat.mtime.toLocaleString()})`);
return loadFileFromDisk(last.file);
@@ -286,7 +284,7 @@ async function loadFileFromDisk(path) {
function getSaveInformation(window, save) {
return new Promise((resolve) => {
ipcMain.once("get-save-info-response", async (event, data) => {
ipcMain.once("get-save-info-response", (event, data) => {
resolve(data);
});
window.webContents.send("get-save-info-request", save);
@@ -303,7 +301,7 @@ function getCurrentSave(window) {
}
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"}`);
});
window.webContents.send("push-save-request", { save, automatic });
@@ -379,7 +377,6 @@ module.exports = {
pushSaveGameForImport,
pushSaveDataToSteamCloud,
getSteamCloudSaveData,
getSteamCloudQuota,
deleteCloudFile,
saveGameToDisk,
loadLastFromDisk,

29
package-lock.json generated
View File

@@ -79,7 +79,7 @@
"copy-webpack-plugin": "^12.0.2",
"css-loader": "^7.1.2",
"csstype": "3.1.2",
"electron": "^29.4.5",
"electron": "^35.1.5",
"electron-packager": "^17.1.2",
"eslint": "^8.52.0",
"eslint-plugin-react": "^7.33.2",
@@ -5129,12 +5129,13 @@
"integrity": "sha512-xPSg0jm4mqgEkNhowKgZFBNtwoEwF6gJ4Dhww+GFpm3IgtNseHQZ5IqdNwnquZEoANxyDAKDRAdVo4Z72VvD/g=="
},
"node_modules/@types/node": {
"version": "20.14.14",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.14.tgz",
"integrity": "sha512-d64f00982fS9YoOgJkAMolK7MN8Iq3TDdVjchbYHdEmjth/DHowx82GnoA+tVUAN+7vxfYUgAzi+JXbKNd2SDQ==",
"version": "22.13.14",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.14.tgz",
"integrity": "sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~5.26.4"
"undici-types": "~6.20.0"
}
},
"node_modules/@types/node-forge": {
@@ -8175,14 +8176,15 @@
"dev": true
},
"node_modules/electron": {
"version": "29.4.5",
"resolved": "https://registry.npmjs.org/electron/-/electron-29.4.5.tgz",
"integrity": "sha512-DlEuzGbWBYl1Qr0qUYgNZdoixJg4YGHy2HC6fkRjSXSlb01UrQ5ORi8hNLzelzyYx8rNQyyE3zDUuk9EnZwYuA==",
"version": "35.1.5",
"resolved": "https://registry.npmjs.org/electron/-/electron-35.1.5.tgz",
"integrity": "sha512-LolvbKKQUSCGvEwbEQNt1cxD1t+YYClDNwBIjn4d28KM8FSqUn9zJuf6AbqNA7tVs9OFl/EQpmg/m4lZV1hH8g==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@electron/get": "^2.0.0",
"@types/node": "^20.9.0",
"@types/node": "^22.7.7",
"extract-zip": "^2.0.1"
},
"bin": {
@@ -18193,10 +18195,11 @@
}
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
"dev": true,
"license": "MIT"
},
"node_modules/unicode-canonical-property-names-ecmascript": {
"version": "2.0.0",

View File

@@ -80,7 +80,7 @@
"copy-webpack-plugin": "^12.0.2",
"css-loader": "^7.1.2",
"csstype": "3.1.2",
"electron": "^29.4.5",
"electron": "^35.1.5",
"electron-packager": "^17.1.2",
"eslint": "^8.52.0",
"eslint-plugin-react": "^7.33.2",

View File

@@ -15,11 +15,6 @@ cd ..
cp -r .app/* .package
cp -r electron/* .package
# steam_appid.txt would end up in the resource dir
rm .package/steam_appid.txt
BUILD_PLATFORM="${1:-"all"}"
# And finally build the app.
npm run electron:packager-$BUILD_PLATFORM
echo .build/* | xargs -n 1 cp electron/steam_appid.txt