| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556 |
- // Server entry point. Runs headless (`npm run server`) or inside an Electron utilityProcess.
- import { DEFAULT_MAP_ID, NET_PORT } from '../shared/constants';
- import { loadCharacterStats, loadMap, loadMaterials, resolveAssetsDir } from './assets';
- import { Game } from './game';
- import { GameServer } from './net';
- interface ParentPort {
- postMessage(message: unknown): void;
- }
- /** Set by Electron when this file runs in a utilityProcess. */
- const parentPort = (process as { parentPort?: ParentPort }).parentPort;
- function arg(name: string): string | undefined {
- const prefix = `--${name}=`;
- return process.argv.find((a) => a.startsWith(prefix))?.slice(prefix.length);
- }
- function main(): void {
- const assetsDir = resolveAssetsDir();
- const mapId = arg('map') ?? DEFAULT_MAP_ID;
- const port = Number(arg('port') ?? NET_PORT);
- // The only place the seed is drawn; everything downstream uses the seeded RNG.
- const seed = Number(arg('seed') ?? Date.now() % 0x7fffffff);
- const materials = loadMaterials(assetsDir);
- const { definition, terrain } = loadMap(assetsDir, mapId, materials);
- const game = new Game({
- map: definition,
- terrain,
- materials,
- seed,
- loadCharacter: (id) => loadCharacterStats(assetsDir, id),
- });
- const server = new GameServer(game, port, '0.0.0.0', () => {
- console.log(`[server] map "${mapId}" (seed ${seed}) listening on :${port}`);
- parentPort?.postMessage({ type: 'ready', port });
- });
- server.start();
- const shutdown = (): void => {
- server.close();
- process.exit(0);
- };
- process.on('SIGINT', shutdown);
- process.on('SIGTERM', shutdown);
- }
- try {
- main();
- } catch (err) {
- console.error('[server] failed to start:', err);
- parentPort?.postMessage({ type: 'error', message: err instanceof Error ? err.message : String(err) });
- process.exit(1);
- }
|