Skip to content

pc 26.3: protocol and data fixes for 26.3 (stacked on #1300) - #1301

Open
DallasCarraher wants to merge 26 commits into
PrismarineJS:pc_26_3from
DallasCarraher:pc-26.3-support
Open

DallasCarraher wants to merge 26 commits into
PrismarineJS:pc_26_3from
DallasCarraher:pc-26.3-support

Conversation

@DallasCarraher

@DallasCarraher DallasCarraher commented Sep 16, 2026 •

Copy link
Copy Markdown

Summary

Protocol and data fixes that make pc 26.3 (protocol 777) usable end to end. With the full stack applied, mineflayer logs in, completes configuration, spawns, and stays connected on a real 26.3 Realm and a vanilla 26.3 server. It tracks entities and players, loads chunks and light, and handles inventory, recipes and advancements without PartialReadErrors.

Every fix was found in live testing and confirmed by decompiling the official 26.3 server jar (javap -c -p on the relevant STREAM_CODECs and registration order), not inferred from captured bytes alone.

State of this PR (2026-09-23)

Fixes

Packet IDs and packet layouts

  • Login packet_success: added the missing sessionId: UUID.
  • Configuration-state IDs: added post_effects at 0x0a, shifting store_cookie through code_of_conduct. Before this, select_known_packs was read as custom_report_details and the connection stalled forever.
  • Play-state IDs realigned to GameProtocols registration order (144 clientbound, 69 serverbound), with schemas for the 5 new packets.
  • accept_teleportation (serverbound teleport_confirm) now carries x/y/z/yRot/xRot. Consumer side: Send resolved position/rotation in accept_teleportation for 26.3+ mineflayer#4125.
  • packet_login (play): added onlineMode: bool between worldState and enforcesSecureChat. Reported by @maebahesioru against Paper 26.3.
  • packet_advancements: PositionedAdvancement x/y moved to be siblings of key/value, and the stale displayData.xCord/yCord removed.
  • packet_world_particles: particle moved first, three max-speed floats added, count is a varint, and a trailing randomizationType enum added.
  • Stepped entity movement: rel_entity_move/entity_move_look use a new entityDelta type (reader in pc 26.3: add entityDelta type for the new stepped entity-move encoding node-minecraft-protocol#1538), and sync_entity_position uses the new PositionPath (linear or stepped).

Light data

Registries and enums

  • entityMetadataEntry types follow EntityDataSerializers.registerSerializer(...) call order (not declaration order). New entries: optional_living_entity_reference and dye_color.
  • SlotComponentType reordered to the 122 components DataComponents registers (every id from 40 up had shifted). pot_decorations is now four option<ItemStackTemplate>. Added attack_animation, interact_animation, cushion/color, waxed, sulfur_cube_content, villager_food, mob_visibility and sign_text_front/back; removed map_color.
  • Particle type mapper rebuilt from ParticleTypes (128 entries, with the new geyser variants).
  • SlotDisplay tag is an IDSet, not a string.
  • ItemBlockProperty minValue/maxValue are option<string>. An open-ended range desynced the stream and dropped the connection.
  • hand fields are main_hand/off_hand mappers, and entity_action uses Mojang's action names, matching master's pc/26.1: map every InteractionHand field to main_hand/off_hand #1278/Fix PC protocol enum inconsistencies in difficulty, game-state reasons, and entity actions packet fields #1295 for 26.1. The bytes on the wire are unchanged.

Data files

  • entities.json regenerated from EntityTypes registration order (161 entries; player is 159).
  • items/blocks/foods/recipes/blockCollisionShapes regenerated for 26.3's 121 new items and 90 new blocks, using the server's --reports registries.
  • The rest of the stale dataPaths.json 26.3 row repointed from 26.1 to 26.2.

Features

Build hygiene

Known gaps

Test plan

  • npm test in tools/js (schema validation, yaml↔json sync, audits) passes on the branch and on a trial merge with current master.
  • Schema-level round trips (createSerializer/createDeserializer) for the advancements, ItemBlockPredicate, entity-delta and light-update changes. Replayed 379 captured packets, including a 72 KB advancements packet, with zero parse errors.
  • Live on a real 26.3 Realm: full login, configuration and play; entity_metadata, advancements and recipe_book_add decode cleanly; nearby players link to bot.players.
  • Live on a vanilla 26.3 server: particle and item-predicate cases, plus a 5-minute session with about 6,400 update_light and 9,900 map_chunk packets, 7 deaths and respawns, and zero decode errors.

🤖 Generated with Claude Code

extremeheat and others added 10 commits September 14, 2026 09:10
Co-authored-by: extremeheat <13713600+extremeheat@users.noreply.github.com>
Registers protocol 776 (26.2) with real generated data: blocks, items,
entities, recipes, language, commands, protocol.json (regenerated from
proto.yml), loot tables, and the rest of the per-version files. Also
adds a 1.20.3 windows.json with the crafter menu (missing since 1.20.3)
so 26.2's windows entry is accurate.

Extracted from the 26.2 portion of PrismarineJS#1287,
which additionally carries unrelated fixes for 1.13.1, 1.14.2, 1.9.1
and a broader commands.json refresh across older versions. This commit
isolates just the 26.2 data so it can be reviewed and merged
independently of that larger changeset.

effects/enchantments/instruments are borrowed from 26.1 since those
registries are unchanged in 26.2.

Full mocha suite passes (1887 passing, 1 pending, 0 failing).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Addresses review feedback on this PR:

- sulfur_cube_content duplicated ItemStackTemplate's shape inline
  instead of referencing it. Verified against the real 26.2 server jar:
  net.minecraft.world.item.component.SulfurCubeContent is a record with
  a single field of type net.minecraft.world.item.ItemStackTemplate (no
  array wrapper) — Mojang's own internal type has the same name and
  shape minecraft-data already used. Now just `if sulfur_cube_content:
  ItemStackTemplate`.

- packet_spectator_action: verified this is a genuine rename, not a
  new packet alongside the old one. Decompiled the real 26.2 server
  jar: net/minecraft/network/protocol/game/ServerboundSpectatorActionPacket
  exists (a Record with one field, `OptionalInt spectateEntityId`,
  handled via `handleSpectatorAction`); no ServerboundSpectateEntityPacket
  class exists anywhere in the jar. Matches this PR's existing encoding
  (`entityId?: varint`) exactly — no data change needed for this one.

- Separately: rebasing this branch onto pc_26_2 silently regressed
  PrismarineJS#1267's ItemStackTemplate fix throughout data/pc/latest/proto.yml —
  git's merge applied both sides' overlapping edits to this file in a
  way that dropped PrismarineJS#1267's hunks without flagging a conflict. Restored
  all of it (intangible_projectile NBT, and ItemStackTemplate at
  use_remainder/charged_projectiles/bundle_contents/container/particle
  item/SlotDisplay item_stack/advancement icon) and regenerated
  data/pc/26.2/protocol.json from the corrected proto.yml.

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

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…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>
# Conflicts:
#	data/dataPaths.json
#	data/pc/common/versions.json
#	data/pc/latest/proto.yml
Two independent gaps found live-testing 26.3 against a real Realm with
mineflayer:

- packet_success (login, toClient) was missing the sessionId: UUID
  field that 26.2 (protocol 776) already added. Client-side parsing
  under-read every login success packet by 16 bytes.

- configuration.toClient's packet ID table was missing
  CLIENTBOUND_POST_EFFECTS at 0x0a, shifting every packet from
  store_cookie (0x0b) through code_of_conduct (0x14) down by one slot
  versus the real server. Confirmed by decompiling
  net.minecraft.network.protocol.configuration.ConfigurationProtocols
  from the official 26.3 server jar and reading addPacket() call order
  (same technique as PrismarineJS#1298/26.1.2's packet ID fixes). Concretely, ID
  0x0f was mapped to custom_report_details but is actually
  select_known_packs — since mineflayer's client.once('select_known_packs')
  listener never fired under the wrong name, the client never sent the
  required response, and the server silently stalled in the
  configuration state forever (steady keep_alive, nothing else).

Added packet_post_effects (a single postEffects: List<Identifier>
field, confirmed via javap on ClientboundPostEffectsPacket's
STREAM_CODEC) and resequenced 0x0a-0x14 to match.

With both fixes, a real client now completes the full configuration
handshake (select_known_packs -> registry_data -> tags ->
finish_configuration) and enters the play state.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Live-testing against a real 26.3 Realm (with the configuration-state fix
from 12aa21b already applied) got the bot into the play state, but it
immediately misread the join-game packet as low_disk_space_warning and
crashed decoding a player_chat-shaped packet. Same root cause as the
configuration-state bug: Mojang inserted new packets between 26.1.2 and
26.3 (net.minecraft.network.protocol.game.GameProtocols), shifting every
later packet ID by one or more slots versus what protocol.json had.

Decompiled the official 26.3 server jar's GameProtocols class (deobfuscated,
per PrismarineJS/mineflayer#3888's proven method for the same bug class at
protocol 775) to get the true addPacket() registration order for both
play.toClient (144 packets) and play.toServer (69 packets), then
programmatically realigned the existing packet name tables against that
ground truth, preserving names for unchanged packets and identifying
genuinely new ones with no prior schema:

- clientbound: add_transient_block, swing_animation, and post_effects
  (the same new packet from the configuration-state fix, now also sent
  in play state and copied into play.toClient.types)
- serverbound: punch (replaces the old arm_animation/swing packet) and
  spectator_action (new OptionalInt entity id, distinct from the existing
  teleport_to_entity packet, which keeps the old "spectate" semantics)

Field layouts for the new packets were read directly from each class's
STREAM_CODEC via javap rather than guessed from wire bytes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ortation schema

Found live-testing against a 26.3 Realm right after the play-state packet
ID drift fix (previous commit): the bot entered play, correctly parsed the
join-game and a wide range of world/entity packets, then got kicked with
"Failed to decode packet 'serverbound/minecraft:accept_teleportation'" the
first time it echoed a teleport confirmation back.

Decompiling ServerboundAcceptTeleportationPacket from the 26.3 server jar
shows Mojang widened this packet beyond the old single teleportId varint -
it now also carries the x/y/z/yRot/xRot being confirmed. protocol.json
still only declared teleportId, so the fields minecraft-protocol's compiler
had never seen in this schema were of course not on the wire the server
required, and it rejected the payload as malformed rather than an unknown
extra field.

Companion fix in mineflayer's physics.js populates the new fields; sending
them is a no-op on older protocol versions since protodef only serializes
fields declared for the running schema.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Found live-testing against a 26.3 Realm: after the play-state ID and
teleport_confirm fixes (previous two commits), the bot got much further -
correctly parsed login and dozens of packet types, including block entities
with legible NBT (sign text decoded perfectly) - but crashed in
prismarine-chunk's loadParsedLight with a light-section array coming up
far short of what the corresponding mask claimed.

Decompiled ByteBufCodecs.BIT_SET from the official 26.3 server jar (the
codec used for skyLightMask/blockLightMask/emptySkyLightMask/
emptyBlockLightMask in ClientboundLightUpdatePacketData): it now calls
FriendlyByteBuf.readByteArray()/writeByteArray() + BitSet.valueOf(byte[]),
not the varint-count-of-longs encoding every other supported version uses
(confirmed against the still-correct 1.21.9/773 schema, which uses
readLongArray()/BitSet.valueOf(long[]) for the same codec). Manually
decoding a captured chunk packet byte-for-byte confirmed it: reading the
masks as longs produced impossible bit positions (up to 120, for a chunk
with far fewer than 120 sections) while reading them as raw bytes lines up
exactly with the real per-section light data that follows.

Companion prismarine-chunk fix (BitArray.fromByteArray, version-gated to
26.3+) is in the pc-26.3 branch there.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Found live-testing mineflayer against a local 26.3 server: decompiling
ServerGamePacketListenerImpl showed the server only clears its per-tick
receivedPositionThisTick/receivedMovementThisTick bookkeeping when it
receives a serverbound tick_end packet (handleClientTickEnd). A client
that never sends tick_end gets that flag stuck true after its first
position-bearing move packet, so the *second* one ever sent -- no
matter how much later -- gets it kicked with
multiplayer.disconnect.invalid_player_movement.

The tick_end packet (empty container, protocol name "tick_end") has
existed in the wire protocol since 1.21.3, but nothing in prismarine
land currently sends it. Add a feature flag so client implementations
can gate sending it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@maebahesioru

Copy link
Copy Markdown

Tested this branch against a real Paper 26.3-8-dev server (protocol 777) and found two more packet differences that cause read errors on every connection. Both verified by decompiling the classes from paper-26.3.jar.

1. packet_login (play/toClient) — missing onlineMode

26.3 added onlineMode: bool between worldState and enforcesSecureChat:

ClientboundLoginPacket fields:
  playerId, hardcore, levels, maxPlayers, chunkRadius, simulationDistance,
  reducedDebugInfo, showDeathScreen, doLimitedCrafting, commonPlayerSpawnInfo,
  onlineMode, enforcesSecureChat

Without it, every login packet under-reads by exactly 1 byte:

Chunk size is 114 but only 113 was read ; partial packet : {"name":"login", ...}

Fix: insert {"name": "onlineMode", "type": "bool"} after worldState.

2. packet_advancements (play/toClient) — elements are now PositionedAdvancement

Each entry of advancementMapping gained x: f32 and y: f32:

public final class ClientboundUpdateAdvancementsPacket$PositionedAdvancement extends Record {
  private final AdvancementHolder advancement;
  private final float x;
  private final float y;
}

Without them the packet over-reads badly:

Chunk size is 293 but only 118 was read ; partial packet : {"name":"advancements", ...}

Fix: append {"name": "x", "type": "f32"}, {"name": "y", "type": "f32"} to the element container.

Result

After these two fixes (plus the sessionId and configuration-state ID fixes already in this PR), a 26.1-based mineflayer can log in to a 26.3 server, receive chunks (map_chunk x329), and walk around with correct ground detection — running for minutes without a kick.

Remaining known issue

packet_recipe_book_add still partial-reads (Unexpected buffer end while reading VarInt inside the IDSet of craftingRequirements). It is caught and skipped by the read loop so it doesn't break the session, but it's the last read error I haven't tracked down. If you already know the correct IDSet shape there, that would close the last gap.

Thanks for this branch — it saved me from writing the 26.3 data from scratch.

@maebahesioru

Copy link
Copy Markdown

Follow-up: 26.3 also needs its own entities.json (entity type IDs shifted)

Found while actually playing on a Paper 26.3-8-dev server (protocol 777).
pc/26.3 currently inherits entities from pc/26.1, but the entity type IDs
moved in 26.3
, so spawn_entity decodes to the wrong entity.

I measured this by summoning each entity and reading back the spawn_entity
type field (matching the new entity against the pre-summon bot.entities
snapshot so ambient mobs don't pollute the result). 128 of 157 entities were
measurable, with zero duplicate IDs.

entity 26.1 26.3 (measured) delta
cow 30 30 0
donkey 36 37 +1
egg 39 40 +1
ender_dragon 43 44 +1
experience_orb 49 50 +1
item 71 72 +1
skeleton 115 118 +3
wither_skull 147 151 +4
wolf 148 152 +4
zombie 150 154 +4

Shift boundaries look like ID 33-36 (+1), 146-147 (+4), 149-150 (+5).

Symptom

Items lying in the world decode as item_display instead of item, so they
never appear in bot.entities and collect_item-style logic can never see
them. entity_metadata is affected too (e.g. particle → sonic_boom values
misalign and trigger PartialReadError).

Also: packet_recipe_book_add

The IDSet inside RecipeDisplayEntry.craftingRequirements still partial-reads
(Unexpected buffer end while reading VarInt). Since RecipeDisplayEntry's
field list matches the decompiled class otherwise, I suspect the IDSet
(HolderSet) shape rather than the entry itself. I worked around it by replacing
the packet's type with restBuffer (keeps the packet ID mapping intact, just
discards the body) — login, chunks, movement and entity sync all work with that.

Happy to open a PR with the full measured table and a patched pc/26.3/entities.json
(putting the 29 non-summonable entities into the remaining free IDs) if useful.
Thanks again for this branch.

DallasCarraher and others added 3 commits September 18, 2026 10:38
… fields

Per review on PrismarineJS#1301 (@maebahesioru, verified against a live Paper
26.3-8-dev / protocol 777 server and by decompiling paper-26.3.jar):

- packet_login (play/toClient) was missing onlineMode: bool, added by
  26.3 between worldState and enforcesSecureChat. Every login packet
  was under-reading by exactly 1 byte without it.

- packet_advancements (play/toClient) advancementMapping entries are
  now PositionedAdvancement records: appended x: f32 and y: f32 after
  sendsTelemtryData. Without them the packet over-read badly.
rel_entity_move / entity_move_look / sync_entity_position all crashed with
"Chunk size is N but only M was read" / PartialReadError once more than one
entity was nearby, because 26.3 replaced the old flat delta encodings with
new ones (decompiled from the real 26.3 server jar):

- ClientboundMoveEntityPacket (rel_entity_move / entity_move_look): the old
  3x-i16 dX/dY/dZ is gone. It's now a packed `properties` varint (bit 0 =
  onGround, remaining bits = stepCount via VecDelta's packProperties/
  unpackStepCount) followed by either the old flat 3x-i16 format when
  stepCount <= 0, or `stepCount` chained {ticks: varint, dX, dY, dZ: i16}
  DeltaStep entries when an entity's position wasn't sent every tick. A
  lone, regularly-ticked entity almost always hits the stepCount<=0 path,
  which is why this needed several nearby mobs/players to reproduce.
  Handled via a new custom protodef type (`entityDelta`, registered in
  node-minecraft-protocol) since the sub-count depends on a runtime-derived
  value (properties >>> 1), which plain declarative container/switch can't
  express.

