/// // runs the game in a worker without any permissions, the host does files and networking import type { GameServer } from "./game_server.ts"; import { TICK_MS } from "./game_server.ts"; import { GameToHost, HostToGame } from "./host_protocol.ts"; import { data_url, start_game } from "./load_mods.ts"; const SAVE_INTERVAL_MS = 30_000; let game: GameServer | undefined; let starting: Promise | undefined; function post(message: GameToHost) { self.postMessage(message); } self.onmessage = async (event: MessageEvent) => { const message = event.data; if (message.type === "init") { starting = init(message); return; } // players can connect while mods are still loading await starting; if (!game) { return; } switch (message.type) { case "connect": game.on_connect(message.conn); break; case "message": game.on_message(message.conn, message.data); break; case "disconnect": game.on_disconnect(message.conn); break; case "shutdown": post({ type: "save", data: game.save(), final: true }); break; } }; async function init(message: Extract) { try { game = await start_game( { send: (conn, data) => post({ type: "send", conn, data }), close: (conn) => post({ type: "close", conn }), }, message.save, message.default_seed, message.atlas, message.mods.map((mod) => ({ listing: mod.listing, data: mod.data, // the worker has no file access, so the host sends the code and it's imported from memory server_url: mod.server_code ? data_url(mod.server_code) : undefined, worldgen_url: mod.worldgen_code ? data_url(mod.worldgen_code) : undefined, })), ); } catch (e) { post({ type: "failed", error: e instanceof Error ? e.message : String(e) }); return; } setInterval(() => game!.tick(), TICK_MS); setInterval(() => { if (game!.world.dirty) { post({ type: "save", data: game!.save(), final: false }); } }, SAVE_INTERVAL_MS); post({ type: "ready", seed: game.world.seed }); }