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);
}