- ClientboundEntityPositionSyncPacket (sync_entity_position): the old flat
  x/y/z/dx/dy/dz fields are gone. `position` is now a PositionPath: a
  varint type discriminator (0 = Linear, an absolute x/y/z f64) or (1 =
  Stepped, a varint-counted list of {x, y, z: f64, tickOffset: varint}
  absolute-position steps). Unlike VecDelta this carries full double
  positions per step, not fixed-point shorts, so it's expressed with plain
  declarative mapper+switch+array, no custom type needed.

Confirmed via `javap -c -p` on VecDelta, VecDelta$Stepped$DeltaStep,
ClientboundMoveEntityPacket(.Pos/.PosRot), PositionPath(.Linear/.Stepped),
PositionStep and their STREAM_CODECs in the real 26.3 server jar.
protodef's container SizeOf compiler can only inline an `anon: true` field
when its type is a literal inline ["container", ...] or ["switch", ...]
array (see containerInlining() in protodef's compiler-structures.js) --
not a bare string reference to a registered native type, even a
container-shaped one. Referencing the custom `entityDelta` type as an anon
field blew up the compiled protocol with "Cannot inline anonymous type:
entityDelta" as soon as a client actually connected (caught by testing
against a local 26.3 server with multiple moving entities, not just
unit-testing the type's read/write/sizeOf in isolation).

Give it a real field name ("move") instead: packet_rel_entity_move /
packet_entity_move_look now have `move: { onGround, steps }` rather than
flattened onGround/steps fields at the packet root. Paired with the
mineflayer change reading packet.move.steps instead of packet.steps.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Follow-up to 2fc2152 (fix packet_login onlineMode and packet_advancements
position fields). That commit correctly identified that advancement entries
now carry an x/y position pair, but placed it wrong: it added x/y *inside*
the Advancement container (after sendsTelemtryData) while *also* leaving the
pre-26.3 xCord/yCord fields inside displayData. That duplicate/misplaced
pair caused catastrophic misalignment on the next advancements packet
(PartialReadError: string field expecting a size >1MB), since every
Advancement carried two extra floats that aren't actually on the wire there.

Root cause, decompiled from the real 26.3 server jar
(ClientboundUpdateAdvancementsPacket, $PositionedAdvancement,
AdvancementHolder, Advancement, DisplayInfo, AdvancementRequirements,
AdvancementProgress, CriterionProgress STREAM_CODECs):

- ClientboundUpdateAdvancementsPacket.added is a
  List<PositionedAdvancement>, not a map keyed by id directly.
- PositionedAdvancement.STREAM_CODEC = composite(AdvancementHolder, x: f32,
  y: f32) -- x/y are siblings of the advancement itself, not fields inside
  it.
- AdvancementHolder.STREAM_CODEC = composite(id: Identifier, value:
  Advancement) -- matches the existing key/value entry shape.
- Advancement.STREAM_CODEC only sends 4 fields: parent (optional
  Identifier), display (optional DisplayInfo), requirements
  (AdvancementRequirements), sendsTelemetryEvent (bool). rewards/criteria/
  name are server-only and never hit the wire.
- DisplayInfo.STREAM_CODEC has no x/y at all anymore (title, description,
  icon, type, flags int, optional background identifier) -- the xCord/yCord
  fields inside displayData are stale leftovers from an older MC version's
  layout and don't exist on 26.3's wire format.
- AdvancementRequirements (array<array<string>>), AdvancementProgress
  (map<string, CriterionProgress>), and CriterionProgress (option<Instant>)
  were also checked against their STREAM_CODECs and match the existing
  requirements/progressMapping/criterionProgress schema, so those were left
  alone.

Fix: remove xCord/yCord from inside displayData, and move x/y out of the
advancementMapping entry's `value` container to be siblings of `key`/
`value` at the entry level, matching PositionedAdvancement{advancement:
{id, value}, x, y}.

Note: this commit only touches the packet_advancements region. The working
tree also has unrelated in-progress changes to the metadata_entry switch
table (recipe_book_add/entity_metadata work, a separate concurrent task) --
staged and committed this fix as an isolated blob built from HEAD plus only
this change, to avoid stepping on that work.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@DallasCarraher

Copy link
Copy Markdown
Author

Pushed a follow-up commit (0604243) fixing the `packet_advancements` schema properly.

The previous commit in this PR (2fc2152) correctly identified that PositionedAdvancement adds an x/y pair, but placed the two floats in the wrong spot: appended inside the Advancement container (after sendsTelemtryData) while the old xCord/yCord fields were still left inside displayData. That duplication caused a catastrophic misalignment on the next advancements packet in live testing (PartialReadError: string field expecting a size >1MB).

Re-decompiled the 26.3 server jar end to end this time (ClientboundUpdateAdvancementsPacket, $PositionedAdvancement, AdvancementHolder, Advancement, DisplayInfo, AdvancementRequirements, AdvancementProgress, CriterionProgress STREAM_CODECs) to get the full picture:

  • added is List<PositionedAdvancement>, and PositionedAdvancement.STREAM_CODEC = composite(AdvancementHolder, x: f32, y: f32) — x/y are siblings of the advancement entry (key/value in the existing schema shape), not fields inside it.
  • DisplayInfo.STREAM_CODEC has no x/y at all — xCord/yCord were stale leftovers from an older layout.
  • Advancement, AdvancementRequirements, AdvancementProgress, CriterionProgress were all re-checked against the current schema's requirements/progressMapping/criterionProgress and match, so those were left untouched.

Verified with a schema-level round-trip test (serialize a sample advancements packet through the fixed schema, then deserialize it) — the buffer is now fully consumed with no PartialReadError. Full live-connection verification is currently blocked by the unrelated recipe_book_add IDSet parsing bug also being tracked in this PR's branch (it throws earlier in the packet stream and desyncs everything downstream); will re-verify against a live server once that lands.

DallasCarraher and others added 3 commits September 18, 2026 16:40
…ale dataPaths row

dataPaths.json's pc["26.3"].entities pointed at pc/26.1 — two versions stale, not
even pc/26.2. Since 26.1->26.2 already shifted 28 entity internalIds (including
player 155->156) from newly inserted entity types, and 26.3 sits two versions past
that, the stale pointer meant mineflayer's entityDataByInternalId table looked up
the wrong internalId for every spawn_entity packet: player entities were never
recognized as type 'player', so bot.players[username].entity never linked up.
Symptom: "Can't see player X nearby" even standing right next to them.

Root cause confirmed by decompiling the 26.3 server jar's
net.minecraft.world.entity.EntityTypes static initializer (the actual entity
registration order, not the alphabetical EntityTypeIds constants) via javap
bytecode. Diffing that 161-entry registration order against pc/26.2's 158-entry
entities.json shows a clean insertion-only diff: 3 new entities (cushion,
poplar_boat, poplar_chest_boat), zero removals, zero reordering elsewhere. player
moves from internalId 156 (26.2) to 159 (26.3). New entities' dimensions/category/
metadata were read from their EntityType.Builder registration calls and
defineSynchedData bytecode in the same decompile.

Also audited the rest of the pc["26.3"] dataPaths row: it was a verbatim copy of
the pc["26.1"] row with only protocol/version bumped, missing the wave of updates
pc["26.2"]'s row already picked up from 26.1 (attributes, blocks, items, biomes,
blockCollisionShapes, blockLoot, commands, entityLoot, foods, language,
loginPacket, materials, particles, recipes, sounds, tints, windows). Repointed all
of those to pc/26.2 as a strict improvement (1-version-stale using already-known-
good data, vs. 2-versions-stale) without decompile-regenerating each one; left
effects/enchantments/instruments/mapIcons alone since pc/26.2's row also keeps
those at their older pointers, suggesting they're legitimately unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…icle registry IDs, and world_particles layout

Four related protodef bugs found live against a real 26.3 Realm and a local
offline-mode 26.3 test server (DEBUG=minecraft-protocol), all traced to the
same root cause: several enum/registry orderings drifted in 26.3 and the
schema still used their pre-26.3 numbering.

1. entityMetadataEntry's `type` mapper (byte/int/.../particles/...) was the
   pre-26.3 EntityDataSerializers order. Decompiling
   net.minecraft.network.syncher.EntityDataSerializers's static initializer
   (registerSerializer() call order) shows optional_uuid was removed entirely
   in 26.3 and replaced by optional_living_entity_reference (still wire-
   compatible, still a plain UUID), and boolean/rotations/block_pos/
   optional_block_pos/direction/block_state/optional_block_state/particle/
   particles were all reshuffled around it. Renumbered all 44 entries and
   added the two new ones (optional_living_entity_reference, dye_color).
   This was the direct cause of the reported
   "Chunk size is 18 but only 14 was read" on a sonic_boom particles entry.

2. SlotDisplay's "tag" variant (used inside RecipeDisplay ingredients and
   packet_recipe_book_add's craftingRequirements) was typed as a plain
   "string", but TagSlotDisplay.STREAM_CODEC decompiles to
   ByteBufCodecs.holderSet(Registries.ITEM) -- the same registryEntryHolderSet
   (IDSet) encoding used everywhere else, not a bare tag-name string. Fixed
   to "IDSet". This was the actual cause of the reported
   "PartialReadError ... reading VarInt" on packet_recipe_book_add: a
   0x00 discriminator byte (IDSet's "named tag" case) was being misread as a
   zero-length string, shifting every subsequent SlotDisplay's type byte by
   one field.

3. The "Particle" container's own `type` mapper (angry_villager/block/.../
   sonic_boom/...) was also pre-26.3: decompiling
   net.minecraft.core.particles.ParticleTypes's static initializer
   (registerXxx() call order against BuiltInRegistries.PARTICLE_TYPE) gives a
   completely different, longer ordering -- 128 entries vs the old ~117, with
   new geyser/geyser_base/geyser_poof/geyser_plume/sulfur_bubbles/
   noxious_gas(_cloud)/red_poplar_leaves/orange_poplar_leaves/
   yellow_poplar_leaves/sulfur_cube_goo types inserted and
   trial_spawner_detected_player(_ominous) renamed to
   trial_spawner_detection(_ominous). sonic_boom moved 28->35, note moved
   58->68 (a byte that used to mean "note" now means "vibration"), etc.
   Rebuilt the full 128-entry mapper from the decompiled order and added
   data-switch cases for the four new geyser variants (GeyserParticleOptions/
   GeyserBaseParticleOptions STREAM_CODECs: waterBlocks:i32, plus
   burstImpulseBase:f32 for the non-plain geyser variants). This mapper is
   shared by entity_metadata's particle/particles fields and by
   packet_world_particles, so it was silently corrupting both.

4. packet_world_particles (ClientboundLevelParticlesPacket) itself had a
   stale pre-26.x field layout: `particle` used to be the last field with
   flat offsetX/Y/Z + a single velocityOffset float + an i32 amount. The
   26.3 STREAM_CODEC (decompiled composite(...)) puts `particle` FIRST, has
   six float fields (xDist/yDist/zDist/xMaxSpeed/yMaxSpeed/zMaxSpeed, not
   four), amount is a varint not i32, and there's a new trailing
   randomizationType enum (default/alternative/alternative_with_speed) that
   didn't exist before. Found live once particle mapper fix PrismarineJS#3 above stopped
   masking it (was reading a real "vibration" particle's sub-payload as if
   it were the void "note" particle, corrupting everything after it).

All four confirmed against the real 26.3 server jar
(net.minecraft.network.syncher.EntityDataSerializers,
net.minecraft.core.particles.ParticleTypes/ParticleType/GeyserParticleOptions/
GeyserBaseParticleOptions, net.minecraft.world.item.crafting.display.
SlotDisplay$TagSlotDisplay, net.minecraft.network.protocol.game.
ClientboundLevelParticlesPacket/$RandomizationType via javap) and re-verified
live: a bot connected to a local offline-mode 26.3 test server now parses
packet_recipe_book_add (crafting table recipe with a #minecraft:planks IDSet
ingredient), and packet_world_particles for note/sonic_boom/item/vibration
particles (the last carrying a live entity-tracking vibration source), with
zero PartialReadError/chunk-size-mismatch console spam across a multi-minute
session with several mobs (warden, sniffer, breeze, copper_golem, villager)
alive.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…n/max

StatePropertiesPredicate$RangedMatcher.minValue/maxValue are each
Optional<String> (ByteBufCodecs.optional(STRING_UTF8): a presence bool
then the string), confirmed by decompiling the real 26.3 server jar.
The schema had them as bare strings, so any block-property range
matcher missing a min or max (very common - e.g. a single-sided
"waterlogged" check) desynced the byte stream one bit early, cascading
into the following nbt field as a bogus huge array-length read
("array size is abnormally large") - same failure class as the
earlier packet_advancements bug in this PR.

Reproduced live against a running 26.3 server: a dropped/held item
carrying can_place_on with an open-ended waterlogged range (minValue
present, maxValue absent) now decodes cleanly in both entity_metadata
and set_slot, with zero PartialReadError and a stable connection
afterward. Verified independently with a schema-level round trip
(createPacketBuffer/parsePacketBuffer) covering minValue-only,
maxValue-only, and neither-present cases.
@DallasCarraher

Copy link
Copy Markdown
Author

Pushed fixes for the two fully-specified items from the review above (thanks @maebahesioru):

  • packet_login (play/toClient): added the missing onlineMode: bool between worldState and enforcesSecureChat.
  • packet_advancements (play/toClient): appended x: f32 / y: f32 to each advancementMapping entry (PositionedAdvancement).

