diff --git a/tests/AcDream.App.Tests/LiveEntityRuntimeFixture.cs b/tests/AcDream.App.Tests/LiveEntityRuntimeFixture.cs
index 635459f7..d062412c 100644
--- a/tests/AcDream.App.Tests/LiveEntityRuntimeFixture.cs
+++ b/tests/AcDream.App.Tests/LiveEntityRuntimeFixture.cs
@@ -79,6 +79,22 @@ internal static class LiveEntityRuntimeFixture
worldOffsetY: 0f);
lifetime.Physics.SetPosition.CommitCollisionGeneration(
landblockId & 0xFFFF0000u, 1UL, ready: true);
+
+ // 670f307c: Create frames are landblock-local, so a first-entry
+ // conductor resolves them through Runtime's world frame
+ // (RuntimeSetPositionState.PrepareMover:1526-1544) and parks on
+ // RetrySetupUnavailable until that frame exists. Only the accepted
+ // local-player Create publishes it
+ // (RuntimeEntityObjectLifetime.RegisterEntityCore:558-570 ->
+ // RuntimePhysicsState.ObserveLocalWorldFrame), and production always
+ // registers the player before driving any broadcast Create. Model
+ // that ambient world fact beside the resident landblock above rather
+ // than registering a second entity, which would consume a local
+ // entity id and perturb every exact-ledger assertion built on this
+ // fixture. RuntimeWorldFrameTests covers the real ordering.
+ lifetime.Physics.ObserveLocalWorldFrame(
+ (landblockId & 0xFFFF0000u) | 0x0001u,
+ teleportAdvanced: false);
var runtime = new LiveEntityRuntime(spatial, resources, lifetime);
var movement = new AcDream.Runtime.Gameplay
.RuntimeLocalPlayerMovementState();
@@ -111,6 +127,7 @@ internal static class LiveEntityRuntimeFixture
lifetime.Placements,
static () => new AcDream.Runtime.RuntimeGenerationToken(1UL),
new AckOnlyPlacementSink(runtime));
+
return new DrivenLiveEntityRuntime
{
Runtime = runtime,
diff --git a/tests/AcDream.App.Tests/World/LiveEntityHydrationControllerTests.cs b/tests/AcDream.App.Tests/World/LiveEntityHydrationControllerTests.cs
index 7b51ef7d..a0f5c329 100644
--- a/tests/AcDream.App.Tests/World/LiveEntityHydrationControllerTests.cs
+++ b/tests/AcDream.App.Tests/World/LiveEntityHydrationControllerTests.cs
@@ -2091,6 +2091,19 @@ public sealed class LiveEntityHydrationControllerTests
worldOffsetY: 0f);
EntityObjects.Physics.SetPosition.CommitCollisionGeneration(
Cell & 0xFFFF0000u, 1UL, ready: true);
+
+ // 670f307c: Create frames are landblock-local, so a first-entry
+ // conductor resolves them through Runtime's world frame
+ // (RuntimeSetPositionState.PrepareMover:1526-1544) and parks on
+ // RetrySetupUnavailable until it exists. Only the accepted
+ // local-player Create publishes it
+ // (RuntimeEntityObjectLifetime.RegisterEntityCore:558-570), and
+ // production always registers the player first. Model that
+ // ambient world fact beside the resident landblock above so
+ // these hydration tests keep their exact single-entity ledgers.
+ EntityObjects.Physics.ObserveLocalWorldFrame(
+ Cell,
+ teleportAdvanced: false);
Movement = new AcDream.Runtime.Gameplay.RuntimeLocalPlayerMovementState();
IdentityState = new AcDream.Runtime.Gameplay.RuntimeLocalPlayerIdentityState();
var publication = new AcDream.Runtime.Gameplay
diff --git a/tests/AcDream.App.Tests/World/RuntimeFirstEntryHostIntegrationTests.cs b/tests/AcDream.App.Tests/World/RuntimeFirstEntryHostIntegrationTests.cs
index 829a1c25..a5834879 100644
--- a/tests/AcDream.App.Tests/World/RuntimeFirstEntryHostIntegrationTests.cs
+++ b/tests/AcDream.App.Tests/World/RuntimeFirstEntryHostIntegrationTests.cs
@@ -450,6 +450,22 @@ public sealed class RuntimeFirstEntryHostIntegrationTests
worldOffsetY: 0f);
EntityObjects.Physics.SetPosition.CommitCollisionGeneration(
Cell & 0xFFFF0000u, 1UL, ready: true);
+
+ // 670f307c: a first-entry conductor resolves its landblock-local
+ // Create origin through Runtime's world frame
+ // (RuntimeSetPositionState.PrepareMover:1526-1544) and parks on
+ // RetrySetupUnavailable until it exists. Production publishes it
+ // from the accepted local-player Create
+ // (RuntimeEntityObjectLifetime.RegisterEntityCore:558-570). The
+ // playerGuid: 0 cases below deliberately model a REMOTE entity,
+ // so the frame must come from the ambient world here exactly as
+ // it does in LiveEntityRuntimeFixture — otherwise the graphical
+ // and direct hosts under comparison would differ only by a
+ // missing precondition. RuntimeWorldFrameTests covers the real
+ // local-player ordering.
+ EntityObjects.Physics.ObserveLocalWorldFrame(
+ Cell,
+ teleportAdvanced: false);
Movement = new AcDream.Runtime.Gameplay
.RuntimeLocalPlayerMovementState();
var runtimeIdentity = new AcDream.Runtime.Gameplay
diff --git a/tests/AcDream.Runtime.Tests/Entities/RuntimeRemoteFirstEntryStateTests.cs b/tests/AcDream.Runtime.Tests/Entities/RuntimeRemoteFirstEntryStateTests.cs
index 7f54988d..abf19a47 100644
--- a/tests/AcDream.Runtime.Tests/Entities/RuntimeRemoteFirstEntryStateTests.cs
+++ b/tests/AcDream.Runtime.Tests/Entities/RuntimeRemoteFirstEntryStateTests.cs
@@ -923,6 +923,21 @@ public sealed class RuntimeRemoteFirstEntryStateTests
var generation = new RuntimeGenerationToken(1UL);
Lifetime.BindEventContext(() => generation, static () => 1UL);
+ // 670f307c: CreateObject frames are landblock-local, so a remote
+ // conductor's PrepareMover resolves them through Runtime's world
+ // frame (RuntimeSetPositionState.PrepareMover:1526-1544) and
+ // returns RetrySetupUnavailable until that frame exists. Only the
+ // accepted local-player Create publishes it
+ // (RuntimeEntityObjectLifetime.RegisterEntityCore:558-570 ->
+ // RuntimePhysicsState.ObserveLocalWorldFrame) — production always
+ // registers the player before any broadcast Create is driven.
+ // Model that ambient world fact here, alongside the resident
+ // terrain above, rather than registering a second entity that
+ // would perturb every ownership-ledger assertion in this class.
+ Lifetime.Physics.ObserveLocalWorldFrame(
+ Cell,
+ teleportAdvanced: false);
+
// The SAME conductor instance RuntimeEntityObjectLifetime itself
// constructs and wires into the residence's multicast retirement
// fan-out and BeginSessionClear — never a standalone copy — so
diff --git a/tests/AcDream.Runtime.Tests/Physics/RuntimeWorldFrameTests.cs b/tests/AcDream.Runtime.Tests/Physics/RuntimeWorldFrameTests.cs
new file mode 100644
index 00000000..2f0f35f3
--- /dev/null
+++ b/tests/AcDream.Runtime.Tests/Physics/RuntimeWorldFrameTests.cs
@@ -0,0 +1,236 @@
+using System.Numerics;
+using AcDream.Core.Net;
+using AcDream.Core.Net.Messages;
+using AcDream.Core.Physics;
+using AcDream.Runtime.Entities;
+
+namespace AcDream.Runtime.Tests.Physics;
+
+///
+/// Coverage for the world-frame owner introduced by 670f307c
+/// (RuntimePhysicsState.ObserveLocalWorldFrame /
+/// TryGetWorldFrameOffset). CreateObject and Position frames are
+/// landblock-local; Runtime converts them into the one streamed world frame
+/// before SetPosition. That mechanism shipped with no tests, and its
+/// absence silently parked every remote first-entry placement on
+/// RetrySetupUnavailable, so these pin its exact contract:
+/// the local player publishes it, remotes never do, ordinary movement across
+/// a landblock boundary must NOT rebase it, and an accepted teleport must.
+///
+/// The "no rebase on ordinary movement" rule is not an arbitrary choice — it
+/// matches App's own coordinate owner, LiveWorldOriginState, whose
+/// Recenter is called only from
+/// StreamingOriginRecenterCoordinator.Advance at a teleport boundary.
+/// If these two ever disagree, remote objects are placed at a multiple of
+/// 192 m from where the world is streamed.
+///
+public sealed class RuntimeWorldFrameTests
+{
+ private const uint CenterLandblock = 0xA9B60000u;
+ private const uint CenterCell = CenterLandblock | 0x0001u;
+
+ [Fact]
+ public void LocalPlayerCreate_PublishesTheWorldFrameAtItsLandblock()
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ BindGeneration(lifetime);
+
+ Assert.False(lifetime.Physics.TryGetWorldFrameOffset(
+ CenterCell,
+ out _,
+ out _));
+
+ lifetime.RegisterEntityWithInitialResidence(
+ Spawn(0x50000001u, CenterCell),
+ isLocalPlayer: true);
+
+ Assert.True(lifetime.Physics.TryGetWorldFrameOffset(
+ CenterCell,
+ out float offsetX,
+ out float offsetY));
+ Assert.Equal(0f, offsetX);
+ Assert.Equal(0f, offsetY);
+ }
+
+ [Fact]
+ public void RemoteCreate_NeverPublishesTheWorldFrame()
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ BindGeneration(lifetime);
+
+ lifetime.RegisterEntityWithInitialResidence(
+ Spawn(0x70000001u, CenterCell),
+ isLocalPlayer: false);
+
+ // Without the frame a remote conductor must park rather than commit a
+ // landblock-local origin as if it were already a world position.
+ Assert.False(lifetime.Physics.TryGetWorldFrameOffset(
+ CenterCell,
+ out _,
+ out _));
+ }
+
+ [Theory]
+ // One landblock east is +192 m on X; one north is +192 m on Y.
+ [InlineData(0xAAB60001u, 192f, 0f)]
+ [InlineData(0xA8B60001u, -192f, 0f)]
+ [InlineData(0xA9B70001u, 0f, 192f)]
+ [InlineData(0xA9B50001u, 0f, -192f)]
+ public void NeighbouringLandblocks_ConvertAt192MetresPerStep(
+ uint cellId,
+ float expectedX,
+ float expectedY)
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ BindGeneration(lifetime);
+ lifetime.Physics.ObserveLocalWorldFrame(
+ CenterCell,
+ teleportAdvanced: false);
+
+ Assert.True(lifetime.Physics.TryGetWorldFrameOffset(
+ cellId,
+ out float offsetX,
+ out float offsetY));
+ Assert.Equal(expectedX, offsetX);
+ Assert.Equal(expectedY, offsetY);
+ }
+
+ [Fact]
+ public void OrdinaryMovementAcrossALandblockBoundary_DoesNotRebaseTheFrame()
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ BindGeneration(lifetime);
+ lifetime.Physics.ObserveLocalWorldFrame(
+ CenterCell,
+ teleportAdvanced: false);
+
+ // Walking east into the next landblock: an accepted Position, but no
+ // teleport. The streamed world keeps its origin, so the frame must
+ // stay put and that landblock must still convert to +192 m.
+ lifetime.Physics.ObserveLocalWorldFrame(
+ 0xAAB60001u,
+ teleportAdvanced: false);
+
+ Assert.True(lifetime.Physics.TryGetWorldFrameOffset(
+ 0xAAB60001u,
+ out float offsetX,
+ out float offsetY));
+ Assert.Equal(192f, offsetX);
+ Assert.Equal(0f, offsetY);
+ }
+
+ [Fact]
+ public void AcceptedTeleport_RebasesTheFrameOnTheDestination()
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ BindGeneration(lifetime);
+ lifetime.Physics.ObserveLocalWorldFrame(
+ CenterCell,
+ teleportAdvanced: false);
+
+ lifetime.Physics.ObserveLocalWorldFrame(
+ 0xAAB60001u,
+ teleportAdvanced: true);
+
+ // The destination is now the origin, and the departure landblock sits
+ // one step west of it.
+ Assert.True(lifetime.Physics.TryGetWorldFrameOffset(
+ 0xAAB60001u,
+ out float destinationX,
+ out float destinationY));
+ Assert.Equal(0f, destinationX);
+ Assert.Equal(0f, destinationY);
+
+ Assert.True(lifetime.Physics.TryGetWorldFrameOffset(
+ CenterCell,
+ out float sourceX,
+ out _));
+ Assert.Equal(-192f, sourceX);
+ }
+
+ [Fact]
+ public void AZeroCellIdNeitherPublishesNorResolves()
+ {
+ using var lifetime = new RuntimeEntityObjectLifetime();
+ BindGeneration(lifetime);
+
+ lifetime.Physics.ObserveLocalWorldFrame(0u, teleportAdvanced: false);
+ Assert.False(lifetime.Physics.TryGetWorldFrameOffset(
+ CenterCell,
+ out _,
+ out _));
+
+ lifetime.Physics.ObserveLocalWorldFrame(
+ CenterCell,
+ teleportAdvanced: false);
+ Assert.False(lifetime.Physics.TryGetWorldFrameOffset(
+ 0u,
+ out _,
+ out _));
+ }
+
+ private static void BindGeneration(RuntimeEntityObjectLifetime lifetime)
+ {
+ var generation = new RuntimeGenerationToken(1UL);
+ lifetime.BindEventContext(() => generation, static () => 1UL);
+ }
+
+ private static WorldSession.EntitySpawn Spawn(uint guid, uint cell)
+ {
+ var position = new CreateObject.ServerPosition(
+ cell, 1f, 2f, 3f, 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: null,
+ MotionTableId: 0x09000001u,
+ SoundTableId: null,
+ PhysicsScriptTableId: null,
+ Parent: null,
+ Children: null,
+ Scale: 1f,
+ Friction: 0.5f,
+ Elasticity: 0.05f,
+ Translucency: null,
+ Velocity: Vector3.Zero,
+ Acceleration: null,
+ AngularVelocity: Vector3.Zero,
+ DefaultScriptType: null,
+ DefaultScriptIntensity: null,
+ Timestamps: timestamps);
+ return new WorldSession.EntitySpawn(
+ Guid: guid,
+ Position: position,
+ SetupTableId: null,
+ AnimPartChanges: Array.Empty(),
+ TextureChanges: Array.Empty(),
+ SubPalettes: Array.Empty(),
+ BasePaletteId: null,
+ ObjScale: 1f,
+ Name: "world-frame-fixture",
+ ItemType: null,
+ MotionState: null,
+ MotionTableId: 0x09000001u,
+ PhysicsState: physics.RawState,
+ ObjectDescriptionFlags: 0x8u,
+ Friction: 0.5f,
+ Elasticity: 0.05f,
+ InstanceSequence: 1,
+ MovementSequence: 1,
+ ServerControlSequence: 1,
+ PositionSequence: 1,
+ Physics: physics);
+ }
+}
diff --git a/tests/AcDream.Runtime.Tests/Session/RuntimeLiveEntitySessionControllerTests.cs b/tests/AcDream.Runtime.Tests/Session/RuntimeLiveEntitySessionControllerTests.cs
index 123e68ac..d30b6a9e 100644
--- a/tests/AcDream.Runtime.Tests/Session/RuntimeLiveEntitySessionControllerTests.cs
+++ b/tests/AcDream.Runtime.Tests/Session/RuntimeLiveEntitySessionControllerTests.cs
@@ -400,6 +400,19 @@ public sealed class RuntimeLiveEntitySessionControllerTests
worldOffsetY: 0f);
runtime.EntityObjects.Physics.SetPosition.CommitCollisionGeneration(
landblockId, 1UL, ready: true);
+
+ // 670f307c: a remote conductor resolves its landblock-local Create
+ // origin through Runtime's world frame
+ // (RuntimeSetPositionState.PrepareMover:1526-1544) and parks on
+ // RetrySetupUnavailable until that frame exists. Production publishes
+ // it from the accepted local-player Create
+ // (RuntimeEntityObjectLifetime.RegisterEntityCore:558-570), which
+ // always precedes broadcast Creates. Model it with the resident
+ // landblock it belongs to, so these direct-sink tests keep asserting
+ // exact single-entity ledgers.
+ runtime.EntityObjects.Physics.ObserveLocalWorldFrame(
+ landblockId | 0x0001u,
+ teleportAdvanced: false);
}
private static RuntimeFirstEntryDriveController CreateDrive(