mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-12-09 16:21:51 +00:00
Basic FS watcher for --load-mod mods
Add a --watch command line flag to be used in tandem with --load-mod. Chokidar is used to detect file updates, and a page reload is triggered when there are any changes to watched mod files. Only mods loaded via --load-mod are affected. This implementation has a minor issue with how cache is cleared - removing disk cache is a bit too aggressive - but the only alternative I could find is to use a non-persistent Electron session, which would get rid of disk cache entirely (this is not a concern).
This commit is contained in:
parent
7cb3477a90
commit
6c3f91e587
29
electron/package-lock.json
generated
29
electron/package-lock.json
generated
@ -9,6 +9,7 @@
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chokidar": "^4.0.3",
|
||||
"semver": "^7.7.1",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
@ -192,6 +193,21 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
|
||||
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"readdirp": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14.16.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/clone-response": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz",
|
||||
@ -733,6 +749,19 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
|
||||
"integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 14.18.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-alpn": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
"start": "tsc && electron ."
|
||||
},
|
||||
"dependencies": {
|
||||
"chokidar": "^4.0.3",
|
||||
"semver": "^7.7.1",
|
||||
"zod": "^3.24.2"
|
||||
},
|
||||
|
||||
@ -56,6 +56,12 @@ function createWindow() {
|
||||
ipc.install(window);
|
||||
window.loadURL(pageUrl);
|
||||
|
||||
modLoader.on("forcereload", () => {
|
||||
// TODO: Find a better way to manage cache when force
|
||||
// reloading (use a non-persistent session?)
|
||||
window.webContents.session.clearData({ dataTypes: ["cache"] }).then(() => window.reload());
|
||||
});
|
||||
|
||||
// Redirect any kind of main frame navigation to external applications
|
||||
window.webContents.on("will-navigate", (ev, url) => {
|
||||
if (url === window.webContents.getURL()) {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import EventEmitter from "node:events";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { DevelopmentModLocator, DistroModLocator, ModLocator, UserModLocator } from "./locator.js";
|
||||
@ -48,14 +49,29 @@ class Mod {
|
||||
}
|
||||
}
|
||||
|
||||
export class ModLoader {
|
||||
export class ModLoader extends EventEmitter {
|
||||
private mods: Mod[] = [];
|
||||
private readonly locators = new Map<ModSource, ModLocator>();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.locators.set("user", new UserModLocator());
|
||||
this.locators.set("distro", new DistroModLocator());
|
||||
this.locators.set("dev", new DevelopmentModLocator());
|
||||
|
||||
const devLocator = new DevelopmentModLocator();
|
||||
this.locators.set("dev", devLocator);
|
||||
|
||||
// If requested, restart automatically when dev mods are modified
|
||||
devLocator.fsWatcher?.on("all", () => this.forceReload());
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets modloader state and reloads all mods, then triggers page reload.
|
||||
*/
|
||||
async forceReload() {
|
||||
await this.loadMods();
|
||||
this.emit("forcereload");
|
||||
}
|
||||
|
||||
async loadMods(): Promise<void> {
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import chokidar, { FSWatcher } from "chokidar";
|
||||
import { app } from "electron";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
@ -10,6 +11,7 @@ const USER_MODS_DIR = path.join(userData, "mods");
|
||||
const DISTRO_MODS_DIR = path.join(executableDir, "mods");
|
||||
|
||||
const DEV_SWITCH = "load-mod";
|
||||
const DEV_WATCH_SWITCH = "watch";
|
||||
const DEV_USER_MOD_PREFIX = "@/";
|
||||
|
||||
export interface ModLocator {
|
||||
@ -153,6 +155,7 @@ export class DistroModLocator extends DirectoryModLocator {
|
||||
|
||||
export class DevelopmentModLocator implements ModLocator {
|
||||
readonly priority = 0;
|
||||
readonly fsWatcher: FSWatcher | null = null;
|
||||
|
||||
private readonly modFiles: string[] = [];
|
||||
private readonly disabledMods = new Set<string>();
|
||||
@ -166,6 +169,19 @@ export class DevelopmentModLocator implements ModLocator {
|
||||
|
||||
const resolved = switchValue.split(",").map(f => this.resolveFile(f));
|
||||
this.modFiles.push(...resolved);
|
||||
|
||||
const watchMode = app.commandLine.hasSwitch(DEV_WATCH_SWITCH);
|
||||
if (!watchMode || this.modFiles.length === 0) {
|
||||
// Skip setting up chokidar
|
||||
return;
|
||||
}
|
||||
|
||||
this.fsWatcher = chokidar.watch(this.modFiles, {
|
||||
persistent: false,
|
||||
ignoreInitial: true,
|
||||
awaitWriteFinish: true,
|
||||
atomic: true,
|
||||
});
|
||||
}
|
||||
|
||||
locateMods(): Promise<string[]> {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user