1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2025-12-13 18:21:51 +00:00

Refactor file system electron event handling

This commit is contained in:
tobspr 2022-01-15 11:23:14 +01:00
parent a0071681e7
commit f12f81a125
5 changed files with 61 additions and 163 deletions

View File

@ -55,8 +55,8 @@ function createWindow() {
autoHideMenuBar: true,
webPreferences: {
nodeIntegration: false,
webSecurity: false,
// sandbox: true,
webSecurity: true,
sandbox: true,
contextIsolation: true,
preload: path.join(__dirname, "preload.js"),
},
@ -229,70 +229,33 @@ async function writeFileSafe(filename, contents) {
});
}
async function performFsJob(job) {
const fname = path.join(storePath, job.filename);
ipcMain.handle("fs-job", async (event, job) => {
const filenameSafe = job.filename.replace(/[^a-z\.\-_0-9]/i, "");
const fname = path.join(storePath, filenameSafe);
switch (job.type) {
case "read": {
if (!fs.existsSync(fname)) {
return {
// Special FILE_NOT_FOUND error code
error: "file_not_found",
};
}
try {
const data = await fs.promises.readFile(fname, "utf8");
return {
success: true,
data,
};
} catch (ex) {
return {
error: ex,
};
return { error: "file_not_found" };
}
return await fs.promises.readFile(fname, "utf8");
}
case "write": {
try {
await writeFileSafe(fname, job.contents);
return {
success: true,
data: job.contents,
};
} catch (ex) {
return {
error: ex,
};
}
return job.contents;
}
case "delete": {
try {
await fs.promises.unlink(fname);
} catch (ex) {
return {
error: ex,
};
}
return {
success: true,
data: null,
};
return;
}
default:
throw new Error("Unknown fs job: " + job.type);
}
}
ipcMain.on("fs-job", async (event, arg) => {
const result = await performFsJob(arg);
event.reply("fs-response", { id: arg.id, result });
});
ipcMain.on("open-mods-folder", async () => {
ipcMain.handle("open-mods-folder", async () => {
shell.openPath(modsPath);
});

View File

@ -1,3 +1,7 @@
const { contextBridge, ipcRenderer } = require("electron");
contextBridge.exposeInMainWorld("ipcRenderer", ipcRenderer);
contextBridge.exposeInMainWorld("ipcRenderer", {
invoke: ipcRenderer.invoke.bind(ipcRenderer),
on: ipcRenderer.on.bind(ipcRenderer),
send: ipcRenderer.send.bind(ipcRenderer),
});

View File

@ -49,9 +49,12 @@ function createWindow() {
// fullscreen: true,
autoHideMenuBar: true,
webPreferences: {
nodeIntegration: true,
webSecurity: false,
contextIsolation: false,
nodeIntegration: false,
webSecurity: true,
sandbox: true,
contextIsolation: true,
preload: path.join(__dirname, "preload.js"),
},
allowRunningInsecureContent: false,
});
@ -165,20 +168,20 @@ async function writeFileSafe(filename, contents) {
console.warn(prefix, "Concurrent write process on", filename);
}
await fileLock.acquire(filename, async () => {
fileLock.acquire(filename, async () => {
console.log(prefix, "Starting write on", niceFileName(filename), "in transaction", transactionId);
if (!fs.existsSync(filename)) {
// this one is easy
console.log(prefix, "Writing file instantly because it does not exist:", niceFileName(filename));
fs.writeFileSync(filename, contents, { encoding: "utf8" });
await fs.promises.writeFile(filename, contents, "utf8");
return;
}
// first, write a temporary file (.tmp-XXX)
const tempName = filename + ".tmp-" + transactionId;
console.log(prefix, "Writing temporary file", niceFileName(tempName));
fs.writeFileSync(tempName, contents, { encoding: "utf8" });
await fs.promises.writeFile(tempName, contents, "utf8");
// now, rename the original file to (.backup-XXX)
const oldTemporaryName = filename + ".backup-" + transactionId;
@ -189,7 +192,7 @@ async function writeFileSafe(filename, contents) {
"to",
niceFileName(oldTemporaryName)
);
fs.renameSync(filename, oldTemporaryName);
await fs.promises.rename(filename, oldTemporaryName);
// now, rename the temporary file (.tmp-XXX) to the target
console.log(
@ -199,7 +202,7 @@ async function writeFileSafe(filename, contents) {
"to the original",
niceFileName(filename)
);
fs.renameSync(tempName, filename);
await fs.promises.rename(tempName, filename);
// we are done now, try to create a backup, but don't fail if the backup fails
try {
@ -208,82 +211,43 @@ async function writeFileSafe(filename, contents) {
if (fs.existsSync(backupFileName)) {
console.log(prefix, "Deleting old backup file", niceFileName(backupFileName));
// delete the old backup
fs.unlinkSync(backupFileName);
await fs.promises.unlink(backupFileName);
}
// rename the old file to the new backup file
console.log(prefix, "Moving", niceFileName(oldTemporaryName), "to the backup file location");
fs.renameSync(oldTemporaryName, backupFileName);
await fs.promises.rename(oldTemporaryName, backupFileName);
} catch (ex) {
console.error(prefix, "Failed to switch backup files:", ex);
}
});
}
async function performFsJob(job) {
const fname = path.join(storePath, job.filename);
ipcMain.handle("fs-job", async (event, job) => {
const filenameSafe = job.filename.replace(/[^a-z\.\-_0-9]/i, "");
const fname = path.join(storePath, filenameSafe);
switch (job.type) {
case "read": {
if (!fs.existsSync(fname)) {
return {
// Special FILE_NOT_FOUND error code
error: "file_not_found",
};
}
try {
const data = fs.readFileSync(fname, { encoding: "utf8" });
return {
success: true,
data,
};
} catch (ex) {
console.error(ex);
return {
error: ex,
};
return { error: "file_not_found" };
}
return await fs.promises.readFile(fname, "utf8");
}
case "write": {
try {
writeFileSafe(fname, job.contents);
return {
success: true,
data: job.contents,
};
} catch (ex) {
console.error(ex);
return {
error: ex,
};
}
await writeFileSafe(fname, job.contents);
return job.contents;
}
case "delete": {
try {
fs.unlinkSync(fname);
} catch (ex) {
console.error(ex);
return {
error: ex,
};
}
return {
success: true,
data: null,
};
await fs.promises.unlink(fname);
return;
}
default:
throw new Error("Unkown fs job: " + job.type);
throw new Error("Unknown fs job: " + job.type);
}
}
ipcMain.on("fs-job", async (event, arg) => {
const result = await performFsJob(arg);
event.sender.send("fs-response", { id: arg.id, result });
});
wegame.init(isDev);
wegame.listen();

View File

@ -1,29 +1,8 @@
import { StorageInterface } from "../storage";
import { createLogger } from "../../core/logging";
const logger = createLogger("electron-storage");
import { FILE_NOT_FOUND, StorageInterface } from "../storage";
export class StorageImplElectron extends StorageInterface {
constructor(app) {
super(app);
/** @type {Object.<number, {resolve:Function, reject: Function}>} */
this.jobs = {};
this.jobId = 0;
ipcRenderer.on("fs-response", (event, arg) => {
const id = arg.id;
if (!this.jobs[id]) {
logger.warn("Got unhandled FS response, job not known:", id);
return;
}
const { resolve, reject } = this.jobs[id];
if (arg.result.success) {
resolve(arg.result.data);
} else {
reject(arg.result.error);
}
});
}
initialize() {
@ -31,44 +10,32 @@ export class StorageImplElectron extends StorageInterface {
}
writeFileAsync(filename, contents) {
return new Promise((resolve, reject) => {
// ipcMain
const jobId = ++this.jobId;
this.jobs[jobId] = { resolve, reject };
ipcRenderer.send("fs-job", {
return ipcRenderer.invoke("fs-job", {
type: "write",
filename,
contents,
id: jobId,
});
});
}
readFileAsync(filename) {
return new Promise((resolve, reject) => {
// ipcMain
const jobId = ++this.jobId;
this.jobs[jobId] = { resolve, reject };
ipcRenderer.send("fs-job", {
return ipcRenderer
.invoke("fs-job", {
type: "read",
filename,
id: jobId,
});
})
.then(res => {
if (res && res.error === FILE_NOT_FOUND) {
throw FILE_NOT_FOUND;
}
return res;
});
}
deleteFileAsync(filename) {
return new Promise((resolve, reject) => {
// ipcMain
const jobId = ++this.jobId;
this.jobs[jobId] = { resolve, reject };
ipcRenderer.send("fs-job", {
return ipcRenderer.invoke("fs-job", {
type: "delete",
filename,
id: jobId,
});
});
}
}

View File

@ -116,7 +116,7 @@ export class ModsState extends TextualGameState {
this.dialogs.showWarning(T.global.error, T.mods.folderOnlyStandalone);
return;
}
ipcRenderer.send("open-mods-folder");
ipcRenderer.invoke("open-mods-folder");
}
onSteamLinkClicked() {