diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs index 3c2454f..12828de 100644 --- a/src/AcDream.App/Rendering/GameWindow.cs +++ b/src/AcDream.App/Rendering/GameWindow.cs @@ -6805,6 +6805,23 @@ public sealed class GameWindow : IDisposable System.Console.WriteLine( $"[SEQSTATE] guid={serverGuid:X8} CurrentMotion=0x{ae.Sequencer.CurrentMotion:X8} " + $"CurrentSpeedMod={ae.Sequencer.CurrentSpeedMod:F3}"); + + // #39 fix-3 evidence (2026-05-06): CURRNODE proves + // whether _currNode is actually on the cycle (anim + // ref hash matches FirstCyclic) or stuck somewhere + // else. SCFULL captures _currNode==_firstCyclic only + // at SetCycle return; this captures it per render + // tick so we can see if something resets it later. + var d = ae.Sequencer.CurrentNodeDiag; + int firstHash = ae.Sequencer.FirstCyclicAnimRefHash; + System.Console.WriteLine( + $"[CURRNODE] guid={serverGuid:X8} " + + $"animRef=0x{d.AnimRefHash:X8} firstCyclicAnimRef=0x{firstHash:X8} " + + $"isOnCyclic={d.AnimRefHash == firstHash && firstHash != 0} " + + $"isLooping={d.IsLooping} fr={d.Framerate:F2} " + + $"frame=[{d.StartFrame}..{d.EndFrame}] " + + $"pos={d.FramePosition:F2} qCount={d.QueueCount}"); + rmDiag.LastSeqStateLogTime = nowSec; } } diff --git a/src/AcDream.Core/Physics/AnimationSequencer.cs b/src/AcDream.Core/Physics/AnimationSequencer.cs index 56e03e4..4bae508 100644 --- a/src/AcDream.Core/Physics/AnimationSequencer.cs +++ b/src/AcDream.Core/Physics/AnimationSequencer.cs @@ -254,6 +254,39 @@ public sealed class AnimationSequencer public int QueueCount => _queue.Count; public bool HasCurrentNode => _currNode != null; + /// + /// Diagnostic snapshot of _currNode's identity + frame state, for + /// the per-tick CURRNODE log line in GameWindow.TickAnimations. + /// Lets the caller see whether the actual node being read by Advance / + /// BuildBlendedFrame is what SetCycle was supposed to leave it on. + /// AnimRefHash uses object-identity hashing on the Animation reference + /// so different Walk vs Run anim resources can be distinguished even + /// without exposing the full Animation type. + /// + public (int AnimRefHash, bool IsLooping, double Framerate, int StartFrame, int EndFrame, double FramePosition, int QueueCount) CurrentNodeDiag + { + get + { + if (_currNode is null) + return (0, false, 0.0, 0, 0, 0.0, _queue.Count); + var n = _currNode.Value; + int hash = System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(n.Anim); + return (hash, n.IsLooping, n.Framerate, n.StartFrame, n.EndFrame, _framePosition, _queue.Count); + } + } + + /// + /// Diagnostic: the AnimRefHash for the FIRST cyclic node in the queue + /// (i.e., what SetCycle is trying to land us on for a locomotion cycle). + /// Compare against 's AnimRefHash to see + /// whether _currNode is actually pointing at the new cycle or + /// something stale. + /// + public int FirstCyclicAnimRefHash => + _firstCyclic is null + ? 0 + : System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(_firstCyclic.Value.Anim); + // ── Private state ──────────────────────────────────────────────────────── private readonly Setup _setup;