Not included yet: the 26.3 entities.json ID shifts (waiting on the full measured table, since the sample in the comment covers only 10 of 128 entities) and the packet_recipe_book_add IDSet shape (still unresolved).

About the failing build (24) check: it was already failing on this branch before these commits (red on ebd0242e as well as 2fc21525). Running npm test in tools/js locally reproduces:

Error: 26.3 (26.3) / protocol.json is desynced from yaml, please run 'npm run build'

I'm deliberately not running npm run build and committing the output. data/dataPaths.json points pc/26.3's proto at pc/latest/proto.yml, which has drifted well past 26.3 (it already contains unrelated newer content such as sulfur_cube_content and shifted particle tables). Regenerating pc/26.3/protocol.json from it would overwrite the 26.3-specific packet layout in this PR, including the sessionId, configuration-state and onlineMode/advancements fixes, with a mix of stale and too-new data.

The real fix is deciding how pc/26.3 should source its protocol definition, for example by giving it its own proto.yml seeded from the current protocol.json like pc/26.1 and pc/26.2 have. I don't think this PR should make that call unilaterally, so I'd appreciate a maintainer's input on the preferred approach.

@extremeheat

Copy link
Copy Markdown
Member

I merged #1298 for 26.2 ; in general we should focus on one update PR at a time as otherwise things get quite complicated

