diff --git a/api/src/main/java/com/velocitypowered/api/proxy/Player.java b/api/src/main/java/com/velocitypowered/api/proxy/Player.java index 813f3af51..f4207477d 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/Player.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/Player.java @@ -237,4 +237,12 @@ public interface Player extends CommandSource, Identified, InboundConnection, return HoverEvent.showEntity(op.apply(HoverEvent.ShowEntity.of(this, getUniqueId(), Component.text(getUsername())))); } + + + /** + * Gets the player's client brand. + * + * @return the player's client brand + */ + @Nullable String getClientBrand(); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java index e26814244..6d4e735f8 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientPlaySessionHandler.java @@ -230,6 +230,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { player.getKnownChannels().removeAll(PluginMessageUtil.getChannels(packet)); backendConn.write(packet.retain()); } else if (PluginMessageUtil.isMcBrand(packet)) { + player.setClientBrand(PluginMessageUtil.readBrandMessage(packet.content())); backendConn.write(PluginMessageUtil .rewriteMinecraftBrand(packet, server.getVersion(), player.getProtocolVersion())); } else if (BungeeCordMessageResponder.isBungeeCordMessage(packet)) { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java index f8c0e973e..4ff218301 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java @@ -144,6 +144,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { .withDynamic(Identity.NAME, this::getUsername) .withStatic(PermissionChecker.POINTER, getPermissionChecker()) .build(); + private @Nullable String clientBrand; ConnectedPlayer(VelocityServer server, GameProfile profile, MinecraftConnection connection, @Nullable InetSocketAddress virtualHost, boolean onlineMode) { @@ -761,6 +762,15 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { return true; } + @Override + public String getClientBrand() { + return clientBrand; + } + + void setClientBrand(String clientBrand) { + this.clientBrand = clientBrand; + } + @Override public void spoofChatInput(String input) { Preconditions.checkArgument(input.length() <= Chat.MAX_SERVERBOUND_MESSAGE_LENGTH, diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java index bad56a843..51787428c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/util/PluginMessageUtil.java @@ -139,11 +139,15 @@ public final class PluginMessageUtil { return new PluginMessage(message.getChannel(), rewrittenBuf); } - private static String readBrandMessage(ByteBuf content) { - // Some clients (mostly poorly-implemented bots) do not send validly-formed brand messages. - // In order to accommodate their broken behavior, we'll first try to read in the 1.8 format, and - // if that fails, treat it as a 1.7-format message (which has no prefixed length). (The message - // Velocity sends will be in the correct format depending on the protocol.) + /** + * Some clients (mostly poorly-implemented bots) do not send validly-formed brand messages. + * In order to accommodate their broken behavior, we'll first try to read in the 1.8 format, and + * if that fails, treat it as a 1.7-format message (which has no prefixed length). (The message + * Velocity sends will be in the correct format depending on the protocol.) + * @param content the brand packet + * @return the client brand + */ + public static String readBrandMessage(ByteBuf content) { try { return ProtocolUtils.readString(content.slice()); } catch (Exception e) {