Files
Crystal/docs/ARCHITECTURE.md
T
2026-09-23 09:23:29 +02:00

4.1 KiB

Architecture

The default application is a native TypeScript game. It loads no CPU interpreter, Game Boy hardware model or WebAssembly emulator. The reference emulator has a separate entry point and build.

flowchart LR
  ROM[User ROM] --> Gate[Exact revision validation]
  Gate --> Assets[ROM reader and asset decoders]
  Assets --> World[Native world rules]
  Assets --> Scripts[Native event and text interpreters]
  Scripts --> World
  Input[Touch and keyboard] --> Runtime[Native runtime]
  Runtime --> World
  World --> View[Canvas world and DOM dialogue]
  Session[Application session] --> Runtime
  Session --> Saves[Native save storage]
  Reference[Separate reference emulator] -. local comparisons only .-> World
Module Responsibility
src/rom/validate.ts Exact cartridge identity, no networking
src/game/rom Banked reads, LZ, maps/tilesets/sprites/palettes and pinned addresses
src/game/world Position, movement, collision, warps, visibility, time selection and native save validation
src/game/scripts The game's event/text formats, no GB instructions
src/game/rendering Planar pixels, metatiles, camera and sprites
src/game/runtime.ts Frame scheduling, input, interpolation and presentation updates
src/input Abstract buttons, multiple sources and browser bindings
src/app Runtime-independent load/play/pause/backup lifecycle
src/storage Versioned envelope, IndexedDB transactions and ordered writes
src/ui, src/main.ts UI composition and DOM dialogue, no ROM offsets
src/platform Browser locks and future native platform adapters
src/reference, src/emulator Original-game reference, excluded from the native build
reference/public Licensed emulator files for the reference build only

GameRuntime is a lifecycle interface, not an emulator contract. Native state is validated JSON: map, position, facing, name/avatar, in-game tile count, flags and sprite variables. It contains no graphics, music, dialogue, GB memory or ROM bytes. The backup envelope retains the historical coreId field; native backups use it for their runtime/schema ID.

Native and reference sessions use different IndexedDB databases and Web Locks. Imports reject incompatible schemas and invalid positions before changing the world. A failed import restores the prior state. Original cartridge saves and emulator snapshots cannot be used as native saves.

Scripts evaluate against a private draft. Unknown commands abort it. Dialogue commits only after the last page is acknowledged. A mid-dialogue save contains the last committed state, allowing the interaction to be replayed without partial rewards/flags. Automatic story scenes and callbacks are explicitly disabled in the exploration milestone.

Asset caches and the ROM reader remain in memory for lazy extraction. Neither ROMs nor extracted assets enter IndexedDB, the build or a backend. Vite blocks serving the root ROM and private reference checkout during development.

Introduce future modules with working features: game/battle, game/party, game/inventory, game/audio, then features/link and features/walking. Rules must not import UI/browser modules. Transports and step providers must not write directly into world state.