using System.Numerics;
using AcDream.App.Physics;
using AcDream.Core.Net.Messages;
using AcDream.Core.Physics;
using AcDream.Core.Physics.Motion;
using AcDream.Core.World;
using AcDream.Runtime;
using AcDream.Runtime.Entities;
using AcDream.Runtime.Physics;
namespace AcDream.App.Tests.Physics;
///
/// C4 route 4a: behavioural proof that the generic top-of-OnPosition
/// render-pose write no longer double-writes a remote whose accepted Position
/// classifies to
/// or
/// , and
/// still writes it for every classification route 4a does not own.
///
///
/// These tests call the PRODUCTION decision-and-mutation entry point
/// ()
/// against a real and real
/// output. Nothing
/// is re-derived in the test body: revert the suppression inside that method
/// and the two suppression tests fail; widen it to route is not null
/// and four of the five legacy-write tests fail (the fifth,
/// ,
/// passes a null route and so survives that particular widening — it
/// discriminates the OTHER direction, an over-narrow guard). That is the gap
/// #292 recorded for route 2's source-text pin.
///
///
///
/// The suite also pins the ORDERING carve-out
/// ():
/// an airborne body's contact packet classifies Interpolate, so
/// precedence — not classification — is the only thing keeping a landing
/// creature planting instead of gliding.
///
///
public sealed class LiveEntityNetworkRemoteSteadyStateIntegrationTests
{
private const uint OldCell = 0x01010001u;
private const uint WireCell = 0x01010002u;
private static readonly Vector3 OldPosition = new(5f, 5f, 5f);
private static readonly Vector3 WirePosition = new(6f, 7f, 8f);
[Fact]
public void NoPositionOperation_LeavesTheRenderEntityPoseAndCellUntouched()
{
// Retail's airborne no-op (D1): MoveOrTeleport @0x00516330 returns 0
// for arg4 == 0 and writes nothing. CommittedCellId is nonzero so the
// cell-less SetPosition branch cannot be what is selected here.
RuntimeAuthoritativePositionRoute route =
Classify(hasContact: false, playerDistance: 1f);
Assert.Equal(
RuntimeAuthoritativePositionDisposition.NoPositionOperation,
route.Disposition);
AssertRenderPoseSuppressed(route);
}
[Fact]
public void Interpolate_LeavesTheRenderEntityPoseAndCellUntouched()
{
// Retail's near InterpolateTo queue @0x005163AF. The resolved body,
// not the raw wire packet, is the branch tail's only writer of the
// render entity — writing the wire pose here first would be the
// second writer route 2's original defect consisted of.
RuntimeAuthoritativePositionRoute route =
Classify(hasContact: true, playerDistance: 10f);
Assert.Equal(
RuntimeAuthoritativePositionDisposition.Interpolate,
route.Disposition);
AssertRenderPoseSuppressed(route);
}
[Fact]
public void FarSetPositionSimple_StillTakesTheLegacyRenderPoseWrite()
{
RuntimeAuthoritativePositionRoute route =
Classify(hasContact: true, playerDistance: 200f);
Assert.Equal(
RuntimeAuthoritativePositionDisposition.SetPositionSimple,
route.Disposition);
AssertRenderPoseWritten(route);
}
[Fact]
public void CellLessRemote_StillTakesTheLegacyRenderPoseWrite()
{
RuntimeAuthoritativePositionRoute route = Classify(
hasContact: false,
playerDistance: 1f,
committedCellId: 0u);
Assert.Equal(
RuntimeAuthoritativePositionDisposition.SetPosition,
route.Disposition);
AssertRenderPoseWritten(route);
}
[Fact]
public void RejectedAuthority_StillTakesTheLegacyRenderPoseWrite()
{
RuntimeAuthoritativePositionRoute route = Classify(
hasContact: true,
playerDistance: 10f,
disposition: PositionTimestampDisposition.Rejected);
Assert.Equal(
RuntimeAuthoritativePositionDisposition.RejectedAuthority,
route.Disposition);
AssertRenderPoseWritten(route);
}
[Fact]
public void RejectedData_StillTakesTheLegacyRenderPoseWrite()
{
RuntimeAuthoritativePositionRoute route =
Classify(hasContact: true, playerDistance: float.NaN);
Assert.Equal(
RuntimeAuthoritativePositionDisposition.RejectedData,
route.Disposition);
AssertRenderPoseWritten(route);
}
[Fact]
public void NoClassificationAtAll_StillTakesTheLegacyRenderPoseWrite()
{
// The login window (no local-player controller yet) and a canonical
// record that has not claimed a local id both produce a null route.
// Its doc comment promises the legacy path runs unchanged — so the
// write must still happen.
AssertRenderPoseWritten(null);
}
// ── Ordering: the airborne snap outranks route 4a's near branch ─────────
[Fact]
public void AirborneBodyWithAContactPacket_SnapsInsteadOfEnqueuing()
{
// The NPC LANDING packet: the client still believes the body airborne
// and ACE reports it in contact, close by. That classifies
// Interpolate — and AP-87's conditions are all benign (0.5 m, sampled,
// DR-ticked) — so route 4a's branch would ENQUEUE it. It must SNAP:
// a drudge knocked off a ledge plants, it does not glide to the
// ground over a packet interval.
RuntimeAuthoritativePositionRoute route =
Classify(hasContact: true, playerDistance: 10f);
Assert.Equal(
RuntimeAuthoritativePositionDisposition.Interpolate,
route.Disposition);
using var fixture = new RemotePlacementDriveFixture();
(RuntimeEntityRecord record, RemoteMotion remote, _) =
fixture.AddRemote(0x70005001u, new Vector3(12f, 14f, 7f));
remote.Body.Position = new Vector3(10f, 10f, 5f);
// AP-140 (retired 2026-08-04): free flight is retail's CONTACT_TS-clear
// (`InterpolationManager::adjust_offset` gates on `transient_state & 1`
// @0x00555D52), not the client `Airborne` walkability flag. Both are
// stated so the test reads the same before and after that change.
remote.Airborne = true;
remote.Body.TransientState = TransientStateFlags.Active;
var landing = new Vector3(10.5f, 10f, 5f); // 0.5 m — well within 4 m.
LiveEntityNetworkUpdateController.RemoteContactRouting routing =
LiveEntityNetworkUpdateController.ApplyRemoteContactRouting(
fixture.Drive,
record,
remote,
route,
landing,
Quaternion.Identity,
willBeDrTicked: true);
Assert.Equal(
LiveEntityNetworkUpdateController.RemoteContactArm.AirborneSnap,
routing.Arm);
Assert.Equal(landing, remote.Body.Position);
}
[Fact]
public void GroundedBodyWithANearContactPacket_TakesRoute4asInterpolateBranch()
{
// The same packet on a body the client already considers grounded is
// route 4a's own branch: queued for the per-tick catch-up, body
// untouched. This is what proves the airborne test above is a
// PRECEDENCE carve-out and not a blanket disable of route 4a.
using var fixture = new RemotePlacementDriveFixture();
(RuntimeEntityRecord record, RemoteMotion remote, _) =
fixture.AddRemote(0x70005002u, new Vector3(12f, 14f, 7f));
remote.Body.Position = new Vector3(10f, 10f, 5f);
remote.Airborne = false;
var target = new Vector3(10.5f, 10f, 5f);
LiveEntityNetworkUpdateController.RemoteContactRouting routing =
LiveEntityNetworkUpdateController.ApplyRemoteContactRouting(
fixture.Drive,
record,
remote,
Classify(hasContact: true, playerDistance: 10f),
target,
Quaternion.Identity,
willBeDrTicked: true);
Assert.Equal(
LiveEntityNetworkUpdateController.RemoteContactArm
.SteadyStateInterpolate,
routing.Arm);
Assert.Equal(new Vector3(10f, 10f, 5f), remote.Body.Position);
}
// ── AP-140 (retired 2026-08-04): the gate's predicate is CONTACT ────────
///
/// The divergence AP-140 recorded, as behaviour. A remote in CONTACT with
/// a NON-walkable face — sliding down a steep roof — is
/// Airborne == true by the client flag's own definition
/// (!Body.OnWalkable), and used to be hard-snapped at
/// UpdatePosition cadence. Retail interpolates it:
/// InterpolationManager::adjust_offset @0x00555D30 gates its entire
/// body on transient_state & 1 @0x00555D52, and bit 0 is
/// CONTACT_TS (acclient.h:3690), not ON_WALKABLE_TS (0x2).
///
///
/// This is the ONE state on which the two predicates disagree, so it is
/// the only test that can discriminate the fix: point the gate back at
/// remote.Airborne and this test alone fails, with
/// AirborneSnap and a moved body.
///
///
[Fact]
public void SteepContactBody_InterpolatesInsteadOfSnapping()
{
using var fixture = new RemotePlacementDriveFixture();
(RuntimeEntityRecord record, RemoteMotion remote, _) =
fixture.AddRemote(0x70005003u, new Vector3(12f, 14f, 7f));
var before = new Vector3(10f, 10f, 5f);
remote.Body.Position = before;
// Contact with a face too steep to stand on: CONTACT set, ON_WALKABLE
// clear — exactly what the sweep's SetPositionInternal commit derives
// on a 52-degree roof since Bug B (204d0ae0) deleted the per-tick
// walkability forge that used to make this state unreachable.
remote.Body.TransientState =
TransientStateFlags.Active | TransientStateFlags.Contact;
Assert.True(remote.Body.InContact);
Assert.False(remote.Body.OnWalkable);
// The client flag agrees with its five writers, all `!Body.OnWalkable`.
remote.Airborne = !remote.Body.OnWalkable;
Assert.True(remote.Airborne);
LiveEntityNetworkUpdateController.RemoteContactRouting routing =
LiveEntityNetworkUpdateController.ApplyRemoteContactRouting(
fixture.Drive,
record,
remote,
Classify(hasContact: true, playerDistance: 10f),
before + new Vector3(0.5f, 0f, 0f),
Quaternion.Identity,
willBeDrTicked: true);
Assert.Equal(
LiveEntityNetworkUpdateController.RemoteContactArm
.SteadyStateInterpolate,
routing.Arm);
// The interpolate arm enqueues a waypoint and leaves the body to the
// per-tick catch-up; the snap arm would have written it directly.
Assert.Equal(before, remote.Body.Position);
}
///
/// The regression guard on the other side of the same gate: a genuinely
/// free-flying remote — no contact with anything — must still take the
/// hard snap. Retail's adjust_offset writes nothing at all with
/// CONTACT_TS clear, so the queued waypoint cannot advance the
/// body and the authoritative position is the only thing that can.
/// Widen the gate to "always interpolate" and this test fails.
///
[Fact]
public void FreeFlightBodyWithNoContact_StillSnaps()
{
using var fixture = new RemotePlacementDriveFixture();
(RuntimeEntityRecord record, RemoteMotion remote, _) =
fixture.AddRemote(0x70005004u, new Vector3(12f, 14f, 7f));
remote.Body.Position = new Vector3(10f, 10f, 5f);
remote.Body.TransientState = TransientStateFlags.Active;
Assert.False(remote.Body.InContact);
remote.Airborne = !remote.Body.OnWalkable;
var landing = new Vector3(10.5f, 10f, 5f);
LiveEntityNetworkUpdateController.RemoteContactRouting routing =
LiveEntityNetworkUpdateController.ApplyRemoteContactRouting(
fixture.Drive,
record,
remote,
Classify(hasContact: true, playerDistance: 10f),
landing,
Quaternion.Identity,
willBeDrTicked: true);
Assert.Equal(
LiveEntityNetworkUpdateController.RemoteContactArm.AirborneSnap,
routing.Arm);
Assert.Equal(landing, remote.Body.Position);
}
///
/// The unchanged third state: contact with WALKABLE ground. Both
/// predicates agree here, and both before and after AP-140's fix the
/// packet interpolates. Pinned so a future edit cannot "simplify" the
/// gate into something that only satisfies the two tests above.
///
[Fact]
public void WalkableGroundedBody_IsUnchangedAndStillInterpolates()
{
using var fixture = new RemotePlacementDriveFixture();
(RuntimeEntityRecord record, RemoteMotion remote, _) =
fixture.AddRemote(0x70005005u, new Vector3(12f, 14f, 7f));
var before = new Vector3(10f, 10f, 5f);
remote.Body.Position = before;
remote.Body.TransientState = TransientStateFlags.Active
| TransientStateFlags.Contact
| TransientStateFlags.OnWalkable;
remote.Airborne = !remote.Body.OnWalkable;
Assert.False(remote.Airborne);
LiveEntityNetworkUpdateController.RemoteContactRouting routing =
LiveEntityNetworkUpdateController.ApplyRemoteContactRouting(
fixture.Drive,
record,
remote,
Classify(hasContact: true, playerDistance: 10f),
before + new Vector3(0.5f, 0f, 0f),
Quaternion.Identity,
willBeDrTicked: true);
Assert.Equal(
LiveEntityNetworkUpdateController.RemoteContactArm
.SteadyStateInterpolate,
routing.Arm);
Assert.Equal(before, remote.Body.Position);
}
private static void AssertRenderPoseSuppressed(
RuntimeAuthoritativePositionRoute? route)
{
WorldEntity entity = MakeEntity();
var beforeRotation = entity.Rotation;
bool written = LiveEntityNetworkUpdateController
.TryApplyGenericRemoteRenderPose(
entity,
route,
WirePosition,
WireCell,
Quaternion.CreateFromAxisAngle(Vector3.UnitZ, 1.1f));
Assert.False(written);
Assert.Equal(OldPosition, entity.Position);
Assert.Equal(OldCell, entity.ParentCellId);
Assert.Equal(beforeRotation, entity.Rotation);
}
private static void AssertRenderPoseWritten(
RuntimeAuthoritativePositionRoute? route)
{
WorldEntity entity = MakeEntity();
var wireRotation = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, 1.1f);
bool written = LiveEntityNetworkUpdateController
.TryApplyGenericRemoteRenderPose(
entity,
route,
WirePosition,
WireCell,
wireRotation);
Assert.True(written);
Assert.Equal(WirePosition, entity.Position);
Assert.Equal(WireCell, entity.ParentCellId);
Assert.Equal(wireRotation, entity.Rotation);
}
private static WorldEntity MakeEntity() => new()
{
Id = 1u,
SourceGfxObjOrSetupId = 1u,
Position = OldPosition,
Rotation = Quaternion.Identity,
MeshRefs = Array.Empty(),
ParentCellId = OldCell,
};
private const uint Cell = 0x0101FFFFu;
private static RuntimeAuthoritativePositionRoute Classify(
bool hasContact,
float playerDistance,
uint committedCellId = OldCell,
PositionTimestampDisposition disposition =
PositionTimestampDisposition.Apply) =>
RuntimeAuthoritativePositionRouteClassifier.ClassifyAcceptedPosition(
new RuntimeAcceptedPositionRouteRequest(
new RuntimeAuthoritativePositionAuthority(
new RuntimeGenerationToken(7),
new RuntimeEntityKey(0x70000001u, 3),
PositionAuthorityVersion: 11UL,
AcceptedPositionSequence: 20,
PreviousTeleportSequence: 10,
AcceptedTeleportSequence: 10,
disposition),
RuntimePositionEntityKind.Remote,
RuntimeAcceptedPositionSource.PositionEvent,
new CreateObject.ServerPosition(
Cell, 10f, 20f, 30f, 1f, 0f, 0f, 0f),
PlacementFrame: 0u,
PositionPackVelocity: Vector3.Zero,
committedCellId,
hasContact,
playerDistance,
UsePositionFromServer: false,
HasAnimations: false,
default));
}