fix(headless): complete connected movement gate

This commit is contained in:
Erik 2026-07-27 10:26:44 +02:00
parent fd9559a063
commit 3f3401257c
11 changed files with 756 additions and 48 deletions

View file

@ -55,6 +55,12 @@ public sealed class PhysicsEngine
/// <summary>Number of registered landblocks (diagnostic).</summary>
public int LandblockCount => _landblocks.Count;
/// <summary>
/// Optional high-volume collision trace sink. Production leaves this
/// unset; focused diagnostic gates may opt in explicitly.
/// </summary>
public Action<string>? DiagnosticLog { get; set; }
/// <summary>
/// True once the landblock covering <paramref name="cellOrLandblockId"/> has had its
/// terrain + cells registered via <see cref="AddLandblock"/>. Accepts a canonical
@ -797,8 +803,8 @@ public sealed class PhysicsEngine
if (physics is null)
{
if (snapDiag)
Console.WriteLine(System.FormattableString.Invariant(
if (snapDiag && DiagnosticLog is { } noLandblockLog)
noLandblockLog(System.FormattableString.Invariant(
$"[snap] claim=0x{cellId:X8} pos=({currentPos.X:F3},{currentPos.Y:F3},{currentPos.Z:F3}) branch=NO-LANDBLOCK (lbs={_landblocks.Count}) -> verbatim"));
return new ResolveResult(candidatePos, cellId, IsOnGround: false);
}
@ -831,8 +837,11 @@ public sealed class PhysicsEngine
float? claimFloorZ = WalkableFloorZNearest(cellId, candidatePos, currentPos.Z);
if (claimFloorZ is not null)
{
Console.WriteLine(System.FormattableString.Invariant(
$"[snap] claim=0x{cellId:X8} pos=({currentPos.X:F3},{currentPos.Y:F3},{currentPos.Z:F3}) VALIDATED -> grounded to its walkable floor z={claimFloorZ.Value:F3}"));
if (DiagnosticLog is { } validatedLog)
{
validatedLog(System.FormattableString.Invariant(
$"[snap] claim=0x{cellId:X8} pos=({currentPos.X:F3},{currentPos.Y:F3},{currentPos.Z:F3}) VALIDATED -> grounded to its walkable floor z={claimFloorZ.Value:F3}"));
}
// #133 (2026-06-13): return the VALIDATED claim's OWN full cell id,
// NOT lbPrefix | (cellId & 0xFFFF). lbPrefix is found by scanning
// resident landblocks for one whose [0,192) local bounds contain
@ -1017,10 +1026,14 @@ public sealed class PhysicsEngine
// max(terrain, z) stays as the under-terrain sanity bound —
// our recoverable stand-in for retail's lost-cell machinery
// (documented divergence, same as the #107 demote).
if (snapDiag && currentPos.Z > terrainZ)
if (snapDiag
&& currentPos.Z > terrainZ)
{
Console.WriteLine(System.FormattableString.Invariant(
if (DiagnosticLog is { } outdoorLog)
{
outdoorLog(System.FormattableString.Invariant(
$"[snap] OUTDOOR claim 0x{cellId:X8} z={currentPos.Z:F3} above terrain {terrainZ:F3} — committing the server Z (retail SetPositionInternal shape; physics settles on tick 1)"));
}
targetZ = currentPos.Z;
}
}
@ -1028,8 +1041,8 @@ public sealed class PhysicsEngine
// Step-height enforcement: block upward movement that exceeds the limit.
float zDelta = targetZ - currentPos.Z;
if (snapDiag)
Console.WriteLine(System.FormattableString.Invariant(
if (snapDiag && DiagnosticLog is { } resultLog)
resultLog(System.FormattableString.Invariant(
$"[snap] claim=0x{cellId:X8} pos=({currentPos.X:F3},{currentPos.Y:F3},{currentPos.Z:F3}) cells={physics.Cells.Count} bestCell=0x{(bestCell?.CellId ?? 0u):X8} bestZ={(bestCellZ?.ToString("F3") ?? "none")} terrainZ={terrainZ:F3} indoor={currentlyIndoor} -> targetZ={targetZ:F3} targetCell=0x{(lbPrefix | (targetCellId & 0xFFFFu)):X8} stepReject={zDelta > stepUpHeight}"));
if (zDelta > stepUpHeight)
{