From 6d3c58b29f0b2c3c3e1015bc182ef1adce94a96e Mon Sep 17 00:00:00 2001 From: BruceChen Date: Sat, 21 Mar 2026 14:11:23 +0800 Subject: [PATCH] fix: handle LpVec3 movement encoding in SpawnEntity for 1.21.9+ Minecraft 1.21.9 changed the SpawnEntity (Add Entity) packet layout: the velocity/movement field was moved before the angle fields and switched from 3 x Short to a new variable-length LpVec3 encoding. Add ReadNextLpVec3() to consume the LpVec3 wire format (1 byte header, optionally 5+ more bytes with a continuation VarInt), and update ReadNextEntity() to use the new field order for protocol >= 773. Made-with: Cursor --- .../Protocol/Handlers/DataTypes.cs | 61 +++++++++++++++---- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/MinecraftClient/Protocol/Handlers/DataTypes.cs b/MinecraftClient/Protocol/Handlers/DataTypes.cs index db6b8c1e..d669fc68 100644 --- a/MinecraftClient/Protocol/Handlers/DataTypes.cs +++ b/MinecraftClient/Protocol/Handlers/DataTypes.cs @@ -559,11 +559,32 @@ namespace MinecraftClient.Protocol.Handlers int data = -1; byte entityPitch, entityYaw; - if (living) + if (protocolversion >= Protocol18Handler.MC_1_21_9_Version) + { + // 1.21.9+: LpVec3 movement before angles, unified format + ReadNextLpVec3(cache); // Movement (LpVec3) + entityPitch = ReadNextByte(cache); // xRot + entityYaw = ReadNextByte(cache); // yRot + ReadNextByte(cache); // yHeadRot + data = ReadNextVarInt(cache); // Data + } + else if (living) { entityYaw = ReadNextByte(cache); // Yaw entityPitch = ReadNextByte(cache); // Pitch entityPitch = ReadNextByte(cache); // Head Pitch + + // Velocity (3 shorts) + if (protocolversion < Protocol18Handler.MC_1_9_Version) + { + // no velocity for living entities in <1.9 + } + else + { + ReadNextShort(cache); + ReadNextShort(cache); + ReadNextShort(cache); + } } else { @@ -577,24 +598,24 @@ namespace MinecraftClient.Protocol.Handlers data = protocolversion >= Protocol18Handler.MC_1_19_Version ? ReadNextVarInt(cache) : ReadNextInt(cache); - } - // In 1.8 those 3 fields for Velocity are optional - if (protocolversion < Protocol18Handler.MC_1_9_Version) - { - if (data != 0) + // Velocity (3 shorts) + if (protocolversion < Protocol18Handler.MC_1_9_Version) + { + if (data != 0) + { + ReadNextShort(cache); + ReadNextShort(cache); + ReadNextShort(cache); + } + } + else { ReadNextShort(cache); ReadNextShort(cache); ReadNextShort(cache); } } - else - { - ReadNextShort(cache); - ReadNextShort(cache); - ReadNextShort(cache); - } return new Entity(entityID, entityType, new Location(entityX, entityY, entityZ), entityYaw, entityPitch, data); @@ -945,6 +966,22 @@ namespace MinecraftClient.Protocol.Handlers } } + /// + /// Read an LpVec3 (low-precision vec3) from the cache (1.21.9+). + /// Variable-length encoding: first byte 0 = zero vector; otherwise + /// 2 bytes + 4 bytes (6 total), plus an optional VarInt continuation. + /// + public void ReadNextLpVec3(Queue cache) + { + int first = ReadNextByte(cache); + if (first == 0) + return; + ReadNextByte(cache); // second byte + ReadData(4, cache); // uint32 + if ((first & 4) == 4) // continuation bit set + ReadNextVarInt(cache); + } + /// /// Consume bytes for a ResolvableProfile (1.21.9+). /// Wire: Either(GameProfile, Partial) + PlayerSkin.Patch