feat(headless): complete deterministic bot command parity

This commit is contained in:
Erik 2026-07-27 08:23:36 +02:00
parent 7e8acb74dd
commit 38e83640d9
37 changed files with 2805 additions and 295 deletions

View file

@ -40,14 +40,87 @@ public sealed class RuntimeLocalPlayerMovementStateTests
public void GraphicalAndDirectCommandsMutateOneAutorunLatch()
{
using var movement = new RuntimeLocalPlayerMovementState();
var input = new MovementInput(
Forward: true,
Run: true);
Assert.True(movement.Execute(RuntimeMovementCommand.ToggleRunLock));
movement.SetCommandInput(input);
Assert.True(movement.AutoRunActive);
Assert.True(movement.View.Snapshot.AutoRunActive);
Assert.True(movement.HasCommandInput);
Assert.Equal(input, movement.CommandInput);
Assert.Equal(input, movement.View.Snapshot.CommandInput);
Assert.True(movement.Execute(RuntimeMovementCommand.Stop));
Assert.False(movement.AutoRunActive);
Assert.False(movement.View.Snapshot.AutoRunActive);
Assert.False(movement.HasCommandInput);
Assert.False(movement.View.Snapshot.HasCommandInput);
}
[Fact]
public void CommandInputIsDeduplicatedAndResetWithSessionIntent()
{
using var movement = new RuntimeLocalPlayerMovementState();
var input = new MovementInput(
StrafeLeft: true,
Run: true);
movement.SetCommandInput(input);
long revision = movement.Revision;
movement.SetCommandInput(input);
Assert.Equal(revision, movement.Revision);
Assert.True(movement.Snapshot.HasCommandInput);
movement.ResetInputIntent();
Assert.False(movement.HasCommandInput);
Assert.Equal(default, movement.CommandInput);
Assert.Equal(revision + 1, movement.Revision);
}
[Theory]
[InlineData(RuntimeMovementCommand.Ready, MotionCommand.Ready)]
[InlineData(RuntimeMovementCommand.Crouch, MotionCommand.Crouch)]
[InlineData(RuntimeMovementCommand.Sit, MotionCommand.Sitting)]
[InlineData(RuntimeMovementCommand.Sleep, MotionCommand.Sleeping)]
public void StanceCommandsUseCanonicalControllerAndEmitOneMovementEdge(
RuntimeMovementCommand command,
uint expectedMotion)
{
var controller = new PlayerMovementController(new PhysicsEngine());
using var movement = new RuntimeLocalPlayerMovementState
{
Controller = controller,
};
movement.SetCommandInput(
new MovementInput(Forward: true, Run: true));
Assert.True(movement.Execute(command));
Assert.False(movement.HasCommandInput);
Assert.Equal(
expectedMotion,
controller.Motion.RawState.ForwardCommand);
MovementResult first = controller.Update(
1f / 60f,
default);
MovementResult second = controller.Update(
1f / 60f,
default);
Assert.True(first.ShouldSendMovementEvent);
Assert.False(second.ShouldSendMovementEvent);
if (command == RuntimeMovementCommand.Ready)
{
Assert.Null(first.ForwardCommand);
}
else
{
Assert.Equal(expectedMotion, first.ForwardCommand);
}
}
[Fact]