using System.Numerics;
using AcDream.Content;
using AcDream.Content.Pak;
using AcDream.Core.Net;
using AcDream.Core.Net.Messages;
using AcDream.Core.Physics;
using AcDream.Runtime;
using AcDream.Runtime.Entities;
using AcDream.Runtime.Physics;
using AcDream.Runtime.Session;
namespace AcDream.App.Tests.Physics;
///
/// C4 route 4b-2: a REAL
/// over a bare
/// , so App-layer acceptance tests
/// drive the production far-snap path end to end instead of simulating it.
/// Mirrors RuntimeRemotePlacementDriveControllerTests' own fixture.
///
internal sealed class RemotePlacementDriveFixture : IDisposable
{
internal const uint SourceLandblock = 0xB1000000u;
internal const uint SourceCell = SourceLandblock | 0x0001u;
internal const uint DestinationLandblock = 0xB2000000u;
internal const uint DestinationCell = DestinationLandblock | 0x0001u;
/// The +X world offset
/// gives the destination landblock, so a committed placement's world
/// position is the authored local position plus this.
internal static readonly Vector3 DestinationWorldOffset = new(192f, 0f, 0f);
internal const float SpawnHeight = 7f;
private readonly ServiceWindow _window = new();
internal RemotePlacementDriveFixture()
{
Lifetime = new RuntimeEntityObjectLifetime(FlatEngine());
Drive = new RuntimeRemotePlacementDriveController(
Lifetime,
new GameRuntimeClock(),
new UnusedCollisionSource(),
_window);
}
internal RuntimeEntityObjectLifetime Lifetime { get; }
internal RuntimeRemotePlacementDriveController Drive { get; }
internal void AllowDestination() => _window.Allow(DestinationLandblock);
///
/// Commits the destination landblock's collision generation and observes
/// the source world frame once, so a placement into
/// can actually resolve.
///
internal void PublishDestinationCollision()
{
var heights = new byte[81];
Array.Fill(heights, (byte)SpawnHeight);
var heightTable = new float[256];
for (int index = 0; index < heightTable.Length; index++)
heightTable[index] = index;
Lifetime.Physics.ObserveLocalWorldFrame(
SourceCell, teleportAdvanced: false);
Lifetime.Physics.SetPosition.BeginCollisionGeneration(
DestinationLandblock, 1UL);
Lifetime.Physics.Engine.AddLandblock(
DestinationLandblock,
new TerrainSurface(heights, heightTable),
Array.Empty(),
Array.Empty(),
worldOffsetX: DestinationWorldOffset.X,
worldOffsetY: DestinationWorldOffset.Y);
Lifetime.Physics.SetPosition.CommitCollisionGeneration(
DestinationLandblock, 1UL, ready: true);
}
///
/// Registers one remote incarnation with a canonical body, its shared
/// , and the accepted destination already merged
/// onto its snapshot — exactly the state
/// RuntimeEntityObjectLifetime.TryApplyPosition leaves behind
/// before OnPosition routes the packet.
///
internal (RuntimeEntityRecord Record, RemoteMotion Remote, PhysicsBody Body)
AddRemote(uint guid, Vector3 destination)
{
RuntimeEntityRecord record = Lifetime.RegisterEntity(
Spawn(guid)).Canonical!;
Lifetime.Entities.SetFinalPhysicsState(record, PhysicsStateFlags.Gravity);
Lifetime.Entities.SetFullCell(
record, SourceCell, (SourceCell & 0xFFFF0000u) | 0xFFFFu);
var body = new PhysicsBody
{
Position = new Vector3(10f, 10f, SpawnHeight),
Orientation = Quaternion.Identity,
LastUpdateTime = 1d,
State = PhysicsStateFlags.Gravity,
// AP-140 (retired 2026-08-04): the remote stands on the flat
// source landblock, so the sweep's SetPositionInternal commit
// would derive CONTACT (`contact_plane_valid` @0x00515430) and
// ON_WALKABLE (`contact_plane.N.z >= floor_z`
// @0x00515465-0x0051548E) for it. Both bits now have to be
// present, because the accepted-Position routing gates read
// retail's CONTACT predicate
// (`InterpolationManager::adjust_offset` @0x00555D52) rather than
// the client `Airborne` walkability flag: a body with a bare
// `Active` transient state is in FREE FLIGHT and every routing
// test would take the free-flight snap arm. Tests that want free
// flight clear these explicitly.
TransientState = TransientStateFlags.Active
| TransientStateFlags.Contact
| TransientStateFlags.OnWalkable,
};
body.SnapToCell(SourceCell, body.Position, body.Position);
Lifetime.Entities.SetPhysicsBody(record, body);
record.ObjectClock.Activate();
Lifetime.Physics.AcknowledgeSpatialProjection(record, spatial: true);
record.Snapshot = record.Snapshot with
{
Position = new CreateObject.ServerPosition(
DestinationCell,
destination.X,
destination.Y,
destination.Z,
1f,
0f,
0f,
0f),
};
RemoteMotion remote = Lifetime.Physics.GetOrCreateRemoteMotion(record);
// Past AP-87's firstUp hint, so the 4 m body-to-target guard is the
// condition under test rather than the first-sample one.
remote.LastServerPosTime = 1_700_000_000d;
return (record, remote, body);
}
///
/// Stands in for the production placement-projection subscription this
/// bare fixture never wires.
///
internal void DrainPlacementFifo()
{
while (Lifetime.Physics.SetPosition.TryPeekProjection(
out RuntimePlacementProjectionSnapshot head))
{
if (!Lifetime.Physics.SetPosition.AcknowledgeProjection(head.Token))
break;
}
}
internal int LiveOperationCount =>
Lifetime.Physics.CaptureOwnership().SetPositionOperationCount;
internal int RemotePlacementLedger =>
Lifetime.CaptureOwnership().RemotePlacementDrivePendingCount;
public void Dispose() => Lifetime.Dispose();
private static WorldSession.EntitySpawn Spawn(uint guid) => new(
guid,
new CreateObject.ServerPosition(
SourceCell, 10f, 10f, SpawnHeight, 1f, 0f, 0f, 0f),
SetupTableId: null,
AnimPartChanges: Array.Empty(),
TextureChanges: Array.Empty(),
SubPalettes: Array.Empty(),
BasePaletteId: null,
ObjScale: null,
Name: "remote",
ItemType: null,
MotionState: null,
MotionTableId: 0x09000001u);
private static PhysicsEngine FlatEngine()
{
var engine = new PhysicsEngine { DataCache = new PhysicsDataCache() };
engine.AddLandblock(
SourceLandblock,
new TerrainSurface(new byte[81], new float[256]),
Array.Empty(),
Array.Empty(),
worldOffsetX: 0f,
worldOffsetY: 0f);
return engine;
}
private sealed class ServiceWindow : IRuntimeRemotePlacementServiceWindow
{
private readonly HashSet _within = [];
internal void Allow(uint landblockId) =>
_within.Add((landblockId & 0xFFFF0000u) | 0xFFFFu);
public bool IsWithinServiceWindow(uint landblockId) =>
_within.Contains((landblockId & 0xFFFF0000u) | 0xFFFFu);
}
private sealed class UnusedCollisionSource : IPreparedCollisionSource
{
public PreparedAssetPresence ProbeCollision(
PakAssetType type,
uint sourceFileId) => PreparedAssetPresence.Available;
public PreparedCollisionReadResult ReadSetupCollision(
uint sourceFileId,
CancellationToken cancellationToken = default) =>
PreparedCollisionReadResult.Missing;
public PreparedCollisionReadResult ReadGfxObjCollision(
uint sourceFileId,
CancellationToken cancellationToken = default) =>
throw new NotSupportedException();
public PreparedCollisionReadResult
ReadCellStructureCollision(
uint sourceFileId,
CancellationToken cancellationToken = default) =>
throw new NotSupportedException();
public PreparedCollisionReadResult ReadEnvCellTopology(
uint sourceFileId,
CancellationToken cancellationToken = default) =>
throw new NotSupportedException();
public PreparedCollisionSourceStats CollisionStats => default;
public void Dispose()
{
}
}
}