126 lines
5.9 KiB
Markdown
Executable File
126 lines
5.9 KiB
Markdown
Executable File
# 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
|
||
|
||
- [x] TGA loader: `Image`, `from_tga`, `to_tileset`, `decode_rle` in `client/src/assets.rs`
|
||
- [x] 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
|
||
- [x] Input system: `GameAction`, `InputMap`, `InputState` in `client/src/input.rs` (over `pbio::Key`)
|
||
- [x] Protocol spec: `notes/protocol.md` — Header, State, Action, Chunk, Entity, EntityQuery,
|
||
EntityDetail, Ping/Pong; reliability model
|
||
- [x] Shared crate: packet structs as `bytemuck::Pod` with compile-time size assertions,
|
||
`player_action` / `packet_type` constants, `chunk_id` / `chunk_coords`
|
||
- [x] 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
|
||
- [x] Client net: `NetClient` in `client/src/net.rs`, non-blocking UDP recv,
|
||
`StatePacket` / `EntityPacket` / `ChunkPacket` / `PongPacket` dispatch, entity-checksum retransmit
|
||
- [x] End-to-end loop: player moves on server, position reflected in `EntityPacket`, rendered on client
|
||
- [x] Tile rendering: chunk-based world rendering from received `ChunkPacket` data (6-bit unpack)
|
||
- [x] Tick-based movement: movement resolved in `entity_tick`, currently every 4th tick (`sim.rs`)
|
||
- [x] Sprite-based entity rendering: entity `type_id` → tile in `entities.tga`, blitted with transparency
|
||
- [x] Phase-staggered broadcast: per-client `send_phase`, flat outbound load across ticks
|
||
- [x] Ping/Pong RTT measurement (`PingPacket` / `PongPacket`)
|
||
- [x] Connection timeout: client emits `Disconnected` after 10 s without a `StatePacket`;
|
||
server evicts clients unseen for 10 s
|
||
- [x] Map loader: Tiled-CSV parser in `server/src/map.rs` (flip-flag masking, content-derived
|
||
dimensions), `load_world` slices it into chunks, invisible solid border at the map rim.
|
||
Replaces the hardcoded `build_world`. See `notes/07-map-loader.md`
|
||
|
||
---
|
||
|
||
## 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.
|
||
|
||
- [x] 24 Hz base loop (`server/src/main.rs`)
|
||
- [x] Phase-staggered broadcast at 12 Hz/client (`send_phase` in `server/src/net.rs`)
|
||
- [x] 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 *(done — see Achieved / `notes/07-map-loader.md`)*
|
||
|
||
Remaining follow-ups when the need is concrete:
|
||
|
||
- Collision vocabulary: `tile_flags` in `map.rs` is currently empty (all walkable). Fill in
|
||
collidable / opaque tile ids as the level design requires.
|
||
- Tile flipping: orientation is discarded on load; revisit with the sprite pass (09).
|
||
- Multiple / object layers (spawns, triggers) — not yet parsed.
|
||
|
||
---
|
||
|
||
### 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.
|
||
|
||
```rust
|
||
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)
|
||
|
||
---
|
||
</content>
|