acdream/docs/plans/2026-04-11-roadmap.md
Erik bb9ff774dc docs(roadmap): mark Phase A.1 (streaming) shipped; note sync-loader caveat
Move A.1 from "ahead" to "shipped" per the roadmap discipline rule.
The shipped row notes that the loader currently runs synchronously
(the original async-worker design hit DatCollection's lack of thread
safety) and that the Channel-based outbox API is preserved so async
loading can return cleanly when Phase A.3 lands a thread-safe dat
wrapper. Pending-spawn list in GpuWorldState handles live spawn /
streaming races without dropping data.

Quick-lookup table updated:
- "Can't walk past the loaded 3×3 window" → A.1 FIXED ✓
- "Frame hitch crossing landblock boundary" → Phase A.3
  (synchronous loader for now; async returns when DatCollection is
  thread-safe)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 23:31:20 +02:00

12 KiB
Raw Blame History

acdream — strategic roadmap

Status: Living document. Updated 2026-04-11 after Phase 6, 7.1, 9.1, 9.2 landed. Purpose: One source of truth for where the project is and where it's going. Every observed defect or missing feature has a named phase that owns it; when something looks wrong in-game, look here to find the phase that'll address it. Implementation details live in per-phase specs under docs/superpowers/specs/, not in this file.


Phases already shipped

Phase What landed Verification
1 Terrain rendering, plugin host scaffold Visual ✓
2a Static stabs/buildings (126 entities) Visual ✓
2b Textured 3×3 landblock grid + FlyCamera + IGameState Visual ✓
2c Procedural scenery (419 trees/rocks/bushes) Visual ✓
2d Interior EnvCell walker (475 static interior objects) Visual ✓
3a/3b Directional sun lighting + per-vertex terrain normals Visual ✓
3c Per-cell terrain texture blending (alpha atlas) Visual ✓
4 Full UDP codec + handshake + character login + WorldSession Live ✓
5 ObjDesc: AnimPartChange + TextureChanges + SubPalettes + ObjScale + Placement.Resting Live ✓
6.1 Idle motion frame resolution (MotionResolver MVP) Live ✓
6.2 Server-sent MovementData stance + forward command honored Live ✓
6.3 Server-supplied MotionTableId override (fixes drudge statue) Live ✓
6.4 Per-frame animation playback (breathing, idle cycles) Live ✓
6.5 Slerp between keyframes for smooth animation Live ✓
6.6 UpdateMotion (0xF74C) parser + dispatch to animation tick Live ✓
6.7 UpdatePosition (0xF748) parser + position reseating Live ✓
7.1 EnvCell room geometry — walls/floors/ceilings via CellStruct + Environment dats Visual ✓
9.1 Translucent render pass (AlphaBlend / Additive / InvAlpha + per-kind blend funcs) Visual ✓
9.2 Back-face culling in translucent pass (fixes lifestone crystal) Visual ✓
A.1 Streaming landblock loader — runtime-configurable visible window (default 5×5, ACDREAM_STREAM_RADIUS), camera-centered offline / player-centered live, hysteresis-based unloads, pending-spawn list for late CreateObject events Live ✓

Plus polish that doesn't get its own phase number:

  • FlyCamera default speed lowered + Shift-to-boost
  • SurfaceDecoder: PFID_P8 / PFID_R8G8B8 / PFID_X8R8G8B8 decoders
  • GfxObjMesh: emit both pos and neg sides of double-sided polygons
  • EnvCell mesh Z-lift to fix ground-floor / terrain flicker

Phases ahead — agreed order

Phase A — Foundation (in progress)

Goal: walk across 10+ landblocks without crashes, without hitches at landblock boundaries, and without framerate cratering.

Sub-pieces:

  • ✓ SHIPPED — A.1 — Streaming landblock loader. Runtime-configurable visible window (default 5×5, ACDREAM_STREAM_RADIUS env var override). Center follows the fly camera offline and the server-sent player position in live mode. Currently runs synchronously on the render thread — the original async-worker design hit DatCollection's lack of thread safety and was reverted for correctness. The Channel-based outbox API is preserved so async loading can return cleanly when Phase A.3 introduces a thread-safe dat wrapper. Pending-spawn list in GpuWorldState parks live CreateObject events whose target landblock hasn't been streamed in yet and back-fills them when it arrives, so spawn-vs-streaming races are no longer racy at all. MaxCompletionsPerFrame=4 spreads the 5×5 first-frame load over ~7 frames (~116ms) to avoid GPU OOM.
  • A.2 — Frustum culling + LOD. Per-landblock AABB test against the view frustum in StaticMeshRenderer.Draw, skipping drawn entities in culled landblocks. Per-entity culling deferred. No LOD mesh levels yet — that's Phase C or later.
  • A.3 — Background net I/O thread. WorldSession runs its receive loop on a dedicated thread; parsed game messages are posted to a concurrent queue the render thread drains from OnUpdate. Event invocations still happen on the render thread (preserves existing handler assumptions). Removes packet drops under frame stalls.
  • A.4 — Async dat decoding. Folded into the streaming worker — it's the worker's read path, not a separate subsystem. Called out here because regressions in dat caching could land on this surface.

Acceptance:

  • Walk across 10+ landblocks in any direction, no crashes, no empty voids.
  • Landblock-boundary crossings produce no visible hitch.
  • Runtime window radius toggleable via environment variable.

Detailed spec: docs/superpowers/specs/2026-04-11-foundation-phase-design.md


Phase B — Gameplay / interaction

Goal: actually play the game — walk the character on the server, click NPCs, pick up items, chat, basic combat loop.

Sub-pieces:

  • B.1 — Outbound ack pump. Background timer that sends sequence acks every ~250ms. Without this the server drops idle clients after ~30s regardless of any other activity.
  • B.2 — PlayerAutonomousMove outbound. Wire WASD + camera state (or a dedicated player-controlled movement mode) to an outbound movement message so the server's view of the character matches ours.
  • B.3 — Collision against terrain. Required for the server to accept moves at all — ACE rejects client positions that are inside geometry or in disallowed Z ranges. Minimum viable: sample the terrain heightmap beneath the player and clamp Z. Proper: walk the CellBSP / PhysicsBSP we already parse.
  • B.4 — Use / UseWithTarget / PickUp. Outbound interaction messages. Drives opening doors, looting, talking to vendors.
  • B.5 — Chat. SendTell, SendChat outbound + receive/display inbound (display side depends on Phase D.1).

References:

  • references/ACE/Source/ACE.Server/Network/Handlers/MovementHandler.cs
  • references/ACE/Source/ACE.Server/Network/Handlers/UseObjectHandler.cs
  • references/holtburger/src/session/send.rs for outbound packet-building patterns

Acceptance: walk on-server with your character, open a door, talk to an NPC, send a chat message and see the echo.


Phase C — Polish / visuals

Goal: close the visible gaps that make the world read as "old / broken" compared to retail.

Sub-pieces:

  • C.1 — VFX / particle system. PhysicsScript parser, per-entity ParticleEmitter state, billboarded-quad particle renderer that lives in the Phase 9.1/9.2 translucent pass. Delivers portal swirls, chimney smoke, and fireplace flames in one implementation.
  • C.2 — Dynamic point lights. Fireplaces and lamps need local lighting; small upgrade to the mesh shader to accumulate N (e.g., 4) nearest point lights per draw. Uniform-buffer or UBO-friendly layout.
  • C.3 — Palette range tuning. Small per-range offset/length tweaks to match retail skin/hair/eye colors. Mostly diff and verify work, no architecture change.
  • C.4 — Double-sided translucent polys. Edge case left by Phase 9.2: neg-side translucent polys are culled because cull is always BACK. Fix by tracking per-sub-mesh CullMode and flipping GL state per draw (or drawing twice with opposite cull). Minor.
  • C.5 — Shadow mapping (optional). Deferred unless it becomes a bottleneck in screenshots — dynamic shadows are a known complexity trap.

