using System.Numerics; using AcDream.Core.Net.Packets; namespace AcDream.Core.Net.Messages; /// /// Outbound GameAction(Jump) message — opcode 0xF61B. /// Sent when the player releases the spacebar to jump. The server uses /// the extent + velocity to validate the jump and replicate it to nearby /// clients. /// /// /// Wire layout — ports retail's JumpPack::Pack verbatim /// (0x00516d10, decomp lines ~284934-284963). Confirmed against the /// Ghidra decompile-by-address bridge during this slice (2026-06-30): /// /// /// GameAction envelope: u32 0xF7B1, u32 sequence, u32 0xF61B /// f32 extent (0.0-1.0 charge power) /// f32 velocity.x, velocity.y, velocity.z /// Position::Pack: u32 cellId, f32 x, f32 y, f32 z, /// f32 qw, f32 qx, f32 qy, f32 qz (32 bytes) /// Sequences: u16 instance, u16 serverControl, /// u16 teleport, u16 forcePosition /// Align to 4 bytes /// /// /// /// D4 fix. Retail's JumpPack does NOT pack an objectGuid or a /// spellId, and DOES pack the full Position — the pre-slice code had /// it backwards (two spurious trailing zero u32s, no Position at all). /// /// public static class JumpAction { public const uint GameActionOpcode = 0xF7B1u; public const uint JumpOpcode = 0xF61Bu; public static byte[] Build( uint gameActionSequence, float extent, Vector3 velocity, uint cellId, Vector3 position, Quaternion rotation, ushort instanceSequence, ushort serverControlSequence, ushort teleportSequence, ushort forcePositionSequence) { var w = new PacketWriter(80); w.WriteUInt32(GameActionOpcode); w.WriteUInt32(gameActionSequence); w.WriteUInt32(JumpOpcode); w.WriteFloat(extent); w.WriteFloat(velocity.X); w.WriteFloat(velocity.Y); w.WriteFloat(velocity.Z); // --- Position::Pack (0x005a9640): cellId + Frame::Pack (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); w.WriteUInt16(instanceSequence); w.WriteUInt16(serverControlSequence); w.WriteUInt16(teleportSequence); w.WriteUInt16(forcePositionSequence); w.AlignTo4(); return w.ToArray(); } }