td

ArchitectureOverview
Login

ArchitectureOverview

Home · Architecture · Combat · Extensions · Known gaps · Source index

Architecture Overview

Shape of the program

The current program is a single-threaded Macroquad game. World owns all mutable simulation state. Systems run in a fixed sequence, behaviors receive a read-only spatial view plus a write-only command buffer, and registries build trait-object content from typed IDs.

main loop
  |
  +-- systems -------- direct local entity updates
  |      |
  |      +-- behavior dispatch
  |             | read: EnemySnapshot + Grid
  |             | write: CommandBuffer
  |             v
  +------ World::apply_commands
                |
                +-- mutate world
                +-- produce GameEvent
                          |
                          +-- bounded delivery waves

Despite the hecs dependency, this is not currently an ECS. Entities are plain structs stored in Vecs, except the singleton tower and player. See src/world.rs.

Module responsibilities

Area Principal source Current role
Entrypoint src/main.rs Window configuration, frame clock, screen input
Orchestration src/systems.rs Phase order, simulation passes, rendering, crate UI
State and mutation src/world.rs World ownership, spawning, IDs, command application, events
Deferred writes src/command.rs Command and CommandBuffer
Hook capabilities src/context.rs Shared and event-specific contexts, damage accumulator
Extensible hooks src/behavior/ Trait families, cloning, dispatch
Content construction src/registry.rs Generic registry, typed IDs, built-in definitions
Domain entities src/enemy.rs, src/tower.rs, src/pool.rs Runtime data and local methods
Concrete content src/weapons/, src/enemies/, src/pools/ Weapon, enemy, movement, split, pool implementations
Query support src/spatial.rs Enemy snapshots, uniform grid, nearest/radius/hit queries
Utilities src/rng.rs, src/geometry.rs Deterministic streams and vector helpers

Ownership and borrowing strategy

Each behavior pass follows run_system:

  1. Fork a deterministic child RNG.
  2. Snapshot all enemies.
  3. Rebuild the world's uniform grid from that snapshot.
  4. Iterate a mutable entity vector while behavior hooks query the immutable snapshot.
  5. Append requested cross-entity changes to a command buffer.
  6. End the borrow, then apply commands and deliver events.

This “read by snapshot, write by ID” boundary avoids re-entrant world mutation and Rust borrow conflicts. Snapshot data may be stale within a pass, but command application resolves IDs against live world state. src/spatial.rs documents and implements this split.

Identity and content

Runtime entities use monotonically allocated u64 EntityIds. Content uses a separate generic Id<K> whose marker type prevents mixing weapon, enemy, damage, and status IDs. A registry interns stable string names and stores factories, display names, and priorities. Factories produce fresh enemies or boxed behavior objects; no serialization or dynamic loading is live.

Architectural status

Live

Partial

Unwired

For exact temporal ordering, see Frame Lifecycle. For extension contracts and their actual reachability, see Behavior System.

Home · Architecture · Combat · Extensions · Known gaps · Source index