docs(N.4) Task 21: mark Week 3 complete + Adjustments 4-5

Week 3 ships: AnimatedEntityState (Tasks 16+18+19, commit ce72c57),
EntitySpawnAdapter routing server-spawned content through the existing
TextureCache.GetOrUploadWithPaletteOverride path (Task 17, commit
c02c307). 947 tests pass.

Adjustment 4: WorldEntity lacks HiddenPartsMask + AnimPartChanges
fields. Adapter scaffolding ships; AnimatedEntityState gets default
values (empty mask + empty override map). Plumbing deferred to Task 22
brainstorm — either add fields to WorldEntity or thread through a
separate parameter to EntitySpawnAdapter.OnCreate.

Adjustment 5: Task 20 (per-instance decode conformance) is structural.
Both old and new paths call the same TextureCache function — bytes
identical by construction. EntitySpawnAdapterTests already cover the
routing. No separate conformance test file needed.

Next: Task 22 (Week 4) — WbDrawDispatcher full draw loop. First task
that actually draws through WB and unlocks Adjustment 3's mitigation
(dual-pipeline cost resolves when legacy renderer can short-circuit
its upload for atlas-tier content).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-08 14:48:20 +02:00
parent c02c307bee
commit 312d3b3ee0

View file

@ -66,9 +66,9 @@ This plan is the **execution source of truth** for N.4. It is updated as tasks l
Status: **Living document — work in progress, started 2026-05-08.** Status: **Living document — work in progress, started 2026-05-08.**
**Progress (2026-05-08):** Weeks 1 + 2 ✅ COMPLETE. WB pipeline running flag-on (constructed + ref-counted + per-frame Tick draining its queues). Three architectural adjustments documented: 1 (DefaultDatReaderWriter discovery, no bridge needed), 2 (renderer is tier-blind; routing belongs in spawn callbacks), 3 (FPS regression root-caused as dual-pipeline cost; Task 22's dispatcher will allow the legacy-renderer short-circuit). Build green, 912 tests pass, 8 pre-existing failures only. **Progress (2026-05-08):** Weeks 1 + 2 + 3 ✅ COMPLETE. WB pipeline running flag-on (constructed + ref-counted + per-frame Tick draining its queues). Per-instance tier wired (`EntitySpawnAdapter` routes server-spawned entities through existing `TextureCache.GetOrUploadWithPaletteOverride` path; per-entity `AnimatedEntityState` accumulates AnimPartChange + HiddenParts data, ready for the dispatcher). Five architectural adjustments documented: 1 (DefaultDatReaderWriter discovery), 2 (renderer is tier-blind), 3 (FPS regression = dual-pipeline cost; resolves at Task 22), 4 (WorldEntity missing HiddenPartsMask + AnimPartChanges fields, plumbing deferred), 5 (Task 20 is structural — same function called both paths). Build green, 947 tests pass, 8 pre-existing failures only.
**Next: Task 16** (Week 3) — `AnimatedEntityState` type + per-instance customization path. **Next: Task 22** (Week 4) — `WbDrawDispatcher` full draw loop. The first task that actually draws through WB and unlocks the dual-pipeline-cost mitigation from Adjustment 3.
| Task | Status | Commit | | Task | Status | Commit |
|---|---|---| |---|---|---|
@ -87,9 +87,18 @@ Status: **Living document — work in progress, started 2026-05-08.**
| 13 — Memory budget verification | ✅ deferred to Task 22 (Adj. 3) | — | | 13 — Memory budget verification | ✅ deferred to Task 22 (Adj. 3) | — |
| 14 — Pending-spawn integration test | ✅ | `f4f0101` | | 14 — Pending-spawn integration test | ✅ | `f4f0101` |
| Tick — drain WB pipeline queues | ✅ added per Adj. 3 | `bf53cb4` | | Tick — drain WB pipeline queues | ✅ added per Adj. 3 | `bf53cb4` |
| 15 — Week 2 wrap-up | ✅ | (this commit) | | 15 — Week 2 wrap-up | ✅ | `36f7a60` |
| 1621 — Week 3: per-instance + animation | pending | — | | 16+18+19 — AnimatedEntityState + AnimPartChange + HiddenParts | ✅ | `ce72c57` |
| 2228 — Week 4: draw dispatcher + ship | pending | — | | 17 — EntitySpawnAdapter | ✅ + Adj. 4 | `c02c307` |
| 20 — Per-instance decode conformance | ✅ structural (Adj. 5) | (no test file) |
| 21 — Week 3 wrap-up | ✅ | (this commit) |
| 22 — WbDrawDispatcher full draw loop | pending | — |
| 23 — Surface metadata side-table population | pending | — |
| 24 — Sky-pass preservation check | pending | — |
| 25 — Component micro-tests round-out | pending | — |
| 26 — Visual verification + flag default-on | pending | — |
| 27 — Delete legacy code paths | pending | — |
| 28 — Update memory + ISSUES + finalize plan | pending | — |
--- ---
@ -935,6 +944,54 @@ without violating Adjustment 2's tier-blind-renderer principle.
infrastructure for Task 22 anyway. We just paid for it without infrastructure for Task 22 anyway. We just paid for it without
seeing FPS recovery yet. seeing FPS recovery yet.
---
### Adjustment 4 (2026-05-08): WorldEntity lacks HiddenParts + AnimPartChange fields — deferred plumbing
**Discovered during Task 17 implementation.** `EntitySpawnAdapter.OnCreate`
needed to populate `AnimatedEntityState` with the entity's `HiddenParts`
mask + `AnimPartChange` override map. But: `WorldEntity` (the per-frame
render-side struct) does not currently expose either field. Both pieces
of customization data live on the network-layer spawn record and are
consumed before the `WorldEntity` is built.
**Resolution.** Task 17 ships the adapter scaffolding with a TODO comment
acknowledging the gap. The created `AnimatedEntityState` always has an
empty override map + zero hidden mask. Per-instance customizations like
"hide this character's head" won't take effect with flag-on until the
plumbing lands.
**Why this is safe to defer.** No production path consumes
`AnimatedEntityState`'s override / hidden data yet — Task 22's
`WbDrawDispatcher` is the first consumer. By the time Task 22 lands, we
either:
1. Add `HiddenPartsMask` + `AnimPartChanges` fields to `WorldEntity` and
populate them at spawn time. Small change to the network → render
pipeline.
2. Inject them into `EntitySpawnAdapter.OnCreate` via a separate
parameter that the spawn handler provides directly (sidesteps the
`WorldEntity` change).
Option 1 is cleaner long-term; Option 2 is faster for landing Task 22
without touching WorldEntity. Decision deferred to Task 22 brainstorm.
### Adjustment 5 (2026-05-08): Task 20 (per-instance decode conformance) is structural, not byte-comparison
**Original plan.** Task 20 was supposed to compare RGBA8 output of
"old path" (`TextureCache.GetOrUploadWithPaletteOverride` direct) vs
"new path" (`EntitySpawnAdapter``ITextureCachePerInstance`
`TextureCache.GetOrUploadWithPaletteOverride`) to prove byte-identity.
**Reality.** Both paths call the **same function**. The new path adds a
seam interface (`ITextureCachePerInstance`) for testability but does
not modify the decode logic — the bytes are identical by construction.
A test asserting byte-equality would be tautological.
**Resolution.** Existing `EntitySpawnAdapterTests` cover the routing
behavior (does the adapter call the cache with the right args?). The
decode-byte conformance is structural: same function = same output.
Mark Task 20 ✅ structurally; no separate test file.
### Task 6 (original — kept for history) ### Task 6 (original — kept for history)
**Files:** **Files:**