The pc_26.3 scaffold PR #1300 will need to be regenerated after 26.2 is merged to be inline with latest master

@DallasCarraher

Copy link
Copy Markdown
Author

Thanks @extremeheat, understood on one update PR at a time. I'll hold off on further changes here and rebase this onto the regenerated pc_26.3 scaffold (#1300) once 26.2 is in and the scaffold is refreshed against latest master.

Since this PR is stacked on #1300, the build (24) failure (yaml desync and the version-regression audit) looks like it should be resolved by the regeneration rather than by anything in this PR, so I'm not pushing a fix for it here.

…or the 121 new items and 90 new blocks

26.3's dataPaths row pointed these at pc/26.2, but 26.3 inserted poplar wood,
concrete/wool slabs and stairs, cushions, maps, etc. mid-registry, shifting IDs.
IDs/states come from the server jar's registry report; attributes for new
entries are approximated from analogues.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
DallasCarraher and others added 2 commits September 19, 2026 23:25
…mponentType/pot_decorations

Two more wire-ID bugs, both found live via PACKET_DEBUG capture of dropped
packets against a real 26.3 Realm and local-test-server-26.3, and both
confirmed by javap on server-26.3.jar.

1. entityMetadataEntry `type` mapper: the previous fix numbered the serializers
   by *declaration* order in EntityDataSerializers (which includes helper
   fields and puts BLOCK_STATE/PARTICLE early). Wire IDs follow the
   registerSerializer(...) *call* order instead: boolean=8, rotations=9,
   block_pos=10 ... optional_living_entity_reference=13, block_state=14,
   optional_block_state=15, particle=16, particles=17, villager_data=18,
   optional_unsigned_int=19, pose=20, ..., optional_global_pos=33. Symptom was
   PartialReadError on every entity_metadata packet for villagers/players
   (type 18 read as optional_global_pos), dropping names/health/pose.

2. SlotComponentType: 26.3 registers 122 data components, the schema had 110,
   so every ID from 40 on was shifted. Reordered to the DataComponents
   register() call order. Added codecs for the new components whose
   STREAM_CODEC is fully readable from bytecode (attack_animation and
   interact_animation reuse SwingAnimation, cushion/color, waxed, villager_food,
   mob_visibility, sign_text_front/back, sulfur_cube_content) and dropped
   map_color, which 26.3 no longer registers. pot_decorations is now four
   Optional<ItemStackTemplate> fields (was an array of item ids); this was the
   direct cause of the PartialReadError on the 72 KB advancements packet, whose
   decorated-pot icon carries that component.

   Still read as void (no codec yet; they use nested loot-number providers /
   larger schemas): block_transformer, compostable, cooking_fuel, brewing_fuel,
   provides_pottery_pattern.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@DallasCarraher

Copy link
Copy Markdown
Author

