fix(physics #145): Slice 3 — carried-anchor membership; closes the far-town cascade

The outdoor membership pick derived the landblock origin from the terrain
registry, which returns (0,0) for an UNSTREAMED neighbour — so a fresh far-town
teleport at a landblock edge marched the cell id one block per physics tick
(the cascade; the 17410 ACE rejects is its wire artifact).

Fix: thread the CARRIED cell-relative frame anchor (body.Position -
body.CellPosition.Frame.Origin) into the pick via SpherePath.CarriedBlockOrigin.
That anchor IS the true landblock world origin, correct even for an unstreamed
neighbour, so the pick re-derives the SAME (consistent) cell and never marches.
- CellTransit.FindCellSet/BuildCellSetAndPickContaining: Vector3? carriedBlockOrigin
  (null default = legacy TryGetTerrainOrigin → every existing caller/test untouched).
- PhysicsEngine.ResolveWithTransition: set the anchor from a SEEDED OUTDOOR body
  whose carried landblock matches the resolve cell (else null → legacy).
- PlayerMovementController.SetPosition: 3-arg overload seeds CellPosition from the
  wire's (cell, local) via SnapToCell; 2-arg delegates with cellLocal=pos (anchor
  (0,0,0) == legacy → zero test churn).
- GameWindow.CellLocalForSeed: the placement seam (_liveCenter used ONCE here to
  derive the cell-local; physics carries it forward without _liveCenter).
Regression: TeleportFarTownRunawayTests (south + east edge, unstreamed neighbour).
Core 1529 / App 480, zero regressions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-21 13:51:04 +02:00
parent 865aae8876
commit 6349ba49aa
6 changed files with 147 additions and 13 deletions

View file

@ -792,8 +792,22 @@ public sealed class PlayerMovementController
}
public void SetPosition(Vector3 pos, uint cellId)
// #145: tests + legacy callers run in the world==block-local frame (no
// streaming center), so the cell-local seed IS the world position. This
// makes the carried anchor (body.Position CellPosition.Origin) == (0,0,0),
// identical to the legacy Zero terrain-origin fallback → behaviour unchanged.
=> SetPosition(pos, cellId, pos);
/// <summary>
/// Server-snap / teleport placement. <paramref name="cellLocal"/> is the
/// LANDBLOCK-relative position (the wire's local, or world landblock origin)
/// which seeds the body's cell-relative <c>CellPosition</c> WITHOUT any streaming
/// center (#145). A teleport is a large jump, so this snaps the cell frame
/// directly via <c>SnapToCell</c> rather than delta-syncing through the setter.
/// </summary>
public void SetPosition(Vector3 pos, uint cellId, Vector3 cellLocal)
{
_body.Position = pos;
_body.SnapToCell(cellId, pos, cellLocal);
_prevPhysicsPos = pos;
_currPhysicsPos = pos;
UpdateCellId(cellId, "teleport");