docs+cleanup: env-var regression + Run↔Walk cycle bug filed; re-throttle diags

End-of-session cleanup of the 2026-05-03 remote-motion debug session.

Documentation:

- CLAUDE.md: add ⚠️ DO-NOT-ENABLE warning for ACDREAM_INTERP_MANAGER=1
  in the diagnostic env-vars list. Add an "Outbound motion wire format"
  section documenting acdream's WalkForward+HoldKey.Run encoding (which
  ACE auto-upgrades to RunForward on relay) so future sessions don't
  re-derive it.

- docs/ISSUES.md: file two new issues:
  * #39 — Run↔Walk cycle transition not visible on observed
    retail-driven player remotes when watched from acdream. Root cause
    located: ApplyServerControlledVelocityCycle is gated by
    IsPlayerGuid, excluding the exact case where ACE doesn't broadcast
    a UM (shift toggle while direction key held). Fix sketch ~10
    lines, separate commit. Cross-references the four-agent
    investigation prompt.
  * #40 — ACDREAM_INTERP_MANAGER=1 env-var path regressed. Documents
    why (e94e791 conflated MoveOrTeleport with update_object), the
    visible symptoms (staircase Z, position blips), and why
    Commit B (039149a)'s ResolveWithTransition port was insufficient
    (env-var path also clears body.Velocity → no horizontal Euler
    motion → sweep input is queue catch-up only, which stair-steps).
    Fix path = separate L.3 follow-up to re-integrate PositionManager
    additively on top of the legacy chain.

Code:

- GameWindow.cs:6042: prepend a ⚠️ REGRESSED warning block at the top
  of the env-var per-frame branch so anyone reading the code is
  immediately aware not to enable it. Cross-references ISSUES.md #40.

- AnimationSequencer.cs: re-throttle [SCFAST]/[SCFULL] diagnostics to
  0.5s per instance (rolled back from A.1's unthrottled experiment).
  Already served its purpose; throttled is enough for steady-state
  diagnostics. Restore _lastSetCycleDiagTime field.

No behavior change for any current launch (env-var unset = legacy
path unchanged). Build green; baseline test failures (8) unchanged
from prior commit, none introduced by this session.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-04 10:10:10 +02:00
parent 039149a9d0
commit a3f53c2644
4 changed files with 238 additions and 24 deletions

View file

@ -46,6 +46,154 @@ Copy this block when adding a new issue:
# Active issues
## #39 — Run↔Walk cycle transition not visible on observed player remotes (acdream-as-observer)
**Status:** OPEN
**Severity:** MEDIUM (visible animation desync; not a correctness/wire bug)
**Filed:** 2026-05-03
**Component:** physics / motion / animation
**Description:** When observing a remote-driven player character through
acdream and the actor toggles Shift while keeping a direction key held
(Run↔Walk demote/promote), the visible leg cycle does NOT update on the
observer side. Body position eventually corrects via UpdatePosition
hard-snaps (causing visible position blips), but the animation cycle
stays at whatever it was last set to (Run sticks; Walk sticks).
Observation matrix:
| Observer | Actor | Cycle Run↔Walk | Z on slopes |
|---|---|---|---|
| Retail | Retail | ✓ | ✓ |
| Retail | Acdream | ✓ | ✓ |
| Acdream | Acdream | ✓ | ✗ (only with env-var path) |
| Acdream | Retail | ✗ | ✗ |
**Root cause / status:**
ACE only broadcasts a fresh `UpdateMotion` (UM) when the wire's
`ForwardCommand` byte changes — i.e. on direction-key state changes
(W press, W release). Toggling Shift while W is held changes
`ForwardSpeed` and `HoldKey` but NOT `ForwardCommand`, so ACE does
NOT broadcast a UM for the demote/promote. The speed change DOES
propagate via `UpdatePosition` (position-delta velocity changes
between Run-pace and Walk-pace), confirmed via `[VEL_DIAG]`
serverSpeed varying ~2.5 m/s (walk) ↔ ~9 m/s (run).
Retail's inbound code uses UP-derived velocity to refine the visible
cycle when no UM tells it. Acdream has the equivalent function —
`ApplyServerControlledVelocityCycle` in `GameWindow.cs:3274` — but
it's gated `if (IsPlayerGuid(serverGuid)) return;` for player
remotes, exactly the case where the gap matters.
(Earlier hypothesized as H2 in the 2026-05-03 four-agent investigation
but marked refuted because the [UPCYCLE] diag never fired — that
was BECAUSE of the gate; un-gating reveals it firing per UP, which
is the correct behavior.)
**Fix sketch (~10 lines):** un-gate `ApplyServerControlledVelocityCycle`
for player remotes when `currentMotion` is a locomotion cycle
(Run/Walk/Sidestep/Backward). UMs still drive direction-key changes
authoritatively; UP-derived velocity refines the speed bucket within
the same direction. Add a `LastUMUpdateTime` grace window (e.g.
500ms) so UMs win when fresh.
**Files:**
- `src/AcDream.App/Rendering/GameWindow.cs:3274``ApplyServerControlledVelocityCycle`
(the gate `if (IsPlayerGuid(serverGuid)) return;` to remove with conditions)
- `src/AcDream.App/Rendering/GameWindow.cs:3640-3660` — call site (already
passes through with HasServerVelocity from synthesized UP-deltas)
- `src/AcDream.Core/Physics/ServerControlledLocomotion.cs:54-76`
`PlanFromVelocity` thresholds (may need re-tuning if banding is observed)
**Research:**
- `docs/research/2026-05-03-remote-anim-cycle/investigation-prompt.md`
full background of the four-agent investigation
- This session's diagnostic logs at `tools/diag-logs/walkrun-A1b-*.log`
(UM_RAW, FWD_WIRE, SETCYCLE traces) confirming ACE's wire pattern
**Acceptance:**
- Observer in acdream watching a retail-driven character toggle Shift
while holding W: visible leg cycle switches Run↔Walk within ~200ms
of the wire change.
- No regression on the working cases (acdream-on-acdream, retail
observers, idle↔Run, idle↔Walk).
- No spurious cycle thrashing during turning while running (ObservedOmega
doesn't trigger velocity-bucket changes).
## #40 — ACDREAM_INTERP_MANAGER=1 env-var path regressed (staircase + blips)
**Status:** OPEN (do-not-enable; pending L.3 follow-up rebuild)
**Severity:** N/A (gated; default behavior unaffected)
**Filed:** 2026-05-03
**Component:** physics / motion (per-tick remote prediction)
**Description:** The `ACDREAM_INTERP_MANAGER=1` per-frame remote tick
introduced by commit `e94e791` (L.3.1+L.3.2 Task 3) is a regression and
should not be enabled. Two visible symptoms:
1. **Z staircase on slopes:** observed remotes running up/down hills
sink into rising terrain or float over receding terrain, then snap
to correct Z at each `UpdatePosition` arrival. Body never follows
the terrain mesh between UPs.
2. **Position blips during steady-state motion:** XY drifts
unconstrained between UPs, then UP hard-snaps cause visible jumps.
Both symptoms ABSENT when env-var unset (default legacy path).
**Root cause:** the env-var path was designed to mirror retail
`CPhysicsObj::MoveOrTeleport` (acclient @ 0x00516330). MoveOrTeleport
is retail's network-packet entry point — minimal work. The per-frame
physics tick is retail's `update_object` (FUN_00515020) — full chain
including `apply_current_movement``UpdatePhysicsInternal`
`Transition::FindTransitionalPosition` (collision sweep). The legacy
path mirrors `update_object` correctly. The env-var path stripped the
collision sweep on a wrong assumption that this was "more retail-
faithful" — it was the opposite.
Commit B (039149a, 2026-05-03) ported `ResolveWithTransition` into the
env-var path, but the symptom persisted because the env-var path also
clears `body.Velocity` for grounded remotes (no Euler integration of
horizontal motion → sweep input is the catch-up offset only, which
itself stair-steps because UPs are sampled at ~1 Hz).
**Files:**
- `src/AcDream.App/Rendering/GameWindow.cs:6042-6260` — env-var per-frame branch
- `src/AcDream.App/Rendering/GameWindow.cs:6260+` — legacy per-frame branch (works)
- `src/AcDream.Core/Physics/PositionManager.cs` — class itself is retail-faithful
(port of CPositionManager::adjust_offset), only the integration was wrong
**Research:**
- This session's `2026-05-03` chronological commit log + visual verification
- `docs/research/2026-05-03-remote-anim-cycle/investigation-prompt.md`
for the four-agent investigation that traced this
**Fix path (separate L.3 follow-up phase, NOT this session):**
The PositionManager class is correct retail-port. Re-integrate it as
ADDITIVE refinement on top of the working legacy chain (small
correction toward queued server positions, applied AFTER
`apply_current_movement` + `UpdatePhysicsInternal` + collision sweep)
— not as a REPLACEMENT for them. Match retail's actual `update_object`
chain ordering: `position_manager::adjust_offset` runs after the
primary motion + collision resolution.
**Acceptance:**
- New per-tick path enabled via env-var (or default after stabilization)
produces the same smooth slope motion + zero blips as the legacy path.
- Inbound `UpdatePosition` queue catch-up nudges body toward server
authoritative position without overriding terrain Z snap or causing
position blips.
- Verification: side-by-side vs legacy default in 2-client setup,
identical visible behavior.
## #38 — Chase camera + player feel "30 fps" since L.5 physics-tick gate
**Status:** OPEN