Skip to content

Fix world border speed: varlong, not varint (regressed in 1.19) - #1200

Open
atiweb wants to merge 4 commits into
PrismarineJS:masterfrom
atiweb:fix/world-border-speed-varlong
Open

Fix world border speed: varlong, not varint (regressed in 1.19)#1200
atiweb wants to merge 4 commits into
PrismarineJS:masterfrom
atiweb:fix/world-border-speed-varlong

Conversation

@atiweb

@atiweb atiweb commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Problem

packet_initialize_world_border and packet_world_border_lerp_size both define their speed field (the border lerp time) as varint. In the vanilla client/server it is a VarLong:

// ClientboundInitializeBorderPacket / ClientboundSetBorderLerpSizePacket
private final long lerpTime;
this.lerpTime = buf.readVarLong();   // decode
buf.writeVarLong(this.lerpTime);     // encode

This is a regression. minecraft-data had speed correct as varlong in 1.17–1.18.2, but it was changed to varint in 1.19 and never corrected, so it has been wrong for every version from 1.19 through 1.21.11:

versions minecraft-data speed vanilla jar
1.17 – 1.18.2 varlong VarLong
1.19 – 1.21.11 varint VarLong

Impact

A VarInt and a VarLong encode to identical bytes for values below 2^28 (~74h expressed in ms), which is why this seldom surfaces. But a long border interpolation — a slow multi-day border shrink (/worldborder set <size> <seconds> with a large duration) — emits a VarLong that needs 5+ bytes, which a VarInt reader mis-parses (wrong value, then stream desync). These packets are clientbound, so it affects any consumer decoding them.

Verification

readVarLong/writeVarLong confirmed against the official Mojang server jars for 1.19.4 (where the regression begins) and 1.21.3 / 1.21.5 / 1.21.6 / 1.21.8 / 1.21.9; 1.17–1.18 already shipped the correct varlong.

Change

speed: varintspeed: varlong in both packets, for 1.19 → 1.21.11. proto.yml edited and protocol.json regenerated with npm run build (32 files, +64/−64; no other content touched). node compileProtocol.js validate passes for all versions.

ClientboundInitializeBorderPacket and ClientboundSetBorderLerpSizePacket encode the
border lerp time as a VarLong in vanilla (long lerpTime, writeVarLong/readVarLong).
minecraft-data had speed correct as varlong in 1.17-1.18.2, but it regressed to varint
in 1.19 and was never fixed - wrong for every version from 1.19 to 1.21.11.

VarInt and VarLong encode identically below 2^28 ms (~74h), so this only breaks on long
(multi-day) border interpolations, where the multi-byte VarLong mis-parses as a VarInt.
Verified readVarLong/writeVarLong against the Mojang server jars for 1.19.4 and 1.21.3-1.21.9.

1.19 and 1.19.2 also needed varlong re-declared as a native type (it had been dropped when
the field regressed to varint and nothing else used it); later versions already declare it.

proto.yml edited, protocol.json regenerated with npm run build.
@atiweb
atiweb force-pushed the fix/world-border-speed-varlong branch from 300231e to 964d484 Compare June 18, 2026 04:25
@extremeheat

Copy link
Copy Markdown
Member

Looks correct

@extremeheat

Copy link
Copy Markdown
Member

CI is failing on protocol.json desync, please run npm run build in tools/js

atiweb added 2 commits July 29, 2026 03:46
Master moved 1.21.11 to its own data/pc/1.21.11/proto.yml and repointed pc/latest at
26.1, so the merge left 1.21.11's protocol.json (already fixed) desynced from its new,
unfixed yaml. Applied speed: varlong there too and reran npm run build; 26.1 now picks
up the fix from pc/latest as well (its ClientboundInitializeBorderPacket reads a VarLong
too). All 21 affected versions are varlong; validate and the schema tests pass.
@extremeheat

Copy link
Copy Markdown
Member

Since this is a data type change (number to BigInt), did you check that mineflayer / nmp et al are able to handle this change?

@atiweb

atiweb commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Good question — I checked, and there's no numberBigInt change: nmp defines varlong itself, as an alias of varint. protodef has no varlong type (it has varint64); nmp adds it in src/datatypes/minecraft.js:

function readVarLong (buffer, offset) { return readVarInt(buffer, offset) }
function writeVarLong (value, buffer, offset) { return writeVarInt(value, buffer, offset) }
function sizeOfVarLong (value) { return sizeOfVarInt(value) }

and compiler-minecraft.js maps the compiled path to those same functions, so interpreted and compiled agree.

Round-tripping packet_world_border_lerp_size through nmp's ProtoDef with master's data vs this PR's data gives identical results — same bytes, same values, typeof === 'number' in both. mineflayer doesn't handle these two packets at all (no world_border handler), so nothing downstream reads the field today. There's also precedent: 1.17–1.18.2 have always had varlong here, so nmp has been consuming a varlong on this field for years.

One correction to my own PR description, since you're looking closely: the "~74h" threshold I quoted was wrong. VarInt and VarLong share the same LEB128 encoding, so the bytes are identical for any given value; what differs is only what a reader accepts. In nmp specifically, because varlong is an alias, behavior is unchanged by this PR either way — values ≥ 2^32 ms (~50 days) already decode incorrectly (protodef's readVarInt accumulates with 32-bit bitwise ops) both before and after. So for nmp this is a no-op; the value of the change is that the data matches vanilla (long lerpTime / readVarLong) for every minecraft-data consumer, and it restores what 1.17–1.18.2 already had.

@extremeheat

Copy link
Copy Markdown
Member

So not a real 'long' varint. varlong cannot correctly express 64-bit integers, we should probably remove and replace it

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants