fix #203: preserve animation on appearance updates

This commit is contained in:
Erik 2026-07-11 00:25:29 +02:00
parent ff06aa3107
commit c7607f019c
9 changed files with 237 additions and 22 deletions

View file

@ -46,6 +46,28 @@ Copy this block when adding a new issue:
---
## #203 — Unequipping armor freezes character and world animations — FIXED
**Status:** DONE — 2026-07-11 (this change)
**Severity:** HIGH
**Component:** live entities / animation / appearance updates
**Description:** After armor was removed, the local character stopped animating
and animation-dependent world interactions such as doors appeared frozen.
**Root cause:** `0xF625 ObjDescEvent` was incorrectly handled as a full
despawn/respawn. That discarded the live `WorldEntity`, animation manager,
physics host, and collision registration while the player movement controller
still referenced the discarded sequencer. Named retail instead applies
`DoObjDescChangesFromDefault` to the existing object.
**Resolution:** Appearance hydration now mutates only the existing entity's
meshes, palette, part overrides, and bounds. Entity identity, animation playback,
movement, physics, collision, selection, and dead-reckoning state survive. The
classification cache is invalidated in place. A regression test pins entity and
sequencer identity across the update. User verified the exact equip/unequip flow
in the Release client.
## #191 — Tapping W (brief forward press) glides forward without playing the step animation
## #194 — WbDrawDispatcher._groups is never pruned (minor)
@ -7161,7 +7183,7 @@ threshold.
**Component:** rendering / `WbDrawDispatcher` / `EntityClassificationCache` / `LandblockLoader`
**Resolution.** New `EntityClassificationCache` keyed by `(entityId, landblockHint)` tuple in `src/AcDream.App/Rendering/Wb/EntityClassificationCache.cs`. The dispatcher routes static entities (NOT in `_animatedEntities`) through the cache — first-frame slow-path populates flat `CachedBatch[]` (one entry per (partIdx, batchIdx) with the part-relative `RestPose` and resolved `BindlessTextureHandle`); subsequent-frame cache hits skip classification entirely and append `cached.RestPose * entityWorld` to each matching group. Animated entities bypass. Invalidation fires from `RemoveLiveEntityByServerGuid` (per-entity, `0xF747`/`0xF625`) and `RemoveEntitiesFromLandblock` (per-LB, Near→Far demote + unload).
**Resolution.** New `EntityClassificationCache` keyed by `(entityId, landblockHint)` tuple in `src/AcDream.App/Rendering/Wb/EntityClassificationCache.cs`. The dispatcher routes static entities (NOT in `_animatedEntities`) through the cache — first-frame slow-path populates flat `CachedBatch[]` (one entry per (partIdx, batchIdx) with the part-relative `RestPose` and resolved `BindlessTextureHandle`); subsequent-frame cache hits skip classification entirely and append `cached.RestPose * entityWorld` to each matching group. Animated entities bypass. Invalidation fires from `RemoveLiveEntityByServerGuid` for real despawns (`0xF747`), directly from the in-place `0xF625` appearance mutation, and from `RemoveEntitiesFromLandblock` (per-LB, Near→Far demote + unload).
**Perf result.** Entity dispatcher cpu_us **median ~1200 µs, p95 ~1500 µs** at horizon-safe + High preset on AMD Radeon RX 9070 XT @ 1440p. Pre-Tier-1 baseline was ~3500m / ~4000p95. ~66% reduction in median, ~63% in p95. Well under the A.5 spec budget (median ≤ 2.0 ms, p95 ≤ 2.5 ms). No `BUDGET_OVER` flag observed.