fix(A.5 T7-T9): migrate entity.Position= → SetPosition; add Promoted arm

Code review on commits 295bce9/a0741bd/4be392b flagged 1 Important + 3
Minor issues. Apply the actionable two:

Important: 6 sites in GameWindow.cs (lines 3900, 4017-4024, 4138, 4270,
4315) wrote entity.Position = X directly, bypassing T8's SetPosition
mutator and therefore never marking AabbDirty. When T18 lands the
dispatcher's "if AabbDirty refresh" cull gate, these direct writes
would silently leave AABB stale (frustum culls dynamic entities at
their previous positions). Migrated all 6 sites to SetPosition().

Minor: Added a silent case LandblockStreamResult.Promoted arm in
StreamingController.Tick with a TODO(A.5 T13) marker. Today the
streamer never produces Promoted, so the arm is unreachable; the
explicit case prevents a future reader from wondering why the case
is missing.

Deferred Minor: surfaceCache thread-safety XML doc comment + style
consistency on System.Collections.Generic using directive — non-
load-bearing cosmetic.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-10 07:25:07 +02:00
parent 4be392b361
commit c5f98b276e
2 changed files with 13 additions and 7 deletions

View file

@ -3897,7 +3897,7 @@ public sealed class GameWindow : IDisposable
// position by adding the residual back (so the visual doesn't jerk
// for one frame before the residual decay kicks in on the next tick).
System.Numerics.Vector3 preSnapPos = entity.Position;
entity.Position = worldPos;
entity.SetPosition(worldPos);
entity.Rotation = rot;
// Commit B 2026-04-29 — keep the shadow registry in sync with
@ -4017,11 +4017,11 @@ public sealed class GameWindow : IDisposable
if (!update.IsGrounded)
{
// Undo the unconditional entity hard-snap at the top of the
// function (entity.Position = worldPos): the body is mid-arc
// function (entity.SetPosition(worldPos)): the body is mid-arc
// and TickAnimations will write entity = body next frame
// anyway. Setting entity = body now prevents a 1-frame
// teleport-to-server-then-yank-back rubber-band.
entity.Position = rmState.Body.Position;
entity.SetPosition(rmState.Body.Position);
return;
}
@ -4130,12 +4130,12 @@ public sealed class GameWindow : IDisposable
}
// Sync the visible entity to the body — overrides the unconditional
// entity.Position = worldPos snap at the top of this function.
// entity.SetPosition(worldPos) snap at the top of this function.
// For the far-snap branch this is a no-op (body == worldPos); for
// the near-enqueue branch this prevents a 1-frame teleport-then-
// yank-back rubber-band as TickAnimations chases worldPos via the
// queue.
entity.Position = rmState.Body.Position;
entity.SetPosition(rmState.Body.Position);
return;
}
@ -4267,7 +4267,7 @@ public sealed class GameWindow : IDisposable
rmState.ServerVelocity);
}
entity.Position = rmState.Body.Position;
entity.SetPosition(rmState.Body.Position);
entity.Rotation = rmState.Body.Orientation;
}
@ -4312,7 +4312,7 @@ public sealed class GameWindow : IDisposable
resolved.Position.X, resolved.Position.Y, resolved.Position.Z);
// 3. Snap player entity + controller.
entity.Position = snappedPos;
entity.SetPosition(snappedPos);
entity.Rotation = rot;
_playerController.SetPosition(snappedPos, resolved.CellId);

View file

@ -107,6 +107,12 @@ public sealed class StreamingController
Console.WriteLine(
$"streaming: worker CRASHED: {crashed.Error}");
break;
case LandblockStreamResult.Promoted:
// TODO(A.5 T13): merge promoted entities into existing
// GpuWorldState entry via AddEntitiesToExistingLandblock.
// Today the streamer never produces Promoted (only LoadNear /
// LoadFar), so this arm is unreachable and silently consumed.
break;
}
}
}