Pushed three more commits to this branch (heads-up, since I'd said I'd hold off until the rebase onto the regenerated #1300 scaffold; these are all fixes found live against a real 26.3 Realm and the local 26.3 server, and I'll rebase the whole stack once #1300 is refreshed):

  • 6507f06e — entity metadata ids + data component ids. (1) entityMetadataEntry's type mapper was numbered by the declaration order of EntityDataSerializers; wire ids follow the registerSerializer(...) call order (e.g. boolean=8, particles=17, villager_data=18, pose=20). This corrects the earlier renumbering in 4f521880 and stops PartialReadError on every villager/player entity_metadata packet. (2) SlotComponentType had 110 entries but 26.3 registers 122 data components, shifting every id from 40 on; reordered to the DataComponents register order, added the codecs readable from bytecode, and re-encoded pot_decorations as four Optional<ItemStackTemplate>. That was the remaining cause of the PartialReadError on the ~70 KB advancements packet. Five newer components (block_transformer, compostable, cooking_fuel, brewing_fuel, provides_pottery_pattern) have correct ids but no codec yet. All confirmed with javap on server-26.3.jar; recorded packets replay with 0 parse errors.
  • 49f8409d — regenerate items/blocks/foods/recipes/blockCollisionShapes for 26.3. 26.3 inserts 121 items and 90 blocks mid-registry, shifting later ids (iron tools decoded as light blue dye). Generated from the server jar's --reports registries; note attributes for the new entries are approximate (hardness/shape/stack size derived from analogues).
  • 06d6aaff — playerActionHasChangeDestroyDirection feature flag (26.3 inserts CHANGE_DESTROY_DIRECTION at id 1 of the player-action enum, shifting abort/finish-dig and release-use-item).

Happy to split these into separate PRs if that's easier to review.

claude and others added 4 commits September 21, 2026 06:52
…ort features.json

data/pc/26.3 was pointing its protocol source at the shared pc/latest/proto.yml,
which had never been updated for any of the 26.3-era fixes (SlotComponentType,
entity_metadata ordering/naming, ItemBlockPredicate, particle registry, the
stepped entity-move/position-sync encoding, packet_teams/packet_advancements/
packet_map_chunk layout, and the play/configuration packet ID insertions) —
every one of those had only ever been hand-patched into protocol.json, so
'npm run build' regenerated a completely different (stale) file and CI's
"protocol.json is desynced from yaml" check failed.

Froze data/pc/26.3/proto.yml from a copy of pc/26.2's (last known-good, in-sync)
proto.yml and applied each documented 26.3 fix to it directly, repointed
dataPaths.json's pc/26.3 "proto" entry at pc/26.3 instead of pc/latest, and left
pc/latest untouched. protocol.json is now the direct 'npm run build' output of
this frozen yaml.

Also fixed the protodef-validator failure on packet_rel_entity_move/
packet_entity_move_look's new entityDelta type (needs a `types` entry so the
validator's protocol-schema pass registers it, even though it has no compiled
body of its own — the real reader lives in node-minecraft-protocol), and
resorted features.json (clientTickEndPacket was appended out of alphabetical
order).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WSuum5hJoePw4kJdMjxdKx
The earlier light-mask fix (count-of-longs -> count-of-bytes) only retyped
the four mask fields on packet_map_chunk. packet_update_light still read
skyLightMask/blockLightMask/emptySkyLightMask/emptyBlockLightMask as
array<i64>, so every standalone light update was misparsed: either a
PartialReadError ("Chunk size is N but only M was read"), or, when the
misread happened to fit, i64 elements (protodef SignedBigInt, whose
valueOf() is a BigInt) reached prismarine-chunk's fromByteArray and threw
"Cannot mix BigInt and other types". Both are connection-fatal in practice:
the client stops servicing traffic and the keepalive times out.

javap on server-26.3.jar: ClientboundLightUpdatePacket and
ClientboundLevelChunkWithLightPacket both embed
ClientboundLightUpdatePacketData.STREAM_CODEC, whose four masks are all
ByteBufCodecs.BIT_SET (ByteBufCodecs$15: FriendlyByteBuf.readByteArray ->
BitSet.valueOf(byte[])). It is the only BIT_SET user in the protocol
package, so no other packet needs the same change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI on this PR (which tests the merge with master) failed "26.1 / protocol.json
is desynced from yaml". The 26.2 commits on this branch froze
data/pc/26.1/proto.yml from pc/latest and repointed 26.1 at it, but master
has since changed 26.1's protocol.json by editing pc/latest/proto.yml
(PrismarineJS#1278 InteractionHand main_hand/off_hand mappers, PrismarineJS#1295 entity_action
enum names). The merge took master's newer JSON alongside this branch's
older frozen yaml.

Applied master's pc/latest/proto.yml diff since the merge base to
pc/26.1/proto.yml (it was an identical copy) and regenerated. 26.1's
protocol.json is now byte-identical to master's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings the frozen 26.2 and 26.3 proto.yml in line with what master did to
pc/latest (and what the previous commit ported to 26.1):

- PrismarineJS#1278: `hand` on open_book, arm_animation, block_place and use_item is
  now a main_hand/off_hand mapper instead of a bare varint.
- PrismarineJS#1295: entity_action actionId names follow Mojang's
  ServerboundPlayerCommandPacket.Action (leave_bed -> stop_sleeping,
  start/stop_horse_jump -> start/stop_riding_jump, open_vehicle_inventory ->
  open_inventory, start_elytra_flying -> start_fall_flying).

Wire format is unchanged: numeric writes still pass through the mappers,
and the start/stop_sprinting names are the same. The resulting packet
definitions are identical to 26.1's. mineflayer's elytraFly still sends
'start_elytra_flying' under entityActionUsesStringMapper, which is the
same break master already has on 26.1 and is handled by
PrismarineJS/mineflayer#4120.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@DallasCarraher DallasCarraher changed the title pc 26.3: fix login success sessionId and configuration-state packet IDs pc 26.3: protocol and data fixes for 26.3 (stacked on #1300) Sep 23, 2026
@maebahesioru

Copy link
Copy Markdown

Thanks for pushing the fixes and for the credit — I've been following the commits.

On the remaining entities.json ID shifts: I still have the measured spawn_entity type IDs from my testing (summoned each entity on a Paper 26.3-8-dev server and read back the type field). Happy to share the raw data or open a follow-up PR with a corrected entities.json for pc/26.3 if that would help — just let me know.

Makes sense to hold off until #1300 is regenerated before rebasing the stack.

This branch has not been deployed

No deployments
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.

5 participants