The application is split into two JavaScript runtimes:
- Main thread (frontend)
- Runs
dist/frontend.js. - Creates the Worker (
dist/backend.js). - Owns DOM and canvas rendering.
- Handles user input and turns it into transport messages.
- Runs
- Worker thread (backend)
- Runs the game engine and clients.
- Instantiates players (1 human + AI opponents).
- Produces full game state and incremental patches.
- Browser loads
index.htmland startsdist/frontend.js. src/js/frontend.tscreatesRenderer(new WorkerTransport(new Worker('dist/backend.js')))and callsinit().- Worker starts from
src/js/backend.tsand createsGame(new ParentTransport()). - Frontend menu sends
startonce setup is complete. - Backend binds engine events, starts engine, and dynamically imports generated plugin imports (
src/js/plugins.ts).
src/js/Engine/Game.ts- Worker-side bootstrap + option handling (
setOption,getOptions,setOptions). - Creates players and chooses client implementation (
DataTransferClientfor player 0,SimpleAIClientfor others).
- Worker-side bootstrap + option handling (
src/js/Engine/DataTransferClient.ts- Adapts core game model into UI-transferable objects.
- Handles player actions and cheats from frontend.
- Emits
gameData(initial full snapshot) andgameDataPatch(incremental updates).
src/js/UI/Renderer.ts- Frontend orchestrator; currently monolithic (~1,500 LOC).
- Reconstitutes plain-object graph into rich UI state.
- Coordinates map layers, action panels, menu windows, notifications, hotkeys.
src/js/UI/components/*- Window and UI primitives for reports, city/unit views, menu flows, and overlays.
src/js/UI/components/Map/*- Canvas layer renderers (terrain, units, cities, fog, improvements, etc.).
- Backend is effectively source-of-truth for game state.
- Frontend holds a large mutable object map (
objectMap) and applies patches in place. - UI emits custom events (
patchdatareceived,dataupdated) for dependent components. - Reconstitution (
reconstituteData) rebuilds object references from transport payloads.
Core channels are defined in src/js/Engine/Transport.ts:
- Control/config:
start,setOption,setOptions,getOptions,notification - Gameplay:
action,chooseFromList,gameNotification - State sync:
gameData,gameDataPatch - Debug/cheat:
cheat
quit and restart are now declared in TransportDataMap, but each is currently one-directional: the backend sends restart on local-player defeat with no frontend receiver, and the main menu sends quit with no backend receiver.
- Current architecture already isolates engine logic from UI logic via worker transport.
- The largest complexity concentration is the frontend orchestration in
Renderer. - A reactive store-based rewrite can preserve worker boundary while replacing in-place patch application + custom event plumbing with derived state/selectors.