refactor(runtime): own local movement and outbound cadence
Move the canonical local movement controller, body/motion managers, object clock, movement wire data, and MTS/jump/AP sender into AcDream.Runtime. Replace process skill defaults with typed Runtime character options, make graphical and direct commands borrow one autorun owner, retain the construction-time PartArray seam, and include movement in terminal ownership convergence. Preserve the accepted pre-inbound movement/jump and post-inbound autonomous-position order while moving the exact packet/cadence fixtures into Runtime tests. Add graphical/direct parity, two-instance isolation, teardown, allocation, architecture, and divergence-path coverage. Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
parent
3456dff038
commit
aa3f4a60f8
36 changed files with 878 additions and 276 deletions
|
|
@ -1,215 +0,0 @@
|
|||
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);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue