acdream/tests/AcDream.Headless.Tests/HeadlessSessionHostTests.cs
Erik 6dc7ba51ee feat(physics): C4 route 4b-3 — remote teleport + cell-less through the canonical placement
Flips the last remote classification (SetPosition: teleport-advanced and
cell-less) onto 4b-1's RuntimeRemotePlacementDriveController, runs retail's
teleport_hook before the placement, and deletes the legacy remote-teleport
machinery. Contract: docs/research/2026-08-04-c4-route-4b-3-contract.md.

Retail: MoveOrTeleport @0x00516330's branch @0x00516386 -> teleport_hook
@0x005163EF -> SetFlags(0x1012) @0x00516414 -> SetPosition @0x00516420 ->
return 1 @0x00516438. The hook @0x00514ED0 runs BEFORE the placement and
regardless of its outcome. Retail places this branch unconditionally, at any
distance and any contact state (arg4 is read only @0x0051638E, after the
branch) — which is what retires AP-137's cell-less enqueue-vs-place delta.

D1 — the classifier's cell-less input is now the PRE-merge committed cell.
Retail's predicate is `this_1->cell == 0`, the BODY's own cell at
MoveOrTeleport entry (this_1 is assigned from this @0x00516334). acdream fed
the POST-merge canonical.FullCellId, which RefreshSnapshot ->
RefreshDerivedState -> SetFullCell has already stamped with the accepted wire
cell; a zero wire cell fails validation into RejectedData first. The shipped
remote cell-less predicate was therefore dead code, not merely different from
remotePlacementRequired. Threaded via a builder overload; route 1's overload
is untouched. The graphical !IsSpatiallyVisible arm of
projectionRequiresTeleportHook is deleted — a presentation predicate with no
retail analogue that fired the teleport machinery on a routine hot path.

Deleted: RemoteTeleportController (605), RemoteTeleportPlacement (85),
RemoteShadowPlacementSynchronizer (49), their 1,709 lines of tests, the
remotePlacementRequired predicate, the TeleportHookRequired plumbing, the
legacy pre-operation ConstrainTo fallback, and the player arm's legacy
!IsGrounded fallback. Net -2,030 lines.

Structural fix (two independent Opus reviews, round 1 FAIL/FAIL): three of the
four MAJORs were one defect — OnPosition carried two parallel inline copies of
the routing tail (player-guid, NPC-guid) that had drifted. Extracted
RunRemoteArmTail (3 call sites) and ApplyWireAirborneLeftoverBookkeeping (2),
both branches now share one implementation.

  A1  ToConstraintArm mapped AirborneSnap -> AirborneNoOperation, so the NPC
      arm armed ConstrainTo ZERO times for an out-of-contact wire-grounded
      creature — a regression this slice introduced while closing a
      structurally identical hole. Now maps to NearInterpolate; switch made
      total with a throwing default proven unreachable.
  R1  D2's write-nothing shape existed on the player arm only; NPC packets
      fell through and wrote the body. Retail makes no player/NPC distinction.
  R2  report_collision_end(this,1) @0x00514F31 was bound to
      ShadowObjects.Suspend, a port of a DIFFERENT retail function
      (remove_shadows_from_cells) that teleport_hook never calls. Now routes
      to RuntimeCollisionReportingState.LeaveWorld, which wraps the private
      ForceEnd in an admission-blocking transaction so a DoCollisionEnd
      callback cannot recreate the contact table.
  R3/A2 A teleported NPC synthesized ServerVelocity from the teleport distance
      (~1,000+ m/s) and planned a run cycle from it. Both the install and
      RemoteServerControlledVelocityCycle.Apply now gate on !isTeleportRoute.

BISECT HAZARD — A1's fix is correct only BECAUSE R1 landed. AirborneSnap is
reachable wire-airborne on the NPC arm only while D2's shape is missing there.
Reverting R1 alone silently inverts A1 into the opposite divergence: arming
where retail returns 0. Revert both or neither.

Also in the velocity hunk: the NPC block's two !IsPlayerGuid(update.Guid)
guards were dropped when it was wrapped in `if (!isTeleportRoute)`. Safe — all
five exit paths of the enclosing IsPlayerGuid block return, so the predicate is
unconditionally false below it — but it was unremarked by both reviews.

Register: AP-137 REWRITTEN (not deleted) to the surviving acdream-only
divergences — null classification during the login window and Rejected*
through UnroutedCatchUp keep a row. AD-42's RemoteTeleportController citation
retired; AP-136/AP-138 writer lists corrected to the two surviving non-Position
rebucket writers; AP-138 gains the teleport arm as a second producer of the
visible-without-collision residual (retirement path remains #309). AP-135 is
untouched and its two airborne bookkeeping writes are preserved on both arms.
AP-131 does not retire; #276 does not close.

Proof obligation 1: ParkCollisionResidents' overlap throw stays unreachable —
the teleport arm adds packets to the same TryBeginExclusiveAuthoredPlacement
one-operation-per-key machinery the far arm uses, opens no new operation shape,
and every DeferredCell outcome cancels synchronously with
restoreCancelledPark: true. The guarded property remains
HasOldPrefixPlacementDebt's stall, not a throw (4b-1's B2 caveat stands).

Correction to an earlier claim: LiveEntityPresentationController's
_activePlacementOwners was NOT write-never at HEAD —
remotePlacementRequired -> BeginPlacement -> Begin -> BeginAuthoritativePlacement
was a live writer chain. It becomes write-never BECAUSE this slice deletes that
chain, which is why deleting the dead half is behaviour-preserving.

Probe: ACDREAM_PROBE_REMOTE_TELEPORT=1 emits one [remote-teleport] line per
routed arm (guid, cause, hook-ran, placement status). TEMPORARY, strip with the
probe family.

Carried, disclosed not fixed: no dedicated bidirectional collision-partner test
for R2 (the wiring, not LeaveWorld itself, is what lacks coverage); the
stress test's teleport step drives hand-written field assignments rather than
the canonical arm; the per-packet runTeleportHook closure allocation (network
path, not the resolve path Slice I's 0 B discipline governs — file before
route 5 adds a fourth call site). B2: IRuntimeCollisionReportObserver has zero
production implementations, so retail's bidirectional DoCollisionEnd half still
reaches no gameplay consumer — this fix closes the wrong-function binding, not
that nobody listens.

Complete Release suite MEASURED at 11,013 passed / 4 skipped / 0 failed
(baseline 11,027/4/0; net -14 = ~33 deleted test cases against ~19 added).
Neither known flake fired (#302 PortalProjectionTests GC-allocation, #308
NakEmissionTests wall-clock).

STILL OWED: the two-client connected gate, which MUST use an NPC/creature
teleport target. Both round-1 MAJORs lived on the NPC arm and the velocity
cycle early-returns for 0x50xxxxxx guids, so a player target structurally
cannot observe A1, A2, or R3.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 16:00:10 +02:00

