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
|
|
@ -0,0 +1,174 @@
|
|||
using System.Numerics;
|
||||
using AcDream.Core.Physics;
|
||||
using AcDream.Runtime.Gameplay;
|
||||
|
||||
namespace AcDream.Runtime.Tests.Gameplay;
|
||||
|
||||
public sealed class RuntimeLocalPlayerMovementStateTests
|
||||
{
|
||||
[Fact]
|
||||
public void ViewProjectsTheExactCanonicalControllerAndAutorunOwner()
|
||||
{
|
||||
var controller = new PlayerMovementController(new PhysicsEngine())
|
||||
{
|
||||
LocalEntityId = 0x50000001u,
|
||||
};
|
||||
controller.SetPosition(
|
||||
new Vector3(11f, 12f, 13f),
|
||||
0xA9B40001u,
|
||||
new Vector3(11f, 12f, 13f));
|
||||
using var movement = new RuntimeLocalPlayerMovementState
|
||||
{
|
||||
Controller = controller,
|
||||
};
|
||||
|
||||
Assert.Same(movement, movement.View);
|
||||
Assert.True(movement.Execute(RuntimeMovementCommand.ToggleRunLock));
|
||||
|
||||
RuntimeMovementSnapshot snapshot = movement.View.Snapshot;
|
||||
Assert.True(snapshot.HasController);
|
||||
Assert.Equal(0x50000001u, snapshot.LocalEntityId);
|
||||
Assert.Equal(controller.CellPosition, snapshot.Position);
|
||||
Assert.Equal(controller.BodyVelocity, snapshot.Velocity);
|
||||
Assert.Equal(controller.IsAirborne, snapshot.IsAirborne);
|
||||
Assert.Equal(controller.SimTimeSeconds, snapshot.SimulationTimeSeconds);
|
||||
Assert.True(snapshot.AutoRunActive);
|
||||
Assert.Equal(movement.Revision, snapshot.Revision);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GraphicalAndDirectCommandsMutateOneAutorunLatch()
|
||||
{
|
||||
using var movement = new RuntimeLocalPlayerMovementState();
|
||||
|
||||
Assert.True(movement.Execute(RuntimeMovementCommand.ToggleRunLock));
|
||||
Assert.True(movement.AutoRunActive);
|
||||
Assert.True(movement.View.Snapshot.AutoRunActive);
|
||||
|
||||
Assert.True(movement.Execute(RuntimeMovementCommand.Stop));
|
||||
Assert.False(movement.AutoRunActive);
|
||||
Assert.False(movement.View.Snapshot.AutoRunActive);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConcurrentRuntimeInstancesHaveIndependentMovementState()
|
||||
{
|
||||
using var first = new RuntimeLocalPlayerMovementState();
|
||||
using var second = new RuntimeLocalPlayerMovementState();
|
||||
var firstController = new PlayerMovementController(new PhysicsEngine())
|
||||
{
|
||||
LocalEntityId = 1u,
|
||||
};
|
||||
var secondController = new PlayerMovementController(new PhysicsEngine())
|
||||
{
|
||||
LocalEntityId = 2u,
|
||||
};
|
||||
first.Controller = firstController;
|
||||
second.Controller = secondController;
|
||||
|
||||
first.Execute(RuntimeMovementCommand.ToggleRunLock);
|
||||
firstController.SetPosition(Vector3.One, 0xA9B40001u, Vector3.One);
|
||||
secondController.SetPosition(
|
||||
new Vector3(2f),
|
||||
0xA9B50001u,
|
||||
new Vector3(2f));
|
||||
|
||||
Assert.True(first.Snapshot.AutoRunActive);
|
||||
Assert.False(second.Snapshot.AutoRunActive);
|
||||
Assert.Equal(1u, first.Snapshot.LocalEntityId);
|
||||
Assert.Equal(2u, second.Snapshot.LocalEntityId);
|
||||
Assert.NotEqual(first.Snapshot.Position, second.Snapshot.Position);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MotionPreparationPublishesOnlyTheConstructionSeam()
|
||||
{
|
||||
using var movement = new RuntimeLocalPlayerMovementState();
|
||||
IRuntimeLocalPlayerMotionSource source = movement;
|
||||
var committed = new PlayerMovementController(new PhysicsEngine());
|
||||
var candidate = new PlayerMovementController(new PhysicsEngine());
|
||||
movement.Controller = committed;
|
||||
bool drained = false;
|
||||
|
||||
using (movement.BeginMotionPreparation(
|
||||
candidate,
|
||||
() =>
|
||||
{
|
||||
drained = true;
|
||||
Assert.Same(committed.Motion, source.Motion);
|
||||
}))
|
||||
{
|
||||
Assert.True(drained);
|
||||
Assert.Same(committed, movement.Controller);
|
||||
Assert.Same(candidate.Motion, source.Motion);
|
||||
}
|
||||
|
||||
Assert.Same(committed.Motion, source.Motion);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TerminalDisposalConvergesWhenPreparationLeaseUnwindsLater()
|
||||
{
|
||||
var movement = new RuntimeLocalPlayerMovementState();
|
||||
var controller = new PlayerMovementController(new PhysicsEngine());
|
||||
IDisposable preparation = movement.BeginMotionPreparation(controller);
|
||||
movement.Controller = controller;
|
||||
movement.Execute(RuntimeMovementCommand.ToggleRunLock);
|
||||
|
||||
movement.Dispose();
|
||||
preparation.Dispose();
|
||||
|
||||
RuntimeLocalMovementOwnershipSnapshot ownership =
|
||||
movement.CaptureOwnership();
|
||||
Assert.True(ownership.IsConverged);
|
||||
Assert.Throws<ObjectDisposedException>(
|
||||
() => movement.Execute(RuntimeMovementCommand.ToggleRunLock));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TypedSkillOptionsPreserveCompleteValuesAndFallbackPerField()
|
||||
{
|
||||
Assert.Equal(
|
||||
new PlayerMovementConstructionOptions(321, 654),
|
||||
PlayerMovementConstructionOptions.From(
|
||||
new RuntimeMovementSkillSnapshot(321, 654, 1)));
|
||||
Assert.Equal(
|
||||
new PlayerMovementConstructionOptions(
|
||||
PlayerMovementConstructionOptions.FallbackRunSkill,
|
||||
654),
|
||||
PlayerMovementConstructionOptions.From(
|
||||
new RuntimeMovementSkillSnapshot(-1, 654, 1)));
|
||||
Assert.Equal(
|
||||
new PlayerMovementConstructionOptions(
|
||||
321,
|
||||
PlayerMovementConstructionOptions.FallbackJumpSkill),
|
||||
PlayerMovementConstructionOptions.From(
|
||||
new RuntimeMovementSkillSnapshot(321, -1, 1)));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WarmMovementSnapshotsAndOwnershipCaptureAllocateNothing()
|
||||
{
|
||||
using var movement = new RuntimeLocalPlayerMovementState();
|
||||
movement.Controller = new PlayerMovementController(new PhysicsEngine())
|
||||
{
|
||||
LocalEntityId = 7u,
|
||||
};
|
||||
|
||||
for (int i = 0; i < 1_000; i++)
|
||||
{
|
||||
_ = movement.Snapshot;
|
||||
_ = movement.CaptureOwnership();
|
||||
}
|
||||
|
||||
long before = GC.GetAllocatedBytesForCurrentThread();
|
||||
for (int i = 0; i < 100_000; i++)
|
||||
{
|
||||
_ = movement.Snapshot;
|
||||
_ = movement.CaptureOwnership();
|
||||
}
|
||||
long allocated = GC.GetAllocatedBytesForCurrentThread() - before;
|
||||
|
||||
Assert.Equal(0L, allocated);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue