Skip to content

Minecraft 26.2#1807

Merged
electronicboy merged 14 commits into
PaperMC:dev/3.0.0from
RealBauHD:update/26.2
Jun 16, 2026
Merged

Minecraft 26.2#1807
electronicboy merged 14 commits into
PaperMC:dev/3.0.0from
RealBauHD:update/26.2

Conversation

@RealBauHD

@RealBauHD RealBauHD commented May 26, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@WouterGritter

WouterGritter commented May 29, 2026

Copy link
Copy Markdown
Contributor

override online mode?

https://mcsrc.dev/1/diff/26.2-snapshot-2/26.2-pre-2/net/minecraft/client/multiplayer/ClientPacketListener (line 571)

Looking at the src the client now uses the onlineMode boolean where it before set the Connection#isEncrypted to true when receiving the EncryptionRequestPacket (velocity) packet.

I haven't done any testing, but the old 26.1 client should only return true for Connection#isEncrypted when InitialLoginSessionHandler#generateEncryptionRequest() was called in velocity. If this indeed a drop-in-refactor on the client we should probably set this value to true iff InitialLoginSessionHandler#generateEncryptionRequest() is called

@electronicboy

electronicboy commented Jun 15, 2026

Copy link
Copy Markdown
Member

team_color registration collides with color for 26.2 clients

In ArgumentPropertyRegistry, minecraft:team_color is registered at index 16, but minecraft:color is still mapped to 16 as well (its mapSet(MINECRAFT_1_19, 16) has no later override). Because ArgumentIdentifier applies each mapping to every protocol >= its version, both identifiers resolve to wire ID 16 for a 26.2 client:

  • writeIdentifier serializes both color and team_color as 16
  • readIdentifier iterates an unordered HashMap and returns whichever of the two matches 16 first — nondeterministic

If team_color is just color renamed in 26.2 (keeping index 16), the fix is to retire color at 26.2 using the existing -1 shadow idiom (same as mob_effect/item_enchantment/entity_summon):

// minecraft:color renamed to minecraft:team_color in 26.2
empty(id("minecraft:color", mapSet(MINECRAFT_26_2, -1), mapSet(MINECRAFT_1_19, 16)));
...
empty(id("minecraft:team_color", mapSet(MINECRAFT_26_2, 16))); // renamed from color in 26.2

That makes ID 16 resolve to color for 1.19–26.1 and team_color for ≥26.2, with no collision in either direction.

success.setProperties(player.getGameProfileProperties());
success.setUuid(player.getUniqueId());
if (inbound.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_26_2)) {
success.setSessionId(UUID.randomUUID()); // use random uuid for now

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interestingly, mojang seems to be a "use the same session ID for everybody but regenerate it when the server is empty"

https://mcsrc.dev/1/26.2-rc-2/net/minecraft/server/network/ServerConnectionListener#L225

https://mcsrc.dev/1/26.2-rc-2/net/minecraft/server/network/ServerConnectionListener#L241

This is used purely for metrics on mojangs side, I guess the "good behavior" here would probably be to reproduce that logic

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering what Mojang sees as a "session" in this case. Does a new session occur the moment the last player online disconnects (or the moment the first player connects, going from 0 -> 1 players, effectively the same), or does a new session only occur when the server was "paused" due to it being empty (correct me if I'm wrong, but a vanilla server should pause processing ticks after no players have been online for x seconds (30?))

If it's the latter that's harder to match in Velocity... Though regardless, if the client expects the session ID to be the same for everyone connected at the same time, it's most likely better to match this behavior. Easiest is to just generate a session ID on boot.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(or uh, grab the session ID from the backend? is that possible here? why do we need to generate our own in velocity land?)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

session ID is sent from the proxy long before we have a backend, so that's not an option;

And I guess their modal is basically single player servers where people join in the eveing, have a "play session" together, and leave, and come back in a day or a few hours or whatever. For a larger server which always had players on that would always be shared

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then again, doesn't really translate to proxies, do we just sent 0,0? I'll ask mojang...

@WouterGritter WouterGritter Jun 15, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a hunch the session ID will be used in the new friends feature that got dropped from the 26.2 release. I'd guess you would be able to easily add players that are on the same server as you. As this will be done through Mojang's own API, the client can just report the session UUID it got from the connected server, and Mojang can match players against the same session UUID and consider them in the same server.

This would break with the current random UUID per connection approach (and also a zero UUID as youre suggesting). Though, it might also be useful for plugins to be able to control this. I could imagine a feature where certain players want to turn this off (staff? content people?). A plugin would be able to check a permission, and override the session UUID with a random one. Though that's all assuming it will be used for the dropped social/friends feature.

@WouterGritter

Copy link
Copy Markdown
Contributor

@electronicboy beat me by 11 minutes, but I had also dug into the duplicate registration --

tl;dr it works as-is but it's a bit fragile, and there's a cleaner way to express it.

It's not actually problematic on the wire. minecraft:color is mapSet(MINECRAFT_1_19, 16), and ArgumentIdentifier back-fills that id to every version >= 1.19, so id 16 already covers 26.2. The new team_color entry also resolves to 16 on 26.2, so both keys end up in byIdentifier and readIdentifier returns whichever the HashMap iteration happens to hit first (can differ from JVM to JVM, which isn't great...). Since both use EMPTY (no payload) and both map to 16 at 26.2, the re-encoded output is identical either way, so it's correct, but the lookup is non-deterministic, which I don't love.

It looks like this is a rename rather than an insertion too, so Mojang just renamed slot 16 color -> team_color in 26.2.

For 1.19+ the string identifier is purely cosmetic. writeIdentifier/readIdentifier only touch the string on the pre-1.19 path, everything >= 1.19 goes by the numeric id. So we don't actually need a second registration (or even a MINECRAFT_26_2 reference) to work with the new protocol here.

What I'd suggest is overloading id() to accept multiple names and folding them into one entry:

empty(id(List.of("minecraft:color", "minecraft:team_color"), mapSet(MINECRAFT_1_19, 16)));

ArgumentIdentifier stores a List<String> (first = canonical, used for the pre-1.19 write path + toString), getIdentifier() keeps returning the first so nothing else breaks, and readIdentifier matches via getIdentifiers().contains(...). One key for slot 16, no ambiguous lookup, zero wire change for any version.

Since this touches ArgumentIdentifier and the registry lookup, probably better as its own PR rather than refactoring this in this PR.

@electronicboy

electronicboy commented Jun 15, 2026

Copy link
Copy Markdown
Member

Yea, I mean, on the wire it doesn't matter but can be an odd moment when debugging it, I'd rather just keep it as two seperate enteries for the sake of not having to worry about that, being able to read back the data when debugging is nice rather than tripping myself up because the id is unexpected


I did in part just want to give claude a spin here

@WouterGritter

Copy link
Copy Markdown
Contributor

Yea, I mean, on the wire it doesn't matter but can be an odd moment when debugging it, I'd rather just keep it as two seperate enteries for the sake of not having to worry about that, being able to read back the data when debugging is nice rather than tripping myself up because the id is unexpected

I agree! Retiring the 1.19 - 26.1 registration in favor of this rename for 26.2+ is the canonical solution, ignore my comment :)

@RealBauHD RealBauHD marked this pull request as ready for review June 16, 2026 00:08
@electronicboy electronicboy merged commit d7ad052 into PaperMC:dev/3.0.0 Jun 16, 2026
1 check passed
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.

3 participants