acdream/tests/AcDream.App.Tests/Physics/PlayerOutboundPositionTests.cs
Erik f961d70023 feat(physics): port retail complete object frame pipeline
Restore the named-retail object update order across local, remote, static, projectile, animation, shadow, teleport, and effect lifetimes. Separate authoritative root commits from spatial rebucketing, preserve per-owner hook/FIFO ordering, and remove update-path allocations with exact lifecycle and residency gates.

Add deterministic conformance, adversarial lifetime, GUID-reuse, pending-cell, quaternion, timestamp, and allocation coverage. Release build is warning-free and all 6,446 tests pass with five intentional skips; retail, architecture, and adversarial reviews are clean.

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-07-20 09:10:31 +02:00

215 lines
7.5 KiB
C#

using System.Numerics;
using AcDream.App.Input;
using AcDream.Core.Physics;
using Xunit;
namespace AcDream.App.Tests.Physics;
public sealed class PlayerOutboundPositionTests
{
private static readonly Plane GroundPlane =
new(Vector3.UnitZ, 0f);
[Fact]
public void CrossLandblockWorldFrame_SendsCarriedCellLocalPosition()
{
var controller = new PlayerMovementController(new PhysicsEngine());
// The render origin remains A9B1, so a point 22.56 m into A9B2 is
// represented at world Y=214.56. The wire frame must remain the
// carried A9B2-local 22.56, never the render-space 214.56.
controller.SetPosition(
new Vector3(116.07f, 214.56f, 83.76f),
0xA9B20021u,
new Vector3(116.07f, 22.56f, 83.76f));
Assert.True(controller.TryGetOutboundPosition(out Position outbound));
Assert.Equal(0xA9B20021u, outbound.ObjCellId);
Assert.Equal(
new Vector3(116.07f, 22.56f, 83.76f),
outbound.Frame.Origin);
Assert.NotEqual(controller.Position, outbound.Frame.Origin);
}
[Fact]
public void UnplacedBody_DoesNotProduceWirePosition()
{
var controller = new PlayerMovementController(new PhysicsEngine());
Assert.False(controller.TryGetOutboundPosition(out _));
}
[Fact]
public void OutboundPosition_PreservesCompleteAuthoritativeQuaternion()
{
var controller = new PlayerMovementController(new PhysicsEngine());
controller.SetPosition(
new Vector3(116.07f, 214.56f, 83.76f),
0xA9B20021u,
new Vector3(116.07f, 22.56f, 83.76f));
Quaternion complete = Quaternion.Normalize(
Quaternion.CreateFromAxisAngle(Vector3.UnitX, 0.7f)
* Quaternion.CreateFromAxisAngle(Vector3.UnitY, -0.4f));
controller.SetBodyOrientation(complete);
Assert.True(controller.TryGetOutboundPosition(out Position outbound));
Assert.InRange(
MathF.Abs(Quaternion.Dot(
complete,
Quaternion.Normalize(outbound.Frame.Orientation))),
0.99999f,
1.00001f);
}
[Fact]
public void PositionEvent_FirstFrameIsDue()
{
var controller = new PlayerMovementController(new PhysicsEngine());
var current = PositionAt(Quaternion.Identity);
Assert.True(controller.ShouldSendPositionEvent(current, GroundPlane, 0f));
}
[Fact]
public void PositionEvent_StationaryHeadingChangeIsDueAfterRetailInterval()
{
var controller = new PlayerMovementController(new PhysicsEngine());
var sent = PositionAt(Quaternion.Identity);
var turned = PositionAt(Quaternion.CreateFromAxisAngle(
Vector3.UnitZ,
MathF.PI / 2f));
controller.NotePositionSent(sent, GroundPlane, nowSeconds: 0f);
Assert.False(controller.ShouldSendPositionEvent(
turned,
GroundPlane,
PlayerMovementController.HeartbeatInterval));
Assert.True(controller.ShouldSendPositionEvent(
turned,
GroundPlane,
PlayerMovementController.HeartbeatInterval + 0.01f));
}
[Fact]
public void PositionEvent_UnchangedCompleteFrameIsNotIdleHeartbeat()
{
var controller = new PlayerMovementController(new PhysicsEngine());
var sent = PositionAt(Quaternion.Identity);
controller.NotePositionSent(sent, GroundPlane, nowSeconds: 0f);
Assert.False(controller.ShouldSendPositionEvent(
sent,
GroundPlane,
PlayerMovementController.HeartbeatInterval + 0.01f));
}
[Fact]
public void MovementEvent_StampsOnlyTime_NotTheRememberedFrame()
{
var controller = new PlayerMovementController(new PhysicsEngine());
var sent = PositionAt(Quaternion.Identity);
var turned = PositionAt(Quaternion.CreateFromAxisAngle(
Vector3.UnitZ,
MathF.PI / 2f));
controller.NotePositionSent(sent, GroundPlane, nowSeconds: 0f);
controller.NoteMovementSent(nowSeconds: 0.9f);
Assert.False(controller.ShouldSendPositionEvent(
turned,
GroundPlane,
nowSeconds: 1.1f));
Assert.True(controller.ShouldSendPositionEvent(
turned,
GroundPlane,
nowSeconds: 1.91f));
}
[Fact]
public void PositionEvent_CellOrContactPlaneChangeIsDueInsideInterval()
{
var controller = new PlayerMovementController(new PhysicsEngine());
var sent = PositionAt(Quaternion.Identity);
controller.NotePositionSent(sent, GroundPlane, nowSeconds: 0f);
var otherCell = sent with { ObjCellId = 0xA9B20022u };
var otherPlane = new Plane(Vector3.Normalize(new Vector3(0.01f, 0f, 1f)), 0f);
Assert.True(controller.ShouldSendPositionEvent(otherCell, GroundPlane, 0.1f));
Assert.True(controller.ShouldSendPositionEvent(sent, otherPlane, 0.1f));
}
[Fact]
public void PositionEvent_QuaternionSignIsStoredFrameDataLikeRetail()
{
var controller = new PlayerMovementController(new PhysicsEngine());
var sent = PositionAt(Quaternion.Identity);
var oppositeStoredQuaternion = PositionAt(new Quaternion(0f, 0f, 0f, -1f));
controller.NotePositionSent(sent, GroundPlane, nowSeconds: 0f);
Assert.True(controller.ShouldSendPositionEvent(
oppositeStoredQuaternion,
GroundPlane,
PlayerMovementController.HeartbeatInterval + 0.01f));
}
[Fact]
public void PositionEvent_RetailEpsilonUsesDifferentOriginAndQuaternionBoundaries()
{
const float epsilon = 0.000199999995f;
var controller = new PlayerMovementController(new PhysicsEngine());
var sent = new Position(0xA9B20021u, Vector3.Zero, Quaternion.Identity);
controller.NotePositionSent(sent, GroundPlane, nowSeconds: 0f);
var exactOriginBoundary = sent with
{
Frame = sent.Frame with { Origin = new Vector3(epsilon, 0f, 0f) },
};
var exactQuaternionBoundary = sent with
{
Frame = sent.Frame with
{
Orientation = new Quaternion(epsilon, 0f, 0f, 1f),
},
};
float elapsed = PlayerMovementController.HeartbeatInterval + 0.01f;
Assert.False(controller.ShouldSendPositionEvent(
exactOriginBoundary,
GroundPlane,
elapsed));
Assert.True(controller.ShouldSendPositionEvent(
exactQuaternionBoundary,
GroundPlane,
elapsed));
}
[Fact]
public void PositionEvent_RetailPlaneDistanceBoundaryIsStrict()
{
const float epsilon = 0.000199999995f;
var controller = new PlayerMovementController(new PhysicsEngine());
var sent = PositionAt(Quaternion.Identity);
controller.NotePositionSent(sent, GroundPlane, nowSeconds: 0f);
var exactNormalBoundary = new Plane(
new Vector3(epsilon, 0f, 1f),
0f);
var exactDistanceBoundary = new Plane(Vector3.UnitZ, epsilon);
Assert.False(controller.ShouldSendPositionEvent(
sent,
exactNormalBoundary,
nowSeconds: 0.1f));
Assert.True(controller.ShouldSendPositionEvent(
sent,
exactDistanceBoundary,
nowSeconds: 0.1f));
}
private static Position PositionAt(Quaternion orientation)
=> new(
0xA9B20021u,
new Vector3(116.07f, 22.56f, 83.76f),
orientation);
}