using System.Numerics; using AcDream.Core.Net.Packets; namespace AcDream.Core.Net.Messages; /// /// Outbound GameAction(AutonomousPosition) message — opcode /// 0xF753. Sent roughly every 200ms while the player is moving to /// give the server a periodic ground-truth position heartbeat. Simpler than /// : no RawMotionState, just the GameAction /// envelope, a WorldPosition, sequence numbers, a contact byte, and 4-byte /// alignment padding. /// /// /// Wire layout (ported from /// references/holtburger/crates/holtburger-protocol/src/messages/movement/actions.rs /// AutonomousPositionActionData::pack): /// /// /// GameAction envelope: u32 0xF7B1, u32 sequence, u32 0xF753 /// WorldPosition: u32 cellId, f32 x, f32 y, f32 z, /// f32 rotW, f32 rotX, f32 rotY, f32 rotZ /// Sequences: u16 instance, u16 serverControl, /// u16 teleport, u16 forcePosition /// Contact byte: u8 (1 = on ground, 0 = airborne) /// Align to 4 bytes /// /// public static class AutonomousPosition { public const uint GameActionOpcode = 0xF7B1u; public const uint AutonomousPositionAction = 0xF753u; /// /// Build an AutonomousPosition GameAction body. /// /// Monotonically increasing counter from /// . /// Landblock cell ID (u32). /// World-space position relative to the landblock. /// Rotation quaternion. AC wire order is W, X, Y, Z. /// Instance sequence number from the server. /// Server-control sequence number. /// Teleport sequence number. /// Force-position sequence number. /// 1 if the character was last on the ground, 0 if airborne. public static byte[] Build( uint gameActionSequence, uint cellId, Vector3 position, Quaternion rotation, ushort instanceSequence, ushort serverControlSequence, ushort teleportSequence, ushort forcePositionSequence, byte lastContact = 1) { var w = new PacketWriter(64); // --- GameAction envelope --- w.WriteUInt32(GameActionOpcode); w.WriteUInt32(gameActionSequence); w.WriteUInt32(AutonomousPositionAction); // --- WorldPosition (32 bytes) --- w.WriteUInt32(cellId); w.WriteFloat(position.X); w.WriteFloat(position.Y); w.WriteFloat(position.Z); // Quaternion wire order: W, X, Y, Z w.WriteFloat(rotation.W); w.WriteFloat(rotation.X); w.WriteFloat(rotation.Y); w.WriteFloat(rotation.Z); // --- Sequence numbers --- w.WriteUInt16(instanceSequence); w.WriteUInt16(serverControlSequence); w.WriteUInt16(teleportSequence); w.WriteUInt16(forcePositionSequence); // --- Contact byte + 4-byte align --- w.WriteByte(lastContact); w.AlignTo4(); return w.ToArray(); } }