References:

  • references/ACE/Source/ACE.DatLoader/FileTypes/PhysicsScript.cs for the emitter schema
  • references/ACViewer/ACViewer/Physics/Particles/ for the visual model
  • references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/ParticleBatcher.cs for the Silk.NET-flavored implementation

Acceptance: portals look like swirly gates, chimneys smoke, fireplaces burn, character skin matches retail screenshots.


Phase D — UI / HUD + Sound

Goal: chat window, nameplates, inventory, and audio. Can run concurrently with Phase B or C because it doesn't touch gameplay/net/rendering surfaces.

Sub-pieces:

  • D.1 — 2D ortho overlay + font rendering. Separate shader and render pass drawn after 3D. Font: FreeType via Silk.NET bindings, or bitmap fonts as a simpler first pass.
  • D.2 — Chat window + nameplates. First UI widgets. Chat consumes Phase B.5 messages; nameplates render per-entity 3D-to-2D projected labels.
  • D.3 — Inventory / character / spell panels. Requires a widget framework (layout, focus, input routing). Scope unbounded — ship minimum viable first.
  • D.4 — Sound. SoundTable parser, Sound dat decode, audio engine (OpenAL via Silk.NET.OpenAL), per-entity 3D positional audio, optional music.

Acceptance: see other players' chat in a chat window, see nameplates above NPCs, hear footsteps and sword hits.


Phase E — Long-tail

Not detailed here; each gets its own brainstorm when it becomes relevant.

  • Dungeon landblocks (0xAAAA0000 family) + teleport-on-door-click + server-side portal handling
  • Phase 7.2 multi-floor stair walking — cells reachable via portals the cell-walker doesn't cross
  • Player character full rig (held weapons, spell effects, death/revive animation)
  • Weather + day/night cycle
  • Spellcasting pipeline
  • Group/fellowship UI

Cross-cutting work tracked in parallel

  • Test coverage. Each phase lands with unit + integration tests in tests/. Current count: 98 Core + 96 Core.Net = 194. Keep the ratio as new phases land.
  • Memory files. Project state under memory/project_phase_*_state.md is updated when a phase ships. MEMORY.md is the index.
  • CLAUDE.md discipline. Check all four references (ACE, ACViewer, WorldBuilder, Chorizite) before committing to an approach. WorldBuilder is the closest stack match and should be checked first.

Explicitly out of scope

  • Server emulation — we use ACE for server, never reimplement.
  • Account creation — direct user to ACE tooling.
  • Anti-cheat / GM tools / live-ops — irrelevant for personal use.
  • Cross-platform support — Windows-only; the dat path assumptions depend on retail Windows install layout. Silk.NET is cross-platform but we don't promise.
  • Custom game content — this is a client for existing AC data, not a toolchain.

"When will my specific complaint be fixed?" — quick lookup

Observation Phase
Drudge statue in wrong pose 6.3 FIXED
Characters in T-pose / wrong idle 6.1 FIXED
No breathing on NPCs 6.4 + sentinel fix FIXED
Lifestone crystal has one side missing 9.2 FIXED
Ground floor flickering with terrain 7.1 FIXED
Houses missing second floors / walls 7.1 FIXED ✓ (interior mesh landed)
Character clothing missing / wrong 5 FIXED
Statue wrong color / wrong scale 5 FIXED
Holtburg sign half-buried 5 FIXED
Can't walk past the loaded 3×3 window A.1 FIXED ✓ (5×5 default, ACDREAM_STREAM_RADIUS to tune)
Frame hitch crossing landblock boundary Phase A.3 (synchronous loader for now; async returns when DatCollection is thread-safe)
Walking around doesn't move me on the server Phase B (Gameplay)
Can't talk to NPCs Phase B
Can't open a door Phase B
Portals render as a rotating black disk Phase C.1 (VFX)
Chimneys have no smoke Phase C.1
Houses have no fireplace fire Phase C.1
No fireplace / torch lighting Phase C.2
Skin/hair color slightly off Phase C.3
No chat window Phase D.2
No sound Phase D.4
Dungeons / foundry interior missing Phase E

If you see something not on this list, add it here and assign a phase.