Conversation
`commandLine.execute([], parent).waitFor()` gives the child process pipes for stdout and stderr that nothing ever reads, and an stdin pipe that is never written to or closed. The data generator logs a lot, so it blocks as soon as the OS pipe buffer fills - at an arbitrary point inside whichever provider happens to be running. On Windows the buffer is only 4 KiB, which makes the build hang for tens of minutes per Minecraft version, or forever. Measured on 1.13-26.3: ~30 minutes per version (when it finished at all) before, 3m40s total after. Redirect the output to a file and close the child's stdin instead, and fail the build with the generator's log path when it does not produce a report - previously the exit code was ignored and the failure surfaced much later as a confusing "Unable to process file: .../blocks.json".
Minecraft 26.3 has 35723 block states, so the highest state id is 35722 and
no longer fits in a short (max 32767). 26.1 and 26.2 peaked at 29872 and
32365, which is why the short typing went unnoticed until now.
`SimpleBlock.init()` fails outright on the new mappings:
NumberFormatException: Value out of range. Value:"32768" Radix:10
at java.lang.Short.parseShort
at SimpleBlock.lambda$init$3(SimpleBlock.java:85)
Only the *state* id needs widening - the block registry id (max 1285 in 26.3)
and the item ids (max 1657) still fit and are left alone, as are the legacy
ids in BlockStorage17 and the 1.8 global palette.
BREAKING CHANGE: VirtualBlock#getModernID, #getID and #getBlockStateID now
return int instead of short. Downstream plugins (LimboAuth, LimboFilter, ...)
need to be recompiled against the new API.
Velocity 4.2.0 (protocol 777) added MINECRAFT_26_3 and raised the maximum
supported version, so LimboAPI refused to start:
NoSuchMethodError: 'void JoinGamePacket.setGamemode(short)'
at LimboImpl.createJoinGamePacket(LimboImpl.java:968)
plus, once recompiled, a protocol-support warning and a client disconnect at
every step of the limbo handshake.
Build
- velocity 3.5.0-SNAPSHOT -> 4.1.2-SNAPSHOT. Elytrium's mirror has no
velocity-proxy 4.2.0 yet; 4.1.2-SNAPSHOT's API and proxy internals are
byte-for-byte the same as 4.2.0's (verified by comparing every class
LimboAPI touches), so this can move to 4.2.0 once it is published.
- Compile target 21 -> 25: velocity-api 4.x is Java 25 bytecode and its
Gradle module metadata rejects a JVM 21 consumer outright. CI already
runs JDK 25.
Protocol
- SUPPORTED_MAXIMUM_PROTOCOL_VERSION_NUMBER 776 -> 777.
- 26.3 renumbers much of the play-state packet id space (+1/+2/+3 depending
on where packets were inserted), so the 11 limbo packets whose ids moved
get a MINECRAFT_26_3 mapping. The ids come from the vanilla data
generator's reports/packets.json and were cross-checked by matching every
class against the 26.1 table. 26.1 -> 26.2 changed nothing, which is why
the previous version reused the 26.1 ids.
- JoinGame/Respawn: 26.3 widened gamemode and previousGamemode from a byte
to a VarInt and redefined previousGamemode as "game mode + 1 or 0"
(CommonPlayerSpawnInfo holds an Optional<GameType> now).
- TeleportConfirm gained the position and rotation being confirmed
(3 doubles + 2 floats). Without this the packet is longer than the
declared bounds and Velocity drops the connection on the leftover bytes.
- Chunk light masks: 26.3 replaced writeBitSet (long array: "count of
longs" + longs) with ByteBufCodecs.BIT_SET (byte array: "count of bytes"
+ bytes). Both are the same size for a mask, but the length prefix means
something different, so a 2-long mask went from 0x02 to 0x10 and the
client read 2 bytes instead of 16, desynchronising the rest of the packet.
Registries
- The config-state registry sync is built once per version range, so a
range must not straddle a version that changed a registry element. 26.3
changed minecraft:trim_material, so the range is split at 26.3.
- trim_material: 26.3 replaced the free-form asset_name with palette_id,
an Identifier (minecraft:trim/<name>).
- Added minecraft:block_transformer and minecraft:decorated_pot_pattern.
The client resolves both while building item data components from the
synced item registry, and throws when an entry vanilla items point at is
missing. block_transformer's codec requires at least one transform, so a
vanilla-derived placeholder is sent; a limbo never transforms blocks.
- Registry elements are no longer read with getCompound(), which silently
turns a non-compound element into an empty compound. block_transformer's
element is a bare list.
Game data
- MINECRAFT_26_3 in MinecraftVersion, WorldVersion and BlockEntityVersion.
26.3 renumbers 1173 block ids and 1465 item ids, and adds 90 blocks and
121 items. The block entity registry is unchanged.
- fallbackdata.json gains the 26.3 blocks that have an obvious older
analogue (the poplar wood set). The remaining 26.3 blocks - concrete and
wool slabs/stairs, red_shrub, shelf_mushroom - have no counterpart to map
to and still fall back to stone on older clients.
Tested against a 26.3 vanilla client on Velocity 4.2.0: connects, loads
registries, receives the chunks and reaches the backend server.
BREAKING CHANGE: VirtualBlock's id getters now return int, see the previous
commit.
UserNugget
left a comment
There was a problem hiding this comment.
Thanks for PR, but i'd better reimplement it myself to see if mojang broke something.
| public interface VirtualBlock { | ||
|
|
||
| short getModernID(); | ||
| int getModernID(); |
There was a problem hiding this comment.
Should be avoidable, there are still ~29k ids left.
| @@ -1,4 +1,30 @@ | |||
| { | |||
| "MINECRAFT_26_3": { | |||
There was a problem hiding this comment.
No new concrete/wool blocks and wrong version, should be MINECRAFT_26_2.
| public class TeleportConfirmPacket implements MinecraftPacket { | ||
|
|
||
| // 26.3 makes the client echo the position and rotation it is confirming: three doubles and two floats. | ||
| private static final int CONFIRMED_POSITION_SIZE = 3 * Double.BYTES + 2 * Float.BYTES; |
There was a problem hiding this comment.
Is there any reason not to decode this packet properly?
| joinGame.setPreviousGamemode((short) -1); | ||
| // 26.3 changed previousGamemode from "game mode or -1" to "game mode + 1 or 0" | ||
| // and widened both fields from byte to VarInt on the wire. | ||
| joinGame.setPreviousGamemode(version.noLessThan(ProtocolVersion.MINECRAFT_26_3) ? 0 : -1); |
There was a problem hiding this comment.
26.3 uses boolean+varint to encode this field, while Velocity uses only varint. Velocity bug.
There was a problem hiding this comment.
This nbt is optional on 26.3.
|
|
||
| // Trim material | ||
| // Trim material. | ||
| // 26.3 replaced the free-form "asset_name" with a reference to a trim palette. | ||
| boolean trimPalettes = version.noLessThan(ProtocolVersion.MINECRAFT_26_3); | ||
| CompoundBinaryTag trim = CompoundBinaryTag.builder() | ||
| .putString("asset_name", "redstone") | ||
| .putString(trimPalettes ? "palette_id" : "asset_name", trimPalettes ? "minecraft:trim/redstone" : "redstone") | ||
| .put("description", CompoundBinaryTag.builder() |
There was a problem hiding this comment.
Should be better reimplemented correctly.
- Widen block state ids to char (unsigned 16-bit) rather than int. 26.3 peaks at 35722, so ~29k ids are still free in 16 bits, and this keeps the field and every map keyed by it the width they were. LimboFactory's two state-id parameters are widened too (the legacy/Block overloads stay short). - fallbackdata: the new entry was keyed MINECRAFT_26_3, which is never read. getBlockID() walks down from MAXIMUM_VERSION and decrements *before* the first lookup, so the highest key it can apply is MAXIMUM_VERSION - 1. Keyed MINECRAFT_26_2 now. Also fills in the 64 concrete/wool slabs and stairs that were missing - mapped to the full block of the same colour, since 26.2 has no coloured slab to preserve the shape with. - TeleportConfirm: decode the echoed position and rotation instead of skipping them. - ChunkDataPacket: 26.3 makes the block entity nbt optional, so an empty one is written as absent rather than as an empty compound. - trim_material: each of the 11 entries now carries its own palette/asset name and colour instead of all 11 sharing redstone's. - LimboProtocol: explicit 26.3 mappings for SetSlotPacket and the three Move* packets. Per the vanilla data generator's reports/packets.json their ids are unchanged between 26.2 and 26.3, so these resolve to the same value the inherited mapping already did - they are there for explicitness.
|
Thanks for the review — pushed a follow-up commit (
Trim materials — each of the 11 now carries its own
The four "Also changed" comments (
and the client classes ( On reimplementing it yourself — no objection at all. Happy to drop this if your version supersedes it. |
Summary
Adds support for Velocity 4.2.0 / Minecraft 26.3 (protocol 777), and fixes two build problems that block it. Verified end-to-end against a vanilla 26.3 client: connects, loads registries, receives chunks, reaches the backend server.
Fixes the startup crash on 4.2.0:
Commits
build: stop the Minecraft data generator from stalling the build— independent bug fix, could be split out if you prefer.refactor(api)!: widen block state ids from short to int— breaking API change, see below.feat: support Minecraft 26.3 / Velocity 4.2.0— the actual 26.3 work.Build
velocity-proxy4.2.0 yet. I diffed every class LimboAPI touches between 4.1.2-SNAPSHOT and 4.2.0 — the API and proxy internals are byte-for-byte identical, so this can be bumped to 4.2.0 once it is published.velocity-api4.x is Java 25 bytecode and its Gradle module metadata rejects a JVM 21 consumer (Dependency resolution is looking for a library compatible with JVM runtime version 21, but ... is only compatible with JVM runtime version 25). CI already runs JDK 25.The data generator deadlock (commit 1)
commandLine.execute([], parent).waitFor()gives the child pipes for stdout/stderr that nothing reads, and a stdin pipe that is never closed. The generator logs a lot, so it blocks as soon as the OS pipe buffer fills — at an arbitrary provider, tens of minutes in, or never. On Windows the buffer is only 4 KiB.Measured over 1.13–26.3: ~30 minutes per version before, 3m40s total after.
Also: the exit code was ignored, so a failed generation surfaced much later as
Unable to process file: .../blocks.json. It now fails with the generator's log path — which is how the JDK 25 requirement became obvious (UnsupportedClassVersionError: class file version 69.0for the 26.x server jars).Protocol changes in 26.3
MINECRAFT_26_3mappingJoinGame/Respawn: gamemode and previousGamemode widened byte → VarInt;previousGamemoderedefined as "game mode + 1 or 0" (CommonPlayerSpawnInfonow holds anOptional<GameType>)TeleportConfirmnow echoes the confirmed position and rotation (3 doubles + 2 floats)writeBitSet(long array: count of longs + longs) toByteBufCodecs.BIT_SET(byte array: count of bytes + bytes)The packet ids come from the vanilla data generator's
reports/packets.json, and each was cross-checked by matching the class back to the 26.1 table (every name lines up:level_chunk_with_light,set_default_spawn_position,map_item_data, …). 26.1 → 26.2 changed nothing, which is why the previous version reused the 26.1 ids.The light mask one is worth calling out: both encodings are the same size for a mask, only the length prefix means something different, so a 2-long mask goes from
0x02to0x10and the client reads 2 bytes instead of 16. That desynchronises the rest of the packet and the client reportsfound 73815 bytes extra whilst reading packet level_chunk_with_light.Registries
The config-state registry sync is built once per version range from that range's first version, so a range must not straddle a version that changed a registry element's shape. 26.3 changed
minecraft:trim_material, so the26.1 → MAXIMUMrange is split at 26.3 — otherwise a 26.3 client is served 26.1-shaped content.trim_material:asset_name(free-form string) →palette_id(anIdentifier,minecraft:trim/<name>).minecraft:block_transformerandminecraft:decorated_pot_pattern. The client resolves both while building item data components from the synced item registry and throwsMissing element ResourceKey[minecraft:block_transformer / minecraft:shovel]when an entry vanilla items point at is absent — so they have to be present even in a limbo.block_transformer's codec isBlockTransformData.CODEC.listOf(1, 200), i.e. at least one transform, so a vanilla-derived placeholder is sent; a limbo never transforms blocks.block_state_provider, the third new registry, is empty in vanilla and unreferenced, so it is correctly omitted.getCompound(), which silently turns a non-compound element into an empty compound —block_transformer's element is a bare list.I checked all 22 registries LimboAPI fabricates against the 26.3 client; only these differed.
BREAKING CHANGE: block state ids are now
int26.3 has 35723 block states, so the highest state id is 35722 and no longer fits in a
short(max 32767). 26.1 and 26.2 peaked at 29872 and 32365, which is why this went unnoticed:VirtualBlock#getModernID,#getIDand#getBlockStateIDnow returnint.Only the state id needed widening — the block registry id (max 1285 in 26.3) and item ids (max 1657) still fit and are untouched, as are the legacy ids in
BlockStorage17and the 1.8 global palette. Downstream plugins (LimboAuth, LimboFilter, …) will need recompiling.Known gaps
red_shrubandshelf_mushroomhave no older counterpart to map to, so they still fall back to stone on older clients. The build prints a note per block. The poplar wood set is mapped.block_transformer's three entries share one vanilla-derived placeholder transform rather than each having its own. Unreachable in a limbo.Testing
Built with JDK 25, ran on Velocity 4.2.0 with a 26.3 vanilla client through LimboCheck and LimboAuth limbos.
One unrelated warning you may also see, from PacketEvents 2.13.0 rather than LimboAPI:
NbtCodecException: Tag asset_name doesn't exist. PacketEvents parses the outgoing registry sync with 26.2-era definitions and cannot read 26.3'spalette_id. It is caught and logged, the packet is untouched, but it will appear on any 26.3 server with PacketEvents installed.