1788 lines
68 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Buffers.Binary;
using System.Collections.Immutable;
using System.Net;
using System.Numerics;
using System.Reflection;
using AcDream.Core.Net;
using AcDream.Core.Net.Messages;
using AcDream.Core.Physics;
using AcDream.Headless.Configuration;
using AcDream.Headless.Credentials;
using AcDream.Headless.Diagnostics;
using AcDream.Headless.Hosting;
using AcDream.Headless.Platform;
using AcDream.Runtime;
using AcDream.Runtime.Entities;
using AcDream.Runtime.Gameplay;
using AcDream.Runtime.Physics;
using AcDream.Runtime.Session;
using AcDream.Runtime.World;
namespace AcDream.Headless.Tests;
public sealed class HeadlessSessionHostTests
{
[Fact]
public void SingleSessionStartsReconnectsAndConvergesWithoutPresentation()
{
var operations = new FixtureSessionOperations();
using var diagnosticsOutput = new StringWriter();
using var credential = new HeadlessCredentialSecret(
"fixture",
"password");
var host = new HeadlessSessionHost(
Descriptor(),
credential,
new HeadlessDiagnosticWriter(diagnosticsOutput),
operations);
RuntimeSessionStartResult first = host.Start();
ulong firstGeneration = host.Runtime.Generation.Value;
host.Tick(0.015d);
RuntimeSessionStartResult second = host.Reconnect();
Assert.Equal(RuntimeSessionStartStatus.Connected, first.Status);
Assert.Equal(RuntimeSessionStartStatus.Connected, second.Status);
Assert.Equal(0x50000002u, first.CharacterId);
Assert.Equal("Headless", host.ActiveCharacterName);
Assert.True(host.Runtime.Session.IsInWorld);
Assert.True(host.Runtime.Generation.Value > firstGeneration);
Assert.Equal(2, operations.CreatedSessionCount);
Assert.Equal(1, operations.DisposedSessionCount);
host.Dispose();
Assert.Equal(2, operations.DisposedSessionCount);
Assert.True(host.Runtime.CaptureOwnership().IsConverged);
Assert.True(credential.IsDisposed);
string diagnostics = diagnosticsOutput.ToString();
Assert.Contains("\"state\":\"start-result\"", diagnostics);
Assert.DoesNotContain("password", diagnostics);
Assert.DoesNotContain("AcDream.App", diagnostics);
}
[Fact]
public async Task ProcessHostRunsUntilCancellationAndReturnsStableExitCode()
{
var configuration = new HeadlessConfiguration
{
Version = 1,
Sessions =
[
Descriptor(
HeadlessCredentialProviderKind.StandardInput,
"stdin-bot"),
],
};
HeadlessPathSet paths = HeadlessPathSet.Resolve(
new HeadlessPathOverrides());
using var diagnostics = new StringWriter();
var operations = new FixtureSessionOperations();
using var host = new HeadlessProcessHost(
configuration,
paths,
new System.IO.StringReader(
"process-password" + Environment.NewLine),
diagnostics,
operations);
using var cancellation = new CancellationTokenSource();
cancellation.Cancel();
HeadlessExitCode result =
await host.RunAsync(cancellation.Token);
Assert.Equal(HeadlessExitCode.Success, result);
Assert.True(host.Session.Runtime.Session.IsInWorld);
Assert.DoesNotContain(
"process-password",
diagnostics.ToString());
}
[Fact]
public async Task DirectCredentialsOverrideSingleConfiguredSession()
{
var configuration = new HeadlessConfiguration
{
Version = 1,
Sessions = [Descriptor()],
};
HeadlessPathSet paths = HeadlessPathSet.Resolve(
new HeadlessPathOverrides());
using var diagnostics = new StringWriter();
var operations = new FixtureSessionOperations();
using var host = new HeadlessProcessHost(
configuration,
paths,
TextReader.Null,
diagnostics,
operations,
directCredentials: new HeadlessDirectCredentials(
"direct-account",
"direct-secret"));
using var cancellation = new CancellationTokenSource();
cancellation.Cancel();
HeadlessExitCode result =
await host.RunAsync(cancellation.Token);
Assert.Equal(HeadlessExitCode.Success, result);
Assert.Equal("direct-account", operations.LastUser);
Assert.Equal("direct-secret", operations.LastPassword);
Assert.DoesNotContain(
"direct-secret",
diagnostics.ToString());
}
[Fact]
public void DirectFrameUsesSharedRetailOrderAndMovementCadence()
{
var operations = new FixtureSessionOperations();
using var credential = new HeadlessCredentialSecret(
"fixture",
"password");
using var host = new HeadlessSessionHost(
Descriptor(),
credential,
new HeadlessDiagnosticWriter(TextWriter.Null),
operations);
Assert.Equal(
RuntimeSessionStartStatus.Connected,
host.Start().Status);
HydrateGroundedPlayer(host.Runtime);
var sent = new List<(byte[] Body, double Time)>();
var trace = new RuntimeTraceRecorder();
using IDisposable subscription =
host.Runtime.Subscribe(trace);
operations.Sessions[^1].GameActionCapture =
body => sent.Add((
body,
host.Runtime.Clock.SimulationTimeSeconds));
RuntimeCommandResult intent = host.Commands.Movement.SetIntent(
host.Runtime.Generation,
new MovementInput(Forward: true, Run: true));
host.Tick(0.015d);
Assert.True(intent.Accepted);
Assert.Contains(
trace.Entries,
static entry =>
entry.Kind == RuntimeTraceKind.Movement);
Assert.Equal(
[
MoveToState.MoveToStateAction,
AutonomousPosition.AutonomousPositionAction,
],
sent.Select(static entry =>
ActionOpcode(entry.Body)).ToArray());
sent.Clear();
for (int index = 0; index < 70; index++)
host.Tick(0.015d);
(byte[] Body, double Time)[] positions = sent
.Where(static entry =>
ActionOpcode(entry.Body)
== AutonomousPosition.AutonomousPositionAction)
.ToArray();
Assert.InRange(positions.Length, 1, 2);
Assert.True(positions[^1].Time >= 1d);
if (positions.Length == 2)
{
Assert.True(
positions[1].Time - positions[0].Time >= 0.99d);
}
Assert.DoesNotContain(
sent,
entry => ActionOpcode(entry.Body)
== MoveToState.MoveToStateAction);
}
[Fact]
public void TeardownRetriesOnlyTheUnfinishedSuffix()
{
var operations = new FixtureSessionOperations();
var writer = new FailOnceTextWriter();
using var credential = new HeadlessCredentialSecret(
"fixture",
"password");
var host = new HeadlessSessionHost(
Descriptor(),
credential,
new HeadlessDiagnosticWriter(writer),
operations);
Assert.Equal(
RuntimeSessionStartStatus.Connected,
host.Start().Status);
writer.FailNextWrite = true;
Assert.Throws<IOException>(host.Dispose);
Assert.Equal(1, operations.DisposedSessionCount);
host.Dispose();
Assert.Equal(1, operations.DisposedSessionCount);
Assert.True(host.Runtime.CaptureOwnership().IsConverged);
}
/// <summary>
/// B5(a) review fix: <see cref="HeadlessSessionEventRouteRetryPendingTests"/>
/// proved the underlying re-offer MECHANISM works, but hand-constructed
/// <see cref="HeadlessSessionEventRoute"/> directly and called
/// <c>route.RetryPending()</c> itself — it never touches
/// <see cref="HeadlessSessionHost.Tick"/>'s own
/// <c>_eventRoute?.RetryPending()</c> call. This test drives <c>Tick</c>
/// itself (via the <c>placementSinkOverride</c> test seam added for this
/// fix, mirroring the existing <c>policyOverride</c> parameter) so a
/// regression that deletes or reorders that exact line would fail HERE,
/// not just in the lower-level subscription test.
/// </summary>
[Fact]
public void TickRetriesAPreviouslyDeclinedPlacementThroughTheRealEventRoute()
{
const uint remote = 0x70004301u;
const uint landblock = 0xA9B40000u;
const uint cell = landblock | 0x0001u;
const float height = 6f;
var operations = new FixtureSessionOperations();
using var credential = new HeadlessCredentialSecret(
"fixture",
"password");
var sink = new DecliningThenAcceptingPlacementSink();
using var host = new HeadlessSessionHost(
Descriptor(),
credential,
new HeadlessDiagnosticWriter(TextWriter.Null),
operations,
placementSinkOverride: sink);
GameRuntime runtime = host.Runtime;
Assert.Equal(
RuntimeSessionStartStatus.Connected,
host.Start().Status);
runtime.EntityObjects.Physics.ObserveLocalWorldFrame(
cell, teleportAdvanced: false);
runtime.EntityObjects.Physics.SetPosition.BeginCollisionGeneration(
landblock, 1UL);
AddFlatLandblock(runtime.EntityObjects.Physics.Engine);
runtime.EntityObjects.Physics.SetPosition.CommitCollisionGeneration(
landblock, 1UL, ready: true);
RuntimeEntityRecord record = runtime.EntityObjects
.RegisterEntity(Spawn(remote, cell))
.Canonical!;
runtime.EntityObjects.Entities.SetFinalPhysicsState(
record, PhysicsStateFlags.Gravity);
runtime.EntityObjects.Entities.SetFullCell(
record, cell, landblock);
var body = new PhysicsBody
{
Position = new Vector3(10f, 10f, height),
Orientation = Quaternion.Identity,
LastUpdateTime = 1d,
State = PhysicsStateFlags.Gravity,
TransientState = TransientStateFlags.Active,
};
body.SnapToCell(cell, body.Position, body.Position);
runtime.EntityObjects.Entities.SetPhysicsBody(record, body);
record.ObjectClock.Activate();
runtime.EntityObjects.Physics.AcknowledgeSpatialProjection(
record, spatial: true);
RuntimeEntityPlacementToken token = runtime.EntityObjects.Physics
.SetPosition.TryBeginExclusiveAuthoredPlacement(
record,
record.PositionAuthorityVersion,
RuntimeSetPositionOperationKind.RemoteAuthoritative);
Assert.True(token.IsValid);
RuntimeSetPositionMoverPreparationStatus status = runtime.EntityObjects
.Physics.SetPosition.TryPrepareAndSubmitAuthoredPlacement(
record,
token,
RuntimeSetPositionOperationKind.RemoteAuthoritative,
PhysicsSetPositionFlags.Teleport | PhysicsSetPositionFlags.Slide,
new LoadedSetupCollisionSource(),
gameTime: runtime.Clock.SimulationTimeSeconds,
out RuntimeSetPositionOutcome outcome,
resolveWorldOffsetFromRuntimeFrame: true);
Assert.Equal(RuntimeSetPositionMoverPreparationStatus.Prepared, status);
Assert.Equal(
RuntimeSetPositionStatus.CommittedHostAcknowledgementPending,
outcome.Status);
// The production HeadlessSessionEventRoute's subscription attached
// during host.Start() already observed this Place synchronously —
// the fake sink is still declining, so it must remain unacknowledged.
Assert.Equal(1, sink.CallCount);
Assert.True(
runtime.EntityObjects.Physics.SetPosition.TryPeekProjection(
out _));
// The sink starts accepting (mirrors a landblock finishing streaming
// in) — driving ONE real host tick is what must re-offer the head,
// through Tick's own wiring, not a hand-built route.
sink.Accept = true;
host.Tick(0.015d);
Assert.Equal(2, sink.CallCount);
Assert.False(
runtime.EntityObjects.Physics.SetPosition.TryPeekProjection(
out _));
}
private sealed class DecliningThenAcceptingPlacementSink
: IRuntimePlacementProjectionSink
{
internal int CallCount { get; private set; }
internal bool Accept { get; set; }
public bool TryApply(in RuntimePlacementProjectionSnapshot projection)
{
CallCount++;
return Accept;
}
}
[Fact]
public void WorldProjectionHydratesCanonicalMovementAndTeleportState()
{
var operations = new FixtureSessionOperations();
using var credential = new HeadlessCredentialSecret(
"fixture",
"password");
using var host = new HeadlessSessionHost(
Descriptor(),
credential,
new HeadlessDiagnosticWriter(TextWriter.Null),
operations);
GameRuntime runtime = host.Runtime;
// C3c: conductor-driven flow — a live generation admits the initial
// residence, the flat landblock's collision generation commits, and
// the world projection's pump drives the local first-entry conductor
// to completion (the deleted SynchronizeLocalPlayer hand-copy's
// replacement).
Assert.Equal(
RuntimeSessionStartStatus.Connected,
host.Start().Status);
const uint player = 0x50000002u;
runtime.PlayerIdentity.ServerGuid = player;
runtime.EntityObjects.Physics.SetPosition.BeginCollisionGeneration(
0xA9B40000u, 1UL);
AddFlatLandblock(runtime.EntityObjects.Physics.Engine);
runtime.EntityObjects.Physics.SetPosition.CommitCollisionGeneration(
0xA9B40000u, 1UL, ready: true);
AcDream.Runtime.Session.RuntimeFirstEntryDriveController firstEntry =
CreateFirstEntryDrive(runtime);
RuntimeEntityRecord record = runtime.EntityObjects
.RegisterEntityWithInitialResidence(Spawn(player), isLocalPlayer: true)
.Canonical!;
Assert.True(runtime.EntityObjects.ApplyAcceptedSpawn(
record,
record.CreateIntegrationVersion,
record.Snapshot,
replaceGeneration: false));
var collision = new FixtureCollisionNeighborhood();
var projection = new HeadlessSessionWorldProjection(
runtime,
collision,
firstEntry);
projection.ProjectSpawn(record, isLocalPlayer: true);
PlayerMovementController controller =
Assert.IsType<PlayerMovementController>(
runtime.MovementOwner.Controller);
controller.SetPosition(
new Vector3(48f, 49f, 50f),
0xA9B40001u);
projection.ProjectPosition(
record,
isLocalPlayer: true,
PositionTimestampDisposition.Apply);
Assert.Same(controller, runtime.MovementOwner.Controller);
Assert.Equal(record.LocalEntityId, controller.LocalEntityId);
Assert.Equal(new Vector3(48f, 49f, 50f), controller.Position);
Assert.Equal(
0xA9B40000u,
controller.CellId & 0xFFFF0000u);
Assert.True((controller.CellId & 0xFFFFu) < 0x0100u);
projection.BeginTeleport();
Assert.Equal(PlayerState.PortalSpace, controller.State);
RuntimeDestinationReadiness readiness =
projection.PrepareDestination(
revealGeneration: 7,
new RuntimeTeleportDestination(
player,
InstanceSequence: 1,
PositionSequence: 2,
TeleportSequence: 1,
ForcePositionSequence: 0,
new Position(
0xA9B40001u,
new Vector3(96f, 97f, 50f),
Quaternion.Identity)));
Assert.True(readiness.IsCollisionReady);
Assert.False(readiness.IsUnhydratable);
Assert.Equal(PlayerState.InWorld, controller.State);
Assert.Equal(3, collision.CenterCount);
Assert.Equal(0xA9B40001u, collision.LastCell);
}
[Fact]
public void WorldProjectionIgnoresNormalEchoButBlipsForcePosition()
{
var operations = new FixtureSessionOperations();
using var credential = new HeadlessCredentialSecret(
"fixture",
"password");
using var host = new HeadlessSessionHost(
Descriptor(),
credential,
new HeadlessDiagnosticWriter(TextWriter.Null),
operations);
GameRuntime runtime = host.Runtime;
// C3c: conductor-driven flow — a live generation admits the initial
// residence, the flat landblock's collision generation commits, and
// the world projection's pump drives the local first-entry conductor
// to completion (the deleted SynchronizeLocalPlayer hand-copy's
// replacement).
Assert.Equal(
RuntimeSessionStartStatus.Connected,
host.Start().Status);
const uint player = 0x50000003u;
runtime.PlayerIdentity.ServerGuid = player;
runtime.EntityObjects.Physics.SetPosition.BeginCollisionGeneration(
0xA9B40000u, 1UL);
AddFlatLandblock(runtime.EntityObjects.Physics.Engine);
runtime.EntityObjects.Physics.SetPosition.CommitCollisionGeneration(
0xA9B40000u, 1UL, ready: true);
AcDream.Runtime.Session.RuntimeFirstEntryDriveController firstEntry =
CreateFirstEntryDrive(runtime);
RuntimeEntityRecord record = runtime.EntityObjects
.RegisterEntityWithInitialResidence(Spawn(player), isLocalPlayer: true)
.Canonical!;
Assert.True(runtime.EntityObjects.ApplyAcceptedSpawn(
record,
record.CreateIntegrationVersion,
record.Snapshot,
replaceGeneration: false));
var collision = new FixtureCollisionNeighborhood();
var projection = new HeadlessSessionWorldProjection(
runtime,
collision,
firstEntry);
projection.ProjectSpawn(record, isLocalPlayer: true);
PlayerMovementController controller =
Assert.IsType<PlayerMovementController>(
runtime.MovementOwner.Controller);
controller.SetPosition(
new Vector3(48f, 49f, 50f),
0xA9B40001u);
projection.ProjectPosition(
record,
isLocalPlayer: true,
PositionTimestampDisposition.Apply);
Assert.Equal(new Vector3(48f, 49f, 50f), controller.Position);
var force = new WorldSession.EntityPositionUpdate(
player,
record.Snapshot.Position!.Value with
{
PositionX = 72f,
PositionY = 73f,
},
Velocity: null,
PlacementId: null,
IsGrounded: true,
InstanceSequence: 1,
PositionSequence: 2,
TeleportSequence: 0,
ForcePositionSequence: 1);
Assert.True(runtime.EntityObjects.TryApplyPosition(
force,
isLocalPlayer: true,
forcePositionRotation: Quaternion.Identity,
currentLocalVelocity: controller.BodyVelocity,
acknowledgeProjection: null,
out PositionTimestampDisposition disposition,
out _,
out AcceptedPhysicsTimestamps timestamps));
Assert.Equal(
PositionTimestampDisposition.ForcePosition,
disposition);
// C4 route 2 (2026-08-03): a ForcePosition on the local player no
// longer routes through HeadlessSessionWorldProjection.ProjectPosition
// at all — RuntimeLiveEntitySessionController.OnPositionUpdated
// dispatches it directly to RuntimeAcceptedPositionDriveController
// instead (the deleted BlipLocalPlayer's replacement), but R2 review
// fix (2026-08-03): it re-centers the collision neighborhood on the
// destination FIRST — the deleted BlipLocalPlayer's own CenterOn
// side effect, restored via CenterOnAcceptedForcePosition, because a
// DeferredCell park this neighborhood's window can never publish is
// a dead end, not a real park
// (RuntimeAcceptedPositionDriveController.Advance's R1 doc comment).
projection.CenterOnAcceptedForcePosition(record);
RuntimeAcceptedPositionDriveController acceptedPositionDrive =
CreateAcceptedPositionDrive(runtime);
RuntimeAcceptedPositionExecutionStatus forceStatus =
acceptedPositionDrive.TryExecuteAcceptedLocalPosition(
record,
force,
disposition,
timestamps,
timestamps.PreviousTeleport);
Assert.Equal(
RuntimeAcceptedPositionExecutionStatus.Committed,
forceStatus);
// R7 review fix (2026-08-03): Z is 50.005f — within 5 mm of the
// wire's bare 50f — NOT 50.48f. The dat-exact human Setup's foot
// sphere is (0,0,0.475) r=.48 (LoadedSetupCollisionSource above);
// its bottom sits at origin + 0.475 0.48 = origin 0.005, so a
// settled origin lands 0.005 m ABOVE the floor it rests on (measured
// empirically against this exact fixture), not a full sphere RADIUS
// above it. Retail's BlipPlayer (CPhysicsObj::SetPositionSimple
// @0x005162B0, called from SmartBox::BlipPlayer @0x00453940) has
// never lifted the origin by a sphere radius — the C4-route-2 FIRST
// implementation pass (uncommitted; this file asserts a bare 50f at
// HEAD, never 50.48f) had fitted a 50.48f assertion to a dummy
// fixture sphere whose offset happened to equal its own radius, not
// to retail behavior. The comment it carried described the sphere's
// CENTRE, then wrongly asserted that description about
// controller.Position, which is the body's ORIGIN
// (PlayerMovementController.cs -> PhysicsBody.cs Position), not the
// sphere centre.
Assert.Equal(new Vector3(72f, 73f, 50.005f), controller.Position);
// CenterCount is 2: ProjectSpawn's initial centering plus the
// ForcePosition's own re-centering above (R2's restored mechanism).
Assert.Equal(2, collision.CenterCount);
}
/// <summary>
/// C3c-R1 review F7: a remote Create whose landblock lies outside the
/// bounded collision neighborhood's service window can never see its
/// deferred placement's collision-generation wake — left alone it would
/// pin its residence (and the drive's pending entry) for the whole
/// session. The host converts it to the celless completion route before
/// the pump: the residence completes with FullCell 0, the accepted wire
/// frame stays on the canonical snapshot (the exact pre-flip
/// accepted-frame behavior for far remotes), and every ledger converges.
/// </summary>
[Fact]
public void FarRemoteCreateCompletesCelllessWithoutPinningItsResidence()
{
var operations = new FixtureSessionOperations();
using var credential = new HeadlessCredentialSecret(
"fixture",
"password");
using var host = new HeadlessSessionHost(
Descriptor(),
credential,
new HeadlessDiagnosticWriter(TextWriter.Null),
operations);
GameRuntime runtime = host.Runtime;
Assert.Equal(
RuntimeSessionStartStatus.Connected,
host.Start().Status);
const uint player = 0x5000000Bu;
runtime.PlayerIdentity.ServerGuid = player;
runtime.EntityObjects.Physics.SetPosition.BeginCollisionGeneration(
0xA9B40000u, 1UL);
AddFlatLandblock(runtime.EntityObjects.Physics.Engine);
runtime.EntityObjects.Physics.SetPosition.CommitCollisionGeneration(
0xA9B40000u, 1UL, ready: true);
AcDream.Runtime.Session.RuntimeFirstEntryDriveController firstEntry =
CreateFirstEntryDrive(runtime);
var collision = new FixtureCollisionNeighborhood();
var projection = new HeadlessSessionWorldProjection(
runtime,
collision,
firstEntry);
RuntimeEntityRecord playerRecord = runtime.EntityObjects
.RegisterEntityWithInitialResidence(Spawn(player), isLocalPlayer: true)
.Canonical!;
Assert.True(runtime.EntityObjects.ApplyAcceptedSpawn(
playerRecord,
playerRecord.CreateIntegrationVersion,
playerRecord.Snapshot,
replaceGeneration: false));
projection.ProjectSpawn(playerRecord, isLocalPlayer: true);
const uint farRemote = 0x70000010u;
const uint farCell = 0x00010001u;
Assert.False(collision.IsWithinServiceWindow(farCell));
RuntimeEntityRecord remote = runtime.EntityObjects
.RegisterEntityWithInitialResidence(
Spawn(farRemote, farCell),
isLocalPlayer: false)
.Canonical!;
Assert.True(runtime.EntityObjects.ApplyAcceptedSpawn(
remote,
remote.CreateIntegrationVersion,
remote.Snapshot,
replaceGeneration: false));
projection.ProjectSpawn(remote, isLocalPlayer: false);
// Celless completion: no pinned residence, FullCell stays 0, the
// accepted wire frame survives on the canonical snapshot.
Assert.False(runtime.EntityObjects.TryGetInitialCreateResidence(
remote,
out _));
Assert.Equal(0u, remote.FullCellId);
Assert.Equal(
farCell,
remote.Snapshot.Position!.Value.LandblockId);
RuntimeEntityObjectOwnershipSnapshot ownership =
runtime.EntityObjects.CaptureOwnership();
Assert.Equal(0, ownership.InitialCreateResidenceLeaseCount);
Assert.Equal(0, ownership.FirstEntryDrivePendingCount);
Assert.Equal(0, firstEntry.PendingCount);
// Draining the placement FIFO the way the host subscription would
// converges the completion-receipt ledger too.
while (runtime.EntityObjects.Physics.SetPosition.TryPeekProjection(
out RuntimePlacementProjectionSnapshot head))
{
if (!runtime.EntityObjects.Physics.SetPosition
.AcknowledgeProjection(head.Token))
{
break;
}
}
Assert.Equal(
0,
runtime.EntityObjects.CaptureOwnership()
.PendingCompletionReceiptCount);
}
[Fact]
public void PlacementReceiptValidationDoesNotRegainMovementOrPhysicsAuthority()
{
var operations = new FixtureSessionOperations();
using var credential = new HeadlessCredentialSecret(
"fixture",
"password");
using var host = new HeadlessSessionHost(
Descriptor(),
credential,
new HeadlessDiagnosticWriter(TextWriter.Null),
operations);
GameRuntime runtime = host.Runtime;
// C3c: conductor-driven flow — a live generation admits the initial
// residence, the flat landblock's collision generation commits, and
// the world projection's pump drives the local first-entry conductor
// to completion (the deleted SynchronizeLocalPlayer hand-copy's
// replacement).
Assert.Equal(
RuntimeSessionStartStatus.Connected,
host.Start().Status);
const uint player = 0x50000004u;
runtime.PlayerIdentity.ServerGuid = player;
runtime.EntityObjects.Physics.SetPosition.BeginCollisionGeneration(
0xA9B40000u, 1UL);
AddFlatLandblock(runtime.EntityObjects.Physics.Engine);
runtime.EntityObjects.Physics.SetPosition.CommitCollisionGeneration(
0xA9B40000u, 1UL, ready: true);
AcDream.Runtime.Session.RuntimeFirstEntryDriveController firstEntry =
CreateFirstEntryDrive(runtime);
RuntimeEntityRecord record = runtime.EntityObjects
.RegisterEntityWithInitialResidence(Spawn(player), isLocalPlayer: true)
.Canonical!;
Assert.True(runtime.EntityObjects.ApplyAcceptedSpawn(
record,
record.CreateIntegrationVersion,
record.Snapshot,
replaceGeneration: false));
var directProjection = new HeadlessSessionWorldProjection(
runtime,
new FixtureCollisionNeighborhood(),
firstEntry);
directProjection.ProjectSpawn(record, isLocalPlayer: true);
PlayerMovementController controller = Assert.IsType<
PlayerMovementController>(runtime.MovementOwner.Controller);
Vector3 positionBefore = controller.Position;
Quaternion orientationBefore = controller.BodyOrientation;
RuntimePhysicsOwnershipSnapshot physicsBefore =
runtime.EntityObjects.Physics.CaptureOwnership();
RuntimePlacementProjectionSnapshot receipt = Placement(
runtime,
record,
RuntimePlacementProjectionKind.Place,
new Vector3(600f, 601f, 602f),
Quaternion.CreateFromAxisAngle(Vector3.UnitY, 1.2f));
var receiptSink = new HeadlessRuntimePlacementProjectionSink(runtime);
Assert.True(receiptSink.TryApply(in receipt));
Assert.Equal(positionBefore, controller.Position);
Assert.Equal(orientationBefore, controller.BodyOrientation);
Assert.Equal(
physicsBefore,
runtime.EntityObjects.Physics.CaptureOwnership());
}
[Fact]
public void PlacementReceiptUsesExactIncarnationAndDiscardIsAckOnly()
{
var operations = new FixtureSessionOperations();
using var credential = new HeadlessCredentialSecret(
"fixture",
"password");
using var host = new HeadlessSessionHost(
Descriptor(),
credential,
new HeadlessDiagnosticWriter(TextWriter.Null),
operations);
GameRuntime runtime = host.Runtime;
RuntimeEntityRecord record = runtime.EntityObjects
.RegisterEntity(Spawn(0x50000005u))
.Canonical!;
Assert.True(runtime.EntityObjects.ApplyAcceptedSpawn(
record,
record.CreateIntegrationVersion,
record.Snapshot,
replaceGeneration: false));
var projection = new HeadlessRuntimePlacementProjectionSink(runtime);
RuntimePlacementProjectionSnapshot place = Placement(
runtime,
record,
RuntimePlacementProjectionKind.Place,
Vector3.One,
Quaternion.Identity);
RuntimePlacementProjectionSnapshot stale = place with
{
Token = place.Token with
{
Entity = place.Token.Entity with
{
Incarnation = unchecked((ushort)(
place.Token.Entity.Incarnation + 1)),
},
},
};
RuntimePlacementProjectionSnapshot discard = stale with
{
Kind = RuntimePlacementProjectionKind.Discard,
Token = stale.Token with
{
SessionLifetimeVersion = ulong.MaxValue,
},
};
Assert.False(projection.TryApply(in stale));
Assert.True(projection.TryApply(in discard));
}
[Fact]
public void ExecutorCompletedReceiptIsAcknowledgeOnlyRegardlessOfRecordValidity()
{
// F1: mirrors PlacementReceiptUsesExactIncarnationAndDiscardIsAckOnly's
// stale-token half - proves ExecutorCompleted is acknowledged
// unconditionally (never gated by the record-lookup/portal-shape
// checks Place/Withdraw depend on), so a genuinely stale/mismatched
// token can never wedge the FIFO behind it.
var operations = new FixtureSessionOperations();
using var credential = new HeadlessCredentialSecret(
"fixture",
"password");
using var host = new HeadlessSessionHost(
Descriptor(),
credential,
new HeadlessDiagnosticWriter(TextWriter.Null),
operations);
GameRuntime runtime = host.Runtime;
RuntimeEntityRecord record = runtime.EntityObjects
.RegisterEntity(Spawn(0x50000006u))
.Canonical!;
Assert.True(runtime.EntityObjects.ApplyAcceptedSpawn(
record,
record.CreateIntegrationVersion,
record.Snapshot,
replaceGeneration: false));
var sink = new HeadlessRuntimePlacementProjectionSink(runtime);
RuntimePlacementProjectionSnapshot completion = Placement(
runtime,
record,
RuntimePlacementProjectionKind.ExecutorCompleted,
Vector3.One,
Quaternion.Identity);
RuntimePlacementProjectionSnapshot stale = completion with
{
Token = completion.Token with
{
Entity = completion.Token.Entity with
{
Incarnation = unchecked((ushort)(
completion.Token.Entity.Incarnation + 1)),
},
SessionLifetimeVersion = ulong.MaxValue,
},
};
Assert.True(sink.TryApply(in completion));
Assert.True(sink.TryApply(in stale));
}
[Fact]
public void SessionEventRouteOwnsOneObserverAndUnsubscribesBeforeNetworkDetach()
{
var operations = new FixtureSessionOperations();
using var credential = new HeadlessCredentialSecret(
"fixture",
"password");
using var host = new HeadlessSessionHost(
Descriptor(),
credential,
new HeadlessDiagnosticWriter(TextWriter.Null),
operations);
GameRuntime runtime = host.Runtime;
int subscriberCountDuringDetach = -1;
var inner = new FixtureEventRoute(
onDispose: () => subscriberCountDuringDetach =
runtime.EntityObjects.Events.PlacementSubscriberCount);
var placements = new HeadlessRuntimePlacementProjectionSink(runtime);
var route = new HeadlessSessionEventRoute(
inner,
runtime,
placements);
Assert.Equal(
0,
runtime.EntityObjects.Events.PlacementSubscriberCount);
route.Attach();
route.Attach();
Assert.Equal(
1,
runtime.EntityObjects.Events.PlacementSubscriberCount);
Assert.Equal(1, inner.AttachCount);
route.Dispose();
route.Dispose();
Assert.Equal(0, subscriberCountDuringDetach);
Assert.Equal(
0,
runtime.EntityObjects.Events.PlacementSubscriberCount);
Assert.Equal(1, inner.DisposeCount);
}
[Fact]
public void ReplacementSessionEventRouteGetsOneFreshObserver()
{
var operations = new FixtureSessionOperations();
using var credential = new HeadlessCredentialSecret(
"fixture",
"password");
using var host = new HeadlessSessionHost(
Descriptor(),
credential,
new HeadlessDiagnosticWriter(TextWriter.Null),
operations);
GameRuntime runtime = host.Runtime;
var placements = new HeadlessRuntimePlacementProjectionSink(runtime);
var first = new HeadlessSessionEventRoute(
new FixtureEventRoute(),
runtime,
placements);
first.Attach();
Assert.Equal(
1,
runtime.EntityObjects.Events.PlacementSubscriberCount);
first.Dispose();
Assert.Equal(
0,
runtime.EntityObjects.Events.PlacementSubscriberCount);
var replacement = new HeadlessSessionEventRoute(
new FixtureEventRoute(),
runtime,
placements);
replacement.Attach();
Assert.Equal(
1,
runtime.EntityObjects.Events.PlacementSubscriberCount);
replacement.Dispose();
Assert.Equal(
0,
runtime.EntityObjects.Events.PlacementSubscriberCount);
}
[Fact]
public void SessionEventRouteRetryDoesNotRestorePlacementObserver()
{
var operations = new FixtureSessionOperations();
using var credential = new HeadlessCredentialSecret(
"fixture",
"password");
using var host = new HeadlessSessionHost(
Descriptor(),
credential,
new HeadlessDiagnosticWriter(TextWriter.Null),
operations);
GameRuntime runtime = host.Runtime;
var inner = new FixtureEventRoute
{
DisposeFailuresRemaining = 1,
};
var route = new HeadlessSessionEventRoute(
inner,
runtime,
new HeadlessRuntimePlacementProjectionSink(runtime));
route.Attach();
Assert.Throws<IOException>(route.Dispose);
Assert.Equal(
0,
runtime.EntityObjects.Events.PlacementSubscriberCount);
Assert.Equal(1, inner.DisposeCount);
route.Dispose();
Assert.Equal(2, inner.DisposeCount);
Assert.Equal(
0,
runtime.EntityObjects.Events.PlacementSubscriberCount);
}
[Fact]
public void CollisionTransactionCancelsPostAdmissionFaultWithoutWithdrawingActiveWorld()
{
using var lifetime = new RuntimeEntityObjectLifetime();
RuntimePhysicsState physics = lifetime.Physics;
const uint landblockId = 0xA9B4FFFFu;
CompleteCollisionGeneration(
physics,
landblockId,
afterAdmission: null,
(admission, prepared) =>
physics.StageCollisionAssets(
admission,
prepared,
CollisionAssets(landblockId, 10f)));
Assert.Throws<FixtureCollisionPublicationException>(() =>
CompleteCollisionGeneration(
physics,
landblockId,
_ => throw new FixtureCollisionPublicationException(),
(_, _) => throw new InvalidOperationException(
"Staging must not run after the injected admission fault.")));
Assert.Equal(10f, physics.Engine.SampleTerrainZ(1f, 1f));
RuntimePhysicsOwnershipSnapshot ownership = physics.CaptureOwnership();
Assert.Equal(1, ownership.LandblockCount);
Assert.Equal(0, ownership.CollisionAdmissionCount);
}
[Fact]
public void CollisionTransactionYieldsTheFirstNonterminalRuntimePoll()
{
using var lifetime = new RuntimeEntityObjectLifetime();
RuntimePhysicsState physics = lifetime.Physics;
const uint landblockId = 0xA9B4FFFFu;
HeadlessCollisionGenerationTransaction transaction =
HeadlessCollisionGenerationTransaction.Begin(
physics,
landblockId,
afterAdmission: null,
(admission, prepared) =>
physics.StageCollisionAssets(
admission,
prepared,
CollisionAssets(landblockId, 10f)));
HeadlessCollisionGenerationAdvance advance;
do
{
advance = transaction.Advance();
Assert.True(advance.Progressed);
}
while (!advance.YieldToCaller);
Assert.False(advance.Completed);
Assert.False(advance.WaitingForProjectionAcknowledgement);
Assert.False(transaction.CompletionCommitted);
do
{
advance = transaction.Advance();
Assert.True(advance.Progressed);
}
while (!advance.Completed && !advance.YieldToCaller);
if (!advance.Completed)
advance = transaction.Advance();
Assert.True(advance.Completed);
Assert.True(transaction.EngineMutationCommitted);
Assert.True(transaction.CompletionCommitted);
Assert.Equal(0, physics.CaptureOwnership().CollisionAdmissionCount);
}
[Fact]
public void CollisionTransactionRetainsPostEngineCancellationUntilPlaceAck()
{
using var lifetime = new RuntimeEntityObjectLifetime();
RuntimePhysicsState physics = lifetime.Physics;
const uint landblockId = 0xA9B4FFFFu;
CompleteCollisionGeneration(
physics,
landblockId,
afterAdmission: null,
(admission, prepared) =>
physics.StageCollisionAssets(
admission,
prepared,
CollisionAssets(landblockId, 10f)));
const uint guid = 0x70004201u;
const uint cell = 0xA9B40001u;
Vector3 position = new(10f, 10f, 0f);
RuntimeEntityRecord record = lifetime.RegisterEntity(
Spawn(guid)).Canonical!;
lifetime.Entities.SetFinalPhysicsState(record, PhysicsStateFlags.Gravity);
lifetime.Entities.SetFullCell(record, cell, landblockId);
var body = new PhysicsBody
{
Position = position,
Orientation = Quaternion.Identity,
LastUpdateTime = 1d,
State = PhysicsStateFlags.Gravity,
TransientState = TransientStateFlags.Active,
};
body.SnapToCell(cell, position, position);
lifetime.Entities.SetPhysicsBody(record, body);
record.ObjectClock.Activate();
physics.AcknowledgeSpatialProjection(record, spatial: true);
RuntimePlacementProjectionToken seeded = SeedRuntimePlacement(
physics,
record,
cell,
position);
Assert.True(physics.SetPosition.AcknowledgeProjection(seeded));
HeadlessCollisionGenerationTransaction transaction =
HeadlessCollisionGenerationTransaction.Begin(
physics,
landblockId,
afterAdmission: null,
(admission, prepared) =>
physics.StageCollisionAssets(
admission,
prepared,
CollisionAssets(landblockId, 20f)));
HeadlessCollisionGenerationAdvance advance;
do
{
advance = transaction.Advance();
}
while (!advance.WaitingForProjectionAcknowledgement);
Assert.False(transaction.EngineMutationCommitted);
Assert.True(physics.SetPosition.TryPeekProjection(
out RuntimePlacementProjectionSnapshot withdrawal));
Assert.True(physics.SetPosition.AcknowledgeProjection(withdrawal.Token));
do
{
advance = transaction.Advance();
}
while (!advance.WaitingForProjectionAcknowledgement);
Assert.True(transaction.EngineMutationCommitted);
Assert.True(physics.SetPosition.TryPeekProjection(
out RuntimePlacementProjectionSnapshot placement));
Assert.False(transaction.TryCancel());
Assert.True(physics.SetPosition.AcknowledgeProjection(placement.Token));
Assert.True(transaction.TryCancel());
RuntimePhysicsOwnershipSnapshot ownership = physics.CaptureOwnership();
Assert.Equal(0, ownership.CollisionPrefixMutationCount);
Assert.Equal(0, ownership.CollisionAdmissionCount);
}
[Fact]
public void CollisionTransactionCancelsStagingFaultWithoutWithdrawingActiveWorld()
{
using var lifetime = new RuntimeEntityObjectLifetime();
RuntimePhysicsState physics = lifetime.Physics;
const uint landblockId = 0xA9B4FFFFu;
CompleteCollisionGeneration(
physics,
landblockId,
afterAdmission: null,
(admission, prepared) =>
physics.StageCollisionAssets(
admission,
prepared,
CollisionAssets(landblockId, 10f)));
Assert.Throws<FixtureCollisionPublicationException>(() =>
CompleteCollisionGeneration(
physics,
landblockId,
afterAdmission: null,
(admission, prepared) =>
{
physics.StageCollisionAssets(
admission,
prepared,
CollisionAssets(landblockId, 25f));
throw new FixtureCollisionPublicationException();
}));
Assert.Equal(10f, physics.Engine.SampleTerrainZ(1f, 1f));
RuntimePhysicsOwnershipSnapshot ownership = physics.CaptureOwnership();
Assert.Equal(1, ownership.LandblockCount);
Assert.Equal(0, ownership.CollisionAdmissionCount);
}
private static HeadlessSessionDescriptor Descriptor(
HeadlessCredentialProviderKind provider =
HeadlessCredentialProviderKind.Environment,
string credentialReference = "BOT_PASSWORD") => new()
{
Id = "bot",
Endpoint = new HeadlessEndpointDescriptor
{
Host = "127.0.0.1",
Port = 9000,
},
Account = "account",
Character = new HeadlessCharacterSelector
{
Name = "headless",
},
Policy = new HeadlessBotPolicyDescriptor
{
Id = "idle",
},
Credential = new HeadlessCredentialReference
{
Provider = provider,
Reference = credentialReference,
},
};
private static void HydrateGroundedPlayer(GameRuntime runtime)
{
const uint player = 0x50000002u;
PhysicsEngine engine = runtime.EntityObjects.Physics.Engine;
AddFlatLandblock(engine);
RuntimeEntityRecord record = runtime.EntityObjects
.RegisterEntity(Spawn(player))
.Canonical!;
Assert.True(runtime.EntityObjects.ApplyAcceptedSpawn(
record,
record.CreateIntegrationVersion,
record.Snapshot,
replaceGeneration: false));
var controller = new PlayerMovementController(engine);
controller.SetPosition(
new Vector3(96f, 97f, 50f),
0xA9B40001u,
new Vector3(96f, 97f, 50f));
runtime.MovementOwner.Controller = controller;
}
private static void CompleteCollisionGeneration(
RuntimePhysicsState physics,
uint landblockId,
Action<RuntimeCollisionAdmission>? afterAdmission,
Action<RuntimeCollisionAdmission,
PreparedLandblockCollisionGeneration> stage)
{
HeadlessCollisionGenerationTransaction transaction =
HeadlessCollisionGenerationTransaction.Begin(
physics,
landblockId,
afterAdmission,
stage);
while (true)
{
HeadlessCollisionGenerationAdvance advance = transaction.Advance();
if (advance.Completed)
return;
Assert.True(advance.Progressed);
Assert.False(advance.WaitingForProjectionAcknowledgement);
}
}
private static RuntimePlacementProjectionToken SeedRuntimePlacement(
RuntimePhysicsState physics,
RuntimeEntityRecord record,
uint cell,
Vector3 position)
{
Type coreMarker = typeof(PhysicsEngine);
Type requestType = coreMarker.Assembly.GetType(
"AcDream.Core.Physics.PhysicsSetPositionRequest",
throwOnError: true)!;
Type flagsType = coreMarker.Assembly.GetType(
"AcDream.Core.Physics.PhysicsSetPositionFlags",
throwOnError: true)!;
Type placementClassType = coreMarker.Assembly.GetType(
"AcDream.Core.Physics.PhysicsPlacementClass",
throwOnError: true)!;
object request = Activator.CreateInstance(
requestType,
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
binder: null,
args:
[
position,
Quaternion.Identity,
cell,
position,
ImmutableArray<FlatCollisionSphere>.Empty,
1f,
0.4f,
0.4f,
PhysicsStateFlags.None,
ObjectInfoState.None,
0u,
Enum.ToObject(placementClassType, 0),
Enum.ToObject(flagsType, 0x011u),
Vector3.Zero,
0f,
0f,
0u,
cell,
],
culture: null)!;
Type runtimeMarker = typeof(RuntimePhysicsState);
Type commandType = runtimeMarker.Assembly.GetType(
"AcDream.Runtime.Physics.RuntimeSetPositionCommand",
throwOnError: true)!;
Type kindType = runtimeMarker.Assembly.GetType(
"AcDream.Runtime.Physics.RuntimeSetPositionOperationKind",
throwOnError: true)!;
object command = Activator.CreateInstance(
commandType,
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
binder: null,
args:
[
request,
Enum.ToObject(kindType, 2),
10d,
0UL,
0f,
0f,
default(RuntimePortalPlacementAuthority),
],
culture: null)!;
MethodInfo apply = physics.SetPosition.GetType().GetMethod(
"Apply",
BindingFlags.Instance | BindingFlags.NonPublic)
?? throw new MissingMethodException("Runtime SetPosition.Apply");
object outcome = apply.Invoke(
physics.SetPosition,
[record, record.PositionAuthorityVersion, command])!;
return (RuntimePlacementProjectionToken)(outcome.GetType().GetProperty(
"Projection",
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
?.GetValue(outcome)
?? throw new MissingMemberException("Runtime placement projection"));
}
private static RuntimeLandblockCollisionAssets CollisionAssets(
uint landblockId,
float terrainHeight)
{
var heights = new byte[81];
var table = new float[256];
table[0] = terrainHeight;
return new RuntimeLandblockCollisionAssets(
landblockId,
new TerrainSurface(heights, table),
Array.Empty<CellSurface>(),
Array.Empty<PortalPlane>(),
0f,
0f,
0u);
}
private sealed class FixtureCollisionPublicationException : Exception;
[Fact]
public void MissingPreparedCollisionYieldsTypedRetryAndCompletesWhenAvailable()
{
// C3c contract: the prepared-collision read failure is the
// conductor's typed AwaitingCollisionSource retry — no
// InvalidDataException (or any exception) escapes the host wiring,
// the entity stays a tracked, re-drivable first entry, and the same
// sequence completes once the source can serve the Setup.
var operations = new FixtureSessionOperations();
using var credential = new HeadlessCredentialSecret(
"fixture",
"password");
using var host = new HeadlessSessionHost(
Descriptor(),
credential,
new HeadlessDiagnosticWriter(TextWriter.Null),
operations);
GameRuntime runtime = host.Runtime;
Assert.Equal(
RuntimeSessionStartStatus.Connected,
host.Start().Status);
const uint player = 0x50000021u;
runtime.PlayerIdentity.ServerGuid = player;
runtime.EntityObjects.Physics.SetPosition.BeginCollisionGeneration(
0xA9B40000u, 1UL);
AddFlatLandblock(runtime.EntityObjects.Physics.Engine);
runtime.EntityObjects.Physics.SetPosition.CommitCollisionGeneration(
0xA9B40000u, 1UL, ready: true);
var source = new FlakySetupCollisionSource();
var firstEntry = new AcDream.Runtime.Session
.RuntimeFirstEntryDriveController(
runtime.EntityObjects,
runtime.Clock,
source,
() => PlayerMovementConstructionOptions.From(
runtime.CharacterOwner.MovementSkills.Snapshot),
static _ => new RuntimeLocalPlayerPhysicsActivationPreparation(
Radius: 0.48f,
Height: 1.835f,
RuntimeLocalPlayerShadowDisposition.ProvenShapeless));
RuntimeEntityRecord record = runtime.EntityObjects
.RegisterEntityWithInitialResidence(Spawn(player), isLocalPlayer: true)
.Canonical!;
Assert.True(runtime.EntityObjects.ApplyAcceptedSpawn(
record,
record.CreateIntegrationVersion,
record.Snapshot,
replaceGeneration: false));
var collision = new FixtureCollisionNeighborhood();
var projection = new HeadlessSessionWorldProjection(
runtime,
collision,
firstEntry);
projection.ProjectSpawn(record, isLocalPlayer: true);
Assert.True(source.SetupReadAttempts >= 1);
Assert.Null(runtime.MovementOwner.Controller);
Assert.Equal(1, firstEntry.PendingCount);
Assert.Equal(0u, record.FullCellId);
source.Available = true;
// The session tick's retry pump.
firstEntry.DriveAll();
Assert.IsType<PlayerMovementController>(
runtime.MovementOwner.Controller);
Assert.Equal(0, firstEntry.PendingCount);
Assert.Equal(0xA9B40000u, record.FullCellId & 0xFFFF0000u);
Assert.NotEqual(0u, record.FullCellId);
}
private sealed class FlakySetupCollisionSource
: AcDream.Content.IPreparedCollisionSource
{
internal bool Available { get; set; }
internal int SetupReadAttempts { get; private set; }
public AcDream.Content.PreparedAssetPresence ProbeCollision(
AcDream.Content.Pak.PakAssetType type,
uint sourceFileId) =>
AcDream.Content.PreparedAssetPresence.Available;
public AcDream.Content.PreparedCollisionReadResult<FlatSetupCollision>
ReadSetupCollision(
uint sourceFileId,
CancellationToken cancellationToken = default)
{
SetupReadAttempts++;
if (!Available)
{
return AcDream.Content.PreparedCollisionReadResult<
FlatSetupCollision>.Missing;
}
return AcDream.Content.PreparedCollisionReadResult<
FlatSetupCollision>.Loaded(new FlatSetupCollision(
System.Collections.Immutable.ImmutableArray<
FlatCollisionCylinder>.Empty,
[new FlatCollisionSphere(Vector3.Zero, 0.48f)],
height: 0f,
radius: 0f,
stepUpHeight: 0.4f,
stepDownHeight: 0.4f));
}
public AcDream.Content.PreparedCollisionReadResult<
FlatGfxObjCollisionAsset> ReadGfxObjCollision(
uint sourceFileId,
CancellationToken cancellationToken = default) =>
throw new NotSupportedException();
public AcDream.Content.PreparedCollisionReadResult<
FlatCellStructureCollisionAsset> ReadCellStructureCollision(
uint sourceFileId,
CancellationToken cancellationToken = default) =>
throw new NotSupportedException();
public AcDream.Content.PreparedCollisionReadResult<FlatEnvCellTopology>
ReadEnvCellTopology(
uint sourceFileId,
CancellationToken cancellationToken = default) =>
throw new NotSupportedException();
public AcDream.Content.PreparedCollisionSourceStats CollisionStats =>
default;
public void Dispose()
{
}
}
private static AcDream.Runtime.Session.RuntimeFirstEntryDriveController
CreateFirstEntryDrive(GameRuntime runtime) => new(
runtime.EntityObjects,
runtime.Clock,
new LoadedSetupCollisionSource(),
() => PlayerMovementConstructionOptions.From(
runtime.CharacterOwner.MovementSkills.Snapshot),
static _ => new RuntimeLocalPlayerPhysicsActivationPreparation(
Radius: 0.48f,
Height: 1.835f,
RuntimeLocalPlayerShadowDisposition.ProvenShapeless));
/// <summary>
/// C4 route 2 (2026-08-03): mirrors <see cref="CreateFirstEntryDrive"/>'s
/// construction pattern for the accepted-Position drive controller. No
/// real WorldSession is needed for these fixture tests —
/// LocalPlayerOutboundController.SendImmediatePosition no-ops on a null
/// session.
/// </summary>
private static RuntimeAcceptedPositionDriveController
CreateAcceptedPositionDrive(GameRuntime runtime) => new(
runtime.EntityObjects,
runtime.Clock,
new LoadedSetupCollisionSource(),
new LocalPlayerOutboundController((_, _, _, _, _, _) => { }),
() => runtime.Generation,
() => runtime.PlayerIdentity.ServerGuid,
() => runtime.MovementOwner.Controller,
() => runtime.CharacterOwner.UsePositionFromServer,
() => null);
private sealed class LoadedSetupCollisionSource
: AcDream.Content.IPreparedCollisionSource
{
public AcDream.Content.PreparedAssetPresence ProbeCollision(
AcDream.Content.Pak.PakAssetType type,
uint sourceFileId) =>
AcDream.Content.PreparedAssetPresence.Available;
public AcDream.Content.PreparedCollisionReadResult<FlatSetupCollision>
ReadSetupCollision(
uint sourceFileId,
CancellationToken cancellationToken = default) =>
AcDream.Content.PreparedCollisionReadResult<FlatSetupCollision>
.Loaded(new FlatSetupCollision(
System.Collections.Immutable.ImmutableArray<
FlatCollisionCylinder>.Empty,
// R7 review fix (2026-08-03): the dat-exact human Setup
// 0x02000001 spheres (Ts46SphereListConformanceTests.cs
// :35-39) — foot (0,0,0.475) r=.48, head/torso
// (0,0,1.350) r=.48. The PREVIOUS single dummy sphere at
// (0,0,0) r=.48 (offset == radius) made a settled origin
// rest a FULL radius above the floor; the real foot
// sphere's bottom is origin + 0.475 0.48 = origin
// 0.005, so a settled origin lands ON the floor within
// 5 mm. Retail's BlipPlayer has never lifted the origin
// by a sphere radius — that was a fixture artifact, not
// a retail-fidelity gain (docs/ISSUES.md #285 correction).
[
new FlatCollisionSphere(
new Vector3(0f, 0f, 0.475f), 0.48f),
new FlatCollisionSphere(
new Vector3(0f, 0f, 1.350f), 0.48f),
],
height: 0f,
radius: 0f,
stepUpHeight: 0.4f,
stepDownHeight: 0.4f));
public AcDream.Content.PreparedCollisionReadResult<
FlatGfxObjCollisionAsset> ReadGfxObjCollision(
uint sourceFileId,
CancellationToken cancellationToken = default) =>
throw new NotSupportedException();
public AcDream.Content.PreparedCollisionReadResult<
FlatCellStructureCollisionAsset> ReadCellStructureCollision(
uint sourceFileId,
CancellationToken cancellationToken = default) =>
throw new NotSupportedException();
public AcDream.Content.PreparedCollisionReadResult<FlatEnvCellTopology>
ReadEnvCellTopology(
uint sourceFileId,
CancellationToken cancellationToken = default) =>
throw new NotSupportedException();
public AcDream.Content.PreparedCollisionSourceStats CollisionStats =>
default;
public void Dispose()
{
}
}
private static void AddFlatLandblock(PhysicsEngine engine)
{
var heights = new byte[81];
Array.Fill(heights, (byte)50);
var heightTable = new float[256];
for (int index = 0; index < heightTable.Length; index++)
heightTable[index] = index;
engine.AddLandblock(
0xA9B4FFFFu,
new TerrainSurface(heights, heightTable),
[],
[],
worldOffsetX: 0f,
worldOffsetY: 0f);
}
private static WorldSession.EntitySpawn Spawn(
uint guid,
uint cellId = 0xA9B40001u)
{
var position = new CreateObject.ServerPosition(
cellId,
96f,
97f,
50f,
1f,
0f,
0f,
0f);
var timestamps = new PhysicsTimestamps(
Position: 1,
Movement: 1,
State: 1,
Vector: 1,
Teleport: 0,
ServerControlledMove: 1,
ForcePosition: 0,
ObjDesc: 1,
Instance: 1);
var physics = new PhysicsSpawnData(
RawState: (uint)PhysicsStateFlags.ReportCollisions,
Position: position,
Movement: null,
AnimationFrame: null,
SetupTableId: 0x02000001u,
MotionTableId: null,
SoundTableId: null,
PhysicsScriptTableId: null,
Parent: null,
Children: null,
Scale: null,
Friction: null,
Elasticity: null,
Translucency: null,
Velocity: null,
Acceleration: null,
AngularVelocity: null,
DefaultScriptType: null,
DefaultScriptIntensity: null,
Timestamps: timestamps);
return new WorldSession.EntitySpawn(
guid,
position,
0x02000001u,
[],
[],
[],
null,
null,
"Headless",
null,
null,
null,
PhysicsState: physics.RawState,
InstanceSequence: 1,
MovementSequence: 1,
ServerControlSequence: 1,
PositionSequence: 1,
Physics: physics);
}
private static RuntimePlacementProjectionSnapshot Placement(
GameRuntime runtime,
RuntimeEntityRecord record,
RuntimePlacementProjectionKind kind,
Vector3 position,
Quaternion orientation)
{
RuntimeEntityKey key = Assert.IsType<RuntimeEntityKey>(record.Key);
var token = new RuntimePlacementProjectionToken(
Sequence: 1,
Revision: 1,
Entity: key,
PositionAuthorityVersion: record.PositionAuthorityVersion,
SpatialAuthorityVersion: record.SpatialAuthorityVersion,
PlacementCommitVersion: record.PlacementCommitVersion,
SessionLifetimeVersion:
runtime.EntityObjects.Entities.SessionLifetimeVersion,
ExactCellId: record.FullCellId,
CollisionGeneration: 1,
Portal: default);
return new RuntimePlacementProjectionSnapshot(
token,
kind,
position,
orientation,
CellLocalPosition: position,
InContact: false,
OnWalkable: false);
}
private static uint ActionOpcode(byte[] body) =>
BinaryPrimitives.ReadUInt32LittleEndian(
body.AsSpan(8, sizeof(uint)));
private sealed class FixtureSessionOperations : ILiveSessionOperations
{
public List<WorldSession> Sessions { get; } = [];
public int CreatedSessionCount { get; private set; }
public int DisposedSessionCount { get; private set; }
public string? LastUser { get; private set; }
public string? LastPassword { get; private set; }
public IPEndPoint ResolveEndpoint(string host, int port) =>
new(IPAddress.Loopback, port);
public WorldSession CreateSession(IPEndPoint endpoint)
{
CreatedSessionCount++;
var session = new WorldSession(endpoint);
Sessions.Add(session);
return session;
}
public void Connect(
WorldSession session,
string user,
string password)
{
LastUser = user;
LastPassword = password;
}
public CharacterList.Parsed GetCharacters(
WorldSession session) =>
new(
0u,
[
new CharacterList.Character(
0x50000001u,
"Other",
0u),
new CharacterList.Character(
0x50000002u,
"Headless",
0u),
],
[],
11,
"account",
true,
true);
public void EnterWorld(
WorldSession session,
int activeCharacterIndex)
{
}
public void Tick(WorldSession session)
{
}
public void DisposeSession(WorldSession session)
{
DisposedSessionCount++;
session.Dispose();
}
}
private sealed class FailOnceTextWriter : StringWriter
{
public bool FailNextWrite { get; set; }
public override void WriteLine(string? value)
{
if (FailNextWrite)
{
FailNextWrite = false;
throw new IOException("fixture write failure");
}
base.WriteLine(value);
}
}
private sealed class FixtureCollisionNeighborhood
: IHeadlessCollisionNeighborhood
{
public int CenterCount { get; private set; }
public uint LastCell { get; private set; }
public void CenterOn(uint fullCellId)
{
CenterCount++;
LastCell = fullCellId;
}
public bool IsReady(uint fullCellId) =>
fullCellId == LastCell;
// C3c-R1 review F7: the fixture window mirrors production's 3x3
// membership around the last requested center; no center yet means
// "within" (never convert before the first CenterOn).
public bool IsWithinServiceWindow(uint fullCellId)
{
if (LastCell == 0u)
return true;
int dx = Math.Abs(
(int)((fullCellId >> 24) & 0xFFu)
- (int)((LastCell >> 24) & 0xFFu));
int dy = Math.Abs(
(int)((fullCellId >> 16) & 0xFFu)
- (int)((LastCell >> 16) & 0xFFu));
return dx <= 1 && dy <= 1;
}
}
private sealed class FixtureEventRoute(
Action? onDispose = null) : ILiveSessionEventRouting
{
public int AttachCount { get; private set; }
public int DisposeCount { get; private set; }
public int DisposeFailuresRemaining { get; set; }
public void Attach() => AttachCount++;
public void Dispose()
{
DisposeCount++;
onDispose?.Invoke();
if (DisposeFailuresRemaining > 0)
{
DisposeFailuresRemaining--;
throw new IOException("fixture route detach failure");
}
}
}
}