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:
parent
78f1eb1896
commit
529e0e9d88
68 changed files with 5977 additions and 831 deletions
|
|
@ -466,6 +466,7 @@ public sealed class EquippedChildProjectionWithdrawalTests
|
|||
ChildInstanceSequence: 1,
|
||||
ChildPositionSequence: 1);
|
||||
|
||||
fixture.CompleteFirstEntry();
|
||||
Assert.True(fixture.Live.TryApplyCreateParent(update, out _));
|
||||
fixture.Controller.OnCreateParentAccepted(update);
|
||||
|
||||
|
|
@ -509,13 +510,20 @@ public sealed class EquippedChildProjectionWithdrawalTests
|
|||
LiveEntityRecord parent = fixture.Spawn(0x70000270u, generation: 1);
|
||||
WorldSession.EntitySpawn childSpawn = ControllerFixture.SpawnData(
|
||||
0x70000271u,
|
||||
generation: 1) with
|
||||
generation: 1);
|
||||
childSpawn = childSpawn with
|
||||
{
|
||||
Position = null,
|
||||
ParentGuid = parent.ServerGuid,
|
||||
ParentLocation = 0,
|
||||
PlacementId = null,
|
||||
PositionSequence = 0,
|
||||
Physics = childSpawn.Physics!.Value with
|
||||
{
|
||||
Position = null,
|
||||
Parent = new PhysicsAttachment(parent.ServerGuid, 0u),
|
||||
AnimationFrame = null,
|
||||
},
|
||||
};
|
||||
fixture.Live.RegisterLiveEntity(childSpawn);
|
||||
fixture.Controller.OnSpawn(childSpawn);
|
||||
|
|
@ -558,13 +566,20 @@ public sealed class EquippedChildProjectionWithdrawalTests
|
|||
LiveEntityRecord parent = fixture.Spawn(0x70000272u, generation: 1);
|
||||
WorldSession.EntitySpawn childSpawn = ControllerFixture.SpawnData(
|
||||
0x70000273u,
|
||||
generation: 1) with
|
||||
generation: 1);
|
||||
childSpawn = childSpawn with
|
||||
{
|
||||
Position = null,
|
||||
ParentGuid = parent.ServerGuid,
|
||||
ParentLocation = 0,
|
||||
PlacementId = 0,
|
||||
PositionSequence = 0,
|
||||
Physics = childSpawn.Physics!.Value with
|
||||
{
|
||||
Position = null,
|
||||
Parent = new PhysicsAttachment(parent.ServerGuid, 0u),
|
||||
AnimationFrame = 0,
|
||||
},
|
||||
};
|
||||
fixture.Live.RegisterLiveEntity(childSpawn);
|
||||
fixture.Controller.OnSpawn(childSpawn);
|
||||
|
|
@ -577,13 +592,20 @@ public sealed class EquippedChildProjectionWithdrawalTests
|
|||
Assert.Null(entity.PaletteOverride);
|
||||
WorldSession.EntitySpawn grandchildSpawn = ControllerFixture.SpawnData(
|
||||
0x70000274u,
|
||||
generation: 1) with
|
||||
generation: 1);
|
||||
grandchildSpawn = grandchildSpawn with
|
||||
{
|
||||
Position = null,
|
||||
ParentGuid = child.ServerGuid,
|
||||
ParentLocation = 0,
|
||||
PlacementId = 0,
|
||||
PositionSequence = 0,
|
||||
Physics = grandchildSpawn.Physics!.Value with
|
||||
{
|
||||
Position = null,
|
||||
Parent = new PhysicsAttachment(child.ServerGuid, 0u),
|
||||
AnimationFrame = 0,
|
||||
},
|
||||
};
|
||||
fixture.Live.RegisterLiveEntity(grandchildSpawn);
|
||||
fixture.Controller.OnSpawn(grandchildSpawn);
|
||||
|
|
@ -734,6 +756,8 @@ public sealed class EquippedChildProjectionWithdrawalTests
|
|||
Position = null,
|
||||
PositionSequence = 1,
|
||||
PlacementId = 0,
|
||||
ParentGuid = parent.ServerGuid,
|
||||
ParentLocation = 0,
|
||||
Physics = physics,
|
||||
};
|
||||
|
||||
|
|
@ -1192,6 +1216,50 @@ public sealed class EquippedChildProjectionWithdrawalTests
|
|||
private const uint Cell = 0x01010001u;
|
||||
private readonly DeferredLiveEntityRuntimeComponentLifecycle _lifecycle = new();
|
||||
private readonly Setup _setup;
|
||||
private readonly AcDream.Runtime.Session.RuntimeFirstEntryDriveController _firstEntry;
|
||||
|
||||
private sealed class NullCollisionSource
|
||||
: 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()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal ControllerFixture(
|
||||
Func<LiveEntityRecord, ulong, ulong, ExactProjectionWithdrawalOutcome>
|
||||
|
|
@ -1201,10 +1269,24 @@ public sealed class EquippedChildProjectionWithdrawalTests
|
|||
(Cell & 0xFFFF0000u) | 0xFFFFu,
|
||||
new LandBlock(),
|
||||
Array.Empty<WorldEntity>()));
|
||||
Live = LiveEntityRuntimeFixture.Create(
|
||||
EntityObjects = new RuntimeEntityObjectLifetime();
|
||||
EntityObjects.BindEventContext(
|
||||
static () => new AcDream.Runtime.RuntimeGenerationToken(1UL),
|
||||
static () => 1UL);
|
||||
_firstEntry = new AcDream.Runtime.Session.RuntimeFirstEntryDriveController(
|
||||
EntityObjects,
|
||||
new AcDream.Runtime.GameRuntimeClock(),
|
||||
new NullCollisionSource(),
|
||||
() => AcDream.Runtime.Gameplay.PlayerMovementConstructionOptions.Fallback,
|
||||
static _ => new AcDream.Runtime.Gameplay.RuntimeLocalPlayerPhysicsActivationPreparation(
|
||||
0.48f,
|
||||
1.835f,
|
||||
AcDream.Runtime.Gameplay.RuntimeLocalPlayerShadowDisposition.ProvenShapeless));
|
||||
Live = new LiveEntityRuntime(
|
||||
Spatial,
|
||||
new DelegateLiveEntityResourceLifecycle(_ => { }, _ => { }),
|
||||
_lifecycle);
|
||||
_lifecycle,
|
||||
EntityObjects);
|
||||
_setup = new Setup
|
||||
{
|
||||
HoldingLocations =
|
||||
|
|
@ -1231,6 +1313,21 @@ public sealed class EquippedChildProjectionWithdrawalTests
|
|||
}
|
||||
|
||||
internal GpuWorldState Spatial { get; } = new();
|
||||
internal RuntimeEntityObjectLifetime EntityObjects { get; }
|
||||
|
||||
/// <summary>
|
||||
/// C3c: completes fresh residences through the first-entry
|
||||
/// conductors (a celless route performs no SetPosition, so its drain
|
||||
/// completes synchronously), releasing the lease so legacy direct
|
||||
/// CreateParent/Parent application stays valid post-flip. The
|
||||
/// controller is constructed lazily but binds no notification of its
|
||||
/// own tracking value until first use, so it sweeps active leases
|
||||
/// directly instead.
|
||||
/// </summary>
|
||||
internal void CompleteFirstEntry()
|
||||
{
|
||||
_firstEntry.DriveAll();
|
||||
}
|
||||
internal ClientObjectTable Objects { get; } = new();
|
||||
internal EntityEffectPoseRegistry Poses { get; } = new();
|
||||
internal LiveEntityRuntime Live { get; }
|
||||
|
|
@ -1254,7 +1351,13 @@ public sealed class EquippedChildProjectionWithdrawalTests
|
|||
{
|
||||
WorldSession.EntitySpawn spawn = SpawnData(guid, generation);
|
||||
if (!hasPosition)
|
||||
spawn = spawn with { Position = null };
|
||||
{
|
||||
spawn = spawn with
|
||||
{
|
||||
Position = null,
|
||||
Physics = spawn.Physics!.Value with { Position = null },
|
||||
};
|
||||
}
|
||||
if (!hasSetup)
|
||||
spawn = spawn with { SetupTableId = null };
|
||||
return Assert.IsType<RuntimeEntityRecord>(
|
||||
|
|
@ -1288,21 +1391,59 @@ public sealed class EquippedChildProjectionWithdrawalTests
|
|||
return record;
|
||||
}
|
||||
|
||||
internal static WorldSession.EntitySpawn SpawnData(uint guid, ushort generation) => new(
|
||||
guid,
|
||||
new CreateObject.ServerPosition(
|
||||
Cell, 0f, 0f, 0f, 1f, 0f, 0f, 0f),
|
||||
0x02000001u,
|
||||
Array.Empty<CreateObject.AnimPartChange>(),
|
||||
Array.Empty<CreateObject.TextureChange>(),
|
||||
Array.Empty<CreateObject.SubPaletteSwap>(),
|
||||
BasePaletteId: null,
|
||||
ObjScale: null,
|
||||
Name: "attached fixture",
|
||||
ItemType: null,
|
||||
MotionState: null,
|
||||
MotionTableId: null,
|
||||
InstanceSequence: generation);
|
||||
internal static WorldSession.EntitySpawn SpawnData(uint guid, ushort generation)
|
||||
{
|
||||
// C3c: residence admission freezes the raw create and requires
|
||||
// the flattened parser projections to agree with the nested
|
||||
// PhysicsDesc block (HasConsistentCreateIdentityAndParent).
|
||||
var position = new CreateObject.ServerPosition(
|
||||
Cell, 0f, 0f, 0f, 1f, 0f, 0f, 0f);
|
||||
var physics = new PhysicsSpawnData(
|
||||
RawState: 0u,
|
||||
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: new PhysicsTimestamps(
|
||||
Position: 0,
|
||||
Movement: 0,
|
||||
State: 0,
|
||||
Vector: 0,
|
||||
Teleport: 0,
|
||||
ServerControlledMove: 0,
|
||||
ForcePosition: 0,
|
||||
ObjDesc: 0,
|
||||
Instance: generation));
|
||||
return new WorldSession.EntitySpawn(
|
||||
guid,
|
||||
position,
|
||||
0x02000001u,
|
||||
Array.Empty<CreateObject.AnimPartChange>(),
|
||||
Array.Empty<CreateObject.TextureChange>(),
|
||||
Array.Empty<CreateObject.SubPaletteSwap>(),
|
||||
BasePaletteId: null,
|
||||
ObjScale: null,
|
||||
Name: "attached fixture",
|
||||
ItemType: null,
|
||||
MotionState: null,
|
||||
MotionTableId: null,
|
||||
InstanceSequence: generation,
|
||||
Physics: physics);
|
||||
}
|
||||
|
||||
internal void InstallAttached(
|
||||
LiveEntityRecord parent,
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public sealed class LiveAppearanceAnimationTests
|
|||
ItemType: null,
|
||||
MotionState: null,
|
||||
MotionTableId: null,
|
||||
InstanceSequence: 1);
|
||||
InstanceSequence: 1).WithConsistentPhysics();
|
||||
LiveEntityRecord record = runtime.RegisterAndMaterializeProjection(
|
||||
spawn,
|
||||
id => Entity(id, 0x01000001u, guid));
|
||||
|
|
@ -175,7 +175,7 @@ public sealed class LiveAppearanceAnimationTests
|
|||
Assert.Equal(1, registry.RetainedRegistrationCount);
|
||||
}
|
||||
|
||||
private static WorldSession.EntitySpawn Spawn(uint guid, uint cell, ushort instance) => new(
|
||||
private static WorldSession.EntitySpawn Spawn(uint guid, uint cell, ushort instance) => new WorldSession.EntitySpawn(
|
||||
guid,
|
||||
new CreateObject.ServerPosition(cell, 1f, 1f, 1f, 1f, 0f, 0f, 0f),
|
||||
0x02000010u,
|
||||
|
|
@ -188,7 +188,7 @@ public sealed class LiveAppearanceAnimationTests
|
|||
ItemType: null,
|
||||
MotionState: null,
|
||||
MotionTableId: null,
|
||||
InstanceSequence: instance);
|
||||
InstanceSequence: instance).WithConsistentPhysics();
|
||||
|
||||
private static WorldEntity Entity(
|
||||
uint id,
|
||||
|
|
|
|||
|
|
@ -74,7 +74,22 @@ public sealed class LiveEntityCreateSupersessionRecoveryTests
|
|||
publishAppearance: _ =>
|
||||
{
|
||||
operations.Add("appearance");
|
||||
runtime.RegisterLiveEntity(Spawn() with { Name = "fresher" });
|
||||
// C3c: a fresher same-generation CreateObject no longer
|
||||
// advances create authority at registration — while a
|
||||
// residence is active it is FIFO-staged and its authority
|
||||
// advance lands at the executor drain's WeenieDescription
|
||||
// stage (AdvanceCreateAuthority). Model that exact advance
|
||||
// directly so the between-stage revalidation guard stays
|
||||
// covered.
|
||||
// C3c-R1 F3: honest MODEL of the executor drain's advance
|
||||
// (ApplyWeenieDescriptionAction — the sole production site,
|
||||
// source-pinned by C3cR1F3DriftModelSourcePinTests); a
|
||||
// nested production OnCreate can no longer reach it —
|
||||
// post-residence registration is description-only
|
||||
// (RuntimeEntityObjectLifetime :660-665,
|
||||
// !beginInitialResidence gate) and ConsumeExecuted already
|
||||
// removed the completed residence entry.
|
||||
record.Canonical.AdvanceCreateAuthority();
|
||||
return true;
|
||||
},
|
||||
publishCurrentSnapshot: _ => operations.Add("current"),
|
||||
|
|
@ -315,7 +330,7 @@ public sealed class LiveEntityCreateSupersessionRecoveryTests
|
|||
return record;
|
||||
}
|
||||
|
||||
private static WorldSession.EntitySpawn Spawn() => new(
|
||||
private static WorldSession.EntitySpawn Spawn() => new WorldSession.EntitySpawn(
|
||||
Guid,
|
||||
new CreateObject.ServerPosition(
|
||||
Cell,
|
||||
|
|
@ -336,7 +351,7 @@ public sealed class LiveEntityCreateSupersessionRecoveryTests
|
|||
ItemType: null,
|
||||
MotionState: null,
|
||||
MotionTableId: null,
|
||||
InstanceSequence: 1);
|
||||
InstanceSequence: 1).WithConsistentPhysics();
|
||||
|
||||
private static LiveEntityAnimationState AnimationState(
|
||||
WorldEntity entity,
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ public sealed class LiveEntityLightControllerTests
|
|||
InstanceSequence: 1,
|
||||
MovementSequence: 1,
|
||||
ServerControlSequence: 1,
|
||||
PositionSequence: 1);
|
||||
PositionSequence: 1).WithConsistentPhysics();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue