feat(runtime): C3c - production placement cutover: both hosts on the residence conductors (routes 1+8)

Campaign P remaining-physics-divergence, placement cutover slice C3c
(docs/plans/2026-08-02-placement-cutover.md). Both production hosts now
register every initial Create through the residence + continuation-
executor + first-entry-conductor machinery (C0-C3b):

- Graphical (route 1): RegisterEntityWithInitialResidence at Create; the
  shared RuntimeFirstEntryDriveController pumps both conductors from the
  placement-receipt flow; MaterializeProjection and RebucketLiveEntity
  are presentation-only while a residence is ACTIVE (ExecutorCompleted is
  the presentation-binding receipt); post-residence entities take the
  full legacy path including the prepare_to_enter_world clock edges.
  PlayerModeController attaches presentation to the Runtime-published
  controller; its legacy resolve/step-heights/host-construction path is
  deleted; presentation-only rollback (retail has no entry-flow rollback).
- Headless (route 8): OnSpawned registers with residence when a drive
  exists; content-less sessions keep the pre-flip direct registration;
  SynchronizeLocalPlayer/CreateController/ApplySetupStepHeights deleted;
  prepared-collision read failure is a typed AwaitingCollisionSource
  retry; far remotes outside the service window complete celless.
- RuntimeLocalPlayerMovementState.Controller setter sealed internal; all
  controller mutation flows through the publication lifecycle.

Fix slices landed within this cutover, each dual-gated:
- F1: live movement-stat/server-physics application routed through the
  Runtime ownership seam (post-logout ingest crash on the retired
  controller eliminated; RuntimeMovementSkillProjection deleted).
- F2: login activation wedge - collision-admission prefix gate factored
  out of the seal (reentrant-commit RejectedAuthority), rearm generation
  identity corrected, PlayerModeAutoEntry requires the Runtime-published
  controller (world reveal can no longer seal unmaterialized).
- F3: landblock-prefix 0-sentinel replaced by explicit absent-id guards;
  map-corner landblocks (grid row/col 0) fully legal through admission,
  park/rearm/retire, quiescence, and outdoor shadow seeds.
- F5: local-player first-entry ground contact seeded by the shared
  SpawnPlacementSettler (moved App->Core) at FinalizeActivation - the
  retail first-gravity-frame touch (enter_world 0x00516170 carries no
  seed); the legacy unconditional force-seed is overwritten by a real
  floor-found contact; airborne spawns stay airborne; outbound contact
  bit verified end-to-end. Fixes the standing-cast 'You can't do that
  while in the air!' rejections.
- R1 (dual-review round): login constraint leash armed at the committed
  placement (HandleReceivedPosition 0x00453FD0 analog); register rows
  AD-61 (settle-timing compression now covering the local player) and
  AD-42 (repointed off the deleted resolve split) in this commit;
  residence-conversion owner API; wire-landblock guards; drive-pending
  ledger in IsConverged; route attach/detach latch; executor-drain drift
  model documented + source-pinned.

Gates: Runtime 1,003, App 4,039/3 skips, Headless 79, complete solution
10,816/0 failed/4 skips (Release, -m:1); connected lifecycle/reconnect
gate PASS (logs/connected-world-gate-20260802-175401; graceful exits,
world-visible, zero airborne rejections). The nine-stop soak remains red
for the pre-existing 6b28ff99 whole-world collision-clone throughput
regression (attributed with evidence; scheduled as its own slice before
C5). Dual Opus reviews (retail-conformance + adversarial): delta PASS.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-02 18:10:33 +02:00
parent 78f1eb1896
commit 529e0e9d88
68 changed files with 5977 additions and 831 deletions

View file

@ -104,18 +104,13 @@ public sealed class RuntimeLocalPlayerFirstEntryStateTests
Assert.Equal(RuntimeLocalPlayerFirstEntryStatus.AwaitingActivation, second);
Assert.Equal(1, fixture.Publication.CaptureOwnership().PendingActivationCount);
const ulong generation = 1UL;
fixture.Lifetime.Physics.SetPosition.BeginCollisionGeneration(
Cell & 0xFFFF0000u, generation);
fixture.Lifetime.Physics.Engine.AddLandblock(
Cell & 0xFFFF0000u,
new TerrainSurface(new byte[81], new float[256]),
Array.Empty<CellSurface>(),
Array.Empty<PortalPlane>(),
worldOffsetX: 0f,
worldOffsetY: 0f);
fixture.Lifetime.Physics.SetPosition.CommitCollisionGeneration(
Cell & 0xFFFF0000u, generation, ready: true);
// C3c-F2: the wake goes through the SAME owner production uses (the
// collision admission ledger). Driving the SetPosition seam directly
// leaves that ledger empty — a state production can never be in, and
// the reason this very test did not catch the login activation wedge.
CommitProductionCollisionGeneration(
fixture.Lifetime.Physics,
Cell & 0xFFFF0000u);
Assert.Equal(RuntimeLocalPlayerFirstEntryStatus.Completed,
fixture.Advance(out RuntimeInitialCreateExecutionReceipt receipt));
@ -603,18 +598,9 @@ public sealed class RuntimeLocalPlayerFirstEntryStateTests
// AwaitingActivation retry test does.
Assert.Equal(RuntimeLocalPlayerFirstEntryStatus.AwaitingActivation,
fixture.Advance(out _));
const ulong generation = 1UL;
fixture.Lifetime.Physics.SetPosition.BeginCollisionGeneration(
Cell & 0xFFFF0000u, generation);
fixture.Lifetime.Physics.Engine.AddLandblock(
Cell & 0xFFFF0000u,
new TerrainSurface(new byte[81], new float[256]),
Array.Empty<CellSurface>(),
Array.Empty<PortalPlane>(),
worldOffsetX: 0f,
worldOffsetY: 0f);
fixture.Lifetime.Physics.SetPosition.CommitCollisionGeneration(
Cell & 0xFFFF0000u, generation, ready: true);
CommitProductionCollisionGeneration(
fixture.Lifetime.Physics,
Cell & 0xFFFF0000u);
RuntimeLocalPlayerFirstEntryStatus status = fixture.Advance(
out RuntimeInitialCreateExecutionReceipt receipt);
@ -631,6 +617,63 @@ public sealed class RuntimeLocalPlayerFirstEntryStateTests
// Helpers
// ---------------------------------------------------------------
/// <summary>
/// C3c-F2: publishes one landblock collision generation through the exact
/// production owner chain — BeginCollisionAdmission -> prepare -> stage ->
/// CommitCollisionGeneration — so a parked local-player activation wakes
/// the way it does live, with RuntimePhysicsState's admission ledger
/// populated and then retired.
/// </summary>
private static void CommitProductionCollisionGeneration(
RuntimePhysicsState physics,
uint landblockId)
{
RuntimeCollisionAdmission admission =
physics.BeginCollisionAdmission(landblockId);
using PreparedLandblockCollisionGeneration prepared =
physics.PrepareCollisionGeneration(admission);
physics.StageCollisionAssets(
admission,
prepared,
new RuntimeLandblockCollisionAssets(
landblockId,
new TerrainSurface(new byte[81], new float[256]),
Array.Empty<CellSurface>(),
Array.Empty<PortalPlane>(),
0f,
0f,
0u));
for (int poll = 0; poll < 10_000; poll++)
{
while (physics.SetPosition.TryPeekProjection(
out RuntimePlacementProjectionSnapshot projection))
{
Assert.True(physics.SetPosition.AcknowledgeProjection(
projection.Token));
}
while (!physics.AdvanceCollisionRetainedOwnerCapture(
admission,
prepared).Completed)
{
}
foreach (uint ownerId in prepared.RetainedOwnerIds)
physics.RefreshCollisionRetainedOwner(admission, prepared, ownerId);
RuntimeCollisionSealStep seal;
do
{
seal = physics.AdvanceCollisionGenerationSeal(admission, prepared);
}
while (!seal.Completed && !seal.Restarted);
if (!seal.Completed)
continue;
if (physics.CommitCollisionGeneration(admission, prepared).Completed)
return;
}
throw new InvalidOperationException(
"Collision generation did not complete its Runtime mutation transaction.");
}
private static (RuntimeEntityRecord Record, RuntimePlacementProjectionToken Token)
BeginPendingOrdinaryPlacement(Fixture fixture, uint guid)
{

View file

@ -244,4 +244,261 @@ public sealed class RuntimeLocalPlayerMovementStateTests
Assert.Equal(0L, allocated);
}
// ── C3c-F1 (2026-08-02): movement-stats application seam ──────────────
//
// The typed seam is the ONLY route by which server stat recomputes
// reach the controller; the publication lifecycle decides whether the
// write lands (live/dormant) or is a typed displaced drop (terminal).
private static RuntimeMovementSkillState CompleteSkillState()
{
var skills = new RuntimeMovementSkillState();
skills.Update(runSkill: 240, jumpSkill: 180);
skills.UpdateBurden(0.5f);
skills.UpdateStamina(37);
// BF_PLAYER (0x8) + BF_PLAYER_KILLER (0x20) → ObjectInfoState.IsPK.
skills.UpdateOwnPwdBitfield(0x28u);
skills.UpdatePlayerKillerStatus(1, 123.5f);
return skills;
}
private static PlayerMovementController NewDormantRuntimeController()
{
PlayerMovementController controller =
PlayerMovementController.CreatePublicationCandidate(
new PhysicsEngine(),
PlayerMovementConstructionOptions.Fallback);
controller.SealPublicationCandidate();
controller.CommitRuntimeOwnership(new RetailObjectQuantumClock());
return controller;
}
private static (float RunRate, float JumpVz, bool CanJump, int JumpCost,
ObjectInfoState PvpFlags) CaptureStatObservables(
PlayerMovementController controller)
{
IWeenieObject weenie = controller.Motion.WeenieObj!;
Assert.True(weenie.InqRunRate(out float runRate));
Assert.True(weenie.InqJumpVelocity(1.0f, out float jumpVz));
bool canJump = weenie.CanJump(1.0f);
Assert.True(((PlayerWeenie)weenie).JumpStaminaCost(1.0f, out int cost));
return (runRate, jumpVz, canJump, cost, controller.OwnPvpFlags);
}
[Fact]
public void ApplyCharacterMovementStats_LiveApplicationIsByteIdenticalToTheRetiredDirectPath()
{
RuntimeMovementSkillState skills = CompleteSkillState();
RuntimeMovementSkillSnapshot snapshot = skills.Snapshot;
// Old direct path (the deleted RuntimeMovementSkillProjection.ApplyTo
// body), applied through the public gated setters.
var direct = new PlayerMovementController(new PhysicsEngine());
direct.SetCharacterSkills(snapshot.RunSkill, snapshot.JumpSkill);
direct.SetCharacterBurden(snapshot.Burden);
direct.SetCharacterStamina(snapshot.CurrentStamina);
direct.OwnPvpFlags = EntityCollisionFlagsExt
.FromPwdBitfield(snapshot.OwnPwdBitfield)
.ToMoverState();
direct.SetCharacterPkStatus(
snapshot.PlayerKillerStatus,
snapshot.LastPkAttackTimestamp);
var routed = new PlayerMovementController(new PhysicsEngine());
using var movement = new RuntimeLocalPlayerMovementState
{
Controller = routed,
};
Assert.Equal(
RuntimeMovementStatsApplication.AppliedLive,
movement.ApplyCharacterMovementStats(skills));
Assert.Equal(
CaptureStatObservables(direct),
CaptureStatObservables(routed));
Assert.Equal(ObjectInfoState.IsPK, routed.OwnPvpFlags);
}
[Fact]
public void ApplyCharacterMovementStats_DormantWindowWriteLandsOnTheControllerThatGoesLive()
{
RuntimeMovementSkillState skills = CompleteSkillState();
PlayerMovementController controller = NewDormantRuntimeController();
using var movement = new RuntimeLocalPlayerMovementState
{
Controller = controller,
};
// The committed-but-unactivated first-entry window: the dormant
// controller is installed in the movement owner while activation
// waits on cell streaming, and inbound recomputes keep pumping.
Assert.True(controller.IsRuntimeOwnedDormant);
Assert.Throws<InvalidOperationException>(
() => controller.SetCharacterSkills(1, 1));
Assert.Equal(
RuntimeMovementStatsApplication.AppliedDormant,
movement.ApplyCharacterMovementStats(skills));
// Activation flips the SAME instance live — the applied values are
// already current when movement starts.
controller.ActivateRuntimePublication();
Assert.True(controller.IsRuntimePublished);
(float runRate, _, _, _, ObjectInfoState pvp) =
CaptureStatObservables(controller);
Assert.Equal(
PlayerWeenie.GetRunRate(0.5f, 240),
runRate,
precision: 5);
Assert.Equal(ObjectInfoState.IsPK, pvp);
}
[Fact]
public void ApplyCharacterMovementStats_TerminalControllerReportsTypedDisplacedDrop()
{
RuntimeMovementSkillState skills = CompleteSkillState();
PlayerMovementController controller = NewDormantRuntimeController();
using var movement = new RuntimeLocalPlayerMovementState
{
Controller = controller,
};
controller.ActivateRuntimePublication();
var before = CaptureStatObservables(controller);
// The Motion surface is gated once terminal; capture the weenie
// reference while the controller is still published.
IWeenieObject weenie = controller.Motion.WeenieObj!;
// Post-teardown transient truth: the controller retired while still
// reachable through a displaced recompute callback. The public
// setter still throws (the seal working); the seam reports the
// typed drop instead and mutates nothing.
controller.RetireRuntimePublication();
Assert.Throws<InvalidOperationException>(
() => controller.SetCharacterSkills(1, 1));
Assert.Equal(
RuntimeMovementStatsApplication.DroppedDisplacedController,
movement.ApplyCharacterMovementStats(skills));
Assert.True(weenie.InqRunRate(out float runRate));
Assert.Equal(before.RunRate, runRate);
Assert.Equal(before.PvpFlags, controller.OwnPvpFlags);
}
[Fact]
public void ApplyCharacterMovementStats_SealedCandidateIsATypedDisplacedDrop()
{
// Defensive lifecycle-matrix row: a sealed candidate is frozen for
// the Prepare→Commit validation handshake and never installed into
// the movement owner in production (Prepare requires the owner
// empty; Commit installs it already-dormant within the same
// synchronous conductor step), so only a direct controller-level
// call can observe this row.
PlayerMovementController controller =
PlayerMovementController.CreatePublicationCandidate(
new PhysicsEngine(),
PlayerMovementConstructionOptions.Fallback);
controller.SealPublicationCandidate();
Assert.Equal(
RuntimeMovementStatsApplication.DroppedDisplacedController,
controller.ApplyCharacterMovementStats(
CompleteSkillState().Snapshot));
}
[Fact]
public void ApplyCharacterMovementStats_AbsentControllerAndIncompleteSnapshotDropSilently()
{
var skills = new RuntimeMovementSkillState();
using var movement = new RuntimeLocalPlayerMovementState();
Assert.Equal(
RuntimeMovementStatsApplication.DroppedNoController,
movement.ApplyCharacterMovementStats(skills));
movement.Controller = new PlayerMovementController(new PhysicsEngine());
Assert.Equal(
RuntimeMovementStatsApplication.DroppedIncompleteSnapshot,
movement.ApplyCharacterMovementStats(skills));
}
[Fact]
public void ApplyCharacterMovementStats_DisposedOwnerToleratesTheDisplacedCallback()
{
var movement = new RuntimeLocalPlayerMovementState
{
Controller = new PlayerMovementController(new PhysicsEngine()),
};
movement.Dispose();
// The displaced-callback-rejection pattern: a recompute landing
// after terminal disposal observes a typed drop, never a fault.
Assert.Equal(
RuntimeMovementStatsApplication.DroppedNoController,
movement.ApplyCharacterMovementStats(CompleteSkillState()));
Assert.False(movement.ReportExhaustion());
}
[Fact]
public void ReportExhaustion_DispatchesOnlyThroughALiveController()
{
using var movement = new RuntimeLocalPlayerMovementState();
Assert.False(movement.ReportExhaustion());
PlayerMovementController controller = NewDormantRuntimeController();
movement.Controller = controller;
Assert.False(movement.ReportExhaustion());
controller.ActivateRuntimePublication();
Assert.True(movement.ReportExhaustion());
controller.RetireRuntimePublication();
Assert.False(movement.ReportExhaustion());
}
[Fact]
public void ApplyServerPhysicsState_DormantDropsForActivationAndLiveAppliesExactly()
{
// The second connected-gate crash chain
// (logs/connected-world-gate-20260802-125907): an inbound local-
// player SetState pushed ApplyPhysicsState at the dormant
// first-entry controller. The typed entry decides by lifecycle.
PlayerMovementController controller =
PlayerMovementController.CreatePublicationCandidate(
new PhysicsEngine(),
PlayerMovementConstructionOptions.Fallback);
PhysicsBody body = controller.PhysicsBody;
PhysicsStateFlags initial = body.State;
PhysicsStateFlags pushed =
PhysicsStateFlags.Gravity
| PhysicsStateFlags.ReportCollisions
| PhysicsStateFlags.Ethereal;
Assert.NotEqual(initial, pushed);
controller.SealPublicationCandidate();
controller.CommitRuntimeOwnership(new RetailObjectQuantumClock());
// Dormant: the activation transaction owns the dormant body's
// physics state; the push is dropped and the body is untouched.
Assert.Equal(
RuntimeServerPhysicsStateApplication.DroppedDormantActivationOwned,
controller.ApplyServerPhysicsState(pushed));
Assert.Equal(initial, body.State);
// Published: byte-identical to the direct ApplyPhysicsState body.
controller.ActivateRuntimePublication();
Assert.Equal(
RuntimeServerPhysicsStateApplication.AppliedLive,
controller.ApplyServerPhysicsState(pushed));
Assert.Equal(pushed, body.State);
// Terminal: a displaced push, typed instead of a fault, mutating
// nothing.
controller.RetireRuntimePublication();
Assert.Throws<InvalidOperationException>(
() => controller.ApplyPhysicsState(initial));
Assert.Equal(
RuntimeServerPhysicsStateApplication.DroppedDisplacedController,
controller.ApplyServerPhysicsState(initial));
Assert.Equal(pushed, body.State);
}
}

View file

@ -198,6 +198,175 @@ public sealed class RuntimeLocalPlayerPhysicsPublicationStateTests
Assert.True(body.InWorld);
}
/// <summary>
/// C3c-F5: retail seeds the local player's ground contact from the first
/// gravity frame after <c>enter_world</c> (SmartBox::HandleCreateObject
/// 0x00454C80 → init_player 0x00455010 → CPhysicsObj::enter_world
/// 0x00516170; SetPosition records no touch), which the activation
/// compresses through the shared #270 settle sweep. Spawn (1,2,3) with a
/// flat floor at 2.7 — feet 0.3 m above it, inside the 0.5 m settle
/// reach — and the production human bottom-sphere origin (0.475) so the
/// authored placement itself stands clear of the floor.
/// </summary>
[Fact]
public void CommitActivationOnFlatGroundSeedsRetailFirstGravityFrameContact()
{
using var fixture = new Fixture(
residentWorld: true,
terrainHeight: 2.7f,
moverSphereOriginZ: 0.475f);
Assert.Equal(RuntimeLocalPlayerPhysicsPublicationStatus.Committed,
fixture.Owner.Commit(
fixture.Prepare(),
out RuntimeLocalPlayerPhysicsActivationToken token));
Assert.Equal(RuntimeLocalPlayerPhysicsActivationStatus.Evaluated,
fixture.Owner.EvaluateActivation(token, out var evaluation));
Assert.Equal(RuntimeDormantSetPositionCommitStatus.Committed,
fixture.Owner.CommitActivation(evaluation, out _));
PhysicsBody body = Assert.IsType<PhysicsBody>(fixture.Record.PhysicsBody);
Assert.True(body.InWorld);
Assert.True(body.InContact);
Assert.True(body.OnWalkable);
Assert.True(body.ContactPlaneValid);
Assert.True(body.ContactPlane.Normal.Z > 0.9f);
// The settle snapped the feet onto the floor, exactly like retail's
// first gravity frame.
Assert.InRange(body.Position.Z, 2.65f, 2.76f);
PlayerMovementController controller =
Assert.IsType<PlayerMovementController>(
fixture.Movement.Controller);
// The exact outbound predicate LocalPlayerOutboundController
// serializes as the wire contact bit (InContact && OnWalkable).
Assert.True(controller.CanSendPositionEvent);
Assert.True(controller.CaptureMovementResult(
mouseLookEvent: false).IsOnGround);
}
/// <summary>
/// C3c-F5 counterpart: a spawn with no floor within the settle's reach
/// (flat terrain at 0, feet at 3) must stay genuinely airborne — the
/// seed only ever commits contact the sweep actually found, exactly
/// like retail's fall after enter_world.
/// </summary>
[Fact]
public void CommitActivationOverVoidLeavesFirstEntryGenuinelyAirborne()
{
using var fixture = new Fixture(
residentWorld: true,
moverSphereOriginZ: 0.475f);
Assert.Equal(RuntimeLocalPlayerPhysicsPublicationStatus.Committed,
fixture.Owner.Commit(
fixture.Prepare(),
out RuntimeLocalPlayerPhysicsActivationToken token));
Assert.Equal(RuntimeLocalPlayerPhysicsActivationStatus.Evaluated,
fixture.Owner.EvaluateActivation(token, out var evaluation));
Assert.Equal(RuntimeDormantSetPositionCommitStatus.Committed,
fixture.Owner.CommitActivation(evaluation, out _));
PhysicsBody body = Assert.IsType<PhysicsBody>(fixture.Record.PhysicsBody);
Assert.True(body.InWorld);
Assert.False(body.InContact);
Assert.False(body.OnWalkable);
Assert.False(body.ContactPlaneValid);
Assert.Equal(3f, body.Position.Z);
PlayerMovementController controller =
Assert.IsType<PlayerMovementController>(
fixture.Movement.Controller);
Assert.False(controller.CanSendPositionEvent);
Assert.False(controller.CaptureMovementResult(
mouseLookEvent: false).IsOnGround);
}
/// <summary>
/// C3c-R1 review R1: the login constraint leash. Retail arms the leash
/// at every accepted-position event (SmartBox::HandleReceivedPosition
/// 0x00453FD0); the flip deleted the App-side login arm
/// (CommitPreparedPosition), so the dormant activation's final commit
/// must arm it — anchored at the committed placement (already
/// floor-snapped by the faithful placement transaction), before the
/// compressed first-gravity-frame settle runs, exactly like retail arms
/// at the received position and only then simulates the first frame.
/// </summary>
[Fact]
public void CommitActivationArmsTheLoginConstraintLeashAtTheCommittedPlacement()
{
using var fixture = new Fixture(
residentWorld: true,
terrainHeight: 2.7f,
moverSphereOriginZ: 0.475f);
Assert.Equal(RuntimeLocalPlayerPhysicsPublicationStatus.Committed,
fixture.Owner.Commit(
fixture.Prepare(),
out RuntimeLocalPlayerPhysicsActivationToken token));
Assert.Equal(RuntimeLocalPlayerPhysicsActivationStatus.Evaluated,
fixture.Owner.EvaluateActivation(token, out var evaluation));
Assert.Equal(RuntimeDormantSetPositionCommitStatus.Committed,
fixture.Owner.CommitActivation(evaluation, out _));
PlayerMovementController controller =
Assert.IsType<PlayerMovementController>(
fixture.Movement.Controller);
ConstraintManager? constraint =
controller.PositionManager?.Constraint;
Assert.NotNull(constraint);
Assert.True(constraint!.IsConstrained);
// The anchor cell is the placement's committed containing cell.
Assert.Equal(
controller.PhysicsBody.CellPosition.ObjCellId,
constraint.ConstraintPos.ObjCellId);
// Anchored at the committed (floor-snapped) placement pose.
Assert.InRange(
constraint.ConstraintPos.Frame.Origin.Z, 2.65f, 2.76f);
Assert.InRange(
controller.PhysicsBody.Position.Z, 2.65f, 2.76f);
Assert.Equal(
ConstraintDistance.GetStartConstraintDistance(
constraint.ConstraintPos.ObjCellId),
constraint.ConstraintDistanceStart);
Assert.Equal(
ConstraintDistance.GetMaxConstraintDistance(
constraint.ConstraintPos.ObjCellId),
constraint.ConstraintDistanceMax);
}
/// <summary>
/// C3c-R1 review R1: the arm is exactly-once — the successful final
/// commit nulls the activation envelope, so a stale CommitActivation
/// retry is RejectedAuthority and can never re-arm the leash.
/// </summary>
[Fact]
public void CommitActivationNeverRearmsTheLeashOnAStaleRetry()
{
using var fixture = new Fixture(
residentWorld: true,
terrainHeight: 2.7f,
moverSphereOriginZ: 0.475f);
Assert.Equal(RuntimeLocalPlayerPhysicsPublicationStatus.Committed,
fixture.Owner.Commit(
fixture.Prepare(),
out RuntimeLocalPlayerPhysicsActivationToken token));
Assert.Equal(RuntimeLocalPlayerPhysicsActivationStatus.Evaluated,
fixture.Owner.EvaluateActivation(token, out var evaluation));
Assert.Equal(RuntimeDormantSetPositionCommitStatus.Committed,
fixture.Owner.CommitActivation(evaluation, out _));
PlayerMovementController controller =
Assert.IsType<PlayerMovementController>(
fixture.Movement.Controller);
Assert.True(
controller.PositionManager!.Constraint!.IsConstrained);
controller.PositionManager.UnConstrain();
Assert.Equal(RuntimeDormantSetPositionCommitStatus.RejectedAuthority,
fixture.Owner.CommitActivation(evaluation, out _));
Assert.False(
controller.PositionManager.Constraint.IsConstrained);
}
[Fact]
public void DeferredActivationEvaluationLeavesExactOwnedGraphDormantAndRetryable()
{
@ -356,10 +525,117 @@ public sealed class RuntimeLocalPlayerPhysicsPublicationStateTests
Assert.Equal(1, fixture.Owner.CaptureOwnership()
.PendingActivationCount);
const ulong generation = 1UL;
fixture.Lifetime.Physics.SetPosition.BeginCollisionGeneration(
Cell & 0xFFFF0000u,
generation);
// C3c-F2: the wake is driven through the SAME owner production uses
// (RuntimePhysicsState's collision admission), not through the raw
// SetPosition seam. Driving SetPosition directly leaves the admission
// ledger empty, which is a state production can never be in and which
// hid the login rearm wedge this test is supposed to cover.
CommitProductionCollisionGeneration(fixture, Cell & 0xFFFF0000u);
Assert.Equal(RuntimeLocalPlayerPhysicsActivationStatus.Evaluated,
fixture.Owner.EvaluateActivation(token, out var ready));
Assert.Equal(RuntimeDormantSetPositionCommitStatus.Committed,
fixture.Owner.CommitActivation(ready, out var projection));
Assert.True(projection.IsValid);
Assert.True(fixture.Record.PhysicsBody!.InWorld);
Assert.Equal(0, fixture.Owner.CaptureOwnership()
.PendingActivationCount);
}
[Fact]
public void DeferredCommitRearmsAfterProductionAdmissionCommitsItsGeneration()
{
// C3c-F2 regression (the connected-gate login wedge). The sibling
// test above wakes the parked lease by calling BeginCollisionGeneration
// / CommitCollisionGeneration DIRECTLY on the SetPosition state, which
// leaves RuntimePhysicsState's admission ledger empty. PRODUCTION
// always wakes through BeginCollisionAdmission -> stage ->
// CommitCollisionGeneration, and that path retires the admission the
// instant the generation commits — after which
// ExpectedCollisionGeneration names the NEXT, never-begun generation.
// The rearm must key off the generation the collision world now HOLDS.
using var fixture = new Fixture();
Assert.Equal(RuntimeLocalPlayerPhysicsPublicationStatus.Committed,
fixture.Owner.Commit(
fixture.Prepare(),
out RuntimeLocalPlayerPhysicsActivationToken token));
Assert.Equal(RuntimeLocalPlayerPhysicsActivationStatus.DeferredCell,
fixture.Owner.EvaluateActivation(token, out var deferred));
Assert.Equal(RuntimeDormantSetPositionCommitStatus.DeferredCell,
fixture.Owner.CommitActivation(deferred, out var noProjection));
Assert.False(noProjection.IsValid);
Assert.False(fixture.Record.PhysicsBody!.InWorld);
Assert.False(fixture.Movement.Controller!.IsRuntimePublished);
Assert.Equal(1, fixture.Owner.CaptureOwnership()
.PendingActivationCount);
RuntimeCollisionAdmission admission = fixture.Lifetime.Physics
.BeginCollisionAdmission(Cell & 0xFFFF0000u);
// While the destination admission is in flight the lease must simply
// keep waiting — never a rejection, never an early rearm.
Assert.Equal(RuntimeLocalPlayerPhysicsActivationStatus.DeferredCell,
fixture.Owner.EvaluateActivation(token, out var waiting));
Assert.False(waiting.IsValid);
using PreparedLandblockCollisionGeneration prepared = fixture
.Lifetime.Physics.PrepareCollisionGeneration(admission);
fixture.Lifetime.Physics.StageCollisionAssets(
admission,
prepared,
CollisionAssets(Cell & 0xFFFF0000u));
Assert.True(CommitPrepared(
fixture.Lifetime.Physics,
admission,
prepared).Committed);
// The exact divergence this test pins: the committed authority is the
// generation the lease parked on; "expected" has already moved past it.
Assert.Equal(
admission.Generation,
fixture.Lifetime.Physics.CollisionGenerationAuthority(Cell));
Assert.NotEqual(
admission.Generation,
fixture.Lifetime.Physics.ExpectedCollisionGeneration(Cell));
Assert.Equal(RuntimeLocalPlayerPhysicsActivationStatus.Evaluated,
fixture.Owner.EvaluateActivation(token, out var ready));
Assert.Equal(RuntimeDormantSetPositionCommitStatus.Committed,
fixture.Owner.CommitActivation(ready, out var projection));
Assert.True(projection.IsValid);
Assert.True(fixture.Record.PhysicsBody!.InWorld);
Assert.NotNull(fixture.Record.PhysicsHost);
Assert.True(fixture.Movement.Controller!.IsRuntimePublished);
Assert.Equal(0, fixture.Owner.CaptureOwnership()
.PendingActivationCount);
}
[Fact]
public void DeferredCommitStaysParkedWhileTheCommittingAdmissionIsStillRegistered()
{
// C3c-F2, the link the live probe caught: the collision-generation
// commit marks the parked lease ready and only THEN retires its
// admission (RuntimePhysicsState.cs:2503 vs :2552-2558). Production
// reenters the first-entry pump inside that window. Rearming there
// moves the lease out of AwaitingCell and its very next evaluation
// fails the seal on the still-registered admission — after which
// EvaluateActivation can no longer answer DeferredCell and reports
// RejectedAuthority, which is TERMINAL for the conductor. The lease
// must stay parked and retryable instead.
using var fixture = new Fixture();
Assert.Equal(RuntimeLocalPlayerPhysicsPublicationStatus.Committed,
fixture.Owner.Commit(
fixture.Prepare(),
out RuntimeLocalPlayerPhysicsActivationToken token));
Assert.Equal(RuntimeLocalPlayerPhysicsActivationStatus.DeferredCell,
fixture.Owner.EvaluateActivation(token, out var deferred));
Assert.Equal(RuntimeDormantSetPositionCommitStatus.DeferredCell,
fixture.Owner.CommitActivation(deferred, out _));
RuntimeCollisionAdmission admission = fixture.Lifetime.Physics
.BeginCollisionAdmission(Cell & 0xFFFF0000u);
// Reconstruct the exact production instant: the landblock is in the
// engine and the parked lease has been marked ready by the SAME call
// RuntimePhysicsState.cs:2503 makes, while the admission that is
// committing has not yet been retired (:2552-2558).
fixture.Lifetime.Physics.Engine.AddLandblock(
Cell & 0xFFFF0000u,
new TerrainSurface(new byte[81], new float[256]),
@ -369,8 +645,39 @@ public sealed class RuntimeLocalPlayerPhysicsPublicationStateTests
worldOffsetY: 0f);
fixture.Lifetime.Physics.SetPosition.CommitCollisionGeneration(
Cell & 0xFFFF0000u,
generation,
admission.Generation,
ready: true);
Assert.Equal(
admission.Generation,
fixture.Lifetime.Physics.CollisionGenerationAuthority(Cell));
Assert.Equal(
admission.Generation,
fixture.Lifetime.Physics.ExpectedCollisionGeneration(Cell));
Assert.False(fixture.Lifetime.Physics
.IsCollisionEvaluationPrefixAdmissible(Cell));
Assert.Equal(RuntimeLocalPlayerPhysicsActivationStatus.DeferredCell,
fixture.Owner.EvaluateActivation(token, out var stillWaiting));
Assert.False(stillWaiting.IsValid);
Assert.Equal(1, fixture.Owner.CaptureOwnership()
.PendingActivationCount);
Assert.False(fixture.Record.PhysicsBody!.InWorld);
Assert.False(fixture.Movement.Controller!.IsRuntimePublished);
using (PreparedLandblockCollisionGeneration prepared = fixture
.Lifetime.Physics.PrepareCollisionGeneration(admission))
{
fixture.Lifetime.Physics.StageCollisionAssets(
admission,
prepared,
CollisionAssets(Cell & 0xFFFF0000u));
Assert.True(CommitPrepared(
fixture.Lifetime.Physics,
admission,
prepared).Committed);
}
Assert.True(fixture.Lifetime.Physics
.IsCollisionEvaluationPrefixAdmissible(Cell));
Assert.Equal(RuntimeLocalPlayerPhysicsActivationStatus.Evaluated,
fixture.Owner.EvaluateActivation(token, out var ready));
@ -378,6 +685,7 @@ public sealed class RuntimeLocalPlayerPhysicsPublicationStateTests
fixture.Owner.CommitActivation(ready, out var projection));
Assert.True(projection.IsValid);
Assert.True(fixture.Record.PhysicsBody!.InWorld);
Assert.True(fixture.Movement.Controller!.IsRuntimePublished);
Assert.Equal(0, fixture.Owner.CaptureOwnership()
.PendingActivationCount);
}
@ -562,15 +870,9 @@ public sealed class RuntimeLocalPlayerPhysicsPublicationStateTests
Assert.Equal(0, fixture.Lifetime.Physics.Engine.ShadowObjects
.PendingSetPositionDispatchCount);
const ulong generation = 1UL;
fixture.Lifetime.Physics.SetPosition.BeginCollisionGeneration(
Cell & 0xFFFF0000u, generation);
fixture.Lifetime.Physics.Engine.AddLandblock(
Cell & 0xFFFF0000u,
new TerrainSurface(new byte[81], new float[256]),
Array.Empty<CellSurface>(), Array.Empty<PortalPlane>(), 0f, 0f);
fixture.Lifetime.Physics.SetPosition.CommitCollisionGeneration(
Cell & 0xFFFF0000u, generation, ready: true);
// C3c-F2: production wake path (collision admission), see
// CommitProductionCollisionGeneration.
CommitProductionCollisionGeneration(fixture, Cell & 0xFFFF0000u);
Assert.Equal(RuntimeLocalPlayerPhysicsActivationStatus.Evaluated,
fixture.Owner.EvaluateActivation(token, out var ready));
Assert.Equal(RuntimeDormantSetPositionCommitStatus.Committed,
@ -2192,6 +2494,11 @@ public sealed class RuntimeLocalPlayerPhysicsPublicationStateTests
private sealed class Fixture : IDisposable
{
private bool _lifetimeDisposed;
// C3c-F5: default 0 keeps every pre-existing test's mover shape
// byte-identical; the first-entry settle tests pass the production
// human Setup's bottom-sphere origin (0.475) so an authored
// placement can stand clear of a floor the settle then reaches.
private readonly float _moverSphereOriginZ;
internal Fixture(
bool preparePlacement = true,
@ -2199,11 +2506,13 @@ public sealed class RuntimeLocalPlayerPhysicsPublicationStateTests
RuntimeLocalPlayerShadowDisposition shadowDisposition =
RuntimeLocalPlayerShadowDisposition.ProvenShapeless,
float terrainHeight = 0f,
float moverSphereOriginZ = 0f,
Vector3? initialVelocity = null,
Vector3? initialOmega = null,
float? initialFriction = null,
float? initialElasticity = null)
{
_moverSphereOriginZ = moverSphereOriginZ;
if (residentWorld)
{
var engine = new PhysicsEngine
@ -2269,7 +2578,9 @@ public sealed class RuntimeLocalPlayerPhysicsPublicationStateTests
Assert.True(Placement.IsValid);
var setup = new FlatSetupCollision(
ImmutableArray<FlatCollisionCylinder>.Empty,
[new FlatCollisionSphere(Vector3.Zero, 0.48f)],
[new FlatCollisionSphere(
new Vector3(0f, 0f, _moverSphereOriginZ),
0.48f)],
height: 0f,
radius: 0f,
stepUpHeight: 0.4f,
@ -2537,6 +2848,31 @@ public sealed class RuntimeLocalPlayerPhysicsPublicationStateTests
"Collision generation did not complete its Runtime mutation transaction.");
}
/// <summary>
/// C3c-F2: publishes one landblock collision generation through the exact
/// production owner chain — BeginCollisionAdmission -> prepare -> stage ->
/// CommitCollisionGeneration. Waking a parked activation any other way
/// (calling the SetPosition seam directly) leaves RuntimePhysicsState's
/// admission ledger empty, a state production can never reach.
/// </summary>
private static void CommitProductionCollisionGeneration(
Fixture fixture,
uint landblockId)
{
RuntimeCollisionAdmission admission = fixture.Lifetime.Physics
.BeginCollisionAdmission(landblockId);
using PreparedLandblockCollisionGeneration prepared = fixture
.Lifetime.Physics.PrepareCollisionGeneration(admission);
fixture.Lifetime.Physics.StageCollisionAssets(
admission,
prepared,
CollisionAssets(landblockId));
Assert.True(CommitPrepared(
fixture.Lifetime.Physics,
admission,
prepared).Committed);
}
private static RuntimeLandblockCollisionAssets CollisionAssets(
uint landblockId) => new(
landblockId,

View file

@ -0,0 +1,356 @@
using System.Numerics;
using AcDream.Core.Physics;
using AcDream.Runtime.Entities;
using AcDream.Runtime.Physics;
namespace AcDream.Runtime.Tests.Physics;
/// <summary>
/// C3c-F3: landblock (0,0) — id 0x0000FFFF, Dereth's south-west map corner —
/// has the legitimate collision prefix 0x00000000. The prefix-0 "absent"
/// sentinel used to make every collision publication, quiescence, park, wake,
/// and retirement against that landblock throw
/// <see cref="ArgumentOutOfRangeException"/> from
/// <c>RuntimeSetPositionState.BeginCollisionPrefixQuiescence</c> (the
/// connected-gate crash at teleport destination (9,4), whose far streaming
/// radius reaches the corner: logs/connected-world-gate-20260802-135444).
/// These tests drive the exact production owner chain against the corner id
/// and, for quiescence semantics, assert step-for-step parity with a
/// nonzero-prefix landblock.
/// </summary>
public sealed partial class RuntimeCollisionPrefixQuiescenceTests
{
private const uint CornerLandblock = 0x0000FFFFu;
private const uint CornerPrefix = 0x00000000u;
private const uint CornerCell = 0x00000001u;
private const uint CornerCell2 = 0x00000002u;
private const uint CornerIndoorCell = 0x00000100u;
private const uint NeighborLandblock = 0x0001FFFFu;
[Fact]
public void CornerLandblockCollisionGenerationCommitsThroughTheProductionAdmissionChain()
{
// Empty engine — production streaming publishes the corner landblock
// from nothing, exactly like LandblockPhysicsPublisher.AdvanceCompleteOne.
using var fixture = new Fixture(
bindGeneration: true,
engine: new PhysicsEngine { DataCache = new PhysicsDataCache() });
RuntimePhysicsState physics = fixture.Lifetime.Physics;
RuntimeCollisionAdmission admission =
physics.BeginCollisionAdmission(CornerLandblock);
Assert.Equal(CornerLandblock, admission.LandblockId);
using PreparedLandblockCollisionGeneration prepared =
PrepareSealedMutation(physics, admission, CornerLandblock);
// Pre-fix this first commit threw ArgumentOutOfRangeException
// ("landblockId") from BeginCollisionPrefixQuiescence's prefix == 0
// sentinel guard.
RuntimeCollisionGenerationCommit commit =
CommitToCompletion(physics, admission, prepared);
Assert.True(commit.Completed);
Assert.True(physics.Engine.IsLandblockTerrainResident(CornerLandblock));
// The neighbouring landblock (0,1) — prefix 0x00010000 — publishes
// identically through the same chain.
RuntimeCollisionAdmission neighborAdmission =
physics.BeginCollisionAdmission(NeighborLandblock);
using PreparedLandblockCollisionGeneration neighborPrepared =
PrepareSealedMutation(physics, neighborAdmission, NeighborLandblock);
RuntimeCollisionGenerationCommit neighborCommit =
CommitToCompletion(physics, neighborAdmission, neighborPrepared);
Assert.True(neighborCommit.Completed);
Assert.True(
physics.Engine.IsLandblockTerrainResident(NeighborLandblock));
RuntimePhysicsOwnershipSnapshot ownership = physics.CaptureOwnership();
Assert.Equal(0, ownership.CollisionPrefixMutationCount);
Assert.Equal(0, ownership.CollisionPrefixQuiescenceCount);
Assert.Equal(0, ownership.PendingCollisionPrefixProjectionCount);
Assert.Equal(0, ownership.CollisionAdmissionCount);
}
[Fact]
public void CornerResidentParksAndRestoresAcrossAnActivationReplacement()
{
// Mirror of ActivationWaitsForExactWithdrawAndPlaceReceipts against
// the corner landblock: publish/park (ParkDeferred's quiescence
// override carries prefix 0x00000000), wake, and restore.
using var fixture = new Fixture(
bindGeneration: true,
engine: CornerEngine());
RuntimeEntityRecord record = fixture.Add(
0x700031F1u,
1,
CornerCell,
new Vector3(11f, 12f, 0f));
RuntimeSetPositionOutcome seeded = fixture.Place(
record,
CornerCell,
new Vector3(11.5f, 12f, 0f));
// SetPosition against a corner cell commits (host acknowledgement of
// the Place projection is the ordinary pending suffix, identical to
// any nonzero-prefix landblock).
Assert.Equal(
RuntimeSetPositionStatus.CommittedHostAcknowledgementPending,
seeded.Status);
Assert.True(fixture.Lifetime.Physics.SetPosition
.AcknowledgeProjection(seeded.Projection));
RuntimePhysicsState physics = fixture.Lifetime.Physics;
RuntimeCollisionAdmission admission =
physics.BeginCollisionAdmission(CornerLandblock);
using PreparedLandblockCollisionGeneration prepared =
PrepareSealedMutation(physics, admission, CornerLandblock);
RuntimeCollisionGenerationCommit first =
physics.CommitCollisionGeneration(admission, prepared);
Assert.False(first.EngineCommitted);
Assert.False(first.Completed);
Assert.False(physics.IsSpatialRoot(record));
Assert.True(physics.SetPosition.TryPeekProjection(
out RuntimePlacementProjectionSnapshot withdrawn));
Assert.Equal(RuntimePlacementProjectionKind.Withdraw, withdrawn.Kind);
Assert.True(physics.SetPosition.AcknowledgeProjection(withdrawn.Token));
_ = SealMutation(physics, admission, prepared);
RuntimeCollisionGenerationCommit transferred =
physics.CommitCollisionGeneration(admission, prepared);
Assert.True(transferred.EngineCommitted);
Assert.False(transferred.Completed);
Assert.True(physics.SetPosition.TryPeekProjection(
out RuntimePlacementProjectionSnapshot restored));
Assert.Equal(RuntimePlacementProjectionKind.Place, restored.Kind);
Assert.True(physics.SetPosition.AcknowledgeProjection(restored.Token));
RuntimeCollisionGenerationCommit completed =
physics.CommitCollisionGeneration(admission, prepared);
Assert.True(completed.Completed);
Assert.True(physics.IsSpatialRoot(record));
Assert.Equal(CornerCell, record.FullCellId);
Assert.Equal(0, physics.CaptureOwnership().CollisionPrefixMutationCount);
Assert.Equal(
0,
physics.CaptureOwnership().CollisionPrefixQuiescenceCount);
}
[Fact]
public void CornerPrefixQuiescenceHoldsAndReleasesExactlyLikeANonzeroPrefix()
{
// Contract test 2: a parked deferral against the prefix-0 landblock
// holds and releases quiescence exactly like a nonzero-prefix
// landblock. The identical script runs against both and every step's
// observable outcome must match.
using var corner = new Fixture(
bindGeneration: true,
engine: CornerEngine());
using var control = new Fixture(bindGeneration: true);
// The corner run addresses landblock (0,0) by its canonical id
// 0x0000FFFF — the raw input 0x00000000 stays reserved for "absent"
// (see AbsentLandblockIdStillCannotBeginQuiescence).
List<string> cornerLog = RunHeldPlacementQuiescenceCycle(
corner,
CornerLandblock,
sourceCell: CornerCell,
targetCell: CornerCell2,
guid: 0x700031F2u);
List<string> controlLog = RunHeldPlacementQuiescenceCycle(
control,
PrefixP,
sourceCell: CellP,
targetCell: PrefixP | 0x0002u,
guid: 0x700031F3u);
Assert.Equal(controlLog, cornerLog);
}
[Fact]
public void CornerLandblockDemotesAndWithdrawsThroughRetirementMutations()
{
using (var demoteFixture = new Fixture(
bindGeneration: true,
engine: CornerEngine()))
{
RuntimeEntityRecord outdoor = demoteFixture.Add(
0x700031F4u,
1,
CornerCell,
new Vector3(12f, 41f, 0f));
RuntimeEntityRecord indoor = demoteFixture.Add(
0x700031F5u,
1,
CornerIndoorCell,
new Vector3(13f, 41f, 0f));
RuntimeCollisionMutationResult first = demoteFixture.Lifetime
.Physics.DemoteCollisionToTerrain(CornerLandblock);
Assert.False(first.Completed);
Assert.True(demoteFixture.Lifetime.Physics.IsSpatialRoot(outdoor));
Assert.False(demoteFixture.Lifetime.Physics.IsSpatialRoot(indoor));
Assert.True(demoteFixture.Lifetime.Physics.SetPosition
.TryPeekProjection(
out RuntimePlacementProjectionSnapshot withdrawal));
Assert.Equal(indoor.Key, withdrawal.Token.Entity);
Assert.True(demoteFixture.Lifetime.Physics.SetPosition
.AcknowledgeProjection(withdrawal.Token));
RuntimeCollisionMutationResult completed = demoteFixture.Lifetime
.Physics.DemoteCollisionToTerrain(CornerLandblock);
Assert.True(completed.Completed);
Assert.True(completed.Ready);
}
using var withdrawFixture = new Fixture(
bindGeneration: true,
engine: CornerEngine());
RuntimeEntityRecord record = withdrawFixture.Add(
0x700031F6u,
1,
CornerCell,
new Vector3(14f, 42f, 0f));
RuntimeSetPositionOutcome seeded = withdrawFixture.Place(
record,
CornerCell,
new Vector3(14.5f, 42f, 0f));
Assert.True(withdrawFixture.Lifetime.Physics.SetPosition
.AcknowledgeProjection(seeded.Projection));
RuntimePhysicsState physics = withdrawFixture.Lifetime.Physics;
RuntimeCollisionMutationResult pending =
physics.WithdrawCollision(CornerLandblock);
Assert.False(pending.Completed);
Assert.True(physics.SetPosition.TryPeekProjection(
out RuntimePlacementProjectionSnapshot removed));
Assert.True(physics.SetPosition.AcknowledgeProjection(removed.Token));
RuntimeCollisionMutationResult withdrawn =
physics.WithdrawCollision(CornerLandblock);
Assert.True(withdrawn.Completed);
Assert.False(withdrawn.Ready);
Assert.False(physics.IsSpatialRoot(record));
}
[Fact]
public void AbsentLandblockIdStillCannotBeginQuiescence()
{
// The prefix-0 sentinel accidentally rejected the corner landblock;
// the genuine "no landblock at all" input (id 0) must keep throwing.
using var fixture = new Fixture(
bindGeneration: true,
engine: CornerEngine());
Assert.Throws<ArgumentOutOfRangeException>(
() => fixture.Begin(0u, 2UL, includeOutdoorCells: true));
Assert.Throws<ArgumentOutOfRangeException>(
() => fixture.Lifetime.Physics.BeginCollisionAdmission(0u));
RuntimeCollisionPrefixQuiescenceToken token = fixture.Begin(
CornerLandblock,
2UL,
includeOutdoorCells: true);
Assert.True(token.IsValid);
Assert.Equal(CornerPrefix, token.LandblockPrefix);
Assert.True(fixture.Lifetime.Physics.CancelCollisionPrefixQuiescence(
token));
}
/// <summary>
/// One held-placement quiescence cycle (the
/// SourceToOutsidePlacementIsHeldThenRestoredBeforeBarrierOpens shape,
/// single-prefix variant), with every observable step outcome recorded so
/// two runs can be compared for exact parity.
/// </summary>
private static List<string> RunHeldPlacementQuiescenceCycle(
Fixture fixture,
uint landblockId,
uint sourceCell,
uint targetCell,
uint guid)
{
var log = new List<string>();
RuntimeEntityRecord record = fixture.Add(
guid,
1,
sourceCell,
new Vector3(10f, 22f, 0f));
RuntimeCollisionPrefixQuiescenceToken token = fixture.Begin(
landblockId,
2UL,
includeOutdoorCells: true);
log.Add($"tokenValid={token.IsValid}");
RuntimeSetPositionOutcome held = fixture.Place(
record,
targetCell,
new Vector3(14f, 22f, 0f),
currentCell: sourceCell);
log.Add($"place={held.Status}");
log.Add($"placeGeneration={held.Projection.CollisionGeneration}");
log.Add($"placeCellLow={held.ExactCellId & 0xFFFFu:X4}");
log.Add(
$"root={fixture.Lifetime.Physics.IsSpatialRoot(record)}");
log.Add($"ackWithdraw={fixture.Lifetime.Physics.SetPosition
.AcknowledgeProjection(held.Projection)}");
log.Add($"acquire1={fixture.TryAcquire(token, out _)}");
log.Add($"acquire2={fixture.TryAcquire(token, out _)}");
log.Add($"cancelRestorePending={fixture.Lifetime.Physics
.CancelCollisionPrefixQuiescence(
token,
successorGeneration: 1UL,
successorReady: true)}");
bool peeked = fixture.Lifetime.Physics.SetPosition.TryPeekProjection(
out RuntimePlacementProjectionSnapshot restored);
log.Add($"restorePeeked={peeked}");
log.Add($"restoreKind={restored.Kind}");
log.Add($"restoreCellLow={restored.Token.ExactCellId & 0xFFFFu:X4}");
log.Add($"ackRestore={fixture.Lifetime.Physics.SetPosition
.AcknowledgeProjection(restored.Token)}");
log.Add($"cancelCompleted={fixture.Lifetime.Physics
.CancelCollisionPrefixQuiescence(
token,
successorGeneration: 1UL,
successorReady: true)}");
log.Add($"finalCellLow={record.FullCellId & 0xFFFFu:X4}");
log.Add(
$"finalRoot={fixture.Lifetime.Physics.IsSpatialRoot(record)}");
RuntimePhysicsOwnershipSnapshot ownership =
fixture.Lifetime.Physics.CaptureOwnership();
log.Add($"quiescences={ownership.CollisionPrefixQuiescenceCount}");
log.Add(
$"pendingProjections={ownership.PendingCollisionPrefixProjectionCount}");
return log;
}
private static RuntimeCollisionGenerationCommit CommitToCompletion(
RuntimePhysicsState physics,
RuntimeCollisionAdmission admission,
PreparedLandblockCollisionGeneration prepared)
{
for (int poll = 0; poll < 10_000; poll++)
{
RuntimeCollisionGenerationCommit commit =
physics.CommitCollisionGeneration(admission, prepared);
if (commit.Completed)
return commit;
while (physics.SetPosition.TryPeekProjection(
out RuntimePlacementProjectionSnapshot projection))
{
Assert.True(physics.SetPosition.AcknowledgeProjection(
projection.Token));
}
if (!commit.EngineCommitted)
_ = SealMutation(physics, admission, prepared);
}
throw new InvalidOperationException(
"Collision generation did not complete its mutation transaction.");
}
private static PhysicsEngine CornerEngine()
{
var engine = new PhysicsEngine
{
DataCache = new PhysicsDataCache(),
};
AddFlatLandblock(engine, CornerPrefix);
return engine;
}
}

View file

@ -17,18 +17,33 @@ public sealed class RuntimeLiveEntitySessionControllerTests
[Fact]
public void DirectSinkOwnsCanonicalCreateUpdateDeleteWithoutProjection()
{
using GameRuntime runtime = CreateRuntime();
// C3c: the direct sink's Create now enters the canonical initial
// residence; this test drives the remote first-entry conductor to
// completion (the direct-host pump's job) before the follow-up
// Position flows the ordinary post-residence path.
using StartedRuntime started = StartRuntime();
GameRuntime runtime = started.Runtime;
CommitLandblockCollision(runtime, 0x01010000u);
RuntimeFirstEntryDriveController drive = CreateDrive(runtime);
using var session = new WorldSession(
new IPEndPoint(IPAddress.Loopback, 9000),
new FixtureTransport());
// C3c-R1 review R3: the residence route requires a drive-backed
// world projection — a content-less (projection-less) controller
// keeps the pre-flip legacy registration instead (see
// ContentLessDirectSink_KeepsPreFlipLegacyRegistration).
var controller = new RuntimeLiveEntitySessionController(
runtime,
session);
session,
worldProjection: new FixtureWorldProjection());
LiveEntitySessionSink sink = controller.CreateSink();
WorldSession.EntitySpawn spawn =
Spawn(0x70000001u, incarnation: 1);
sink.Spawned(spawn);
drive.DriveAll();
Assert.Equal(0, drive.PendingCount);
DrainPlacementFifo(runtime);
sink.PositionUpdated(new WorldSession.EntityPositionUpdate(
spawn.Guid,
spawn.Position!.Value with
@ -62,10 +77,79 @@ public sealed class RuntimeLiveEntitySessionControllerTests
runtime.EntityObjects.Entities.PendingTeardownCount);
}
/// <summary>
/// C3c-R1 review R3: a CONTENT-LESS headless host (validated-legal
/// configuration: HeadlessConfigurationLoader accepts a null
/// process.content) builds no world projection and no first-entry
/// drive. Its Creates must keep the exact pre-flip legacy registration
/// — the accepted frame commits directly (FullCellId derives from the
/// wire cell at registration), no residence lease ever opens, and the
/// entity/residence/drive ledgers stay at zero — because a residence
/// with no drive to pump it would park every Create (and every packet
/// FIFO'd behind its pending residence) forever.
/// </summary>
[Fact]
public void ContentLessDirectSink_KeepsPreFlipLegacyRegistration()
{
using StartedRuntime started = StartRuntime();
GameRuntime runtime = started.Runtime;
using var session = new WorldSession(
new IPEndPoint(IPAddress.Loopback, 9000),
new FixtureTransport());
var controller = new RuntimeLiveEntitySessionController(
runtime,
session);
LiveEntitySessionSink sink = controller.CreateSink();
WorldSession.EntitySpawn spawn =
Spawn(0x70000003u, incarnation: 1);
sink.Spawned(spawn);
Assert.True(
runtime.EntityObjects.Entities.TryGetActive(
spawn.Guid,
out RuntimeEntityRecord canonical));
Assert.Equal(
spawn.Position!.Value.LandblockId,
canonical.FullCellId);
RuntimeEntityObjectOwnershipSnapshot ownership =
runtime.EntityObjects.CaptureOwnership();
Assert.Equal(0, ownership.InitialCreateResidenceLeaseCount);
Assert.Equal(0, ownership.FirstEntryDrivePendingCount);
Assert.Equal(1, runtime.Entities.Count);
Assert.Equal(1, runtime.Inventory.ObjectCount);
// Position packets flow the ordinary immediate path — nothing is
// FIFO'd behind a pending residence.
sink.PositionUpdated(new WorldSession.EntityPositionUpdate(
spawn.Guid,
spawn.Position!.Value with
{
PositionX = 20f,
},
Velocity: null,
PlacementId: null,
IsGrounded: true,
InstanceSequence: 1,
PositionSequence: 2,
TeleportSequence: 0,
ForcePositionSequence: 0));
Assert.Equal(
20f,
canonical.Snapshot.Position!.Value.PositionX);
sink.Deleted(new DeleteObject.Parsed(spawn.Guid, 1));
Assert.Equal(0, runtime.Entities.Count);
Assert.Equal(
0,
runtime.EntityObjects.Entities.PendingTeardownCount);
}
[Fact]
public void DirectSinkCompletesExactPortalAndSendsLoginComplete()
{
using GameRuntime runtime = CreateRuntime();
using StartedRuntime started = StartRuntime();
GameRuntime runtime = started.Runtime;
const uint playerGuid = 0x50000001u;
runtime.PlayerIdentity.ServerGuid = playerGuid;
using var session = new WorldSession(
@ -106,7 +190,8 @@ public sealed class RuntimeLiveEntitySessionControllerTests
[Fact]
public void DirectSinkProjectsAcceptedLocalWorldStateThroughOneHostSeam()
{
using GameRuntime runtime = CreateRuntime();
using StartedRuntime started = StartRuntime();
GameRuntime runtime = started.Runtime;
const uint playerGuid = 0x50000002u;
runtime.PlayerIdentity.ServerGuid = playerGuid;
using var session = new WorldSession(
@ -154,16 +239,268 @@ public sealed class RuntimeLiveEntitySessionControllerTests
Assert.True(runtime.Portal.Snapshot.Completed);
}
private static GameRuntime CreateRuntime()
/// <summary>
/// C3c-R1 review F6: the drive controller outlives its session routes,
/// so "session reset precedes a new route" is an asserted latch, not a
/// silent assumption — a second route cannot attach before the prior
/// route detached, and a route that never owned the drive cannot clear
/// the live route's tracked entries.
/// </summary>
[Fact]
public void FirstEntryDriveServesOneRouteAtATimeAndScopesClearToTheOwner()
{
using StartedRuntime started = StartRuntime();
GameRuntime runtime = started.Runtime;
RuntimeFirstEntryDriveController drive = CreateDrive(runtime);
var routeA = new object();
var routeB = new object();
drive.AttachRoute(routeA);
// Re-attaching the same owner is a no-op; a SECOND route asserts.
drive.AttachRoute(routeA);
Assert.Throws<InvalidOperationException>(
() => drive.AttachRoute(routeB));
_ = runtime.EntityObjects.RegisterEntityWithInitialResidence(
Spawn(0x70000004u, incarnation: 1),
isLocalPlayer: false);
Assert.Equal(1, drive.PendingCount);
// A non-owner detach (never-attached / displaced route rollback)
// must not clear the live route's entries.
drive.DetachRoute(routeB);
Assert.Equal(1, drive.PendingCount);
drive.DetachRoute(routeA);
Assert.Equal(0, drive.PendingCount);
// After the owner detached, a replacement route may attach.
drive.AttachRoute(routeB);
drive.DetachRoute(routeB);
}
/// <summary>
/// C3c: initial-residence admission requires a live session generation
/// (RuntimeInitialCreateResidenceState.CanAcceptCreate), so these direct
/// sink tests start one through the same fixture-session shape
/// DirectGameRuntimeCommandAdapterTests uses.
/// </summary>
private sealed class StartedRuntime : IDisposable
{
internal required GameRuntime Runtime { get; init; }
internal required LiveSessionHost Live { get; init; }
public void Dispose()
{
_ = Live.Stop(Runtime.Generation);
Runtime.Dispose();
}
}
private static StartedRuntime StartRuntime()
{
var operations = new FixtureGameplayOperations();
var sessionOperations = new FixtureSessionOperations();
var runtime = new GameRuntime(new GameRuntimeDependencies(
operations,
operations,
operations,
operations));
operations,
SessionOperations: sessionOperations));
operations.Bind(runtime);
return runtime;
var resetHost = new FixtureResetHost();
var options = new LiveSessionConnectOptions(
true,
"127.0.0.1",
9000,
"account",
"password");
var live = new LiveSessionHost(
runtime.Session,
new LiveSessionHostBindings(
new LiveSessionRoutingFactories(
_ => new FixtureEventRoute(),
_ => new FixtureCommandRoute()),
generation => runtime.ResetGeneration(generation, resetHost),
new LiveSessionSelectionBindings(
id => runtime.PlayerIdentity.ServerGuid = id,
_ => { },
runtime.CommunicationOwner.Chat.SetLocalPlayerGuid,
_ => { },
_ => { },
runtime.ActionOwner.Combat.Clear),
new LiveSessionEnteredWorldBindings(
_ => { },
() => { },
() => { },
_ => { },
() => { }),
(_, _, _) => { },
() => { }),
options);
LiveSessionStartResult startResult = live.Start(options);
Assert.Equal(LiveSessionStartStatus.Connected, startResult.Status);
Assert.NotEqual(0UL, runtime.Generation.Value);
return new StartedRuntime { Runtime = runtime, Live = live };
}
private static void CommitLandblockCollision(
GameRuntime runtime,
uint landblockId)
{
runtime.EntityObjects.Physics.SetPosition.BeginCollisionGeneration(
landblockId, 1UL);
runtime.EntityObjects.Physics.Engine.AddLandblock(
landblockId,
new TerrainSurface(new byte[81], new float[256]),
Array.Empty<CellSurface>(),
Array.Empty<PortalPlane>(),
worldOffsetX: 0f,
worldOffsetY: 0f);
runtime.EntityObjects.Physics.SetPosition.CommitCollisionGeneration(
landblockId, 1UL, ready: true);
}
private static RuntimeFirstEntryDriveController CreateDrive(
GameRuntime runtime) =>
new(
runtime.EntityObjects,
runtime.Clock,
new UnusedCollisionSource(),
() => PlayerMovementConstructionOptions.Fallback,
static _ => new RuntimeLocalPlayerPhysicsActivationPreparation(
Radius: 0.48f,
Height: 1.835f,
RuntimeLocalPlayerShadowDisposition.ProvenShapeless));
/// <summary>
/// Drains/acknowledges every still-pending placement receipt (the
/// ExecutorCompleted correlation is reaped by its acknowledgement) the
/// way a host subscription would.
/// </summary>
private static void DrainPlacementFifo(GameRuntime runtime)
{
while (runtime.EntityObjects.Physics.SetPosition.TryPeekProjection(
out AcDream.Runtime.Physics.RuntimePlacementProjectionSnapshot head))
{
if (!runtime.EntityObjects.Physics.SetPosition
.AcknowledgeProjection(head.Token))
{
break;
}
}
}
private sealed class UnusedCollisionSource
: AcDream.Content.IPreparedCollisionSource
{
public AcDream.Content.PreparedAssetPresence ProbeCollision(
AcDream.Content.Pak.PakAssetType type,
uint sourceFileId) =>
AcDream.Content.PreparedAssetPresence.Available;
public AcDream.Content.PreparedCollisionReadResult<
AcDream.Core.Physics.FlatSetupCollision> ReadSetupCollision(
uint sourceFileId,
CancellationToken cancellationToken = default) =>
AcDream.Content.PreparedCollisionReadResult<
AcDream.Core.Physics.FlatSetupCollision>.Missing;
public AcDream.Content.PreparedCollisionReadResult<
AcDream.Core.Physics.FlatGfxObjCollisionAsset> ReadGfxObjCollision(
uint sourceFileId,
CancellationToken cancellationToken = default) =>
throw new NotSupportedException();
public AcDream.Content.PreparedCollisionReadResult<
AcDream.Core.Physics.FlatCellStructureCollisionAsset>
ReadCellStructureCollision(
uint sourceFileId,
CancellationToken cancellationToken = default) =>
throw new NotSupportedException();
public AcDream.Content.PreparedCollisionReadResult<
AcDream.Core.Physics.FlatEnvCellTopology> ReadEnvCellTopology(
uint sourceFileId,
CancellationToken cancellationToken = default) =>
throw new NotSupportedException();
public AcDream.Content.PreparedCollisionSourceStats CollisionStats =>
default;
public void Dispose()
{
}
}
private sealed class FixtureSessionOperations : ILiveSessionOperations
{
public IPEndPoint ResolveEndpoint(string host, int port) =>
new(IPAddress.Loopback, port);
public WorldSession CreateSession(IPEndPoint endpoint) =>
new(endpoint, new FixtureTransport());
public void Connect(WorldSession session, string user, string password)
{
}
public CharacterList.Parsed GetCharacters(WorldSession session) =>
new(
0u,
[new CharacterList.Character(0x50000001u, "Direct", 0u)],
[],
11,
"account",
true,
true);
public void EnterWorld(WorldSession session, int activeCharacterIndex)
{
}
public void Tick(WorldSession session)
{
}
public void DisposeSession(WorldSession session) =>
session.Dispose();
}
private sealed class FixtureEventRoute : ILiveSessionEventRouting
{
public void Attach()
{
}
public void Dispose()
{
}
}
private sealed class FixtureCommandRoute : ILiveSessionCommandRouting
{
public void Activate()
{
}
public void Dispose()
{
}
}
private sealed class FixtureResetHost : IRuntimeGenerationResetHost
{
public void RetireEntityProjection(RuntimeEntityRecord entity)
{
}
public void DrainEntityProjectionBoundary()
{
}
public void CompleteEntityProjectionRetirement()
{
}
}
private static WorldSession.EntitySpawn Spawn(
@ -194,7 +531,7 @@ public sealed class RuntimeLiveEntitySessionControllerTests
Position: position,
Movement: null,
AnimationFrame: null,
SetupTableId: 0x02000001u,
SetupTableId: null,
MotionTableId: null,
SoundTableId: null,
PhysicsScriptTableId: null,
@ -213,7 +550,7 @@ public sealed class RuntimeLiveEntitySessionControllerTests
return new WorldSession.EntitySpawn(
guid,
position,
0x02000001u,
null,
[],
[],
[],