Pass explicit grounded/airborne contact bytes from MovementResult into MoveToState and AutonomousPosition, and add ACDREAM_DUMP_MOVE_TRUTH logging for outbound movement plus player UpdatePosition echoes. Co-authored-by: OpenAI Codex <codex@openai.com>
148 lines
4.8 KiB
C#
148 lines
4.8 KiB
C#
using System;
|
|
using System.Buffers.Binary;
|
|
using System.Numerics;
|
|
using AcDream.Core.Net.Messages;
|
|
using Xunit;
|
|
|
|
namespace AcDream.Core.Net.Tests.Messages;
|
|
|
|
public class AutonomousPositionTests
|
|
{
|
|
[Fact]
|
|
public void Build_ProducesValidGameAction()
|
|
{
|
|
var body = AutonomousPosition.Build(
|
|
gameActionSequence: 5,
|
|
cellId: 0xA9B40001u,
|
|
position: new Vector3(100f, 100f, 50f),
|
|
rotation: Quaternion.Identity,
|
|
instanceSequence: 0,
|
|
serverControlSequence: 0,
|
|
teleportSequence: 0,
|
|
forcePositionSequence: 0);
|
|
|
|
uint opcode = BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(0));
|
|
Assert.Equal(0xF7B1u, opcode);
|
|
|
|
uint seq = BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(4));
|
|
Assert.Equal(5u, seq);
|
|
|
|
uint actionType = BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(8));
|
|
Assert.Equal(0xF753u, actionType);
|
|
}
|
|
|
|
[Fact]
|
|
public void Build_ContainsCellIdAfterHeader()
|
|
{
|
|
var body = AutonomousPosition.Build(
|
|
gameActionSequence: 1,
|
|
cellId: 0xDEADBEEFu,
|
|
position: Vector3.Zero,
|
|
rotation: Quaternion.Identity,
|
|
instanceSequence: 0,
|
|
serverControlSequence: 0,
|
|
teleportSequence: 0,
|
|
forcePositionSequence: 0);
|
|
|
|
// After the 12-byte GameAction header, the WorldPosition starts
|
|
// with u32 cell_id.
|
|
uint cellId = BinaryPrimitives.ReadUInt32LittleEndian(body.AsSpan(12));
|
|
Assert.Equal(0xDEADBEEFu, cellId);
|
|
}
|
|
|
|
[Fact]
|
|
public void Build_ContainsPosition_AfterCellId()
|
|
{
|
|
var body = AutonomousPosition.Build(
|
|
gameActionSequence: 2,
|
|
cellId: 0xA9B40001u,
|
|
position: new Vector3(12.5f, 34.0f, 56.75f),
|
|
rotation: Quaternion.Identity,
|
|
instanceSequence: 0,
|
|
serverControlSequence: 0,
|
|
teleportSequence: 0,
|
|
forcePositionSequence: 0);
|
|
|
|
// WorldPosition: cellId (4) + x (4) + y (4) + z (4) at offsets 12-27
|
|
float x = BinaryPrimitives.ReadSingleLittleEndian(body.AsSpan(16));
|
|
float y = BinaryPrimitives.ReadSingleLittleEndian(body.AsSpan(20));
|
|
float z = BinaryPrimitives.ReadSingleLittleEndian(body.AsSpan(24));
|
|
Assert.Equal(12.5f, x);
|
|
Assert.Equal(34.0f, y);
|
|
Assert.Equal(56.75f, z);
|
|
}
|
|
|
|
[Fact]
|
|
public void Build_IsAlignedTo4Bytes()
|
|
{
|
|
var body = AutonomousPosition.Build(
|
|
gameActionSequence: 3,
|
|
cellId: 0xA9B40001u,
|
|
position: Vector3.Zero,
|
|
rotation: Quaternion.Identity,
|
|
instanceSequence: 0,
|
|
serverControlSequence: 0,
|
|
teleportSequence: 0,
|
|
forcePositionSequence: 0);
|
|
|
|
Assert.Equal(0, body.Length % 4);
|
|
}
|
|
|
|
[Fact]
|
|
public void Build_TotalLengthIsCorrect_NoCommandsNoExtraFields()
|
|
{
|
|
// 12 (envelope) + 32 (WorldPosition) + 8 (4x u16 sequences) + 1 (contact) + 3 (align) = 56
|
|
var body = AutonomousPosition.Build(
|
|
gameActionSequence: 4,
|
|
cellId: 0xA9B40001u,
|
|
position: Vector3.Zero,
|
|
rotation: Quaternion.Identity,
|
|
instanceSequence: 0,
|
|
serverControlSequence: 0,
|
|
teleportSequence: 0,
|
|
forcePositionSequence: 0);
|
|
|
|
Assert.Equal(56, body.Length);
|
|
}
|
|
|
|
[Fact]
|
|
public void Build_UsesExplicitAirborneContactByte()
|
|
{
|
|
var body = AutonomousPosition.Build(
|
|
gameActionSequence: 7,
|
|
cellId: 0xA9B40001u,
|
|
position: Vector3.Zero,
|
|
rotation: Quaternion.Identity,
|
|
instanceSequence: 0,
|
|
serverControlSequence: 0,
|
|
teleportSequence: 0,
|
|
forcePositionSequence: 0,
|
|
lastContact: 0);
|
|
|
|
Assert.Equal(0, body[52]);
|
|
}
|
|
|
|
[Fact]
|
|
public void Build_ContainsIdentityRotation_AfterPosition()
|
|
{
|
|
var body = AutonomousPosition.Build(
|
|
gameActionSequence: 6,
|
|
cellId: 0xA9B40001u,
|
|
position: Vector3.Zero,
|
|
rotation: Quaternion.Identity, // W=1, X=Y=Z=0
|
|
instanceSequence: 0,
|
|
serverControlSequence: 0,
|
|
teleportSequence: 0,
|
|
forcePositionSequence: 0);
|
|
|
|
// Rotation starts at offset 28: rotW(4), rotX(4), rotY(4), rotZ(4)
|
|
float rotW = BinaryPrimitives.ReadSingleLittleEndian(body.AsSpan(28));
|
|
float rotX = BinaryPrimitives.ReadSingleLittleEndian(body.AsSpan(32));
|
|
float rotY = BinaryPrimitives.ReadSingleLittleEndian(body.AsSpan(36));
|
|
float rotZ = BinaryPrimitives.ReadSingleLittleEndian(body.AsSpan(40));
|
|
Assert.Equal(1.0f, rotW);
|
|
Assert.Equal(0.0f, rotX);
|
|
Assert.Equal(0.0f, rotY);
|
|
Assert.Equal(0.0f, rotZ);
|
|
}
|
|
}
|