Skip to content

Fix LpVec3 entity velocity decoding (big-endian word + notch units)#1494

Open
atiweb wants to merge 2 commits into
PrismarineJS:masterfrom
atiweb:fix/lpvec3-velocity-encoding
Open

Fix LpVec3 entity velocity decoding (big-endian word + notch units)#1494
atiweb wants to merge 2 commits into
PrismarineJS:masterfrom
atiweb:fix/lpvec3-velocity-encoding

Conversation

@atiweb

@atiweb atiweb commented Jun 15, 2026

Copy link
Copy Markdown

Problem

src/datatypes/lpVec3.js (added in #1453 for the 1.21.9+ entity-velocity encoding) decodes every entity velocity incorrectly. Two separate issues:

1. Wrong endianness. The 32-bit word is read with readUInt32LE and written with a little-endian writeUInt32LE/writeUInt16LE layout, but Minecraft's net.minecraft.network.LpVec3 reads/writes it with FriendlyByteBuf.readUnsignedInt / writeInt, which are big-endian. The decoded packet is still the correct length, so no parse error is raised — the velocity is just silently wrong. The existing test only covers the zero vector, which is a single 0 byte and therefore endian-agnostic, so this was never caught.

2. Wrong units. entity_velocity/spawn_entity use lpVec3 on >= 1.21.9 and vec3i16 on older versions, and consumers apply fromNotchVelocity (÷ 8000) to packet.velocity uniformly (e.g. mineflayer lib/plugins/entities.js, PrismarineJS/mineflayer#3839). vec3i16 yields raw 1/8000-block-per-tick units, but lpVec3 yielded raw blocks-per-tick — i.e. 8000× too small.

Together these break entity velocity / knockback on 1.21.9–1.21.11. See PrismarineJS/mineflayer#3887 ("Bot freezes in mid-air after taking knockback on 1.21.11").

Fix

  • Read and write the 32-bit word big-endian, and rewrite the write path so the bytes match the on-wire layout packed = c << 16 | b << 8 | a (byte, byte, int32 big-endian).
  • Scale the decoded vector to 1/8000-block-per-tick units to match vec3i16, so the existing fromNotchVelocity consumers work unchanged across all versions.

Validation

  • New test/lpVec3Test.js decodes and round-trips velocity payloads captured from a real vanilla 1.21.11 server (e.g. f9ff7ffeebed → ≈ (0, -0.078, 0) blocks/tick) plus the zero-vector single-byte case.
  • Verified live against a 1.21.11 server with mineflayer: a piglin brute's knockback now decodes to (0.087, 0.177, -0.258) blocks/tick and the bot takes knockback normally instead of freezing in mid-air.

Reference: net.minecraft.network.LpVec3.read = readUnsignedByte, readUnsignedByte, readUnsignedInt; the resulting Vec3 is the entity's blocks-per-tick movement.

@atiweb atiweb closed this Jun 15, 2026
@atiweb atiweb reopened this Jun 15, 2026
The 32-bit word was read/written little-endian, but Minecraft's LpVec3 uses
FriendlyByteBuf.readUnsignedInt/writeInt (big-endian), so every entity velocity
decoded to a wrong but same-length (hence silent) value. Also scale the decoded
vector to 1/8000-block-per-tick units to match vec3i16, so fromNotchVelocity()
consumers work uniformly across versions.

Fixes broken entity velocity / knockback on 1.21.9-1.21.11 (mineflayer#3887).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@atiweb
atiweb force-pushed the fix/lpvec3-velocity-encoding branch from 157649e to 64c8cee Compare June 15, 2026 16:25
@GenerelSchwerz

Copy link
Copy Markdown

the big endian fix here seems correct but offsetting the generated value by 8000 is redundant and should be handled as a supportFeature in mineflayer.

@atiweb

atiweb commented Jul 13, 2026

Copy link
Copy Markdown
Author

Thanks — agreed on both.

The big-endian read is the core fix (FriendlyByteBuf writes that word with writeInt / reads it with readUnsignedInt, so it's big-endian).

On the ×8000: you're right that it's units-normalization, not wire format, so it doesn't belong here. I added it so mineflayer's existing fromNotchVelocity() would work unchanged across versions — but that's exactly the version-specific semantic that should live in mineflayer behind a supportFeature. I'll drop NOTCH_UNITS_PER_BLOCK so lpVec3 decodes to its natural blocks-per-tick value (the raw codec's units), and add the supportFeature on the mineflayer side to skip the /8000 for 1.21.9+.

Updating shortly.

The 1/8000 notch-unit normalization is a version-specific consumer concern, not
wire format, so it does not belong in nmp. LpVec3 now decodes and encodes in blocks
per tick (the codec's natural units); mineflayer will gate the /8000 for pre-1.21.9
velocity behind a supportFeature. Keeps the big-endian word fix; round-trip unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants