Files
forgotten_caves/notes/roadmap.md
T
2026-06-17 08:23:13 +02:00

5.6 KiB
Executable File
Raw Blame History

Roadmap

Workspace layout: shared/ (wire types), server/ (authoritative sim), client/ (render terminal). The platform layer — window, GPU, input, RGB332 palette — lives in the external pbio crate (git dependency), not in this repo.


Achieved milestones

  • TGA loader: Image, from_tga, to_tileset, decode_rle in client/src/assets.rs
  • Pixel helpers: set_pixel, extract, blit, blit_tile in client/src/game/pixelhelper.rs — signed i32 offsets, clip against all four framebuffer edges, index 0 = transparent
  • Input system: GameAction, InputMap, InputState in client/src/input.rs (over pbio::Key)
  • Protocol spec: notes/protocol.md — Header, State, Action, Chunk, Entity, EntityQuery, EntityDetail, Ping/Pong; reliability model
  • Shared crate: packet structs as bytemuck::Pod with compile-time size assertions, player_action / packet_type constants, chunk_id / chunk_coords
  • Server binary: 24 Hz tick loop, 3×3-chunk test world (build_world), wall collision, per-client action queue, ChunkPacket dispatch on cache miss, stale-client eviction
  • Client net: NetClient in client/src/net.rs, non-blocking UDP recv, StatePacket / EntityPacket / ChunkPacket / PongPacket dispatch, entity-checksum retransmit
  • End-to-end loop: player moves on server, position reflected in EntityPacket, rendered on client
  • Tile rendering: chunk-based world rendering from received ChunkPacket data (6-bit unpack)
  • Tick-based movement: movement resolved in entity_tick, currently every 4th tick (sim.rs)
  • Sprite-based entity rendering: entity type_id → tile in entities.tga, blitted with transparency
  • Phase-staggered broadcast: per-client send_phase, flat outbound load across ticks
  • Ping/Pong RTT measurement (PingPacket / PongPacket)
  • Connection timeout: client emits Disconnected after 10 s without a StatePacket; server evicts clients unseen for 10 s

Upcoming work

06 — Hybrid tick loop (partially done)

Design of record: notes/06-hybrid-tick-loop.md (24 Hz base tick + stride scheduling). This supersedes the original 20/10/1 Hz three-tier sketch.

  • 24 Hz base loop (server/src/main.rs)
  • Phase-staggered broadcast at 12 Hz/client (send_phase in server/src/net.rs)
  • Stride layout settled: entity_tick at %4 (6 Hz, sets movement rate), broadcast at %2 (12 Hz/client). The 06 note now documents this and why.
  • Action-point model (energy / speed per entity, act when energy >= ACTION_COST). Removed from entity.rs for now as premature — reintroduce when NPCs (08) actually need it. Then entity_tick can move to %2 (12 Hz) and speed sets the effective movement rate.

07 — Tiled map loader

Replace the hardcoded build_world wall loop with real level data.

  • Parse Tiled TMX (XML) or JSON export — only the subset actually used
  • Define the tile vocabulary: TileKind variants map 1:1 to Tiled tile IDs
  • Load a hand-authored starting area as the first real level
  • Procedural generation comes later; hand-authored first

08 — Basic NPC entity + AI budget

One dumb wandering enemy. Validates the simulation architecture before complexity accumulates. Depends on the action-point model from 06 being reintroduced.

  • Add EntityKind::Npc with a think() -> u32 method returning budget cost
  • Per sim-tick: distribute think_budget = BASE / (clients + 1) across entities ordered by player proximity
  • Complex entities consume more budget; simple ones less. Loop breaks at zero — natural load shedding.
  • No framework. No trait objects yet. A match on EntityKind is fine.
let mut budget: u32 = BASE_BUDGET / (client_count + 1).max(1);
for entity in entities_by_player_proximity() {
    if budget == 0 { break; }
    budget = budget.saturating_sub(entity.think(&mut world));
}

09 — Client camera (sprite rendering already done)

Sprite rendering already landed (see Achieved). What remains is the camera:

  • Smooth camera: lerp between last known and current server position; do not snap (currently player_pos snaps hard to the server position in game.rs)
  • Store previous + current position per entity, interpolate on render
  • Camera math is architectural — affects how entity state is stored. Do it before UI.

Later — UI / HUD

  • Bitmap font renderer: 8×8 glyph sheet TGA, draw_text(frame, font, x, y, color, text) — see notes/04
  • UI primitives in client/src/ui.rs: fill_rect, draw_rect, draw_hbar — see notes/05
  • HUD layout (320×240): HP bar + player name bottom 16px, message log right panel
  • A real clip-rect on the blit primitives belongs here — once the viewport and HUD panels are two distinct regions, the abstraction earns its keep. Not before.

Later — Robustness + auth

  • Auth token handshake: replace source-address identity (auth_token field exists but is unused)
  • Multi-datagram EntityPacket: server currently truncates at 66 entities (net.rs TODO)
  • Asset embedding: include_bytes! for single-binary distribution

Later — World depth

Once the simulation architecture is stable and a real level exists:

  • Doors, interactive objects (server-authoritative state)
  • Combat: melee range check, HP, death, respawn
  • Inventory: on-demand via EntityQueryPacket / EntityDetailPacket
  • Day/night and weather on the slow tick — affect visibility, spawns
  • Procedural dungeon generation (BSP or cellular automata feeding into the same tile format)