Resumed to project. INIT

This commit is contained in:
2026-06-17 08:23:13 +02:00
commit fd02ed6d8b
51 changed files with 5067 additions and 0 deletions
+122
View File
@@ -0,0 +1,122 @@
# 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
---
## 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
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.
```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>