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

View File

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

View File

@ -1,29 +1,8 @@
import { StorageInterface } from "../storage"; import { FILE_NOT_FOUND, StorageInterface } from "../storage";
import { createLogger } from "../../core/logging";
const logger = createLogger("electron-storage");
export class StorageImplElectron extends StorageInterface { export class StorageImplElectron extends StorageInterface {
constructor(app) { constructor(app) {
super(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() { initialize() {
@ -31,44 +10,32 @@ export class StorageImplElectron extends StorageInterface {
} }
writeFileAsync(filename, contents) { writeFileAsync(filename, contents) {
return new Promise((resolve, reject) => { return ipcRenderer.invoke("fs-job", {
// ipcMain
const jobId = ++this.jobId;
this.jobs[jobId] = { resolve, reject };
ipcRenderer.send("fs-job", {
type: "write", type: "write",
filename, filename,
contents, contents,
id: jobId,
});
}); });
} }
readFileAsync(filename) { readFileAsync(filename) {
return new Promise((resolve, reject) => { return ipcRenderer
// ipcMain .invoke("fs-job", {
const jobId = ++this.jobId;
this.jobs[jobId] = { resolve, reject };
ipcRenderer.send("fs-job", {
type: "read", type: "read",
filename, filename,
id: jobId, })
}); .then(res => {
if (res && res.error === FILE_NOT_FOUND) {
throw FILE_NOT_FOUND;
}
return res;
}); });
} }
deleteFileAsync(filename) { deleteFileAsync(filename) {
return new Promise((resolve, reject) => { return ipcRenderer.invoke("fs-job", {
// ipcMain
const jobId = ++this.jobId;
this.jobs[jobId] = { resolve, reject };
ipcRenderer.send("fs-job", {
type: "delete", type: "delete",
filename, filename,
id: jobId,
});
}); });
} }
} }

View File

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