Repository navigation
feat(net): serve Minecraft 26.2 (protocol 776) and drop the valence 763 pre-play path - #972
Merged
Merged
Conversation
Replaces the four pre-play states with a hand-written 26.2 state machine and deletes the valence 763 path rather than gating it, so there is one protocol in the tree and no dead branch to keep compiling. - crates/hyperion/src/net/protocol: handshake, status, login and the configuration state, plus the play Login packet that puts a client in the world. Encoding reuses the existing framing: hyperion's own length prefix, zlib and packet id are protocol-independent, and the proxy forwards bytes without reading them. - registries.rs carries names only. A client that reports the vanilla core data pack already has the contents, so RegistrySynchronization sends each element with an empty payload; the names came off the server jar's data generator, and the registry order out of RegistryDataLoader.SYNCHRONIZED_REGISTRIES. - ClientInformation is handled in configuration, where 1.20.2 moved it, so DisplayedSkinParts is set before the player enters the world. - proto crate gains ClientboundLoginFinishedPacket, which the config work left out.
tools/client-26.2.py is the 776 counterpart to smash-client.py. It exists because on 776 the proxy's player count is even further from proof than it was on 763: there are now two gates before the world, the configuration state and the known-pack negotiation, and either can swallow a client silently. The command tree exposed one. hyperion-permission unicast it the moment a permission group was set, which is during login. On 763 that landed after LoginSuccess, where the client is already reading play packets, so it worked by accident. On 776 the handover is explicit and the same bytes arrive in the configuration state, where the client reads them as whatever configuration packet shares that id. Gated on the player actually being in play.
#971 landed the build-time generator while this was in flight. Login, handshake and status are now generated from protocol.json, so the hand-written ClientboundLoginFinishedPacket added here is gone in favour of the generated one. Configuration follows the generated types where they exist -- select_known_packs, registry_data, update_enabled_features and finish_configuration -- rather than the hand-written duplicates, which shortens the reconciliation that crate's packets/mod.rs describes instead of lengthening it. client_information, custom_payload and update_tags stay hand-written; the generator cannot express them.
andrewgazelka
force-pushed
the
feat/serve-26.2
branch
from
July 27, 2026 12:07
646d7e6 to
b513f2f
Compare
Benchmark Results for generalComparing to 6f7c0d4 |
Benchmark Results for generalComparing to 6f7c0d4 |
Codecov Report❌ Patch coverage is @@ Coverage Diff @@
## main #972 +/- ##
==========================================
+ Coverage 34.95% 36.08% +1.13%
==========================================
Files 219 230 +11
Lines 22476 23243 +767
Branches 835 878 +43
==========================================
+ Hits 7856 8387 +531
- Misses 14438 14650 +212
- Partials 182 206 +24
... and 2 files with indirect coverage changes 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Serves Minecraft 26.2 (protocol 776) through
crates/hyperion-minecraft-proto.A scripted client reaches play state; a vanilla client does not yet stay there,
and the reason is spelled out below.
What changed
The four states before play -- handshake, status, login, and the configuration
state that did not exist in 1.20.1 -- are now
crates/hyperion/src/net/protocol/.The valence 763 path was deleted rather than feature-gated: a branch that
only one of two protocols ever takes is a branch nobody exercises.
The transport did not change with the protocol. hyperion frames packets itself
and the proxy forwards bytes without reading them, so
net::encoder,net::decoderandpacket_channelare untouched; one adapter turns a proto-crateEncodeplus a packet id into aPacketBundleand the existing compression andlength framing carry it.
Registry contents did not have to be written.
RegistrySynchronization.packRegistrywrites an element with an empty payloadwhen the client reported the pack that defines it. Offering
minecraft:core:26.2and getting it back means all 397 elements of all 29synchronised registries go out as names alone, so
registries.rsis a nametable. The order is
RegistryDataLoader.SYNCHRONIZED_REGISTRIESand the namesare what the vanilla data generator writes; both read off the jar. A client that
does not report the core pack is refused with a message saying exactly that.
ClientboundLoginFinishedPacketwas the one packet the proto crate was missing.It was added here by hand and then dropped again when #971 landed mid-flight and
generated it; this branch is rebased onto that and uses the generated login,
handshake and status structs. Configuration follows the generated types where
they exist rather than the hand-written duplicates, which shortens the
reconciliation that crate's
packets/mod.rsdescribes rather than lengtheningit.
client_information,custom_payloadandupdate_tagsstay hand-written,because the generator cannot express them.
Verified
nix run .#lintandnix run .#testpass (315 tests). Full transcripts are indocs/minecraft-26.2-migration.md. The join, against a real running stack ondedicated ports:
tools/client-26.2.pyis the 776 counterpart tosmash-client.py. It existsbecause a player count is not evidence of a join, and on 776 there are two more
gates before the world than there were on 763.
Two bugs this was watched for
own
finish_configurationrather than off a skin channel, so a skin thatnever arrives can no longer keep anyone out.
tests/join_world.rsstillguards the channel that applies skins.
ClientInformationinto configuration, so it ishandled there and
DisplayedSkinPartsis set before the player enters theworld.
The test client caught a third.
hyperion-permissionunicast the command treethe moment a permission group was set, during login. On 763 that landed after
LoginSuccess, where the client is already reading play packets, so it workedby accident; on 776 the same bytes arrive in the configuration state and are
read as whatever configuration packet shares that id. Now gated on the player
being in play.
What a reviewer should not assume works
A vanilla client disconnects immediately after the join packet. Clientbound
play is still valence at 1.20.1 ids -- the same transcript continues
0x1E x4096, 0x24 x2434, 0x4E x1, 0x65 x154, which areUnloadChunkS2c,ChunkDataS2c,ChunkRenderDistanceCenterS2candPlayerListHeaderS2c. Thescripted client counts them and carries on, which is what lets it prove the
join; a real client will not. Serverbound play is likewise still decoded on
valence ids.
So the next piece is the whole of clientbound play, not just terrain.
registries.rsshould also be deleted in favour of generated contents once theproto crate produces them.