From 4620442744f5d7e6684828cdbb58960e77e504f1 Mon Sep 17 00:00:00 2001 From: Lulu13022002 <41980282+Lulu13022002@users.noreply.github.com> Date: Wed, 3 Jun 2026 19:46:30 +0200 Subject: [PATCH] Expose client brand to connection --- .../paper/connection/PlayerCommonConnection.java | 10 ++++++++++ .../connection/PlayerConfigurationConnection.java | 1 - .../src/main/java/org/bukkit/entity/Player.java | 12 ++++++------ .../0024-Improve-keepalive-ping-system.patch | 14 +++++++------- .../server/network/CommonListenerCookie.java.patch | 2 +- .../ServerCommonPacketListenerImpl.java.patch | 10 +++++----- .../paper/connection/PaperCommonConnection.java | 5 +++++ .../connection/PaperPlayerGameConnection.java | 4 ++-- .../org/bukkit/craftbukkit/entity/CraftPlayer.java | 9 ++++----- 9 files changed, 40 insertions(+), 27 deletions(-) diff --git a/paper-api/src/main/java/io/papermc/paper/connection/PlayerCommonConnection.java b/paper-api/src/main/java/io/papermc/paper/connection/PlayerCommonConnection.java index 7cba94e4916b..157fc8ffe597 100644 --- a/paper-api/src/main/java/io/papermc/paper/connection/PlayerCommonConnection.java +++ b/paper-api/src/main/java/io/papermc/paper/connection/PlayerCommonConnection.java @@ -5,6 +5,7 @@ import com.destroystokyo.paper.ClientOption; import org.bukkit.ServerLinks; import org.bukkit.plugin.messaging.PluginMessageRecipient; +import org.jspecify.annotations.Nullable; /** * Represents a connection that has properties shared between the GAME and CONFIG stage. @@ -43,4 +44,13 @@ public interface PlayerCommonConnection extends WritablePlayerCookieConnection, * @return the client option value of the player */ T getClientOption(ClientOption type); + + /** + * Returns player's client brand name. If the client didn't send this information, the brand name will be null. + *

+ * For the Notchian client this name defaults to {@code vanilla}. Some modified clients report other names such as {@code neoforge}. + * + * @return client brand name + */ + @Nullable String getClientBrandName(); } diff --git a/paper-api/src/main/java/io/papermc/paper/connection/PlayerConfigurationConnection.java b/paper-api/src/main/java/io/papermc/paper/connection/PlayerConfigurationConnection.java index dc68010fca8c..d024bae2d35b 100644 --- a/paper-api/src/main/java/io/papermc/paper/connection/PlayerConfigurationConnection.java +++ b/paper-api/src/main/java/io/papermc/paper/connection/PlayerConfigurationConnection.java @@ -1,6 +1,5 @@ package io.papermc.paper.connection; -import com.destroystokyo.paper.ClientOption; import com.destroystokyo.paper.profile.PlayerProfile; import net.kyori.adventure.audience.Audience; diff --git a/paper-api/src/main/java/org/bukkit/entity/Player.java b/paper-api/src/main/java/org/bukkit/entity/Player.java index fe3f4b7552e1..10ae2b1a0b53 100644 --- a/paper-api/src/main/java/org/bukkit/entity/Player.java +++ b/paper-api/src/main/java/org/bukkit/entity/Player.java @@ -3776,15 +3776,15 @@ public int getPing() { // Paper end } - // Paper start - brand support /** - * Returns player's client brand name. If the client didn't send this information, the brand name will be null.
- * For the Notchian client this name defaults to vanilla. Some modified clients report other names such as forge.
+ * Returns player's client brand name. If the client didn't send this information, the brand name will be null. + *

+ * For the Notchian client this name defaults to {@code vanilla}. Some modified clients report other names such as {@code neoforge}. + * * @return client brand name + * @see io.papermc.paper.connection.PlayerCommonConnection#getClientBrandName() */ - @Nullable - String getClientBrandName(); - // Paper end + @Nullable String getClientBrandName(); // Paper start - Teleport API /** diff --git a/paper-server/patches/features/0024-Improve-keepalive-ping-system.patch b/paper-server/patches/features/0024-Improve-keepalive-ping-system.patch index ab4d9c4f71b5..85bacd0493d2 100644 --- a/paper-server/patches/features/0024-Improve-keepalive-ping-system.patch +++ b/paper-server/patches/features/0024-Improve-keepalive-ping-system.patch @@ -102,22 +102,22 @@ index 0000000000000000000000000000000000000000..66d6af2833d76af28fbeb2ce70c194a5 + } +} diff --git a/net/minecraft/server/network/CommonListenerCookie.java b/net/minecraft/server/network/CommonListenerCookie.java -index d87a00ee3cd04ce25dbe46ae6b386b7f0799102c..ace440c2b82a6368aa27a85ff433ba2cbc8f39a5 100644 +index 021c2b0d4d8b55cb74b16a1210535e97576a83f1..da63706a8199ecfa9cd9de87bef20c8d1898a950 100644 --- a/net/minecraft/server/network/CommonListenerCookie.java +++ b/net/minecraft/server/network/CommonListenerCookie.java @@ -3,8 +3,8 @@ package net.minecraft.server.network; import com.mojang.authlib.GameProfile; import net.minecraft.server.level.ClientInformation; --public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred, @org.jspecify.annotations.Nullable String brandName, java.util.Set channels) { // Paper -+public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred, @org.jspecify.annotations.Nullable String brandName, java.util.Set channels, io.papermc.paper.util.KeepAlive keepAlive) { // Paper // Paper +-public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred, @org.jspecify.annotations.Nullable String clientBrand, java.util.Set channels) { // Paper ++public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred, @org.jspecify.annotations.Nullable String clientBrand, java.util.Set channels, io.papermc.paper.util.KeepAlive keepAlive) { // Paper // Paper public static CommonListenerCookie createInitial(final GameProfile gameProfile, final boolean transferred) { - return new CommonListenerCookie(gameProfile, 0, ClientInformation.createDefault(), transferred, null, new java.util.HashSet<>()); // Paper + return new CommonListenerCookie(gameProfile, 0, ClientInformation.createDefault(), transferred, null, new java.util.HashSet<>(), new io.papermc.paper.util.KeepAlive()); // Paper // Paper } } diff --git a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java -index f9be3e8cbc05fe74ac7816f8b2d81d52f6f23e40..8b194a59f79eea32c9e7a46227034942f6a6e816 100644 +index 71156340713bbc95c2caf839dc2e75b4467a2f0c..bdf98fedaa168732e0477ec5b3a16f935ce3d015 100644 --- a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java +++ b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java @@ -39,12 +39,13 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack @@ -147,7 +147,7 @@ index f9be3e8cbc05fe74ac7816f8b2d81d52f6f23e40..8b194a59f79eea32c9e7a46227034942 this.latency = cookie.latency(); this.transferred = cookie.transferred(); // Paper start - this.playerBrand = cookie.brandName(); + this.clientBrand = cookie.clientBrand(); this.cserver = server.server; this.pluginMessagerChannels = cookie.channels(); + this.keepAlive = cookie.keepAlive(); @@ -238,14 +238,14 @@ index f9be3e8cbc05fe74ac7816f8b2d81d52f6f23e40..8b194a59f79eea32c9e7a46227034942 } protected CommonListenerCookie createCookie(final ClientInformation clientInformation) { -- return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred, this.playerBrand, this.pluginMessagerChannels); // Paper +- return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred, this.clientBrand, this.pluginMessagerChannels); // Paper + // Paper start - listener handoff should reset pending keepalive expectations + return new CommonListenerCookie( + this.playerProfile(), + this.latency, + clientInformation, + this.transferred, -+ this.playerBrand, ++ this.clientBrand, + this.pluginMessagerChannels, + this.keepAlive.copyForListenerHandoff() + ); diff --git a/paper-server/patches/sources/net/minecraft/server/network/CommonListenerCookie.java.patch b/paper-server/patches/sources/net/minecraft/server/network/CommonListenerCookie.java.patch index 52d65de72cab..592dd2a333ee 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/CommonListenerCookie.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/CommonListenerCookie.java.patch @@ -5,7 +5,7 @@ import net.minecraft.server.level.ClientInformation; -public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred) { -+public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred, @org.jspecify.annotations.Nullable String brandName, java.util.Set channels) { // Paper ++public record CommonListenerCookie(GameProfile gameProfile, int latency, ClientInformation clientInformation, boolean transferred, @org.jspecify.annotations.Nullable String clientBrand, java.util.Set channels) { // Paper public static CommonListenerCookie createInitial(final GameProfile gameProfile, final boolean transferred) { - return new CommonListenerCookie(gameProfile, 0, ClientInformation.createDefault(), transferred); + return new CommonListenerCookie(gameProfile, 0, ClientInformation.createDefault(), transferred, null, new java.util.HashSet<>()); // Paper diff --git a/paper-server/patches/sources/net/minecraft/server/network/ServerCommonPacketListenerImpl.java.patch b/paper-server/patches/sources/net/minecraft/server/network/ServerCommonPacketListenerImpl.java.patch index 19e75f568090..68960f62bfb0 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/ServerCommonPacketListenerImpl.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/ServerCommonPacketListenerImpl.java.patch @@ -20,7 +20,7 @@ + private static final long KEEPALIVE_LIMIT = Long.getLong("paper.playerconnection.keepalive", 30) * 1000; // Paper - provide property to set keepalive limit + protected static final net.minecraft.resources.Identifier MINECRAFT_BRAND = net.minecraft.resources.Identifier.withDefaultNamespace("brand"); // Paper - Brand support + // Paper start - retain certain values -+ public @Nullable String playerBrand; ++ public @Nullable String clientBrand; + public final java.util.Set pluginMessagerChannels; + // Paper end - retain certain values @@ -31,7 +31,7 @@ this.latency = cookie.latency(); this.transferred = cookie.transferred(); + // Paper start -+ this.playerBrand = cookie.brandName(); ++ this.clientBrand = cookie.clientBrand(); + this.cserver = server.server; + this.pluginMessagerChannels = cookie.channels(); + // Paper end @@ -95,13 +95,13 @@ + } + + if (identifier.equals(MINECRAFT_BRAND)) { -+ this.playerBrand = new net.minecraft.network.FriendlyByteBuf(io.netty.buffer.Unpooled.wrappedBuffer(data)).readUtf(256); ++ this.clientBrand = new net.minecraft.network.FriendlyByteBuf(io.netty.buffer.Unpooled.wrappedBuffer(data)).readUtf(256); + } + + this.cserver.getMessenger().dispatchIncomingMessage(paperConnection(), identifier.toString(), data); + } catch (final Exception e) { + LOGGER.error("Couldn't handle custom payload on channel {}", identifier, e); -+ this.disconnect(net.minecraft.network.chat.Component.literal("Invalid custom payload payload!"), io.papermc.paper.connection.DisconnectionReason.INVALID_PAYLOAD); // Paper - kick event cause ++ this.disconnect(Component.literal("Invalid custom payload payload!"), io.papermc.paper.connection.DisconnectionReason.INVALID_PAYLOAD); // Paper - kick event cause + } + } + @@ -335,6 +335,6 @@ protected CommonListenerCookie createCookie(final ClientInformation clientInformation) { - return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred); -+ return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred, this.playerBrand, this.pluginMessagerChannels); // Paper ++ return new CommonListenerCookie(this.playerProfile(), this.latency, clientInformation, this.transferred, this.clientBrand, this.pluginMessagerChannels); // Paper } } diff --git a/paper-server/src/main/java/io/papermc/paper/connection/PaperCommonConnection.java b/paper-server/src/main/java/io/papermc/paper/connection/PaperCommonConnection.java index eb3c167d0bb5..5cc97ae87c1d 100644 --- a/paper-server/src/main/java/io/papermc/paper/connection/PaperCommonConnection.java +++ b/paper-server/src/main/java/io/papermc/paper/connection/PaperCommonConnection.java @@ -70,6 +70,11 @@ public T getClientOption(ClientOption type) { throw new RuntimeException("Unknown settings type"); } + @Override + public @Nullable String getClientBrandName() { + return this.packetListener.clientBrand; + } + @Override public void disconnect(final Component component) { this.packetListener.disconnect(PaperAdventure.asVanilla(component), DisconnectionReason.UNKNOWN); diff --git a/paper-server/src/main/java/io/papermc/paper/connection/PaperPlayerGameConnection.java b/paper-server/src/main/java/io/papermc/paper/connection/PaperPlayerGameConnection.java index ac252f2f43f1..0b17dc4eb635 100644 --- a/paper-server/src/main/java/io/papermc/paper/connection/PaperPlayerGameConnection.java +++ b/paper-server/src/main/java/io/papermc/paper/connection/PaperPlayerGameConnection.java @@ -8,8 +8,8 @@ public class PaperPlayerGameConnection extends PaperCommonConnection implements PlayerGameConnection { - public PaperPlayerGameConnection(final ServerGamePacketListenerImpl serverConfigurationPacketListenerImpl) { - super(serverConfigurationPacketListenerImpl); + public PaperPlayerGameConnection(final ServerGamePacketListenerImpl packetListener) { + super(packetListener); } @Override diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index 2e95fee39986..071aaa500a21 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -138,7 +138,6 @@ import net.minecraft.world.level.storage.ValueOutput; import org.bukkit.BanEntry; import org.bukkit.BanList; -import org.bukkit.Bukkit; import org.bukkit.DyeColor; import org.bukkit.Effect; import org.bukkit.EntityEffect; @@ -188,7 +187,6 @@ import org.bukkit.craftbukkit.conversations.ConversationTracker; import org.bukkit.craftbukkit.event.CraftEventFactory; import org.bukkit.craftbukkit.inventory.CraftItemStack; -import org.bukkit.craftbukkit.inventory.CraftRecipe; import org.bukkit.craftbukkit.map.CraftMapCursor; import org.bukkit.craftbukkit.map.CraftMapView; import org.bukkit.craftbukkit.map.RenderData; @@ -3097,12 +3095,13 @@ public int getPing() { // Paper end }; - // Paper start - brand support @Override public String getClientBrandName() { - return getHandle().connection.playerBrand; + if (this.getHandle().connection == null) { + return null; + } + return this.getHandle().connection.clientBrand; } - // Paper end // Paper start @Override