const { app, BrowserWindow, protocol, session, Menu, dialog, } = require("electron"); const path = require("node:path"); const { readGame } = require("./protocol.cjs"); const settings = require("./game-config.json"); protocol.registerSchemesAsPrivileged([ { scheme: "forma", privileges: { standard: true, secure: true, supportFetchAPI: true, corsEnabled: true, stream: true, }, }, ]); app.setName(settings.name); let win; async function createWindow() { win = new BrowserWindow({ title: settings.name, width: settings.width, height: settings.height, minWidth: 320, minHeight: 320, fullscreen: settings.fullscreen, backgroundColor: "#151719", show: false, autoHideMenuBar: true, webPreferences: { sandbox: true, contextIsolation: true, nodeIntegration: false, nodeIntegrationInWorker: false, webSecurity: true, devTools: settings.mode === "debug", }, }); win.webContents.setWindowOpenHandler(() => ({ action: "deny" })); win.webContents.on("will-navigate", (e, url) => { if (url !== "forma://game/index.html") e.preventDefault(); }); win.webContents.on("will-attach-webview", (e) => e.preventDefault()); win.webContents.on("before-input-event", (e, input) => { if (input.type === "keyDown" && input.key === "F11") { win.setFullScreen(!win.isFullScreen()); e.preventDefault(); } if ( input.type === "keyDown" && input.key === "Escape" && win.isFullScreen() ) win.setFullScreen(false); }); win.once("ready-to-show", () => win.show()); await win.loadURL("forma://game/index.html"); } app .whenReady() .then(async () => { if (app.commandLine.hasSwitch("no-sandbox")) { dialog.showErrorBox( "Sandbox unavailable", "This game requires Chromium sandbox support. Enable unprivileged user namespaces on Linux, then restart without --no-sandbox.", ); app.exit(1); return; } Menu.setApplicationMenu(null); session.defaultSession.setPermissionRequestHandler((_wc, _p, cb) => cb(false), ); session.defaultSession.setPermissionCheckHandler(() => false); session.defaultSession.webRequest.onBeforeRequest((details, cb) => cb({ cancel: !details.url.startsWith("forma://game/") && !details.url.startsWith("blob:forma://game/"), }), ); protocol.handle("forma", async (request) => { const r = await readGame( path.join(__dirname, "game"), request.url, request.method, ); return new Response(r.body, r); }); await createWindow(); }) .catch((e) => { console.error(e); app.exit(1); }); app.on("window-all-closed", () => app.quit());