diff --git a/src/AcDream.Core.Net/Messages/VectorUpdate.cs b/src/AcDream.Core.Net/Messages/VectorUpdate.cs index f9dfcff..d695022 100644 --- a/src/AcDream.Core.Net/Messages/VectorUpdate.cs +++ b/src/AcDream.Core.Net/Messages/VectorUpdate.cs @@ -39,27 +39,38 @@ public static class VectorUpdate ushort VectorSequence); /// - /// Parse a 0xF74E body. Returns null if the buffer is truncated or - /// malformed (sequence-number mismatch is not checked here — the - /// session-level handler decides what to do). + /// Parse a 0xF74E body. must start with the + /// 4-byte opcode (matches the convention used by UpdateMotion / + /// UpdatePosition / etc.). Returns null on truncation or opcode + /// mismatch. /// public static Parsed? TryParse(ReadOnlySpan body) { - if (body.Length < 32) return null; + // K-fix16 (2026-04-26): body convention includes the opcode at + // offset 0 — every other parser in this folder verifies the + // opcode word before reading payload fields. The previous + // version of this method skipped that, reading guid from the + // opcode bytes (and shifting every subsequent field by 4), + // which is why VU.recv lines showed guid=0xF74E and gigantic + // garbage velocity values. + if (body.Length < 4 + 32) return null; try { - uint guid = BinaryPrimitives.ReadUInt32LittleEndian(body[..4]); + uint opcode = BinaryPrimitives.ReadUInt32LittleEndian(body.Slice(0, 4)); + if (opcode != Opcode) return null; - float vx = BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(body.Slice(4, 4))); - float vy = BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(body.Slice(8, 4))); - float vz = BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(body.Slice(12, 4))); + uint guid = BinaryPrimitives.ReadUInt32LittleEndian(body.Slice(4, 4)); - float ox = BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(body.Slice(16, 4))); - float oy = BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(body.Slice(20, 4))); - float oz = BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(body.Slice(24, 4))); + float vx = BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(body.Slice(8, 4))); + float vy = BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(body.Slice(12, 4))); + float vz = BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(body.Slice(16, 4))); - ushort instSeq = BinaryPrimitives.ReadUInt16LittleEndian(body.Slice(28, 2)); - ushort vecSeq = BinaryPrimitives.ReadUInt16LittleEndian(body.Slice(30, 2)); + float ox = BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(body.Slice(20, 4))); + float oy = BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(body.Slice(24, 4))); + float oz = BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(body.Slice(28, 4))); + + ushort instSeq = BinaryPrimitives.ReadUInt16LittleEndian(body.Slice(32, 2)); + ushort vecSeq = BinaryPrimitives.ReadUInt16LittleEndian(body.Slice(34, 2)); return new Parsed(guid, new Vector3(vx, vy, vz), new Vector3(ox, oy, oz), instSeq, vecSeq); }