Skip to content

pc/protocol: entity_teleport layout for 1.21.2+ (velocity, f32 rotation, relative flags) - #1273

Merged
rom1504 merged 4 commits into
PrismarineJS:masterfrom
u9g:fix/entity-teleport-1-21-2
Sep 14, 2026
Merged

rom1504 merged 4 commits into
PrismarineJS:masterfrom
u9g:fix/entity-teleport-1-21-2

Conversation

@u9g

@u9g u9g commented Sep 6, 2026

Copy link
Copy Markdown
Member

Since 1.21.2 ClientboundTeleportEntityPacket has the same layout as the player position packet: PositionMoveRotation (position, delta movement, f32 yaw/pitch), a Set<Relative> written as a 32-bit int, then onGround. minecraft-data still described the pre-1.21.2 layout (i8 rotation, no velocity, no flags), so every entity_teleport on 1.21.3+ fails to parse (Chunk size is 67 but only 33 was read, PrismarineJS/mineflayer#3759).

Verified against a captured 1.21.4 packet from a Velocity/ViaVersion network:

77                         packet id
97 80 80 80 04             entityId varint (5 bytes)
c02fef0a40000000 ... x3    x y z
0000000000000000 ... x3    dx dy dz
c2b40000 00000000          yaw pitch (f32)
00000000                   relatives (i32)
01                         onGround

1 + 5 + 24 + 24 + 8 + 4 + 1 = 67, which is the chunk size in the error. The flags field reuses PositionUpdateRelatives (already defined for packet_position on these versions), so consumers can treat both packets the same way.

Applied to 1.21.3 through 1.21.11 (proto.yml, protocol.json rebuilt with npm run build) and 26.1 (protocol.json only, it has no proto.yml). Decompiled 26.1 ClientboundTeleportEntityPacket / Relative.SET_STREAM_CODEC = ByteBufCodecs.INT confirm the same layout there.

Relation to the open PRs: #1154 covers only 1.21.3; #1251 covers 1.21.4–1.21.11 but encodes the relatives as a 9-bit bitfield, which protodef reads as 2 bytes, not the 4 bytes vanilla writes, so those packets would still be partial reads. This PR supersedes both.

The mineflayer side (consuming the relative flags in the entity_teleport handler) is in a separate PR.

…ative flags since 1.21.2

ClientboundTeleportEntityPacket changed in 1.21.2 to the same layout as
the player position packet: PositionMoveRotation (position, delta
movement, f32 yaw/pitch), a Set<Relative> written as a 32-bit int, then
onGround. minecraft-data still described the pre-1.21.2 layout (i8
rotation, no velocity, no flags), so every entity_teleport on 1.21.3+
failed with 'Chunk size is 67 but only 33 was read'.

Applied to 1.21.3 through 1.21.11 (proto.yml + rebuilt protocol.json) and
26.1 (protocol.json only).
Lets consumers pick the entity_teleport handler once per bot instead of
sniffing packet.flags on every packet (PrismarineJS/mineflayer#4062).
u9g added a commit to u9g/mineflayer that referenced this pull request Sep 6, 2026
…sRelativeFlags

entity_teleport is a hot packet, so choose between the 1.21.2+ relative
layout and the byte-rotation layout at inject time (like player_info)
instead of testing packet.flags on every packet. The feature comes from
PrismarineJS/minecraft-data#1273 alongside the layout change; older data
resolves it to false and keeps the legacy path.
@rom1504

rom1504 commented Sep 7, 2026

Copy link
Copy Markdown
Member

Can you double check in the decompiled code ?

@u9g

u9g commented Sep 7, 2026

Copy link
Copy Markdown
Member Author

Yes — double-checked in the decompiled sources for 1.21.4, 1.21.11 and 26.1 (same layout in all three):

ClientboundTeleportEntityPacket.STREAM_CODEC (net/minecraft/network/protocol/game/ClientboundTeleportEntityPacket.java):

public static final StreamCodec<FriendlyByteBuf, ClientboundTeleportEntityPacket> STREAM_CODEC = StreamCodec.composite(
    ByteBufCodecs.VAR_INT, ClientboundTeleportEntityPacket::id,
    PositionMoveRotation.STREAM_CODEC, ClientboundTeleportEntityPacket::change,
    Relative.SET_STREAM_CODEC, ClientboundTeleportEntityPacket::relatives,
    ByteBufCodecs.BOOL, ClientboundTeleportEntityPacket::onGround,
    ClientboundTeleportEntityPacket::new
);

PositionMoveRotation.STREAM_CODEC:

StreamCodec.composite(
    Vec3.STREAM_CODEC, PositionMoveRotation::position,      // 3 x f64
    Vec3.STREAM_CODEC, PositionMoveRotation::deltaMovement, // 3 x f64
    ByteBufCodecs.FLOAT, PositionMoveRotation::yRot,        // f32
    ByteBufCodecs.FLOAT, PositionMoveRotation::xRot         // f32
);

Relative.SET_STREAM_CODEC:

public static final StreamCodec<ByteBuf, Set<Relative>> SET_STREAM_CODEC = ByteBufCodecs.INT.map(Relative::unpack, Relative::pack);

i.e. a plain 32-bit int (4 bytes), confirming a u32 bitflags is the right encoding here (a 9-bit protodef bitfield reads 2 bytes, which is why #1251 would still under-read). The Relative enum is identical in all three versions (X(0) … ROTATE_DELTA(8)), matching the existing PositionUpdateRelatives flag order.

So the wire format is: varint entityId, 3×f64 pos, 3×f64 delta, f32 yaw, f32 pitch, u32 relative flags, bool onGround — exactly what this PR encodes.

Also noticed the 26.1 hunk was missing from the pushed commit (d5256da only touched 1.21.3–1.21.11 + features.json) — pushed c12191f applying the same layout to data/pc/26.1/protocol.json (it has no proto.yml, and PositionUpdateRelatives was already defined there for packet_position).

@rom1504
rom1504 merged commit 0087fbf into PrismarineJS:master Sep 14, 2026
4 checks passed
DallasCarraher added a commit to DallasCarraher/minecraft-data that referenced this pull request Sep 14, 2026
…ng, cubemob metadata, bee size, knockback_resistance min

All five verified by decompiling the real 26.2 server jar
(versions/26.2/server-26.2.jar) with CFR, cross-referenced against
extremeheat/extracted_minecraft_data where cited in review.

- packet_entity_teleport: the pc_26_2 rebase also silently dropped
  PrismarineJS#1273's 1.21.2+ layout fix (dx/dy/dz, f32 yaw/pitch,
  PositionUpdateRelatives flags) the same way it dropped PrismarineJS#1267's
  ItemStackTemplate fix. Restored — ClientboundTeleportEntityPacket
  still uses PositionMoveRotation in 26.2, confirmed against the real
  class.

- packet_spectator_action: entityId?: varint (protodef presence-byte
  optional) doesn't match vanilla's wire format. Decompiled
  ByteBufCodecs.OPTIONAL_VAR_INT: it maps a single raw VarInt directly
  (no separate presence byte) — 0 = absent, n = entity id (n - 1).
  Changed to `entityId: optvarint`, the same sentinel-varint alias
  already used for entity metadata's optional_block_state/
  optional_unsigned_int.

- data/pc/26.2/entities.json: slime/magma_cube/sulfur_cube were
  missing the `baby`/`age_locked` metadata keys AgeableMob defines
  (confirmed via SynchedEntityData.defineId call order in
  AgeableMob/AbstractCubeMob/SulfurCube bytecode), and sulfur_cube had
  max_fuse/from_bucket swapped. Fixed all three to
  mob_flags, baby, age_locked, size, max_fuse, from_bucket.

- bee width/height: EntityTypes.BEE registers .sized(0.55f, 0.5f) in
  26.2; entities.json still had 26.1's 0.7/0.6. Fixed.

- data/pc/26.2/attributes.json: knockbackResistance's min was 0.0;
  Attributes.KNOCKBACK_RESISTANCE registers RangedAttribute(default
  0.0, min -2.0, max 1.0). Fixed.

Full test suite still green: 1887 passing, 1 pending, 0 failing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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