fix(physics): #30 #34 L.2a movement truth diagnostics

Pass explicit grounded/airborne contact bytes from MovementResult into MoveToState and AutonomousPosition, and add ACDREAM_DUMP_MOVE_TRUTH logging for outbound movement plus player UpdatePosition echoes.

Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
Erik 2026-04-29 21:52:53 +02:00
parent d4c3f947d2
commit 3be0c8b7c7
5 changed files with 166 additions and 56 deletions

View file

@ -409,6 +409,8 @@ public sealed class GameWindow : IDisposable
private AcDream.UI.Abstractions.Panels.Debug.DebugVM? _debugVm;
private static readonly bool DevToolsEnabled =
Environment.GetEnvironmentVariable("ACDREAM_DEVTOOLS") == "1";
private static readonly bool DumpMoveTruthEnabled =
Environment.GetEnvironmentVariable("ACDREAM_DUMP_MOVE_TRUTH") == "1";
// Phase I.3 — real ICommandBus for live sessions. Constructed when
// the live session spins up (so SendChatCmd handlers can close over
@ -464,6 +466,19 @@ public sealed class GameWindow : IDisposable
private uint? _playerCurrentAnimCommand;
private float _playerCurrentAnimSpeed = 1f;
private uint? _playerMotionTableId; // server-sent MotionTable override for the player's character
private MovementTruthOutbound? _lastMovementTruthOutbound;
private readonly record struct MovementTruthOutbound(
string Kind,
uint Sequence,
System.DateTime TimeUtc,
System.Numerics.Vector3 LocalWorldPosition,
uint LocalCellId,
System.Numerics.Vector3 WirePosition,
uint WireCellId,
bool IsOnGround,
byte ContactByte,
System.Numerics.Vector3 Velocity);
// K-fix7 (2026-04-26): server-authoritative Run + Jump skill values
// received from PlayerDescription. -1 = "not yet received, fall back
@ -3076,6 +3091,7 @@ public sealed class GameWindow : IDisposable
0f);
var worldPos = new System.Numerics.Vector3(p.PositionX, p.PositionY, p.PositionZ) + origin;
var rot = new System.Numerics.Quaternion(p.RotationX, p.RotationY, p.RotationZ, p.RotationW);
DumpMovementTruthServerEcho(update, worldPos);
// Capture the pre-update render position for the soft-snap residual
// calculation below. Assign entity.Position to the server truth up
@ -4853,6 +4869,7 @@ public sealed class GameWindow : IDisposable
uint wireCellId = ((uint)lbX << 24) | ((uint)lbY << 16) | (result.CellId & 0xFFFFu);
var wirePos = new System.Numerics.Vector3(localX, localY, result.Position.Z);
var wireRot = YawToAcQuaternion(_playerController.Yaw);
byte contactByte = result.IsOnGround ? (byte)1 : (byte)0;
if (result.MotionStateChanged)
{
@ -4885,7 +4902,10 @@ public sealed class GameWindow : IDisposable
instanceSequence: _liveSession.InstanceSequence,
serverControlSequence: _liveSession.ServerControlSequence,
teleportSequence: _liveSession.TeleportSequence,
forcePositionSequence: _liveSession.ForcePositionSequence);
forcePositionSequence: _liveSession.ForcePositionSequence,
contactLongJump: contactByte);
DumpMovementTruthOutbound(
"MTS", seq, result, wirePos, wireCellId, contactByte);
_liveSession.SendGameAction(body);
}
@ -4900,7 +4920,10 @@ public sealed class GameWindow : IDisposable
instanceSequence: _liveSession.InstanceSequence,
serverControlSequence: _liveSession.ServerControlSequence,
teleportSequence: _liveSession.TeleportSequence,
forcePositionSequence: _liveSession.ForcePositionSequence);
forcePositionSequence: _liveSession.ForcePositionSequence,
lastContact: contactByte);
DumpMovementTruthOutbound(
"AP", seq, result, wirePos, wireCellId, contactByte);
_liveSession.SendGameAction(body);
}
@ -4924,6 +4947,76 @@ public sealed class GameWindow : IDisposable
}
}
private void DumpMovementTruthOutbound(
string kind,
uint sequence,
AcDream.App.Input.MovementResult result,
System.Numerics.Vector3 wirePosition,
uint wireCellId,
byte contactByte)
{
if (!DumpMoveTruthEnabled) return;
var velocity = _playerController?.BodyVelocity ?? System.Numerics.Vector3.Zero;
_lastMovementTruthOutbound = new MovementTruthOutbound(
kind,
sequence,
System.DateTime.UtcNow,
result.Position,
result.CellId,
wirePosition,
wireCellId,
result.IsOnGround,
contactByte,
velocity);
Console.WriteLine(System.FormattableString.Invariant($"move-truth OUT kind={kind} seq={sequence} local={Fmt(result.Position)} localCell=0x{result.CellId:X8} wire={Fmt(wirePosition)} wireCell=0x{wireCellId:X8} grounded={result.IsOnGround} contact={contactByte} vel={Fmt(velocity)} f={FmtCmd(result.ForwardCommand)} s={FmtCmd(result.SidestepCommand)} t={FmtCmd(result.TurnCommand)}"));
}
private void DumpMovementTruthServerEcho(
AcDream.Core.Net.WorldSession.EntityPositionUpdate update,
System.Numerics.Vector3 serverWorldPosition)
{
if (!DumpMoveTruthEnabled || update.Guid != _playerServerGuid) return;
var now = System.DateTime.UtcNow;
var localPosition = _playerController?.Position;
var localCellId = _playerController?.CellId;
var deltaLocal = localPosition.HasValue
? serverWorldPosition - localPosition.Value
: (System.Numerics.Vector3?)null;
string localText = localPosition.HasValue ? Fmt(localPosition.Value) : "-";
string localCellText = localCellId.HasValue
? System.FormattableString.Invariant($"0x{localCellId.Value:X8}")
: "-";
string deltaLocalText = deltaLocal.HasValue ? Fmt(deltaLocal.Value) : "-";
string deltaLocalLen = deltaLocal.HasValue
? System.FormattableString.Invariant($"{deltaLocal.Value.Length():F3}")
: "-";
string lastText = "-";
if (_lastMovementTruthOutbound is { } last)
{
var deltaOut = serverWorldPosition - last.LocalWorldPosition;
var ageMs = (now - last.TimeUtc).TotalMilliseconds;
lastText = System.FormattableString.Invariant($"{last.Kind}:{last.Sequence} ageMs={ageMs:F0} outGrounded={last.IsOnGround} outContact={last.ContactByte} outCell=0x{last.WireCellId:X8} deltaOut={Fmt(deltaOut)} distOut={deltaOut.Length():F3}");
}
string state = _playerController?.State.ToString() ?? "-";
string velocityText = update.Velocity.HasValue ? Fmt(update.Velocity.Value) : "-";
Console.WriteLine(System.FormattableString.Invariant($"move-truth ECHO guid=0x{update.Guid:X8} server={Fmt(serverWorldPosition)} serverCell=0x{update.Position.LandblockId:X8} local={localText} localCell={localCellText} deltaLocal={deltaLocalText} distLocal={deltaLocalLen} serverVel={velocityText} state={state} lastOut={lastText}"));
}
private static string Fmt(System.Numerics.Vector3 v) =>
System.FormattableString.Invariant($"({v.X:F3},{v.Y:F3},{v.Z:F3})");
private static string FmtCmd(uint? command) =>
command.HasValue
? System.FormattableString.Invariant($"0x{command.Value:X8}")
: "-";
/// <summary>
/// Convert our internal yaw (math convention: 0=+X East, PI/2=+Y North)
/// to AC's quaternion heading convention.