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( () => 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); } }