fix(net): VectorUpdate parser was reading guid from opcode bytes — remote jumps invisible
User report: "in ACdream client, when other client is jumping,
nothing happens at all".
Diagnostic [VU.recv] revealed the parser was reading
guid = 0x0000F74E (= the opcode itself) and velocity values in
the billions:
[VU.recv] guid=0x0000F74E vel=(8589944832.00,0.00,0.00)
isLocal=False hasRemote=False
WorldSession.ProcessDatagram passes the FULL reassembled body
including the 4-byte opcode at offset 0 — every other parser
in src/AcDream.Core.Net/Messages/ verifies the opcode word
before reading payload (UpdateMotion.TryParse:77,
UpdatePosition.TryParse, etc.). VectorUpdate.TryParse skipped
that step and read every field shifted four bytes early,
making the guid the opcode bytes and the velocities random
floats from later in the buffer. With guid=0xF74E never
matching any tracked entity, OnLiveVectorUpdated returned
early and remote jumps rendered nothing.
Fix: read + verify opcode at offset 0 in TryParse, then read
guid at offset 4, velocity at 8/12/16, omega at 20/24/28,
sequences at 32/34. Body length now 4 (opcode) + 32 (payload).
Tests stay 1222 green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
68d521df4f
commit
0ebf0cad09
1 changed files with 24 additions and 13 deletions
|
|
@ -39,27 +39,38 @@ public static class VectorUpdate
|
||||||
ushort VectorSequence);
|
ushort VectorSequence);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parse a 0xF74E body. Returns null if the buffer is truncated or
|
/// Parse a 0xF74E body. <paramref name="body"/> must start with the
|
||||||
/// malformed (sequence-number mismatch is not checked here — the
|
/// 4-byte opcode (matches the convention used by UpdateMotion /
|
||||||
/// session-level handler decides what to do).
|
/// UpdatePosition / etc.). Returns null on truncation or opcode
|
||||||
|
/// mismatch.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Parsed? TryParse(ReadOnlySpan<byte> body)
|
public static Parsed? TryParse(ReadOnlySpan<byte> 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
|
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)));
|
uint guid = BinaryPrimitives.ReadUInt32LittleEndian(body.Slice(4, 4));
|
||||||
float vy = BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(body.Slice(8, 4)));
|
|
||||||
float vz = BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(body.Slice(12, 4)));
|
|
||||||
|
|
||||||
float ox = BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(body.Slice(16, 4)));
|
float vx = BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(body.Slice(8, 4)));
|
||||||
float oy = BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(body.Slice(20, 4)));
|
float vy = BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(body.Slice(12, 4)));
|
||||||
float oz = BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(body.Slice(24, 4)));
|
float vz = BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(body.Slice(16, 4)));
|
||||||
|
|
||||||
ushort instSeq = BinaryPrimitives.ReadUInt16LittleEndian(body.Slice(28, 2));
|
float ox = BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(body.Slice(20, 4)));
|
||||||
ushort vecSeq = BinaryPrimitives.ReadUInt16LittleEndian(body.Slice(30, 2));
|
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);
|
return new Parsed(guid, new Vector3(vx, vy, vz), new Vector3(ox, oy, oz), instSeq, vecSeq);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue