From efb9eee28641a050664edb10143e5542aede5494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D0=B8=D1=97=D0=BB=20=D0=93=D1=80=D0=B8?= =?UTF-8?q?=D0=B3=D0=BE=D1=80=27=D1=94=D0=B2?= Date: Tue, 10 Jun 2025 12:15:38 +0300 Subject: [PATCH] Remove main process compression To be simplified and replaced with renderer-side web workers. --- electron/src/fsjob.ts | 38 +++++++++++----------- electron/src/ipc.ts | 5 ++- electron/src/storage/interface.ts | 5 --- electron/src/storage/raw.ts | 16 --------- electron/src/storage/saves.ts | 54 ------------------------------- 5 files changed, 20 insertions(+), 98 deletions(-) delete mode 100644 electron/src/storage/interface.ts delete mode 100644 electron/src/storage/raw.ts delete mode 100644 electron/src/storage/saves.ts diff --git a/electron/src/fsjob.ts b/electron/src/fsjob.ts index 9cc3c866..ffcf9003 100644 --- a/electron/src/fsjob.ts +++ b/electron/src/fsjob.ts @@ -2,7 +2,6 @@ import { BrowserWindow, dialog, FileFilter } from "electron"; import fs from "fs/promises"; import path from "path"; import { userData } from "./config.js"; -import { StorageInterface } from "./storage/interface.js"; interface GenericFsJob { id: string; @@ -11,30 +10,28 @@ interface GenericFsJob { export type InitializeFsJob = GenericFsJob & { type: "initialize" }; type ListFsJob = GenericFsJob & { type: "list"; filename: string }; type ReadFsJob = GenericFsJob & { type: "read"; filename: string }; -type WriteFsJob = GenericFsJob & { type: "write"; filename: string; contents: T }; +type WriteFsJob = GenericFsJob & { type: "write"; filename: string; contents: Uint8Array }; type DeleteFsJob = GenericFsJob & { type: "delete"; filename: string }; type OpenExternalFsJob = GenericFsJob & { type: "open-external"; extension: string }; -type SaveExternalFsJob = GenericFsJob & { type: "save-external"; filename: string; contents: T }; +type SaveExternalFsJob = GenericFsJob & { type: "save-external"; filename: string; contents: Uint8Array }; -export type FsJob = +export type FsJob = | InitializeFsJob | ListFsJob | ReadFsJob - | WriteFsJob + | WriteFsJob | DeleteFsJob | OpenExternalFsJob - | SaveExternalFsJob; -type FsJobResult = T | string[] | void; + | SaveExternalFsJob; +type FsJobResult = Uint8Array | string[] | void; -export class FsJobHandler { +export class FsJobHandler { readonly rootDir: string; - private readonly storage: StorageInterface; private initialized = false; - constructor(subDir: string, storage: StorageInterface) { + constructor(subDir: string) { this.rootDir = path.join(userData, subDir); - this.storage = storage; } async initialize(): Promise { @@ -47,7 +44,7 @@ export class FsJobHandler { this.initialized = true; } - handleJob(job: FsJob): Promise> { + handleJob(job: FsJob): Promise { switch (job.type) { case "initialize": return this.initialize(); @@ -63,18 +60,18 @@ export class FsJobHandler { case "list": return this.list(filename); case "read": - return this.storage.read(filename); + return fs.readFile(filename); case "write": return this.write(filename, job.contents); case "delete": - return this.storage.delete(filename); + return fs.unlink(filename); } // @ts-expect-error this method can actually receive garbage throw new Error(`Unknown FS job type: ${job.type}`); } - private async openExternal(extension: string): Promise { + private async openExternal(extension: string): Promise { const filters = this.getFileDialogFilters(extension === "*" ? undefined : extension); const window = BrowserWindow.getAllWindows()[0]!; @@ -83,10 +80,10 @@ export class FsJobHandler { return undefined; } - return await this.storage.read(result.filePaths[0]); + return await fs.readFile(result.filePaths[0]); } - private async saveExternal(filename: string, contents: T): Promise { + private async saveExternal(filename: string, contents: Uint8Array): Promise { // Try to guess extension const ext = filename.indexOf(".") < 1 ? filename.split(".").at(-1)! : undefined; const filters = this.getFileDialogFilters(ext); @@ -97,7 +94,7 @@ export class FsJobHandler { return; } - return await this.storage.write(result.filePath, contents); + return await fs.writeFile(result.filePath, contents); } private getFileDialogFilters(extension?: string): FileFilter[] { @@ -118,12 +115,13 @@ export class FsJobHandler { return fs.readdir(subdir); } - private async write(file: string, contents: T): Promise { + private async write(file: string, contents: Uint8Array): Promise { // The target directory might not exist, ensure it does const parentDir = path.dirname(file); await fs.mkdir(parentDir, { recursive: true }); - await this.storage.write(file, contents); + console.log(contents); + await fs.writeFile(file, contents); } private safeFileName(name: string) { diff --git a/electron/src/ipc.ts b/electron/src/ipc.ts index d44c972e..b3cd52a1 100644 --- a/electron/src/ipc.ts +++ b/electron/src/ipc.ts @@ -1,10 +1,9 @@ import { BrowserWindow, IpcMainInvokeEvent, ipcMain } from "electron"; import { FsJob, FsJobHandler } from "./fsjob.js"; import { ModLoader } from "./mods/loader.js"; -import { SavesStorage } from "./storage/saves.js"; export class IpcHandler { - private readonly savesHandler = new FsJobHandler("saves", new SavesStorage()); + private readonly savesHandler = new FsJobHandler("saves"); private readonly modLoader: ModLoader; constructor(modLoader: ModLoader) { @@ -20,7 +19,7 @@ export class IpcHandler { // ipcMain.handle("open-mods-folder", ...) } - private handleFsJob(_event: IpcMainInvokeEvent, job: FsJob) { + private handleFsJob(_event: IpcMainInvokeEvent, job: FsJob) { if (job.id !== "saves") { throw new Error("Storages other than saves/ are not implemented yet"); } diff --git a/electron/src/storage/interface.ts b/electron/src/storage/interface.ts deleted file mode 100644 index f0e59804..00000000 --- a/electron/src/storage/interface.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface StorageInterface { - read(file: string): Promise; - write(file: string, contents: T): Promise; - delete(file: string): Promise; -} diff --git a/electron/src/storage/raw.ts b/electron/src/storage/raw.ts deleted file mode 100644 index 0d6a1a56..00000000 --- a/electron/src/storage/raw.ts +++ /dev/null @@ -1,16 +0,0 @@ -import fs from "node:fs/promises"; -import { StorageInterface } from "./interface.js"; - -export class RawStorage implements StorageInterface { - read(file: string): Promise { - return fs.readFile(file, "utf-8"); - } - - write(file: string, contents: string): Promise { - return fs.writeFile(file, contents, "utf-8"); - } - - delete(file: string): Promise { - return fs.unlink(file); - } -} diff --git a/electron/src/storage/saves.ts b/electron/src/storage/saves.ts deleted file mode 100644 index 750bd571..00000000 --- a/electron/src/storage/saves.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { decodeAsync, encode } from "@msgpack/msgpack"; -import fs from "node:fs"; -import { pipeline } from "node:stream/promises"; -import { createGunzip, createGzip } from "node:zlib"; -import { StorageInterface } from "./interface.js"; - -/** - * This storage implementation is used for savegame files and other - * ReadWriteProxy objects. It uses gzipped MessagePack as the file format. - */ -export class SavesStorage implements StorageInterface { - async read(file: string): Promise { - const stream = fs.createReadStream(file); - const gunzip = createGunzip(); - - try { - // Any filesystem errors will be uncovered here. This code ensures we return the most - // relevant rejection, or resolve with the decoded data - const [readResult, decodeResult] = await Promise.allSettled([ - pipeline(stream, gunzip), - decodeAsync(gunzip), - ]); - - if (decodeResult.status === "fulfilled") { - return decodeResult.value; - } - - // Return the most relevant error - throw readResult.status === "rejected" ? readResult.reason : decodeResult.reason; - } finally { - stream.close(); - gunzip.close(); - } - } - - async write(file: string, contents: unknown): Promise { - const stream = fs.createWriteStream(file); - const gzip = createGzip(); - - try { - const encoded = encode(contents); - const blob = new Blob([encoded]); - - return await pipeline(blob.stream(), gzip, stream); - } finally { - gzip.close(); - stream.close(); - } - } - - delete(file: string): Promise { - return fs.promises.unlink(file); - } -}