Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166// The Steam shell for oskiewar: an Electron window around the same browser// runtime oskiewar.com serves and the reel factory renders three times a day.// Nothing here is a fourth runtime — the game file is byte-identical to the// one xbox/live serves, and this process only supplies the host it expects.
const { app, BrowserWindow, protocol, net } = require("electron");const { readFileSync } = require("node:fs");const { extname, join, normalize, resolve } = require("node:path");const { pathToFileURL } = require("node:url");
// Packaged, the runtime lives beside the app (stage.mjs put it there) and the// out-of-tree assets have already been flattened into the same root. In the// working tree we read xbox/live directly, so a save is the next reload.const packaged = app.isPackaged;const staged = join(process.resourcesPath || "", "staged");const live = packaged ? staged : resolve(__dirname, "../../live");const repo = packaged ? staged : resolve(live, "../..");
// The shell reaches outside xbox/live for the QR encoder, analytics, cursors// and the typeface; oskiewar.com resolves those through lith. Kept in step// with xbox/tools/serve-live.mjs — if that map grows, this one does too.const elsewhere = new Map([ ["/aesthetic.computer/dep/@akamfoad/qr/qr.mjs", "system/public/aesthetic.computer/dep/@akamfoad/qr/qr.mjs"], ["/aesthetic.computer/lib/product-analytics.mjs", "system/public/aesthetic.computer/lib/product-analytics.mjs"], ["/aesthetic.computer/lib/oskiewar-analytics.mjs", "system/public/aesthetic.computer/lib/oskiewar-analytics.mjs"], ["/aesthetic.computer/cursors/precise.svg", "system/public/aesthetic.computer/cursors/precise.svg"], ["/aesthetic.computer/cursors/active.svg", "system/public/aesthetic.computer/cursors/active.svg"], ["/ComicRelief-Regular.ttf", "system/public/papers.aesthetic.computer/foundry/fonts/ComicRelief-Regular.ttf"], ["/ComicRelief-Regular.woff2", "system/public/papers.aesthetic.computer/foundry/fonts/ComicRelief-Regular.woff2"], // account.mjs imports this statically, so without it the page's whole // module graph fails and the window stays blank. It ships inert: the // account corner is hidden by the trim and nothing calls it. ["/aesthetic.computer/lib/auth0-otp.mjs", "system/public/aesthetic.computer/lib/auth0-otp.mjs"],]);
// A module script will not load over a scheme Chromium treats as opaque, and// the page is all modules, so app:// has to be declared before app ready.protocol.registerSchemesAsPrivileged([{ scheme: "app", privileges: { standard: true, secure: true, supportFetchAPI: true, stream: true },}]);
function fileFor(pathname) { if (pathname === "/" || pathname === "/index.html") return join(live, packaged ? "index.html" : "mac-test.html"); // stage.mjs already flattened the out-of-tree six to their URL paths, so a // packaged build needs no map — everything resolves under one root. if (!packaged) { const mapped = elsewhere.get(pathname); if (mapped) return join(repo, mapped); } const target = normalize(join(live, pathname)); return target.startsWith(live) ? target : "";}
async function serve(request) { const { pathname } = new URL(request.url); // The page asks for its analytics config at boot and tolerates a failure, // but an empty object is quieter than a 404 in the console every launch. if (pathname === "/api/product-analytics-config") return new Response("{}", { headers: { "content-type": "application/json" } }); const path = fileFor(pathname); if (!path) return new Response("outside xbox/live", { status: 403 }); try { return await net.fetch(pathToFileURL(path).toString()); } catch { // Every remote the page reaches for is wrapped in a catch, so a miss is // survivable — but an unnamed one is how an asset silently stops shipping. console.log(`[shell] missing: ${pathname}`); return new Response("not found", { status: 404 }); }}
// Steam's overlay hooks the GPU present call, which Electron's out-of-process// GPU defeats. steamworks.js applies the in-process-gpu switch for us, but the// dependency is optional so the shell still runs before anyone has installed// it — and appid 480 (Spacewar) stands in until oskiewar has its own.function initSteam() { let steamworks; try { steamworks = require("steamworks.js"); } catch { console.log("[steam] steamworks.js absent — running unwired"); return null; } try { steamworks.electronEnableSteamOverlay(); const appId = Number(readFileSync(join(__dirname, "steam_appid.txt"), "utf8").trim()); const client = steamworks.init(appId); console.log(`[steam] initialized against appid ${appId}`); return client; } catch (error) { console.log(`[steam] not initialized: ${error.message}`); return null; }}
const steam = initSteam();
app.whenReady().then(() => { protocol.handle("app", serve);
const win = new BrowserWindow({ width: 1280, height: 720, useContentSize: true, backgroundColor: "#07081c", title: "oskiewar", webPreferences: { preload: join(__dirname, "preload.js"), contextIsolation: true, sandbox: false, }, });
// The game pins vertical to 1080 logical units and derives width from the // box aspect, so a 16:9 window *is* 1920x1080 to the game. There is no // set-resolution host call; this is the whole mechanism. win.setAspectRatio(16 / 9); win.setMenuBarVisibility(false); // The page cycles document.title as a tab toy; a window is not a tab. win.on("page-title-updated", (event) => event.preventDefault()); // The page's console is the only place a packaged build says what went // wrong, so it is mirrored to stdout: launch the binary from a terminal // and the boot line (or the exception) is right there. win.webContents.on("console-message", (event) => { const { level, message, lineNumber, sourceId } = event; if (level === "error" || level === "warning" || /\[boot\]/.test(message)) console.log(`[page:${level}] ${message} (${sourceId}:${lineNumber})`); }); win.webContents.on("render-process-gone", (_, details) => console.log(`[page] renderer gone: ${details.reason}`)); win.loadURL("app://local/");
// The frame driver stops on visibilitychange, which a backgrounded desktop // window never fires — without this it free-runs behind other apps. const visible = (state) => win.webContents.executeJavaScript( `globalThis.__oskiewarDriver?.setVisible(${state})`, true).catch(() => {}); win.on("focus", () => visible(true)); win.on("blur", () => visible(false));
if (steam) console.log(`[steam] player: ${steam.localplayer.getName()}`);
// OSKIEWAR_SHELL_SHOT=/path.png: boot, wait eight seconds, write what the // window shows, quit. A packaged build's smoke test with no screen recorder // in the loop — screencapture cannot see a window it cannot find. const shot = process.env.OSKIEWAR_SHELL_SHOT; if (shot) setTimeout(async () => { const image = await win.webContents.capturePage(); require("node:fs").writeFileSync(shot, image.toPNG()); console.log(`[shell] wrote ${shot} (${image.getSize().width}×${image.getSize().height})`); app.quit(); }, 8000);});
app.on("window-all-closed", () => app.quit());