From d47848cb93a5f94a95059c2794c0197df6360f29 Mon Sep 17 00:00:00 2001 From: Emil <12966472+Emilxyz@users.noreply.github.com> Date: Sat, 2 Aug 2025 18:43:06 +0200 Subject: [PATCH 01/36] feat: map show_dialog & clear_dialog in CONFIG state (#1621) --- .../connection/MinecraftSessionHandler.java | 10 +++ .../proxy/protocol/StateRegistry.java | 6 ++ .../protocol/packet/DialogClearPacket.java | 45 +++++++++++++ .../protocol/packet/DialogShowPacket.java | 64 +++++++++++++++++++ 4 files changed, 125 insertions(+) create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/DialogClearPacket.java create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/DialogShowPacket.java diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftSessionHandler.java index b36d9f0a..c25980d9 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftSessionHandler.java @@ -24,6 +24,8 @@ import com.velocitypowered.proxy.protocol.packet.BundleDelimiterPacket; import com.velocitypowered.proxy.protocol.packet.ClientSettingsPacket; import com.velocitypowered.proxy.protocol.packet.ClientboundCookieRequestPacket; import com.velocitypowered.proxy.protocol.packet.ClientboundStoreCookiePacket; +import com.velocitypowered.proxy.protocol.packet.DialogClearPacket; +import com.velocitypowered.proxy.protocol.packet.DialogShowPacket; import com.velocitypowered.proxy.protocol.packet.DisconnectPacket; import com.velocitypowered.proxy.protocol.packet.EncryptionRequestPacket; import com.velocitypowered.proxy.protocol.packet.EncryptionResponsePacket; @@ -364,4 +366,12 @@ public interface MinecraftSessionHandler { default boolean handle(ClientboundServerLinksPacket packet) { return false; } + + default boolean handle(DialogClearPacket packet) { + return false; + } + + default boolean handle(DialogShowPacket packet) { + return false; + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java index f8c761ac..d5e135a3 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java @@ -59,6 +59,8 @@ import com.velocitypowered.proxy.protocol.packet.BundleDelimiterPacket; import com.velocitypowered.proxy.protocol.packet.ClientSettingsPacket; import com.velocitypowered.proxy.protocol.packet.ClientboundCookieRequestPacket; import com.velocitypowered.proxy.protocol.packet.ClientboundStoreCookiePacket; +import com.velocitypowered.proxy.protocol.packet.DialogClearPacket; +import com.velocitypowered.proxy.protocol.packet.DialogShowPacket; import com.velocitypowered.proxy.protocol.packet.DisconnectPacket; import com.velocitypowered.proxy.protocol.packet.EncryptionRequestPacket; import com.velocitypowered.proxy.protocol.packet.EncryptionResponsePacket; @@ -237,6 +239,10 @@ public enum StateRegistry { map(0x0F, MINECRAFT_1_21, false)); clientbound.register(ClientboundServerLinksPacket.class, ClientboundServerLinksPacket::new, map(0x10, MINECRAFT_1_21, false)); + clientbound.register(DialogClearPacket.class, () -> DialogClearPacket.INSTANCE, + map(0x11, MINECRAFT_1_21_6, false)); + clientbound.register(DialogShowPacket.class, () -> new DialogShowPacket(this), + map(0x12, MINECRAFT_1_21_6, false)); } }, PLAY { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/DialogClearPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/DialogClearPacket.java new file mode 100644 index 00000000..4188abfd --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/DialogClearPacket.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2018-2025 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.protocol.packet; + +import com.velocitypowered.api.network.ProtocolVersion; +import com.velocitypowered.proxy.connection.MinecraftSessionHandler; +import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; +import io.netty.buffer.ByteBuf; + +public class DialogClearPacket implements MinecraftPacket { + + public static final DialogClearPacket INSTANCE = new DialogClearPacket(); + + private DialogClearPacket() { + } + + @Override + public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { + } + + @Override + public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { + } + + @Override + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/DialogShowPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/DialogShowPacket.java new file mode 100644 index 00000000..67d4b8f8 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/DialogShowPacket.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2018-2025 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.protocol.packet; + +import com.velocitypowered.api.network.ProtocolVersion; +import com.velocitypowered.proxy.connection.MinecraftSessionHandler; +import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolUtils; +import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; +import com.velocitypowered.proxy.protocol.StateRegistry; +import io.netty.buffer.ByteBuf; +import net.kyori.adventure.nbt.BinaryTag; +import net.kyori.adventure.nbt.BinaryTagIO; + +public class DialogShowPacket implements MinecraftPacket { + + private final StateRegistry state; + private int id; + private BinaryTag nbt; + + public DialogShowPacket(final StateRegistry state) { + this.state = state; + } + + @Override + public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { + this.id = this.state == StateRegistry.CONFIG ? 0 : ProtocolUtils.readVarInt(buf); + if (this.id == 0) { + this.nbt = ProtocolUtils.readBinaryTag(buf, protocolVersion, BinaryTagIO.reader()); + } + } + + @Override + public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { + if (this.state == StateRegistry.CONFIG) { + ProtocolUtils.writeBinaryTag(buf, protocolVersion, this.nbt); + } else { + ProtocolUtils.writeVarInt(buf, this.id); + if (this.id == 0) { + ProtocolUtils.writeBinaryTag(buf, protocolVersion, this.nbt); + } + } + } + + @Override + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); + } +} From db8d16fd6e386ba6f486ce1f40a1b9d2bb1a807a Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Fri, 8 Aug 2025 21:44:05 -0400 Subject: [PATCH 02/36] Bump to Netty 4.2.3 Closes #1615 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fb1e863f..84b9f840 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ configurate3 = "3.7.3" configurate4 = "4.1.2" flare = "2.0.1" log4j = "2.24.3" -netty = "4.2.1.Final" +netty = "4.2.3.Final" [plugins] fill = "io.papermc.fill.gradle:1.0.3" From 49e2988e3745d1aa015855ce64428a09c56a958a Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Fri, 8 Aug 2025 21:47:49 -0400 Subject: [PATCH 03/36] Utilize `ByteBuf.readString()` --- .../java/com/velocitypowered/proxy/protocol/ProtocolUtils.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java index a90ee9dc..5b54e0f0 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java @@ -279,8 +279,7 @@ public enum ProtocolUtils { checkFrame(buf.isReadable(length), "Trying to read a string that is too long (wanted %s, only have %s)", length, buf.readableBytes()); - String str = buf.toString(buf.readerIndex(), length, StandardCharsets.UTF_8); - buf.skipBytes(length); + String str = buf.readString(length, StandardCharsets.UTF_8); checkFrame(str.length() <= cap, "Got a too-long string (got %s, max %s)", str.length(), cap); return str; } From a509a878e9006d2fe0cae42b57bff7d109e6910c Mon Sep 17 00:00:00 2001 From: Timon Seidel Date: Wed, 13 Aug 2025 21:09:52 +0200 Subject: [PATCH 04/36] [ci skip] chore: migrate legacy url (#1606) --- .../main/java/com/velocitypowered/proxy/VelocityServer.java | 5 +++-- .../proxy/command/builtin/VelocityCommand.java | 3 +-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index c3e8d472..7d55ae23 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -119,7 +119,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; */ public class VelocityServer implements ProxyServer, ForwardingAudience { - public static final String VELOCITY_URL = "https://velocitypowered.com"; + public static final String VELOCITY_URL = "https://papermc.io/software/velocity"; private static final Logger logger = LogManager.getLogger(VelocityServer.class); public static final Gson GENERAL_GSON = new GsonBuilder() @@ -216,7 +216,8 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { ProxyVersion version = getVersion(); PluginDescription description = new VelocityPluginDescription( "velocity", version.getName(), version.getVersion(), "The Velocity proxy", - VELOCITY_URL, ImmutableList.of(version.getVendor()), Collections.emptyList(), null); + version.getName().equals("Velocity") ? VELOCITY_URL : null, + ImmutableList.of(version.getVendor()), Collections.emptyList(), null); VelocityPluginContainer container = new VelocityPluginContainer(description); container.setInstance(VelocityVirtualPlugin.INSTANCE); return container; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/VelocityCommand.java b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/VelocityCommand.java index 1e393476..6b233b87 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/VelocityCommand.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/builtin/VelocityCommand.java @@ -176,8 +176,7 @@ public final class VelocityCommand { .append(Component.text() .content("PaperMC") .color(NamedTextColor.GREEN) - .clickEvent( - ClickEvent.openUrl("https://papermc.io/software/velocity")) + .clickEvent(ClickEvent.openUrl(VelocityServer.VELOCITY_URL)) .build()) .append(Component.text(" - ")) .append(Component.text() From 5d450ab3c74ae7ccca13abe2cde20b8473fe64e4 Mon Sep 17 00:00:00 2001 From: Gero Date: Wed, 13 Aug 2025 22:12:15 +0200 Subject: [PATCH 05/36] Support all component-like and literal tooltips and errors (#1600) --- .../proxy/command/VelocityCommandManager.java | 6 +++--- .../client/ClientPlaySessionHandler.java | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java index 21f82f34..095445bf 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/VelocityCommandManager.java @@ -35,7 +35,6 @@ import com.velocitypowered.api.command.CommandManager; import com.velocitypowered.api.command.CommandMeta; import com.velocitypowered.api.command.CommandResult; import com.velocitypowered.api.command.CommandSource; -import com.velocitypowered.api.command.VelocityBrigadierMessage; import com.velocitypowered.api.event.command.CommandExecuteEvent; import com.velocitypowered.api.event.command.PostCommandInvocationEvent; import com.velocitypowered.api.plugin.PluginManager; @@ -59,6 +58,7 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Collectors; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentLike; import net.kyori.adventure.text.format.NamedTextColor; import org.checkerframework.checker.lock.qual.GuardedBy; import org.checkerframework.checker.nullness.qual.Nullable; @@ -242,8 +242,8 @@ public class VelocityCommandManager implements CommandManager { CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownCommand()); if (isSyntaxError) { final Message message = e.getRawMessage(); - if (message instanceof VelocityBrigadierMessage velocityMessage) { - source.sendMessage(velocityMessage.asComponent().applyFallbackStyle(NamedTextColor.RED)); + if (message instanceof ComponentLike componentLike) { + source.sendMessage(componentLike.asComponent().applyFallbackStyle(NamedTextColor.RED)); } else { source.sendMessage(Component.text(e.getMessage(), NamedTextColor.RED)); } 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 10b837fa..4b658cd6 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 @@ -21,7 +21,6 @@ import static com.velocitypowered.proxy.protocol.util.PluginMessageUtil.construc import com.google.common.collect.ImmutableList; import com.mojang.brigadier.suggestion.Suggestion; -import com.velocitypowered.api.command.VelocityBrigadierMessage; import com.velocitypowered.api.event.connection.PluginMessageEvent; import com.velocitypowered.api.event.player.CookieReceiveEvent; import com.velocitypowered.api.event.player.PlayerChannelRegisterEvent; @@ -87,6 +86,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentLinkedQueue; import net.kyori.adventure.key.Key; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentLike; import net.kyori.adventure.text.format.NamedTextColor; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -698,10 +698,10 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { for (Suggestion suggestion : suggestions.getList()) { String offer = suggestion.getText(); ComponentHolder tooltip = null; - if (suggestion.getTooltip() != null - && suggestion.getTooltip() instanceof VelocityBrigadierMessage) { - tooltip = new ComponentHolder(player.getProtocolVersion(), - ((VelocityBrigadierMessage) suggestion.getTooltip()).asComponent()); + if (suggestion.getTooltip() instanceof ComponentLike componentLike) { + tooltip = new ComponentHolder(player.getProtocolVersion(), componentLike.asComponent()); + } else if (suggestion.getTooltip() != null) { + tooltip = new ComponentHolder(player.getProtocolVersion(), Component.text(suggestion.getTooltip().getString())); } offers.add(new Offer(offer, tooltip)); } @@ -765,10 +765,10 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { offer = offer.substring(command.length()); } ComponentHolder tooltip = null; - if (suggestion.getTooltip() != null - && suggestion.getTooltip() instanceof VelocityBrigadierMessage) { - tooltip = new ComponentHolder(player.getProtocolVersion(), - ((VelocityBrigadierMessage) suggestion.getTooltip()).asComponent()); + if (suggestion.getTooltip() instanceof ComponentLike componentLike) { + tooltip = new ComponentHolder(player.getProtocolVersion(), componentLike.asComponent()); + } else if (suggestion.getTooltip() != null) { + tooltip = new ComponentHolder(player.getProtocolVersion(), Component.text(suggestion.getTooltip().getString())); } response.getOffers().add(new Offer(offer, tooltip)); } From 946e5c47d46d52eff3699437a474b79bfbfe43c3 Mon Sep 17 00:00:00 2001 From: Emil <12966472+Emilxyz@users.noreply.github.com> Date: Thu, 14 Aug 2025 18:43:20 +0200 Subject: [PATCH 06/36] fix: send callback command to >= 1.21.6 clients (#1627) --- .../connection/backend/BackendPlaySessionHandler.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java index 7c89f659..eb9c9bca 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java @@ -359,7 +359,12 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { // Inject commands from the proxy. final CommandGraphInjector injector = server.getCommandManager().getInjector(); injector.inject(rootNode, serverConn.getPlayer()); - rootNode.removeChildByName("velocity:callback"); + + // In 1.21.6 a confirmation prompt was added when executing a command via `run_command` click + // action if the command is unknown. To prevent this prompt we have to send the command. + if (this.playerConnection.getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_21_6)) { + rootNode.removeChildByName("velocity:callback"); + } } server.getEventManager().fire( From 60a22ff330086778fbcf3fa15858fe6d518d34fe Mon Sep 17 00:00:00 2001 From: Emil <12966472+Emilxyz@users.noreply.github.com> Date: Thu, 14 Aug 2025 18:44:06 +0200 Subject: [PATCH 07/36] chore: bump adventure to 4.24.0 (#1628) --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 84b9f840..47a216c1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,8 +12,8 @@ shadow = "com.gradleup.shadow:8.3.6" spotless = "com.diffplug.spotless:6.25.0" [libraries] -adventure-bom = "net.kyori:adventure-bom:4.23.0" -adventure-text-serializer-json-legacy-impl = "net.kyori:adventure-text-serializer-json-legacy-impl:4.23.0" +adventure-bom = "net.kyori:adventure-bom:4.24.0" +adventure-text-serializer-json-legacy-impl = "net.kyori:adventure-text-serializer-json-legacy-impl:4.24.0" adventure-facet = "net.kyori:adventure-platform-facet:4.3.4" asm = "org.ow2.asm:asm:9.8" auto-service = "com.google.auto.service:auto-service:1.0.1" From d2d333a958af801a7b09465aa7402b0f7857aeb2 Mon Sep 17 00:00:00 2001 From: Shane Freeder Date: Thu, 14 Aug 2025 21:14:51 +0100 Subject: [PATCH 08/36] Bumpy netty to 4.2.4 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 47a216c1..84bba38e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ configurate3 = "3.7.3" configurate4 = "4.1.2" flare = "2.0.1" log4j = "2.24.3" -netty = "4.2.3.Final" +netty = "4.2.4.Final" [plugins] fill = "io.papermc.fill.gradle:1.0.3" From bfd15e1a816b0928d51abef03333166ecc225358 Mon Sep 17 00:00:00 2001 From: Timon Seidel Date: Sun, 31 Aug 2025 17:14:32 +0200 Subject: [PATCH 09/36] fix: kick logging ignoring config (#1636) --- .../proxy/connection/client/ConnectedPlayer.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) 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 1fb3884b..6d32535f 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 @@ -733,15 +733,19 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, Component disconnectReason = disconnect.getReason().getComponent(); String plainTextReason = PASS_THRU_TRANSLATE.serialize(disconnectReason); if (connectedServer != null && connectedServer.getServerInfo().equals(server.getServerInfo())) { - logger.info("{}: kicked from server {}: {}", this, server.getServerInfo().getName(), - plainTextReason); + if (this.server.getConfiguration().isLogPlayerConnections()) { + logger.info("{}: kicked from server {}: {}", this, server.getServerInfo().getName(), + plainTextReason); + } handleConnectionException(server, disconnectReason, Component.translatable("velocity.error.moved-to-new-server", NamedTextColor.RED, Component.text(server.getServerInfo().getName()), disconnectReason), safe); } else { - logger.error("{}: disconnected while connecting to {}: {}", this, - server.getServerInfo().getName(), plainTextReason); + if (this.server.getConfiguration().isLogPlayerConnections()) { + logger.error("{}: disconnected while connecting to {}: {}", this, + server.getServerInfo().getName(), plainTextReason); + } handleConnectionException(server, disconnectReason, Component.translatable("velocity.error.cant-connect", NamedTextColor.RED, Component.text(server.getServerInfo().getName()), From 311e2bc18da4864bc1353d7b238df93501cc77bb Mon Sep 17 00:00:00 2001 From: Timon Seidel Date: Wed, 3 Sep 2025 04:44:52 +0200 Subject: [PATCH 10/36] fix(temp): pass though custom click action in config state (#1640) --- .../connection/MinecraftSessionHandler.java | 6 +++ .../proxy/protocol/StateRegistry.java | 3 ++ .../ServerboundCustomClickActionPacket.java | 48 +++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCustomClickActionPacket.java diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftSessionHandler.java index c25980d9..a3ba0005 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftSessionHandler.java @@ -50,6 +50,7 @@ import com.velocitypowered.proxy.protocol.packet.ServerDataPacket; import com.velocitypowered.proxy.protocol.packet.ServerLoginPacket; import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccessPacket; import com.velocitypowered.proxy.protocol.packet.ServerboundCookieResponsePacket; +import com.velocitypowered.proxy.protocol.packet.ServerboundCustomClickActionPacket; import com.velocitypowered.proxy.protocol.packet.SetCompressionPacket; import com.velocitypowered.proxy.protocol.packet.StatusPingPacket; import com.velocitypowered.proxy.protocol.packet.StatusRequestPacket; @@ -374,4 +375,9 @@ public interface MinecraftSessionHandler { default boolean handle(DialogShowPacket packet) { return false; } + + default boolean handle(ServerboundCustomClickActionPacket packet) { + return false; + } + } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java index d5e135a3..e9ff3289 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java @@ -83,6 +83,7 @@ import com.velocitypowered.proxy.protocol.packet.ServerDataPacket; import com.velocitypowered.proxy.protocol.packet.ServerLoginPacket; import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccessPacket; import com.velocitypowered.proxy.protocol.packet.ServerboundCookieResponsePacket; +import com.velocitypowered.proxy.protocol.packet.ServerboundCustomClickActionPacket; import com.velocitypowered.proxy.protocol.packet.SetCompressionPacket; import com.velocitypowered.proxy.protocol.packet.StatusPingPacket; import com.velocitypowered.proxy.protocol.packet.StatusRequestPacket; @@ -185,6 +186,8 @@ public enum StateRegistry { KnownPacksPacket.class, KnownPacksPacket::new, map(0x07, MINECRAFT_1_20_5, false)); + serverbound.register(ServerboundCustomClickActionPacket.class, ServerboundCustomClickActionPacket::new, + map(0x08, MINECRAFT_1_21_6, false)); clientbound.register( ClientboundCookieRequestPacket.class, ClientboundCookieRequestPacket::new, diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCustomClickActionPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCustomClickActionPacket.java new file mode 100644 index 00000000..3659d7fc --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCustomClickActionPacket.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2018-2025 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.protocol.packet; + +import com.velocitypowered.api.network.ProtocolVersion; +import com.velocitypowered.proxy.connection.MinecraftSessionHandler; +import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolUtils; +import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder; +import io.netty.buffer.ByteBuf; + +public class ServerboundCustomClickActionPacket extends DeferredByteBufHolder implements MinecraftPacket { + + public ServerboundCustomClickActionPacket() { + super(null); + } + + @Override + public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { + replace(buf.readRetainedSlice(buf.readableBytes())); + } + + @Override + public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { + buf.writeBytes(content()); + } + + @Override + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); + } + +} From 180af8c8445956bcfd24b55fe057e077641231c4 Mon Sep 17 00:00:00 2001 From: Emil <12966472+Emilxyz@users.noreply.github.com> Date: Wed, 3 Sep 2025 18:09:12 +0200 Subject: [PATCH 11/36] fix(resourcepack): apply server-side translations to resource pack prompt (#1611) --- .../player/resourcepack/handler/ResourcePackHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/handler/ResourcePackHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/handler/ResourcePackHandler.java index e5df9f65..b3843885 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/handler/ResourcePackHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/handler/ResourcePackHandler.java @@ -111,7 +111,7 @@ public abstract sealed class ResourcePackHandler } request.setRequired(queued.getShouldForce()); request.setPrompt(queued.getPrompt() == null ? null : - new ComponentHolder(player.getProtocolVersion(), queued.getPrompt())); + new ComponentHolder(player.getProtocolVersion(), player.translateMessage(queued.getPrompt()))); player.getConnection().write(request); } From 1532fb360b83663f7dd52ed511fe4c70af44df26 Mon Sep 17 00:00:00 2001 From: Emil <12966472+Emilxyz@users.noreply.github.com> Date: Wed, 3 Sep 2025 23:43:02 +0200 Subject: [PATCH 12/36] fix: forward custom click packet in config state (#1641) --- .../client/ClientConfigSessionHandler.java | 11 +++++++ .../ServerboundCustomClickActionPacket.java | 30 +++++++++---------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java index dc033f57..6117721a 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java @@ -40,6 +40,7 @@ import com.velocitypowered.proxy.protocol.packet.PingIdentifyPacket; import com.velocitypowered.proxy.protocol.packet.PluginMessagePacket; import com.velocitypowered.proxy.protocol.packet.ResourcePackResponsePacket; import com.velocitypowered.proxy.protocol.packet.ServerboundCookieResponsePacket; +import com.velocitypowered.proxy.protocol.packet.ServerboundCustomClickActionPacket; import com.velocitypowered.proxy.protocol.packet.config.FinishedUpdatePacket; import com.velocitypowered.proxy.protocol.packet.config.KnownPacksPacket; import com.velocitypowered.proxy.protocol.util.PluginMessageUtil; @@ -205,6 +206,16 @@ public class ClientConfigSessionHandler implements MinecraftSessionHandler { return true; } + @Override + public boolean handle(ServerboundCustomClickActionPacket packet) { + if (player.getConnectionInFlight() != null) { + player.getConnectionInFlight().ensureConnected().write(packet.retain()); + return true; + } + + return false; + } + @Override public void handleGeneric(MinecraftPacket packet) { VelocityServerConnection serverConnection = player.getConnectedServer(); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCustomClickActionPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCustomClickActionPacket.java index 3659d7fc..2092b260 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCustomClickActionPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCustomClickActionPacket.java @@ -26,23 +26,23 @@ import io.netty.buffer.ByteBuf; public class ServerboundCustomClickActionPacket extends DeferredByteBufHolder implements MinecraftPacket { - public ServerboundCustomClickActionPacket() { - super(null); - } + public ServerboundCustomClickActionPacket() { + super(null); + } - @Override - public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - replace(buf.readRetainedSlice(buf.readableBytes())); - } + @Override + public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { + replace(buf.readRetainedSlice(buf.readableBytes())); + } - @Override - public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { - buf.writeBytes(content()); - } + @Override + public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { + buf.writeBytes(content()); + } - @Override - public boolean handle(MinecraftSessionHandler handler) { - return handler.handle(this); - } + @Override + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); + } } From 6e80f57739d5faea3b4e85c993c3649fe5e98cd7 Mon Sep 17 00:00:00 2001 From: VelVeV <147647046+VelVeV@users.noreply.github.com> Date: Mon, 8 Sep 2025 03:05:41 +0900 Subject: [PATCH 13/36] Bump to Netty 4.2.5 (#1643) --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 84bba38e..c44264fa 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ configurate3 = "3.7.3" configurate4 = "4.1.2" flare = "2.0.1" log4j = "2.24.3" -netty = "4.2.4.Final" +netty = "4.2.5.Final" [plugins] fill = "io.papermc.fill.gradle:1.0.3" From 8406979e71da1ff4e7d1653e50aeffa338783187 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=91=95=E1=96=87EE=E1=91=ADY=E1=91=95=E1=96=87EE?= =?UTF-8?q?=E1=91=ADE=E1=96=87?= <121470455+Maxsh001@users.noreply.github.com> Date: Thu, 18 Sep 2025 01:17:48 +0800 Subject: [PATCH 14/36] Fix Weird Behavior in the Annotation Processor (#1645) * fix: weird behavior of the annotation processor * optimize imports --- .../api/plugin/ap/PluginAnnotationProcessor.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/api/src/ap/java/com/velocitypowered/api/plugin/ap/PluginAnnotationProcessor.java b/api/src/ap/java/com/velocitypowered/api/plugin/ap/PluginAnnotationProcessor.java index d1c7be51..b44f2847 100644 --- a/api/src/ap/java/com/velocitypowered/api/plugin/ap/PluginAnnotationProcessor.java +++ b/api/src/ap/java/com/velocitypowered/api/plugin/ap/PluginAnnotationProcessor.java @@ -14,7 +14,6 @@ import com.velocitypowered.api.plugin.Plugin; import java.io.BufferedWriter; import java.io.IOException; import java.io.Writer; -import java.util.Objects; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.ProcessingEnvironment; @@ -68,8 +67,8 @@ public class PluginAnnotationProcessor extends AbstractProcessor { Name qualifiedName = ((TypeElement) element).getQualifiedName(); - if (Objects.equals(pluginClassFound, qualifiedName.toString())) { - if (!warnedAboutMultiplePlugins) { + if (pluginClassFound != null) { + if (!pluginClassFound.equals(qualifiedName.toString()) && !warnedAboutMultiplePlugins) { environment.getMessager() .printMessage(Diagnostic.Kind.WARNING, "Velocity does not yet currently support " + "multiple plugins. We are using " + pluginClassFound From 87f74eaeda442162e957bc071f09243cb21f8614 Mon Sep 17 00:00:00 2001 From: Ecconia <18501527+Ecconia@users.noreply.github.com> Date: Mon, 22 Sep 2025 21:34:06 +0200 Subject: [PATCH 15/36] [ci skip] Improve documentation for priority parameter in EventManager (#1619) * Improve documentation for priority parameter in EventManager PostOrder was deprecated in commit (4f227badc20dc30b0f6d84b5349c8809481dcbb1) in favor of priorities. PostOrder itself was very descriptive on which PostOrder is processed first. A number cannot be descriptive about that - it is never clear if higher or lower numbers are processed first. The Subscribe event attribute does contain a description on how priorities are evaluated. The EventManager did not, which literally did confused developers manually registering events. This commit fixes this by describing the priority argument in EventManager with the same description that Subscribe uses. * Fixed checkstyle --------- Co-authored-by: Adrian Gonzales --- .../main/java/com/velocitypowered/api/event/EventManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/src/main/java/com/velocitypowered/api/event/EventManager.java b/api/src/main/java/com/velocitypowered/api/event/EventManager.java index b8702ab3..ed11986c 100644 --- a/api/src/main/java/com/velocitypowered/api/event/EventManager.java +++ b/api/src/main/java/com/velocitypowered/api/event/EventManager.java @@ -60,7 +60,8 @@ public interface EventManager { * * @param plugin the plugin to associate with the handler * @param eventClass the class for the event handler to register - * @param postOrder the relative order in which events should be posted to the handler + * @param postOrder the relative order in which events should be posted to the handler. The higher + * the priority, the earlier the event handler will be called * @param handler the handler to register * @param the event type to handle */ From 37f622f22633bca83921fd7c37835e9a19eeb9ba Mon Sep 17 00:00:00 2001 From: DartCZ Date: Mon, 22 Sep 2025 23:27:25 +0200 Subject: [PATCH 16/36] feat: add ProxyPreShutdownEvent before players are disconnected (#1626) * feat: delay player disconnect until ProxyShutdownEvent completes * fix: added back empty line * feat: added ProxyPreShutdownEvent * feat: CR changes * chore: fixed license, annotated with Beta annotation * Update proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java Co-authored-by: Timon Seidel * Update proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java Co-authored-by: Timon Seidel * chore: consolidated log message * Update proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java Co-authored-by: Timon Seidel * Update proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java Co-authored-by: powercas_gamer * Update api/src/main/java/com/velocitypowered/api/event/proxy/ProxyPreShutdownEvent.java Co-authored-by: powercas_gamer * feat: make ProxyPreShutdownEvent timeout configurable via system property * fix: cs * Document velocity.pre-shutdown-timeout system property --------- Co-authored-by: Timon Seidel Co-authored-by: powercas_gamer Co-authored-by: Adrian Gonzales --- .../event/proxy/ProxyPreShutdownEvent.java | 33 +++++++++++++++++++ .../velocitypowered/proxy/VelocityServer.java | 17 ++++++++++ 2 files changed, 50 insertions(+) create mode 100644 api/src/main/java/com/velocitypowered/api/event/proxy/ProxyPreShutdownEvent.java diff --git a/api/src/main/java/com/velocitypowered/api/event/proxy/ProxyPreShutdownEvent.java b/api/src/main/java/com/velocitypowered/api/event/proxy/ProxyPreShutdownEvent.java new file mode 100644 index 00000000..893942c0 --- /dev/null +++ b/api/src/main/java/com/velocitypowered/api/event/proxy/ProxyPreShutdownEvent.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2018-2025 Velocity Contributors + * + * The Velocity API is licensed under the terms of the MIT License. For more details, + * reference the LICENSE file in the api top-level directory. + */ + +package com.velocitypowered.api.event.proxy; + +import com.google.common.annotations.Beta; +import com.velocitypowered.api.event.annotation.AwaitingEvent; + +/** + * This event is fired by the proxy after it has stopped accepting new connections, + * but before players are disconnected. + * This is the last point at which you can interact with currently connected players, + * for example to transfer them to another proxy or perform other cleanup tasks. + * + * @implNote Velocity will wait for all event listeners to complete before disconnecting players, + * but note that the event will time out after the configured value of the + * velocity.pre-shutdown-timeout system property, default 10 seconds, + * in seconds to prevent shutdown from hanging indefinitely + * @since 3.4.0 + */ +@Beta +@AwaitingEvent +public final class ProxyPreShutdownEvent { + + @Override + public String toString() { + return "ProxyPreShutdownEvent"; + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index 7d55ae23..ccfc5f14 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -24,6 +24,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.velocitypowered.api.command.BrigadierCommand; import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; +import com.velocitypowered.api.event.proxy.ProxyPreShutdownEvent; import com.velocitypowered.api.event.proxy.ProxyReloadEvent; import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; import com.velocitypowered.api.network.ProtocolVersion; @@ -150,6 +151,8 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { ) .registerTypeHierarchyAdapter(Favicon.class, FaviconSerializer.INSTANCE) .create(); + private static final int PRE_SHUTDOWN_TIMEOUT = + Integer.getInteger("velocity.pre-shutdown-timeout", 10); private final ConnectionManager cm; private final ProxyOptions options; @@ -579,6 +582,20 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { // done first to refuse new connections cm.shutdown(); + try { + eventManager.fire(new ProxyPreShutdownEvent()) + .toCompletableFuture() + .get(PRE_SHUTDOWN_TIMEOUT, TimeUnit.SECONDS); + } catch (TimeoutException ignored) { + logger.warn("Your plugins took over {} seconds during pre shutdown.", + PRE_SHUTDOWN_TIMEOUT); + } catch (ExecutionException ee) { + logger.error("Exception in ProxyPreShutdownEvent handler; continuing shutdown.", ee); + } catch (InterruptedException ignored) { + Thread.currentThread().interrupt(); + logger.warn("Interrupted while waiting for ProxyPreShutdownEvent; continuing shutdown."); + } + ImmutableList players = ImmutableList.copyOf(connectionsByUuid.values()); for (ConnectedPlayer player : players) { player.disconnect(reason); From ec793a9fdbbf66fcce31fe544e8f0208cb9f9520 Mon Sep 17 00:00:00 2001 From: Joo200 Date: Wed, 24 Sep 2025 07:50:05 +0200 Subject: [PATCH 17/36] Log console command executions (#1137) --- .../com/velocitypowered/proxy/console/VelocityConsole.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java b/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java index fe360d4d..90b6292f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/console/VelocityConsole.java @@ -136,6 +136,10 @@ public final class VelocityConsole extends SimpleTerminalConsole implements Cons if (!this.server.getCommandManager().executeAsync(this, command).join()) { sendMessage(Component.translatable("velocity.command.command-does-not-exist", NamedTextColor.RED)); + return; + } + if (server.getConfiguration().isLogCommandExecutions()) { + logger.info("CONSOLE -> executed command /{}", command); } } catch (Exception e) { logger.error("An error occurred while running this command.", e); From 94368d5021cb861255871a60434ba57b7930a300 Mon Sep 17 00:00:00 2001 From: Adrian Gonzales Date: Thu, 25 Sep 2025 19:28:54 -0500 Subject: [PATCH 18/36] Update publishing endpoint --- build-logic/src/main/kotlin/velocity-publish.gradle.kts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build-logic/src/main/kotlin/velocity-publish.gradle.kts b/build-logic/src/main/kotlin/velocity-publish.gradle.kts index a7cc744a..8b20d66b 100644 --- a/build-logic/src/main/kotlin/velocity-publish.gradle.kts +++ b/build-logic/src/main/kotlin/velocity-publish.gradle.kts @@ -9,9 +9,9 @@ extensions.configure { credentials(PasswordCredentials::class.java) name = if (version.toString().endsWith("SNAPSHOT")) "paperSnapshots" else "paper" // "paper" is seemingly not defined - val base = "https://repo.papermc.io/repository/maven" - val releasesRepoUrl = "$base-releases/" - val snapshotsRepoUrl = "$base-snapshots/" + val base = "https://artifactory.papermc.io/artifactory" + val releasesRepoUrl = "$base/releases/" + val snapshotsRepoUrl = "$base/snapshots/" setUrl(if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl) } } From ba014927903f72314c67c3848bde8cd4c6526fd3 Mon Sep 17 00:00:00 2001 From: Aaron <71191102+RealBauHD@users.noreply.github.com> Date: Sat, 27 Sep 2025 19:15:02 +0200 Subject: [PATCH 19/36] Minecraft 1.21.9 (#1651) * 1.21.9-pr1 - not tested yet * 1.21.9-pre2 * feat: forward code of conduct packets in CONFIG state * 1.21.9 --------- Co-authored-by: Emilxyz <12966472+Emilxyz@users.noreply.github.com> --- .../api/network/ProtocolVersion.java | 3 +- .../connection/MinecraftSessionHandler.java | 10 +++ .../backend/ConfigSessionHandler.java | 7 ++ .../client/ClientConfigSessionHandler.java | 11 +++ .../proxy/protocol/StateRegistry.java | 69 +++++++++++++------ .../config/CodeOfConductAcceptPacket.java | 45 ++++++++++++ .../packet/config/CodeOfConductPacket.java | 47 +++++++++++++ 7 files changed, 171 insertions(+), 21 deletions(-) create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/CodeOfConductAcceptPacket.java create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/CodeOfConductPacket.java diff --git a/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java b/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java index 039fec40..916e3db5 100644 --- a/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java +++ b/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java @@ -92,7 +92,8 @@ public enum ProtocolVersion implements Ordered { MINECRAFT_1_21_4(769, "1.21.4"), MINECRAFT_1_21_5(770, "1.21.5"), MINECRAFT_1_21_6(771, "1.21.6"), - MINECRAFT_1_21_7(772, "1.21.7", "1.21.8"); + MINECRAFT_1_21_7(772, "1.21.7", "1.21.8"), + MINECRAFT_1_21_9(773, "1.21.9"); private static final int SNAPSHOT_BIT = 30; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftSessionHandler.java index a3ba0005..94620218 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftSessionHandler.java @@ -70,6 +70,8 @@ import com.velocitypowered.proxy.protocol.packet.chat.session.SessionPlayerComma import com.velocitypowered.proxy.protocol.packet.config.ActiveFeaturesPacket; import com.velocitypowered.proxy.protocol.packet.config.ClientboundCustomReportDetailsPacket; import com.velocitypowered.proxy.protocol.packet.config.ClientboundServerLinksPacket; +import com.velocitypowered.proxy.protocol.packet.config.CodeOfConductAcceptPacket; +import com.velocitypowered.proxy.protocol.packet.config.CodeOfConductPacket; import com.velocitypowered.proxy.protocol.packet.config.FinishedUpdatePacket; import com.velocitypowered.proxy.protocol.packet.config.KnownPacksPacket; import com.velocitypowered.proxy.protocol.packet.config.RegistrySyncPacket; @@ -380,4 +382,12 @@ public interface MinecraftSessionHandler { return false; } + default boolean handle(CodeOfConductPacket packet) { + return false; + } + + default boolean handle(CodeOfConductAcceptPacket packet) { + return false; + } + } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java index fb0e48d1..90bb1a30 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java @@ -52,6 +52,7 @@ import com.velocitypowered.proxy.protocol.packet.ResourcePackResponsePacket; import com.velocitypowered.proxy.protocol.packet.TransferPacket; import com.velocitypowered.proxy.protocol.packet.config.ClientboundCustomReportDetailsPacket; import com.velocitypowered.proxy.protocol.packet.config.ClientboundServerLinksPacket; +import com.velocitypowered.proxy.protocol.packet.config.CodeOfConductPacket; import com.velocitypowered.proxy.protocol.packet.config.FinishedUpdatePacket; import com.velocitypowered.proxy.protocol.packet.config.RegistrySyncPacket; import com.velocitypowered.proxy.protocol.packet.config.StartUpdatePacket; @@ -358,6 +359,12 @@ public class ConfigSessionHandler implements MinecraftSessionHandler { return true; } + @Override + public boolean handle(CodeOfConductPacket packet) { + this.serverConn.getPlayer().getConnection().write(packet.retain()); + return true; + } + @Override public void disconnected() { resultFuture.completeExceptionally( diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java index 6117721a..776f99d6 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ClientConfigSessionHandler.java @@ -41,6 +41,7 @@ import com.velocitypowered.proxy.protocol.packet.PluginMessagePacket; import com.velocitypowered.proxy.protocol.packet.ResourcePackResponsePacket; import com.velocitypowered.proxy.protocol.packet.ServerboundCookieResponsePacket; import com.velocitypowered.proxy.protocol.packet.ServerboundCustomClickActionPacket; +import com.velocitypowered.proxy.protocol.packet.config.CodeOfConductAcceptPacket; import com.velocitypowered.proxy.protocol.packet.config.FinishedUpdatePacket; import com.velocitypowered.proxy.protocol.packet.config.KnownPacksPacket; import com.velocitypowered.proxy.protocol.util.PluginMessageUtil; @@ -216,6 +217,16 @@ public class ClientConfigSessionHandler implements MinecraftSessionHandler { return false; } + @Override + public boolean handle(CodeOfConductAcceptPacket packet) { + if (this.player.getConnectionInFlight() != null) { + this.player.getConnectionInFlight().ensureConnected().write(packet); + return true; + } + + return false; + } + @Override public void handleGeneric(MinecraftPacket packet) { VelocityServerConnection serverConnection = player.getConnectedServer(); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java index e9ff3289..42d60029 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java @@ -41,6 +41,7 @@ import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_2; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_4; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_5; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_6; +import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_21_9; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_7_2; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9; @@ -104,6 +105,8 @@ import com.velocitypowered.proxy.protocol.packet.chat.session.UnsignedPlayerComm import com.velocitypowered.proxy.protocol.packet.config.ActiveFeaturesPacket; import com.velocitypowered.proxy.protocol.packet.config.ClientboundCustomReportDetailsPacket; import com.velocitypowered.proxy.protocol.packet.config.ClientboundServerLinksPacket; +import com.velocitypowered.proxy.protocol.packet.config.CodeOfConductAcceptPacket; +import com.velocitypowered.proxy.protocol.packet.config.CodeOfConductPacket; import com.velocitypowered.proxy.protocol.packet.config.FinishedUpdatePacket; import com.velocitypowered.proxy.protocol.packet.config.KnownPacksPacket; import com.velocitypowered.proxy.protocol.packet.config.RegistrySyncPacket; @@ -188,6 +191,10 @@ public enum StateRegistry { map(0x07, MINECRAFT_1_20_5, false)); serverbound.register(ServerboundCustomClickActionPacket.class, ServerboundCustomClickActionPacket::new, map(0x08, MINECRAFT_1_21_6, false)); + serverbound.register( + CodeOfConductAcceptPacket.class, + () -> CodeOfConductAcceptPacket.INSTANCE, + map(0x09, MINECRAFT_1_21_9, false)); clientbound.register( ClientboundCookieRequestPacket.class, ClientboundCookieRequestPacket::new, @@ -246,6 +253,8 @@ public enum StateRegistry { map(0x11, MINECRAFT_1_21_6, false)); clientbound.register(DialogShowPacket.class, () -> new DialogShowPacket(this), map(0x12, MINECRAFT_1_21_6, false)); + clientbound.register(CodeOfConductPacket.class, CodeOfConductPacket::new, + map(0x13, MINECRAFT_1_21_9, false)); } }, PLAY { @@ -474,7 +483,8 @@ public enum StateRegistry { map(0x1A, MINECRAFT_1_19_4, false), map(0x1B, MINECRAFT_1_20_2, false), map(0x1D, MINECRAFT_1_20_5, false), - map(0x1C, MINECRAFT_1_21_5, false)); + map(0x1C, MINECRAFT_1_21_5, false), + map(0x20, MINECRAFT_1_21_9, false)); clientbound.register( KeepAlivePacket.class, KeepAlivePacket::new, @@ -493,7 +503,8 @@ public enum StateRegistry { map(0x24, MINECRAFT_1_20_2, false), map(0x26, MINECRAFT_1_20_5, false), map(0x27, MINECRAFT_1_21_2, false), - map(0x26, MINECRAFT_1_21_5, false)); + map(0x26, MINECRAFT_1_21_5, false), + map(0x2B, MINECRAFT_1_21_9, false)); clientbound.register( JoinGamePacket.class, JoinGamePacket::new, @@ -512,7 +523,8 @@ public enum StateRegistry { map(0x29, MINECRAFT_1_20_2, false), map(0x2B, MINECRAFT_1_20_5, false), map(0x2C, MINECRAFT_1_21_2, false), - map(0x2B, MINECRAFT_1_21_5, false)); + map(0x2B, MINECRAFT_1_21_5, false), + map(0x30, MINECRAFT_1_21_9, false)); clientbound.register( RespawnPacket.class, RespawnPacket::new, @@ -534,14 +546,16 @@ public enum StateRegistry { map(0x45, MINECRAFT_1_20_3, true), map(0x47, MINECRAFT_1_20_5, true), map(0x4C, MINECRAFT_1_21_2, true), - map(0x4B, MINECRAFT_1_21_5, true)); + map(0x4B, MINECRAFT_1_21_5, true), + map(0x50, MINECRAFT_1_21_9, true)); clientbound.register( RemoveResourcePackPacket.class, RemoveResourcePackPacket::new, map(0x43, MINECRAFT_1_20_3, false), map(0x45, MINECRAFT_1_20_5, false), map(0x4A, MINECRAFT_1_21_2, false), - map(0x49, MINECRAFT_1_21_5, false)); + map(0x49, MINECRAFT_1_21_5, false), + map(0x4E, MINECRAFT_1_21_9, false)); clientbound.register( ResourcePackRequestPacket.class, ResourcePackRequestPacket::new, @@ -563,7 +577,8 @@ public enum StateRegistry { map(0x44, MINECRAFT_1_20_3, false), map(0x46, MINECRAFT_1_20_5, false), map(0x4B, MINECRAFT_1_21_2, false), - map(0x4A, MINECRAFT_1_21_5, false)); + map(0x4A, MINECRAFT_1_21_5, false), + map(0x4F, MINECRAFT_1_21_9, false)); clientbound.register( HeaderAndFooterPacket.class, HeaderAndFooterPacket::new, @@ -586,7 +601,8 @@ public enum StateRegistry { map(0x6A, MINECRAFT_1_20_3, true), map(0x6D, MINECRAFT_1_20_5, true), map(0x74, MINECRAFT_1_21_2, true), - map(0x73, MINECRAFT_1_21_5, true)); + map(0x73, MINECRAFT_1_21_5, true), + map(0x78, MINECRAFT_1_21_9, true)); clientbound.register( LegacyTitlePacket.class, LegacyTitlePacket::new, @@ -608,7 +624,8 @@ public enum StateRegistry { map(0x61, MINECRAFT_1_20_3, true), map(0x63, MINECRAFT_1_20_5, true), map(0x6A, MINECRAFT_1_21_2, true), - map(0x69, MINECRAFT_1_21_5, true)); + map(0x69, MINECRAFT_1_21_5, true), + map(0x6E, MINECRAFT_1_21_9, true)); clientbound.register( TitleTextPacket.class, TitleTextPacket::new, @@ -621,7 +638,8 @@ public enum StateRegistry { map(0x63, MINECRAFT_1_20_3, true), map(0x65, MINECRAFT_1_20_5, true), map(0x6C, MINECRAFT_1_21_2, true), - map(0x6B, MINECRAFT_1_21_5, true)); + map(0x6B, MINECRAFT_1_21_5, true), + map(0x70, MINECRAFT_1_21_9, true)); clientbound.register( TitleActionbarPacket.class, TitleActionbarPacket::new, @@ -634,7 +652,8 @@ public enum StateRegistry { map(0x4A, MINECRAFT_1_20_3, true), map(0x4C, MINECRAFT_1_20_5, true), map(0x51, MINECRAFT_1_21_2, true), - map(0x50, MINECRAFT_1_21_5, true)); + map(0x50, MINECRAFT_1_21_5, true), + map(0x55, MINECRAFT_1_21_9, true)); clientbound.register( TitleTimesPacket.class, TitleTimesPacket::new, @@ -647,7 +666,8 @@ public enum StateRegistry { map(0x64, MINECRAFT_1_20_3, true), map(0x66, MINECRAFT_1_20_5, true), map(0x6D, MINECRAFT_1_21_2, true), - map(0x6C, MINECRAFT_1_21_5, true)); + map(0x6C, MINECRAFT_1_21_5, true), + map(0x71, MINECRAFT_1_21_9, true)); clientbound.register( TitleClearPacket.class, TitleClearPacket::new, @@ -677,7 +697,8 @@ public enum StateRegistry { map(0x3B, MINECRAFT_1_20_2, false), map(0x3D, MINECRAFT_1_20_5, false), map(0x3F, MINECRAFT_1_21_2, false), - map(0x3E, MINECRAFT_1_21_5, false)); + map(0x3E, MINECRAFT_1_21_5, false), + map(0x43, MINECRAFT_1_21_9, false)); clientbound.register( UpsertPlayerInfoPacket.class, UpsertPlayerInfoPacket::new, @@ -686,12 +707,14 @@ public enum StateRegistry { map(0x3C, MINECRAFT_1_20_2, false), map(0x3E, MINECRAFT_1_20_5, false), map(0x40, MINECRAFT_1_21_2, false), - map(0x3F, MINECRAFT_1_21_5, false)); + map(0x3F, MINECRAFT_1_21_5, false), + map(0x44, MINECRAFT_1_21_9, false)); clientbound.register( ClientboundStoreCookiePacket.class, ClientboundStoreCookiePacket::new, map(0x6B, MINECRAFT_1_20_5, false), map(0x72, MINECRAFT_1_21_2, false), - map(0x71, MINECRAFT_1_21_5, false)); + map(0x71, MINECRAFT_1_21_5, false), + map(0x76, MINECRAFT_1_21_9, false)); clientbound.register( SystemChatPacket.class, SystemChatPacket::new, @@ -703,7 +726,8 @@ public enum StateRegistry { map(0x69, MINECRAFT_1_20_3, true), map(0x6C, MINECRAFT_1_20_5, true), map(0x73, MINECRAFT_1_21_2, true), - map(0x72, MINECRAFT_1_21_5, true)); + map(0x72, MINECRAFT_1_21_5, true), + map(0x77, MINECRAFT_1_21_9, true)); clientbound.register( PlayerChatCompletionPacket.class, PlayerChatCompletionPacket::new, @@ -724,7 +748,8 @@ public enum StateRegistry { map(0x49, MINECRAFT_1_20_3, false), map(0x4B, MINECRAFT_1_20_5, false), map(0x50, MINECRAFT_1_21_2, false), - map(0x4F, MINECRAFT_1_21_5, false)); + map(0x4F, MINECRAFT_1_21_5, false), + map(0x54, MINECRAFT_1_21_9, false)); clientbound.register( StartUpdatePacket.class, () -> StartUpdatePacket.INSTANCE, @@ -732,7 +757,8 @@ public enum StateRegistry { map(0x67, MINECRAFT_1_20_3, false), map(0x69, MINECRAFT_1_20_5, false), map(0x70, MINECRAFT_1_21_2, false), - map(0x6F, MINECRAFT_1_21_5, false)); + map(0x6F, MINECRAFT_1_21_5, false), + map(0x74, MINECRAFT_1_21_9, false)); clientbound.register( BundleDelimiterPacket.class, () -> BundleDelimiterPacket.INSTANCE, @@ -741,17 +767,20 @@ public enum StateRegistry { TransferPacket.class, TransferPacket::new, map(0x73, MINECRAFT_1_20_5, false), - map(0x7A, MINECRAFT_1_21_2, false)); + map(0x7A, MINECRAFT_1_21_2, false), + map(0x7F, MINECRAFT_1_21_9, false)); clientbound.register( ClientboundCustomReportDetailsPacket.class, ClientboundCustomReportDetailsPacket::new, map(0x7A, MINECRAFT_1_21, false), - map(0x81, MINECRAFT_1_21_2, false)); + map(0x81, MINECRAFT_1_21_2, false), + map(0x86, MINECRAFT_1_21_9, false)); clientbound.register( ClientboundServerLinksPacket.class, ClientboundServerLinksPacket::new, map(0x7B, MINECRAFT_1_21, false), - map(0x82, MINECRAFT_1_21_2, false)); + map(0x82, MINECRAFT_1_21_2, false), + map(0x87, MINECRAFT_1_21_9, false)); } }, LOGIN { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/CodeOfConductAcceptPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/CodeOfConductAcceptPacket.java new file mode 100644 index 00000000..e9811f9f --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/CodeOfConductAcceptPacket.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2018-2025 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.protocol.packet.config; + +import com.velocitypowered.api.network.ProtocolVersion; +import com.velocitypowered.proxy.connection.MinecraftSessionHandler; +import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; +import io.netty.buffer.ByteBuf; + +public class CodeOfConductAcceptPacket implements MinecraftPacket { + + public static final CodeOfConductAcceptPacket INSTANCE = new CodeOfConductAcceptPacket(); + + private CodeOfConductAcceptPacket() { + } + + @Override + public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { + } + + @Override + public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { + } + + @Override + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); + } +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/CodeOfConductPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/CodeOfConductPacket.java new file mode 100644 index 00000000..44adb743 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/CodeOfConductPacket.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2018-2025 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.protocol.packet.config; + +import com.velocitypowered.api.network.ProtocolVersion; +import com.velocitypowered.proxy.connection.MinecraftSessionHandler; +import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; +import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder; +import io.netty.buffer.ByteBuf; + +public class CodeOfConductPacket extends DeferredByteBufHolder implements MinecraftPacket { + + public CodeOfConductPacket() { + super(null); + } + + @Override + public void decode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { + this.replace(buf.readRetainedSlice(buf.readableBytes())); + } + + @Override + public void encode(ByteBuf buf, Direction direction, ProtocolVersion protocolVersion) { + buf.writeBytes(this.content()); + } + + @Override + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); + } +} From c8c27af7c3da99987e4f5be18ffe5830eaf98538 Mon Sep 17 00:00:00 2001 From: Timon Seidel Date: Mon, 29 Sep 2025 16:22:19 +0200 Subject: [PATCH 20/36] feat: Add primitive support for sound api (#1422) * feat: Add primitive support for sound api * change to fail silently fix: implement the correct playSound method fix: bumped "since" version * chore: update 1.21.5 * chore: enforce adventure's policy of not throwing exceptions on unsupported actions * feat: allow sounds to be played from other players (on the same server) * chore(fix): add missing getters/setters in packets * chore: update 1.21.6 chore: added own notes to playSound method, as adventure moved them to the Sound class * chore: cleanup * fix: ignore invalid sound source fix: sound source error on wrong version * chore: prettify key writing * Implement missing Player#playSound(Sound) * Reverted Player#playSound(Sound) implementation Also, improved documentation related to #playSound mehtods * chore(jd): mark dialog operations unsupported * chore: update 1.21.9 --------- Co-authored-by: Adrian Gonzales --- .../com/velocitypowered/api/proxy/Player.java | 67 +++++++++-- .../connection/MinecraftSessionHandler.java | 9 ++ .../backend/VelocityServerConnection.java | 10 ++ .../client/ClientPlaySessionHandler.java | 2 + .../connection/client/ConnectedPlayer.java | 48 ++++++++ .../proxy/protocol/ProtocolUtils.java | 45 ++++++++ .../proxy/protocol/StateRegistry.java | 22 ++++ .../packet/ClientboundSoundEntityPacket.java | 101 ++++++++++++++++ .../packet/ClientboundStopSoundPacket.java | 109 ++++++++++++++++++ 9 files changed, 403 insertions(+), 10 deletions(-) create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientboundSoundEntityPacket.java create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientboundStopSoundPacket.java 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 04e65c84..efe21cd7 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/Player.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/Player.java @@ -29,6 +29,7 @@ import java.util.Locale; import java.util.Optional; import java.util.UUID; import java.util.function.UnaryOperator; +import net.kyori.adventure.dialog.DialogLike; import net.kyori.adventure.identity.Identified; import net.kyori.adventure.inventory.Book; import net.kyori.adventure.key.Key; @@ -48,7 +49,7 @@ public interface Player extends /* Fundamental Velocity interfaces */ CommandSource, InboundConnection, ChannelMessageSource, ChannelMessageSink, /* Adventure-specific interfaces */ - Identified, HoverEventSource, Keyed, KeyIdentifiable { + Identified, HoverEventSource, Keyed, KeyIdentifiable, Sound.Emitter { /** * Returns the player's current username. @@ -383,8 +384,12 @@ public interface Player extends /** * {@inheritDoc} * - * This method is not currently implemented in Velocity - * and will not perform any actions. + * + * @apiNote This method is not currently implemented in Velocity + * and will not perform any actions. + * @see #playSound(Sound, Sound.Emitter) + * @see + * Unsupported Adventure Operations */ @Override default void playSound(@NotNull Sound sound) { @@ -393,8 +398,11 @@ public interface Player extends /** * {@inheritDoc} * - * This method is not currently implemented in Velocity - * and will not perform any actions. + * @apiNote This method is not currently implemented in Velocity + * and will not perform any actions. + * @see #playSound(Sound, Sound.Emitter) + * @see + * Unsupported Adventure Operations */ @Override default void playSound(@NotNull Sound sound, double x, double y, double z) { @@ -403,18 +411,28 @@ public interface Player extends /** * {@inheritDoc} * - * This method is not currently implemented in Velocity - * and will not perform any actions. + *

Note: Due to MC-146721, stereo sounds are always played globally in 1.14+. + * + *

Note: Due to MC-138832, the volume and pitch are ignored when using this method in 1.14 to 1.16.5. + * + * @param sound the sound to play + * @param emitter the emitter of the sound; may be another player of this player's server + * @since 3.4.0 + * @sinceMinecraft 1.19.3 + * @apiNote This method is currently only implemented for players on 1.19.3+ + * and requires a present {@link #getCurrentServer} for the emitting player as well as this player. */ @Override - default void playSound(@NotNull Sound sound, Sound.Emitter emitter) { + default void playSound(@NotNull Sound sound, @NotNull Sound.Emitter emitter) { } /** * {@inheritDoc} * - * This method is not currently implemented in Velocity - * and will not perform any actions. + * @param stop the sound and/or a sound source, to stop + * @since 3.4.0 + * @sinceMinecraft 1.19.3 + * @apiNote This method is currently only implemented for players on 1.19.3+. */ @Override default void stopSound(@NotNull SoundStop stop) { @@ -425,11 +443,40 @@ public interface Player extends * * This method is not currently implemented in Velocity * and will not perform any actions. + * + * @see + * Unsupported Adventure Operations */ @Override default void openBook(@NotNull Book book) { } + /** + * {@inheritDoc} + * + * This method is not currently implemented in Velocity + * and will not perform any actions. + * + * @see + * Unsupported Adventure Operations + */ + @Override + default void showDialog(@NotNull DialogLike dialog) { + } + + /** + * {@inheritDoc} + * + * This method is not currently implemented in Velocity + * and will not perform any actions. + * + * @see + * Unsupported Adventure Operations + */ + @Override + default void closeDialog() { + } + /** * Transfers a Player to a host. * diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftSessionHandler.java index 94620218..d1101d6a 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftSessionHandler.java @@ -23,6 +23,8 @@ import com.velocitypowered.proxy.protocol.packet.BossBarPacket; import com.velocitypowered.proxy.protocol.packet.BundleDelimiterPacket; import com.velocitypowered.proxy.protocol.packet.ClientSettingsPacket; import com.velocitypowered.proxy.protocol.packet.ClientboundCookieRequestPacket; +import com.velocitypowered.proxy.protocol.packet.ClientboundSoundEntityPacket; +import com.velocitypowered.proxy.protocol.packet.ClientboundStopSoundPacket; import com.velocitypowered.proxy.protocol.packet.ClientboundStoreCookiePacket; import com.velocitypowered.proxy.protocol.packet.DialogClearPacket; import com.velocitypowered.proxy.protocol.packet.DialogShowPacket; @@ -390,4 +392,11 @@ public interface MinecraftSessionHandler { return false; } + default boolean handle(ClientboundSoundEntityPacket packet) { + return false; + } + + default boolean handle(ClientboundStopSoundPacket packet) { + return false; + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java index af3f8179..71ebd7bc 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/VelocityServerConnection.java @@ -53,6 +53,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.jetbrains.annotations.NotNull; @@ -70,6 +71,7 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, private boolean gracefulDisconnect = false; private BackendConnectionPhase connectionPhase = BackendConnectionPhases.UNKNOWN; private final Map pendingPings = new HashMap<>(); + private @MonotonicNonNull Integer entityId; /** * Initializes a new server connection. @@ -324,6 +326,14 @@ public class VelocityServerConnection implements MinecraftConnectionAssociation, return pendingPings; } + public Integer getEntityId() { + return entityId; + } + + public void setEntityId(Integer entityId) { + this.entityId = entityId; + } + /** * Ensures that this server connection remains "active": the connection is established and not * closed, the player is still connected to the server, and the player still remains online. 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 4b658cd6..254842cf 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 @@ -575,6 +575,8 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { } } + destination.setEntityId(joinGame.getEntityId()); // used for sound api + // Remove previous boss bars. These don't get cleared when sending JoinGame, thus the need to // track them. for (UUID serverBossBar : serverBossBars) { 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 6d32535f..44fe95c5 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 @@ -73,6 +73,8 @@ import com.velocitypowered.proxy.protocol.netty.MinecraftEncoder; import com.velocitypowered.proxy.protocol.packet.BundleDelimiterPacket; import com.velocitypowered.proxy.protocol.packet.ClientSettingsPacket; import com.velocitypowered.proxy.protocol.packet.ClientboundCookieRequestPacket; +import com.velocitypowered.proxy.protocol.packet.ClientboundSoundEntityPacket; +import com.velocitypowered.proxy.protocol.packet.ClientboundStopSoundPacket; import com.velocitypowered.proxy.protocol.packet.ClientboundStoreCookiePacket; import com.velocitypowered.proxy.protocol.packet.DisconnectPacket; import com.velocitypowered.proxy.protocol.packet.HeaderAndFooterPacket; @@ -127,6 +129,8 @@ import net.kyori.adventure.pointer.PointersSupplier; import net.kyori.adventure.resource.ResourcePackInfoLike; import net.kyori.adventure.resource.ResourcePackRequest; import net.kyori.adventure.resource.ResourcePackRequestLike; +import net.kyori.adventure.sound.Sound; +import net.kyori.adventure.sound.SoundStop; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.logger.slf4j.ComponentLogger; @@ -1042,6 +1046,50 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, this.clientBrand = clientBrand; } + @Override + public void playSound(@NotNull Sound sound, @NotNull Sound.Emitter emitter) { + Preconditions.checkNotNull(sound, "sound"); + Preconditions.checkNotNull(emitter, "emitter"); + VelocityServerConnection soundTargetServerConn = getConnectedServer(); + if (getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_19_3) + || connection.getState() != StateRegistry.PLAY + || soundTargetServerConn == null + || (sound.source() == Sound.Source.UI + && getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_21_5))) { + return; + } + + VelocityServerConnection soundEmitterServerConn; + if (emitter == Sound.Emitter.self()) { + soundEmitterServerConn = soundTargetServerConn; + } else if (emitter instanceof ConnectedPlayer player) { + if ((soundEmitterServerConn = player.getConnectedServer()) == null) { + return; + } + + if (!soundEmitterServerConn.getServer().equals(soundTargetServerConn.getServer())) { + return; + } + } else { + return; + } + + connection.write(new ClientboundSoundEntityPacket(sound, null, soundEmitterServerConn.getEntityId())); + } + + @Override + public void stopSound(@NotNull SoundStop stop) { + Preconditions.checkNotNull(stop, "stop"); + if (getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_19_3) + || connection.getState() != StateRegistry.PLAY + || (stop.source() == Sound.Source.UI + && getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_21_5))) { + return; + } + + connection.write(new ClientboundStopSoundPacket(stop)); + } + @Override public void transferToHost(final InetSocketAddress address) { Preconditions.checkNotNull(address); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java index 5b54e0f0..f0d1d63b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java @@ -44,6 +44,7 @@ import net.kyori.adventure.nbt.BinaryTagIO; import net.kyori.adventure.nbt.BinaryTagType; import net.kyori.adventure.nbt.BinaryTagTypes; import net.kyori.adventure.nbt.CompoundBinaryTag; +import net.kyori.adventure.sound.Sound; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.json.JSONOptions; import net.kyori.adventure.text.serializer.json.legacyimpl.NBTLegacyHoverEventSerializer; @@ -316,6 +317,16 @@ public enum ProtocolUtils { writeString(buf, key.asString()); } + /** + * Writes the key to the buffer, dropping the "minecraft:" namespace when present. + * + * @param buf the buffer to write to + * @param key the key to write + */ + public static void writeMinimalKey(ByteBuf buf, Key key) { + writeString(buf, key.asMinimalString()); + } + /** * Reads a standard Mojang Text namespaced:key array from the buffer. * @@ -781,6 +792,40 @@ public enum ProtocolUtils { return new IdentifiedKeyImpl(revision, key, expiry, signature); } + /** + * Reads a {@link Sound.Source} from the buffer. + * + * @param buf the buffer + * @param version the protocol version + * @return the sound source + */ + public static Sound.Source readSoundSource(ByteBuf buf, ProtocolVersion version) { + int ordinal = readVarInt(buf); + + if (version.lessThan(ProtocolVersion.MINECRAFT_1_21_5) + && ordinal == Sound.Source.UI.ordinal()) { + throw new UnsupportedOperationException("UI sound-source is only supported in 1.21.5+"); + } + + return Sound.Source.values()[ordinal]; + } + + /** + * Writes a {@link Sound.Source} to the buffer. + * + * @param buf the buffer + * @param version the protocol version + * @param source the sound source to write + */ + public static void writeSoundSource(ByteBuf buf, ProtocolVersion version, Sound.Source source) { + if (version.lessThan(ProtocolVersion.MINECRAFT_1_21_5) + && source == Sound.Source.UI) { + throw new UnsupportedOperationException("UI sound-source is only supported in 1.21.5+"); + } + + writeVarInt(buf, source.ordinal()); + } + /** * Represents the direction in which a packet flows. */ diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java index 42d60029..67b01087 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/StateRegistry.java @@ -59,6 +59,8 @@ import com.velocitypowered.proxy.protocol.packet.BossBarPacket; import com.velocitypowered.proxy.protocol.packet.BundleDelimiterPacket; import com.velocitypowered.proxy.protocol.packet.ClientSettingsPacket; import com.velocitypowered.proxy.protocol.packet.ClientboundCookieRequestPacket; +import com.velocitypowered.proxy.protocol.packet.ClientboundSoundEntityPacket; +import com.velocitypowered.proxy.protocol.packet.ClientboundStopSoundPacket; import com.velocitypowered.proxy.protocol.packet.ClientboundStoreCookiePacket; import com.velocitypowered.proxy.protocol.packet.DialogClearPacket; import com.velocitypowered.proxy.protocol.packet.DialogShowPacket; @@ -448,6 +450,26 @@ public enum StateRegistry { ClientboundCookieRequestPacket.class, ClientboundCookieRequestPacket::new, map(0x16, MINECRAFT_1_20_5, false), map(0x15, MINECRAFT_1_21_5, false)); + clientbound.register( + ClientboundSoundEntityPacket.class, ClientboundSoundEntityPacket::new, + map(0x5D, MINECRAFT_1_19_3, true), + map(0x61, MINECRAFT_1_19_4, true), + map(0x63, MINECRAFT_1_20_2, true), + map(0x65, MINECRAFT_1_20_3, true), + map(0x67, MINECRAFT_1_20_5, true), + map(0x6E, MINECRAFT_1_21_2, true), + map(0x6D, MINECRAFT_1_21_5, true), + map(0x72, MINECRAFT_1_21_9, true)); + clientbound.register( + ClientboundStopSoundPacket.class, ClientboundStopSoundPacket::new, + map(0x5F, MINECRAFT_1_19_3, true), + map(0x63, MINECRAFT_1_19_4, true), + map(0x66, MINECRAFT_1_20_2, true), + map(0x68, MINECRAFT_1_20_3, true), + map(0x6A, MINECRAFT_1_20_5, true), + map(0x71, MINECRAFT_1_21_2, true), + map(0x70, MINECRAFT_1_21_5, true), + map(0x75, MINECRAFT_1_21_9, true)); clientbound.register( PluginMessagePacket.class, PluginMessagePacket::new, diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientboundSoundEntityPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientboundSoundEntityPacket.java new file mode 100644 index 00000000..459f1430 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientboundSoundEntityPacket.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2025 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.protocol.packet; + +import com.velocitypowered.api.network.ProtocolVersion; +import com.velocitypowered.proxy.connection.MinecraftSessionHandler; +import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolUtils; +import io.netty.buffer.ByteBuf; +import net.kyori.adventure.sound.Sound; +import org.jetbrains.annotations.Nullable; + +import java.util.Random; + +public class ClientboundSoundEntityPacket implements MinecraftPacket { + + private static final Random SEEDS_RANDOM = new Random(); + + private Sound sound; + private @Nullable Float fixedRange; + private int emitterEntityId; + + public ClientboundSoundEntityPacket() {} + + public ClientboundSoundEntityPacket(Sound sound, @Nullable Float fixedRange, int emitterEntityId) { + this.sound = sound; + this.fixedRange = fixedRange; + this.emitterEntityId = emitterEntityId; + } + + @Override + public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + throw new UnsupportedOperationException("Decode is not implemented"); + } + + @Override + public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + ProtocolUtils.writeVarInt(buf, 0); // version-dependent, hardcoded sound ID + + ProtocolUtils.writeMinimalKey(buf, sound.name()); + + buf.writeBoolean(fixedRange != null); + if (fixedRange != null) + buf.writeFloat(fixedRange); + + ProtocolUtils.writeSoundSource(buf, protocolVersion, sound.source()); + + ProtocolUtils.writeVarInt(buf, emitterEntityId); + + buf.writeFloat(sound.volume()); + + buf.writeFloat(sound.pitch()); + + buf.writeLong(sound.seed().orElse(SEEDS_RANDOM.nextLong())); + } + + @Override + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); + } + + public Sound getSound() { + return sound; + } + + public void setSound(Sound sound) { + this.sound = sound; + } + + public @Nullable Float getFixedRange() { + return fixedRange; + } + + public void setFixedRange(@Nullable Float fixedRange) { + this.fixedRange = fixedRange; + } + + public int getEmitterEntityId() { + return emitterEntityId; + } + + public void setEmitterEntityId(int emitterEntityId) { + this.emitterEntityId = emitterEntityId; + } + +} diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientboundStopSoundPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientboundStopSoundPacket.java new file mode 100644 index 00000000..3e085d38 --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ClientboundStopSoundPacket.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2025 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.protocol.packet; + +import com.velocitypowered.api.network.ProtocolVersion; +import com.velocitypowered.proxy.connection.MinecraftSessionHandler; +import com.velocitypowered.proxy.protocol.MinecraftPacket; +import com.velocitypowered.proxy.protocol.ProtocolUtils; +import io.netty.buffer.ByteBuf; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.sound.Sound; +import net.kyori.adventure.sound.SoundStop; + +import javax.annotation.Nullable; + +public class ClientboundStopSoundPacket implements MinecraftPacket { + + private @Nullable Sound.Source source; + private @Nullable Key soundName; + + public ClientboundStopSoundPacket() {} + + public ClientboundStopSoundPacket(SoundStop soundStop) { + this(soundStop.source(), soundStop.sound()); + } + + public ClientboundStopSoundPacket(@Nullable Sound.Source source, @Nullable Key soundName) { + this.source = source; + this.soundName = soundName; + } + + @Override + public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + int flagsBitmask = buf.readByte(); + + if ((flagsBitmask & 1) != 0) { + source = ProtocolUtils.readSoundSource(buf, protocolVersion); + } else { + source = null; + } + + if ((flagsBitmask & 2) != 0) { + soundName = ProtocolUtils.readKey(buf); + } else { + soundName = null; + } + } + + @Override + public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { + int flagsBitmask = 0; + if (source != null && soundName == null) { + flagsBitmask |= 1; + } else if (soundName != null && source == null) { + flagsBitmask |= 2; + } else if (source != null /*&& sound != null*/) { + flagsBitmask |= 3; + } + + buf.writeByte(flagsBitmask); + + if (source != null) { + ProtocolUtils.writeSoundSource(buf, protocolVersion, source); + } + + if (soundName != null) { + ProtocolUtils.writeMinimalKey(buf, soundName); + } + } + + @Override + public boolean handle(MinecraftSessionHandler handler) { + return handler.handle(this); + } + + @Nullable + public Sound.Source getSource() { + return source; + } + + public void setSource(@Nullable Sound.Source source) { + this.source = source; + } + + @Nullable + public Key getSoundName() { + return soundName; + } + + public void setSoundName(@Nullable Key soundName) { + this.soundName = soundName; + } + +} From b1dd26fbc4ffdc1a6d9c8281cee2165107e66f14 Mon Sep 17 00:00:00 2001 From: Aaron <71191102+RealBauHD@users.noreply.github.com> Date: Tue, 7 Oct 2025 16:40:25 +0200 Subject: [PATCH 21/36] 1.21.10 (#1658) --- .../java/com/velocitypowered/api/network/ProtocolVersion.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java b/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java index 916e3db5..f2a629b1 100644 --- a/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java +++ b/api/src/main/java/com/velocitypowered/api/network/ProtocolVersion.java @@ -93,7 +93,7 @@ public enum ProtocolVersion implements Ordered { MINECRAFT_1_21_5(770, "1.21.5"), MINECRAFT_1_21_6(771, "1.21.6"), MINECRAFT_1_21_7(772, "1.21.7", "1.21.8"), - MINECRAFT_1_21_9(773, "1.21.9"); + MINECRAFT_1_21_9(773, "1.21.9", "1.21.10"); private static final int SNAPSHOT_BIT = 30; From d266059abe6b2b150b2084dc72ab44317387bca1 Mon Sep 17 00:00:00 2001 From: Cedric Date: Fri, 10 Oct 2025 10:40:10 +0200 Subject: [PATCH 22/36] Update adventure to version 4.25.0 (#1660) --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c44264fa..726a1ec0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,8 +12,8 @@ shadow = "com.gradleup.shadow:8.3.6" spotless = "com.diffplug.spotless:6.25.0" [libraries] -adventure-bom = "net.kyori:adventure-bom:4.24.0" -adventure-text-serializer-json-legacy-impl = "net.kyori:adventure-text-serializer-json-legacy-impl:4.24.0" +adventure-bom = "net.kyori:adventure-bom:4.25.0" +adventure-text-serializer-json-legacy-impl = "net.kyori:adventure-text-serializer-json-legacy-impl:4.25.0" adventure-facet = "net.kyori:adventure-platform-facet:4.3.4" asm = "org.ow2.asm:asm:9.8" auto-service = "com.google.auto.service:auto-service:1.0.1" From 806b386cdb8da23051a9679682ed6a76370b9818 Mon Sep 17 00:00:00 2001 From: Ross <32296125+Rossterd@users.noreply.github.com> Date: Sun, 12 Oct 2025 04:11:44 +0200 Subject: [PATCH 23/36] Fix command suggestion offset (#1662) --- .../client/ClientPlaySessionHandler.java | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) 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 254842cf..bccd8375 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 @@ -696,22 +696,34 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { return; } - List offers = new ArrayList<>(); - for (Suggestion suggestion : suggestions.getList()) { - String offer = suggestion.getText(); - ComponentHolder tooltip = null; - if (suggestion.getTooltip() instanceof ComponentLike componentLike) { - tooltip = new ComponentHolder(player.getProtocolVersion(), componentLike.asComponent()); - } else if (suggestion.getTooltip() != null) { - tooltip = new ComponentHolder(player.getProtocolVersion(), Component.text(suggestion.getTooltip().getString())); + int startPos = -1; + for (var suggestion : suggestions.getList()) { + if (startPos == -1 || startPos > suggestion.getRange().getStart()) { + startPos = suggestion.getRange().getStart(); } - offers.add(new Offer(offer, tooltip)); } - int startPos = packet.getCommand().lastIndexOf(' ') + 1; + if (startPos > 0) { + List offers = new ArrayList<>(); + for (Suggestion suggestion : suggestions.getList()) { + String offer; + if (suggestion.getRange().getStart() == startPos) { + offer = suggestion.getText(); + } else { + offer = command.substring(startPos, suggestion.getRange().getStart()) + suggestion.getText(); + } + ComponentHolder tooltip = null; + if (suggestion.getTooltip() instanceof ComponentLike componentLike) { + tooltip = new ComponentHolder(player.getProtocolVersion(), componentLike.asComponent()); + } else if (suggestion.getTooltip() != null) { + tooltip = new ComponentHolder(player.getProtocolVersion(), Component.text(suggestion.getTooltip().getString())); + } + offers.add(new Offer(offer, tooltip)); + } + TabCompleteResponsePacket resp = new TabCompleteResponsePacket(); resp.setTransactionId(packet.getTransactionId()); - resp.setStart(startPos); + resp.setStart(startPos + 1); resp.setLength(packet.getCommand().length() - startPos); resp.getOffers().addAll(offers); player.getConnection().write(resp); From 5753548b44193d2b72a4b70d3f12b7f21d6c1d1d Mon Sep 17 00:00:00 2001 From: Ross <32296125+Rossterd@users.noreply.github.com> Date: Mon, 13 Oct 2025 21:41:33 +0200 Subject: [PATCH 24/36] Fix SimpleCommand suggestion offset (#1664) * Fix command suggestion offset * fix length error * add test * checkstyle --------- Co-authored-by: Ross <2086824-trashp@users.noreply.gitlab.com> --- .../registrar/InvocableCommandRegistrar.java | 8 ++++-- .../client/ClientPlaySessionHandler.java | 2 +- .../command/SuggestionsProviderTests.java | 25 +++++++++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/command/registrar/InvocableCommandRegistrar.java b/proxy/src/main/java/com/velocitypowered/proxy/command/registrar/InvocableCommandRegistrar.java index 380f45e3..847d67d1 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/command/registrar/InvocableCommandRegistrar.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/command/registrar/InvocableCommandRegistrar.java @@ -103,13 +103,17 @@ abstract class InvocableCommandRegistrar, .requiresWithContext((context, reader) -> requirement.test(context)) .executes(callback) .suggests((context, builder) -> { + // Offset the suggestion to the last space seperated word + int lastSpace = builder.getRemaining().lastIndexOf(' ') + 1; + final var offsetBuilder = builder.createOffset(builder.getStart() + lastSpace); + final I invocation = invocationFactory.create(context); return command.suggestAsync(invocation).thenApply(suggestions -> { for (String value : suggestions) { Preconditions.checkNotNull(value, "suggestion"); - builder.suggest(value); + offsetBuilder.suggest(value); } - return builder.build(); + return offsetBuilder.build(); }); }) .build(); 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 bccd8375..288315d7 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 @@ -724,7 +724,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { TabCompleteResponsePacket resp = new TabCompleteResponsePacket(); resp.setTransactionId(packet.getTransactionId()); resp.setStart(startPos + 1); - resp.setLength(packet.getCommand().length() - startPos); + resp.setLength(packet.getCommand().length() - startPos - 1); resp.getOffers().addAll(offers); player.getConnection().write(resp); } diff --git a/proxy/src/test/java/com/velocitypowered/proxy/command/SuggestionsProviderTests.java b/proxy/src/test/java/com/velocitypowered/proxy/command/SuggestionsProviderTests.java index 9fe86335..fa282f71 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/command/SuggestionsProviderTests.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/command/SuggestionsProviderTests.java @@ -18,14 +18,17 @@ package com.velocitypowered.proxy.command; import static com.mojang.brigadier.arguments.StringArgumentType.word; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import com.google.common.collect.ImmutableList; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.suggestion.Suggestions; import com.velocitypowered.api.command.Command; import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.command.RawCommand; +import com.velocitypowered.api.command.SimpleCommand; import java.util.List; import java.util.concurrent.CompletableFuture; import org.junit.jupiter.api.Test; @@ -286,4 +289,26 @@ public class SuggestionsProviderTests extends CommandTestSuite { return ImmutableList.of(); } } + + @Test + void testSuggestionOffset() { + final var meta = manager.metaBuilder("offset").build(); + manager.register(meta, new SimpleCommand() { + @Override + public void execute(final Invocation invocation) { + fail(); + } + + @Override + public List suggest(final Invocation invocation) { + return List.of("bump"); + } + }); + + assertSuggestions("offset bu", "bump"); + for (int i = 10; i < 20; i++) { + final Suggestions suggestions = manager.offerBrigadierSuggestions(source, "offset " + "bump ".repeat(i)).join(); + assertEquals(7 + 5 * i, suggestions.getList().get(0).getRange().getStart()); + } + } } From 1140fc65baddc41723e71977aeafe224d6b14c42 Mon Sep 17 00:00:00 2001 From: Emil <12966472+Emilxyz@users.noreply.github.com> Date: Tue, 14 Oct 2025 20:10:57 +0200 Subject: [PATCH 25/36] fix: Enable EMIT_CLICK_URL_HTTPS on component serializers (#1665) --- .../com/velocitypowered/proxy/protocol/ProtocolUtils.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java index f0d1d63b..0be9065a 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java @@ -62,6 +62,8 @@ public enum ProtocolUtils { .legacyHoverEventSerializer(NBTLegacyHoverEventSerializer.get()) .options( OptionSchema.globalSchema().stateBuilder() + // general options + .value(JSONOptions.EMIT_CLICK_URL_HTTPS, Boolean.TRUE) // before 1.16 .value(JSONOptions.EMIT_RGB, Boolean.FALSE) .value(JSONOptions.EMIT_HOVER_EVENT_TYPE, JSONOptions.HoverEventValueMode.VALUE_FIELD) @@ -80,6 +82,8 @@ public enum ProtocolUtils { .legacyHoverEventSerializer(NBTLegacyHoverEventSerializer.get()) .options( OptionSchema.globalSchema().stateBuilder() + // general options + .value(JSONOptions.EMIT_CLICK_URL_HTTPS, Boolean.TRUE) // after 1.16 .value(JSONOptions.EMIT_RGB, Boolean.TRUE) .value(JSONOptions.EMIT_HOVER_EVENT_TYPE, JSONOptions.HoverEventValueMode.CAMEL_CASE) @@ -99,6 +103,8 @@ public enum ProtocolUtils { .legacyHoverEventSerializer(NBTLegacyHoverEventSerializer.get()) .options( OptionSchema.globalSchema().stateBuilder() + // general options + .value(JSONOptions.EMIT_CLICK_URL_HTTPS, Boolean.TRUE) // after 1.16 .value(JSONOptions.EMIT_RGB, Boolean.TRUE) .value(JSONOptions.EMIT_HOVER_EVENT_TYPE, JSONOptions.HoverEventValueMode.CAMEL_CASE) @@ -118,6 +124,8 @@ public enum ProtocolUtils { .legacyHoverEventSerializer(NBTLegacyHoverEventSerializer.get()) .options( OptionSchema.globalSchema().stateBuilder() + // general options + .value(JSONOptions.EMIT_CLICK_URL_HTTPS, Boolean.TRUE) // after 1.16 .value(JSONOptions.EMIT_RGB, Boolean.TRUE) .value(JSONOptions.EMIT_HOVER_EVENT_TYPE, JSONOptions.HoverEventValueMode.SNAKE_CASE) From 4cd3b6869729484887b4fa58b7a6c3b007710a10 Mon Sep 17 00:00:00 2001 From: okx-code Date: Fri, 17 Oct 2025 05:12:57 +0100 Subject: [PATCH 26/36] Fix players disconnecting when updating boss bars (#1656) * Fix 1.20.2+ clients disconnecting when updating boss bars On 1.20.2, the Minecraft client started clearing boss bars after the login packet, which meant that the ProxyServer#showBossbar API would result in the player getting kicked if the boss bar they were previously shown was updated after switching servers. Therefore, I have added BossBarManager which drops boss bar packets once the client enters the configure phase to ensure that they do not disconnect, and then re-adds the boss bar once the client enters the login phase. This ensures that clients do not receive boss bar updates for boss bars that they don't exist and causing them to disconnect. I have also taken care to ensure that this logic only applies on 1.20.2 and up, as it is not necessary for older clients. --------- Co-authored-by: Adrian Gonzales --- .../VelocityBossBarImplementation.java | 22 ++++-- .../backend/BackendPlaySessionHandler.java | 12 +-- .../client/ClientPlaySessionHandler.java | 27 ++++--- .../connection/client/ConnectedPlayer.java | 7 ++ .../player/bossbar/BossBarManager.java | 79 +++++++++++++++++++ 5 files changed, 125 insertions(+), 22 deletions(-) create mode 100644 proxy/src/main/java/com/velocitypowered/proxy/connection/player/bossbar/BossBarManager.java diff --git a/proxy/src/main/java/com/velocitypowered/proxy/adventure/VelocityBossBarImplementation.java b/proxy/src/main/java/com/velocitypowered/proxy/adventure/VelocityBossBarImplementation.java index 9dec3309..e2451188 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/adventure/VelocityBossBarImplementation.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/adventure/VelocityBossBarImplementation.java @@ -53,15 +53,23 @@ public final class VelocityBossBarImplementation implements BossBar.Listener, viewer.getProtocolVersion(), viewer.translateMessage(this.bar.name()) ); - viewer.getConnection().write(BossBarPacket.createAddPacket(this.id, this.bar, name)); + viewer.getBossBarManager().writeUpdate(this, BossBarPacket.createAddPacket(this.id, this.bar, name)); return true; } return false; } + public void createDirect(final ConnectedPlayer viewer) { + final ComponentHolder name = new ComponentHolder( + viewer.getProtocolVersion(), + viewer.translateMessage(this.bar.name()) + ); + viewer.getConnection().write(BossBarPacket.createAddPacket(this.id, this.bar, name)); + } + public boolean viewerRemove(final ConnectedPlayer viewer) { if (this.viewers.remove(viewer)) { - viewer.getConnection().write(BossBarPacket.createRemovePacket(this.id, this.bar)); + viewer.getBossBarManager().remove(this, BossBarPacket.createRemovePacket(this.id, this.bar)); return true; } return false; @@ -84,7 +92,7 @@ public final class VelocityBossBarImplementation implements BossBar.Listener, this.bar, new ComponentHolder(viewer.getProtocolVersion(), translated) ); - viewer.getConnection().write(packet); + viewer.getBossBarManager().writeUpdate(this, packet); } } @@ -96,7 +104,7 @@ public final class VelocityBossBarImplementation implements BossBar.Listener, ) { final BossBarPacket packet = BossBarPacket.createUpdateProgressPacket(this.id, this.bar); for (final ConnectedPlayer viewer : this.viewers) { - viewer.getConnection().write(packet); + viewer.getBossBarManager().writeUpdate(this, packet); } } @@ -108,7 +116,7 @@ public final class VelocityBossBarImplementation implements BossBar.Listener, ) { final BossBarPacket packet = BossBarPacket.createUpdateStylePacket(this.id, this.bar); for (final ConnectedPlayer viewer : this.viewers) { - viewer.getConnection().write(packet); + viewer.getBossBarManager().writeUpdate(this, packet); } } @@ -120,7 +128,7 @@ public final class VelocityBossBarImplementation implements BossBar.Listener, ) { final BossBarPacket packet = BossBarPacket.createUpdateStylePacket(this.id, this.bar); for (final ConnectedPlayer viewer : this.viewers) { - viewer.getConnection().write(packet); + viewer.getBossBarManager().writeUpdate(this, packet); } } @@ -132,7 +140,7 @@ public final class VelocityBossBarImplementation implements BossBar.Listener, ) { final BossBarPacket packet = BossBarPacket.createUpdatePropertiesPacket(this.id, this.bar); for (final ConnectedPlayer viewer : this.viewers) { - viewer.getConnection().write(packet); + viewer.getBossBarManager().writeUpdate(this, packet); } } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java index eb9c9bca..ed30057a 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java @@ -177,10 +177,12 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { @Override public boolean handle(BossBarPacket packet) { - if (packet.getAction() == BossBarPacket.ADD) { - playerSessionHandler.getServerBossBars().add(packet.getUuid()); - } else if (packet.getAction() == BossBarPacket.REMOVE) { - playerSessionHandler.getServerBossBars().remove(packet.getUuid()); + if (serverConn.getPlayer().getProtocolVersion().lessThan(ProtocolVersion.MINECRAFT_1_20_2)) { + if (packet.getAction() == BossBarPacket.ADD) { + playerSessionHandler.getServerBossBars().add(packet.getUuid()); + } else if (packet.getAction() == BossBarPacket.REMOVE) { + playerSessionHandler.getServerBossBars().remove(packet.getUuid()); + } } return false; // forward } @@ -516,4 +518,4 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler { playerConnection.setAutoReading(writable); } -} \ No newline at end of file +} 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 288315d7..17eb708b 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 @@ -535,9 +535,13 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { // Config state clears everything in the client. No need to clear later. spawned = false; - serverBossBars.clear(); player.clearPlayerListHeaderAndFooterSilent(); player.getTabList().clearAllSilent(); + if (player.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) { + player.getBossBarManager().dropPackets(); + } else { + serverBossBars.clear(); + } } player.switchToConfigState(); @@ -576,16 +580,19 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { } destination.setEntityId(joinGame.getEntityId()); // used for sound api - - // Remove previous boss bars. These don't get cleared when sending JoinGame, thus the need to - // track them. - for (UUID serverBossBar : serverBossBars) { - BossBarPacket deletePacket = new BossBarPacket(); - deletePacket.setUuid(serverBossBar); - deletePacket.setAction(BossBarPacket.REMOVE); - player.getConnection().delayedWrite(deletePacket); + if (player.getProtocolVersion().noLessThan(ProtocolVersion.MINECRAFT_1_20_2)) { + player.getBossBarManager().sendBossBars(); + } else { + // Remove previous boss bars. These don't get cleared when sending JoinGame (up until 1.20.2), + // thus the need to track them. + for (UUID serverBossBar : serverBossBars) { + BossBarPacket deletePacket = new BossBarPacket(); + deletePacket.setUuid(serverBossBar); + deletePacket.setAction(BossBarPacket.REMOVE); + player.getConnection().delayedWrite(deletePacket); + } + serverBossBars.clear(); } - serverBossBars.clear(); // Tell the server about the proxy's plugin message channels. ProtocolVersion serverVersion = serverMc.getProtocolVersion(); 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 44fe95c5..17f4fb5c 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 @@ -62,6 +62,7 @@ import com.velocitypowered.proxy.adventure.VelocityBossBarImplementation; import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; +import com.velocitypowered.proxy.connection.player.bossbar.BossBarManager; import com.velocitypowered.proxy.connection.player.bundle.BundleDelimiterHandler; import com.velocitypowered.proxy.connection.player.resourcepack.VelocityResourcePackInfo; import com.velocitypowered.proxy.connection.player.resourcepack.handler.ResourcePackHandler; @@ -201,6 +202,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, private @Nullable ClientSettingsPacket clientSettingsPacket; private volatile ChatQueue chatQueue; private final ChatBuilderFactory chatBuilderFactory; + private final BossBarManager bossBarManager; ConnectedPlayer(VelocityServer server, GameProfile profile, MinecraftConnection connection, @Nullable InetSocketAddress virtualHost, @Nullable String rawVirtualHost, boolean onlineMode, @@ -227,6 +229,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, this.chatQueue = new ChatQueue(this); this.chatBuilderFactory = new ChatBuilderFactory(this.getProtocolVersion()); this.resourcePackHandler = ResourcePackHandler.create(this, server); + this.bossBarManager = new BossBarManager(this); } /** @@ -1431,6 +1434,10 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, return handshakeIntent; } + public BossBarManager getBossBarManager() { + return bossBarManager; + } + private final class ConnectionRequestBuilderImpl implements ConnectionRequestBuilder { private final RegisteredServer toConnect; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/player/bossbar/BossBarManager.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/player/bossbar/BossBarManager.java new file mode 100644 index 00000000..03ff4f7f --- /dev/null +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/player/bossbar/BossBarManager.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2019-2023 Velocity Contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.velocitypowered.proxy.connection.player.bossbar; + +import com.velocitypowered.proxy.adventure.VelocityBossBarImplementation; +import com.velocitypowered.proxy.connection.client.ConnectedPlayer; +import com.velocitypowered.proxy.protocol.packet.BossBarPacket; +import java.util.HashSet; +import java.util.Set; + +/** + * Handles dropping and resending boss bar packets on versions 1.20.2 and newer because the client now + * deletes all boss bars during the login phase, and sending update packets would cause the client to be disconnected. + */ +public final class BossBarManager { + + private final ConnectedPlayer player; + private final Set bossBars = new HashSet<>(); + + private boolean dropPackets = false; + + public BossBarManager(ConnectedPlayer player) { + this.player = player; + } + + /** + * Records the specified boss bar to be re-sent when a player changes server, and sends the update packet + * if the client is able to receive it and not be disconnected. + */ + public synchronized void writeUpdate(VelocityBossBarImplementation bar, BossBarPacket packet) { + this.bossBars.add(bar); + if (!this.dropPackets) { + this.player.getConnection().write(packet); + } + } + + /** + * Removes the specified boss bar from the player to ensure it is not re-sent. + */ + public synchronized void remove(VelocityBossBarImplementation bar, BossBarPacket packet) { + this.bossBars.remove(bar); + if (!this.dropPackets) { + this.player.getConnection().write(packet); + } + } + + /** + * Re-creates the boss bars the player can see with any updates that may have occurred in the meantime, + * and allows update packets for those boss bars to be sent. + */ + public synchronized void sendBossBars() { + for (VelocityBossBarImplementation bossBar : bossBars) { + bossBar.createDirect(player); + } + this.dropPackets = false; + } + + /** + * Prevents the player from receiving boss bar update packets while logging in to a new server. + */ + public synchronized void dropPackets() { + this.dropPackets = true; + } +} From 70c3eabdb1459419cb7e7187cd0a741cdefaa66a Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 18 Oct 2025 16:15:22 -0400 Subject: [PATCH 27/36] Minor optimizations for `MinecraftCompressorAndLengthEncoder` and friends No need to bounce around changing the writer index, we can just set the value directly. Also pull out the handshake checks into a separate function, to improve inlining. --- .../proxy/protocol/ProtocolUtils.java | 13 ++-- .../MinecraftCompressorAndLengthEncoder.java | 9 +-- .../netty/MinecraftVarintFrameDecoder.java | 69 ++++++++++--------- .../proxy/protocol/ProtocolUtilsTest.java | 3 +- 4 files changed, 48 insertions(+), 46 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java index 0be9065a..6e8ba6ac 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java @@ -150,7 +150,7 @@ public enum ProtocolUtils { BinaryTagTypes.COMPOUND, BinaryTagTypes.INT_ARRAY, BinaryTagTypes.LONG_ARRAY}; private static final QuietDecoderException BAD_VARINT_CACHED = new QuietDecoderException("Bad VarInt decoded"); - private static final int[] VAR_INT_LENGTHS = new int[65]; + private static final int[] VAR_INT_LENGTHS = new int[33]; static { for (int i = 0; i <= 32; ++i) { @@ -250,16 +250,15 @@ public enum ProtocolUtils { } /** - * Writes the specified {@code value} as a 21-bit Minecraft VarInt to the specified {@code buf}. + * Directly encodes a 21-bit Minecraft VarInt, ready to be written with {@link ByteBuf#writeMedium(int)}. * The upper 11 bits will be discarded. * - * @param buf the buffer to read from - * @param value the integer to write + * @param value the value to encode + * @return the encoded value */ - public static void write21BitVarInt(ByteBuf buf, int value) { + public static int encode21BitVarInt(int value) { // See https://steinborn.me/posts/performance/how-fast-can-you-write-a-varint/ - int w = (value & 0x7F | 0x80) << 16 | ((value >>> 7) & 0x7F | 0x80) << 8 | (value >>> 14); - buf.writeMedium(w); + return (value & 0x7F | 0x80) << 16 | ((value >>> 7) & 0x7F | 0x80) << 8 | (value >>> 14); } public static String readString(ByteBuf buf) { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressorAndLengthEncoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressorAndLengthEncoder.java index 90952a72..990d1732 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressorAndLengthEncoder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftCompressorAndLengthEncoder.java @@ -46,7 +46,7 @@ public class MinecraftCompressorAndLengthEncoder extends MessageToByteEncoder 0) { if (state == StateRegistry.HANDSHAKE && direction == ProtocolUtils.Direction.SERVERBOUND) { - StateRegistry.PacketRegistry.ProtocolRegistry registry = - state.getProtocolRegistry(direction, ProtocolVersion.MINIMUM_VERSION); - - final int index = in.readerIndex(); - final int packetId = readRawVarInt21(in); - // Index hasn't changed, we've read nothing - if (index == in.readerIndex()) { - in.resetReaderIndex(); + if (validateServerboundHandshakePacket(in, length)) { return; } - final int payloadLength = length - ProtocolUtils.varIntBytes(packetId); - - MinecraftPacket packet = registry.createPacket(packetId); - - // We handle every packet in this phase, if you said something we don't know, something is really wrong - if (packet == null) { - throw UNKNOWN_PACKET; - } - - // We 'technically' have the incoming bytes of a payload here, and so, these can actually parse - // the packet if needed, so, we'll take advantage of the existing methods - int expectedMinLen = packet.expectedMinLength(in, direction, registry.version); - int expectedMaxLen = packet.expectedMaxLength(in, direction, registry.version); - if (expectedMaxLen != -1 && payloadLength > expectedMaxLen) { - throw handleOverflow(packet, expectedMaxLen, in.readableBytes()); - } - if (payloadLength < expectedMinLen) { - throw handleUnderflow(packet, expectedMaxLen, in.readableBytes()); - } - - - in.readerIndex(index); } } @@ -139,6 +109,41 @@ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder { } } + private boolean validateServerboundHandshakePacket(ByteBuf in, int length) throws Exception { + StateRegistry.PacketRegistry.ProtocolRegistry registry = + state.getProtocolRegistry(direction, ProtocolVersion.MINIMUM_VERSION); + + final int index = in.readerIndex(); + final int packetId = readRawVarInt21(in); + // Index hasn't changed, we've read nothing + if (index == in.readerIndex()) { + in.resetReaderIndex(); + return true; + } + final int payloadLength = length - ProtocolUtils.varIntBytes(packetId); + + MinecraftPacket packet = registry.createPacket(packetId); + + // We handle every packet in this phase, if you said something we don't know, something is really wrong + if (packet == null) { + throw UNKNOWN_PACKET; + } + + // We 'technically' have the incoming bytes of a payload here, and so, these can actually parse + // the packet if needed, so, we'll take advantage of the existing methods + int expectedMinLen = packet.expectedMinLength(in, direction, registry.version); + int expectedMaxLen = packet.expectedMaxLength(in, direction, registry.version); + if (expectedMaxLen != -1 && payloadLength > expectedMaxLen) { + throw handleOverflow(packet, expectedMaxLen, in.readableBytes()); + } + if (payloadLength < expectedMinLen) { + throw handleUnderflow(packet, expectedMaxLen, in.readableBytes()); + } + + in.readerIndex(index); + return false; + } + @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { if (MinecraftDecoder.DEBUG) { diff --git a/proxy/src/test/java/com/velocitypowered/proxy/protocol/ProtocolUtilsTest.java b/proxy/src/test/java/com/velocitypowered/proxy/protocol/ProtocolUtilsTest.java index 1ed59cd8..ccd9cb7a 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/protocol/ProtocolUtilsTest.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/protocol/ProtocolUtilsTest.java @@ -17,6 +17,7 @@ package com.velocitypowered.proxy.protocol; +import static com.velocitypowered.proxy.protocol.ProtocolUtils.encode21BitVarInt; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -83,7 +84,7 @@ public class ProtocolUtilsTest { private void writeReadTest3Bytes(ByteBuf buf, int test) { buf.clear(); - ProtocolUtils.write21BitVarInt(buf, test); + buf.writeMedium(encode21BitVarInt(test)); assertEquals(test, ProtocolUtils.readVarInt(buf)); } From 38a0a7ed27230dceb33f3c241e740a91ae73d328 Mon Sep 17 00:00:00 2001 From: Shane Freeder Date: Sat, 18 Oct 2025 21:22:26 +0100 Subject: [PATCH 28/36] use correct string length for newer MC versions (Fixes #1629) (#1668) --- .../packet/chat/session/SessionPlayerCommandPacket.java | 3 ++- .../packet/chat/session/UnsignedPlayerCommandPacket.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionPlayerCommandPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionPlayerCommandPacket.java index 993587dc..f4ea3a1e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionPlayerCommandPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/SessionPlayerCommandPacket.java @@ -41,7 +41,8 @@ public class SessionPlayerCommandPacket implements MinecraftPacket { @Override public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { - this.command = ProtocolUtils.readString(buf, 256); + int cap = protocolVersion.lessThan(ProtocolVersion.MINECRAFT_1_20_5) ? 256 : ProtocolUtils.DEFAULT_MAX_STRING_SIZE; + this.command = ProtocolUtils.readString(buf, cap); this.timeStamp = Instant.ofEpochMilli(buf.readLong()); this.salt = buf.readLong(); this.argumentSignatures = new ArgumentSignatures(buf); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/UnsignedPlayerCommandPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/UnsignedPlayerCommandPacket.java index cb5ac3c4..915f0cfb 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/UnsignedPlayerCommandPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/session/UnsignedPlayerCommandPacket.java @@ -28,7 +28,7 @@ public class UnsignedPlayerCommandPacket extends SessionPlayerCommandPacket { @Override public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { - this.command = ProtocolUtils.readString(buf, 256); + this.command = ProtocolUtils.readString(buf, ProtocolUtils.DEFAULT_MAX_STRING_SIZE); } @Override From 13a1c93ea6e4816f489b6dbdf7b2cb94e64f6b2c Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 18 Oct 2025 16:22:27 -0400 Subject: [PATCH 29/36] Bump Netty to 4.2.7.Final --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 726a1ec0..d0bb2c84 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ configurate3 = "3.7.3" configurate4 = "4.1.2" flare = "2.0.1" log4j = "2.24.3" -netty = "4.2.5.Final" +netty = "4.2.7.Final" [plugins] fill = "io.papermc.fill.gradle:1.0.3" From 498a38cf74de86fccecf4fe715983d2036c3024e Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 18 Oct 2025 16:40:54 -0400 Subject: [PATCH 30/36] Re-enable adaptive allocator Recent Netty versions have improved the adaptive allocator, and we shouldn't be seeing the OOM issues others were noticing before. Let's re-enable it. As for the buffer resizing issue, the upstream issue netty/netty#14912 is long fixed. I think we *should* pre-allocate the buffers beforehand much more aggressively, but that has to be future work. --- proxy/src/main/java/com/velocitypowered/proxy/Velocity.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/Velocity.java b/proxy/src/main/java/com/velocitypowered/proxy/Velocity.java index a6fc850b..0ec8622d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/Velocity.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/Velocity.java @@ -47,11 +47,6 @@ public class Velocity { System.setProperty("io.netty.native.workdir", System.getProperty("velocity.natives-tmpdir")); } - // Restore allocator used before Netty 4.2 due to oom issues with the adaptive allocator - if (System.getProperty("io.netty.allocator.type") == null) { - System.setProperty("io.netty.allocator.type", "pooled"); - } - // Disable the resource leak detector by default as it reduces performance. Allow the user to // override this if desired. if (!VelocityProperties.hasProperty("io.netty.leakDetection.level")) { From d2c13c2a4c509ca208d5f8bb6840ee1556163786 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 18 Oct 2025 17:36:39 -0400 Subject: [PATCH 31/36] Provide encode buffer hint --- .../proxy/protocol/MinecraftPacket.java | 9 +++++-- .../proxy/protocol/ProtocolUtils.java | 11 +++++++++ .../protocol/netty/MinecraftDecoder.java | 4 ++-- .../protocol/netty/MinecraftEncoder.java | 13 ++++++++++ .../netty/MinecraftVarintFrameDecoder.java | 4 ++-- .../packet/AvailableCommandsPacket.java | 8 +++++++ .../packet/EncryptionResponsePacket.java | 6 ++--- .../protocol/packet/HandshakePacket.java | 12 ++++++++-- .../packet/LoginAcknowledgedPacket.java | 2 +- .../packet/LoginPluginMessagePacket.java | 6 +++++ .../packet/LoginPluginResponsePacket.java | 6 +++++ .../protocol/packet/PluginMessagePacket.java | 6 +++++ .../protocol/packet/ServerDataPacket.java | 6 +++++ .../protocol/packet/ServerLoginPacket.java | 2 +- .../packet/ServerLoginSuccessPacket.java | 8 +++++++ .../ServerboundCustomClickActionPacket.java | 5 ++++ .../protocol/packet/StatusPingPacket.java | 4 ++-- .../protocol/packet/StatusRequestPacket.java | 2 +- .../protocol/packet/StatusResponsePacket.java | 6 +++++ .../packet/UpsertPlayerInfoPacket.java | 24 +++++++------------ .../packet/config/CodeOfConductPacket.java | 5 ++++ .../packet/config/FinishedUpdatePacket.java | 2 +- .../packet/config/RegistrySyncPacket.java | 6 +++++ .../packet/config/StartUpdatePacket.java | 2 +- .../packet/config/TagsUpdatePacket.java | 19 +++++++++++++++ 25 files changed, 145 insertions(+), 33 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/MinecraftPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/MinecraftPacket.java index e54ee770..1722f42b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/MinecraftPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/MinecraftPacket.java @@ -32,13 +32,18 @@ public interface MinecraftPacket { boolean handle(MinecraftSessionHandler handler); - default int expectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, + default int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { return -1; } - default int expectedMinLength(ByteBuf buf, ProtocolUtils.Direction direction, + default int decodeExpectedMinLength(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { return 0; } + + default int encodeSizeHint(ProtocolUtils.Direction direction, + ProtocolVersion version) { + return -1; + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java index 6e8ba6ac..efc0ed17 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/ProtocolUtils.java @@ -292,6 +292,17 @@ public enum ProtocolUtils { return str; } + /** + * Determines the size of the written {@code str} if encoded as a VarInt-prefixed UTF-8 string. + * + * @param str the string to write + * @return the encoded size + */ + public static int stringSizeHint(CharSequence str) { + int size = ByteBufUtil.utf8Bytes(str); + return varIntBytes(size) + size; + } + /** * Writes the specified {@code str} to the {@code buf} with a VarInt prefix. * diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java index 5389de73..35ddccd5 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java @@ -96,8 +96,8 @@ public class MinecraftDecoder extends ChannelInboundHandlerAdapter { } private void doLengthSanityChecks(ByteBuf buf, MinecraftPacket packet) throws Exception { - int expectedMinLen = packet.expectedMinLength(buf, direction, registry.version); - int expectedMaxLen = packet.expectedMaxLength(buf, direction, registry.version); + int expectedMinLen = packet.decodeExpectedMinLength(buf, direction, registry.version); + int expectedMaxLen = packet.decodeExpectedMaxLength(buf, direction, registry.version); if (expectedMaxLen != -1 && buf.readableBytes() > expectedMaxLen) { throw handleOverflow(packet, expectedMaxLen, buf.readableBytes()); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftEncoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftEncoder.java index 7133f1d2..4af366ce 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftEncoder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftEncoder.java @@ -54,6 +54,19 @@ public class MinecraftEncoder extends MessageToByteEncoder { msg.encode(out, direction, registry.version); } + @Override + protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, MinecraftPacket msg, + boolean preferDirect) throws Exception { + int hint = msg.encodeSizeHint(direction, registry.version); + if (hint < 0) { + return super.allocateBuffer(ctx, msg, preferDirect); + } + + int packetId = this.registry.getPacketId(msg); + int totalHint = ProtocolUtils.varIntBytes(packetId) + hint; + return preferDirect ? ctx.alloc().ioBuffer(totalHint) : ctx.alloc().heapBuffer(totalHint); + } + public void setProtocolVersion(final ProtocolVersion protocolVersion) { this.registry = state.getProtocolRegistry(direction, protocolVersion); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java index 1301ed5b..7d4d8d6d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftVarintFrameDecoder.java @@ -131,8 +131,8 @@ public class MinecraftVarintFrameDecoder extends ByteToMessageDecoder { // We 'technically' have the incoming bytes of a payload here, and so, these can actually parse // the packet if needed, so, we'll take advantage of the existing methods - int expectedMinLen = packet.expectedMinLength(in, direction, registry.version); - int expectedMaxLen = packet.expectedMaxLength(in, direction, registry.version); + int expectedMinLen = packet.decodeExpectedMinLength(in, direction, registry.version); + int expectedMaxLen = packet.decodeExpectedMaxLength(in, direction, registry.version); if (expectedMaxLen != -1 && payloadLength > expectedMaxLen) { throw handleOverflow(packet, expectedMaxLen, in.readableBytes()); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/AvailableCommandsPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/AvailableCommandsPacket.java index 08fe23b2..d3fc3a82 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/AvailableCommandsPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/AvailableCommandsPacket.java @@ -362,4 +362,12 @@ public class AvailableCommandsPacket implements MinecraftPacket { return builder.buildFuture(); } } + + @Override + public int encodeSizeHint(Direction direction, ProtocolVersion version) { + // This is a very complex packet to encode. Paper 1.21.10 + Velocity with Spark has a size of + // 30,334, but this is likely on the lower side. We'll use 128KiB as a more realistically-sized + // amount. + return 128 * 1024; + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionResponsePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionResponsePacket.java index d6591e7b..fedb67a5 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionResponsePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/EncryptionResponsePacket.java @@ -107,7 +107,7 @@ public class EncryptionResponsePacket implements MinecraftPacket { } @Override - public int expectedMaxLength(ByteBuf buf, Direction direction, ProtocolVersion version) { + public int decodeExpectedMaxLength(ByteBuf buf, Direction direction, ProtocolVersion version) { // It turns out these come out to the same length, whether we're talking >=1.8 or not. // The length prefix always winds up being 2 bytes. int base = 256 + 2 + 2; @@ -123,8 +123,8 @@ public class EncryptionResponsePacket implements MinecraftPacket { } @Override - public int expectedMinLength(ByteBuf buf, Direction direction, ProtocolVersion version) { - int base = expectedMaxLength(buf, direction, version); + public int decodeExpectedMinLength(ByteBuf buf, Direction direction, ProtocolVersion version) { + int base = decodeExpectedMaxLength(buf, direction, version); if (version.noLessThan(ProtocolVersion.MINECRAFT_1_19)) { // These are "optional" base -= 128 + 8; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/HandshakePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/HandshakePacket.java index 9eb7a93e..88cb3688 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/HandshakePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/HandshakePacket.java @@ -24,6 +24,7 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; +import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import io.netty.buffer.ByteBuf; public class HandshakePacket implements MinecraftPacket { @@ -108,14 +109,21 @@ public class HandshakePacket implements MinecraftPacket { } @Override - public int expectedMinLength(ByteBuf buf, ProtocolUtils.Direction direction, + public int decodeExpectedMinLength(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { return 7; } @Override - public int expectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, + public int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { return 9 + (MAXIMUM_HOSTNAME_LENGTH * 3); } + + @Override + public int encodeSizeHint(Direction direction, ProtocolVersion version) { + // We could compute an exact size, but 4KiB ought to be enough to encode all reasonable + // sizes of this packet. + return 4 * 1024; + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginAcknowledgedPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginAcknowledgedPacket.java index dad9e08d..16cf519b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginAcknowledgedPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginAcknowledgedPacket.java @@ -36,7 +36,7 @@ public class LoginAcknowledgedPacket implements MinecraftPacket { } @Override - public int expectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, + public int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { return 0; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginMessagePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginMessagePacket.java index 682785eb..2fa82e92 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginMessagePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginMessagePacket.java @@ -21,6 +21,7 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; +import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -86,4 +87,9 @@ public class LoginPluginMessagePacket extends DeferredByteBufHolder implements M public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + @Override + public int encodeSizeHint(Direction direction, ProtocolVersion version) { + return content().readableBytes(); + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginResponsePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginResponsePacket.java index dee33c13..e7d9443d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginResponsePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/LoginPluginResponsePacket.java @@ -21,6 +21,7 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; +import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -88,4 +89,9 @@ public class LoginPluginResponsePacket extends DeferredByteBufHolder implements public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + @Override + public int encodeSizeHint(Direction direction, ProtocolVersion version) { + return content().readableBytes(); + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PluginMessagePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PluginMessagePacket.java index d78ed833..ecf2887f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PluginMessagePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/PluginMessagePacket.java @@ -23,6 +23,7 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; +import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder; import io.netty.buffer.ByteBuf; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @@ -143,4 +144,9 @@ public class PluginMessagePacket extends DeferredByteBufHolder implements Minecr public PluginMessagePacket touch(Object hint) { return (PluginMessagePacket) super.touch(hint); } + + @Override + public int encodeSizeHint(Direction direction, ProtocolVersion version) { + return content().readableBytes(); + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerDataPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerDataPacket.java index 610462a9..325a3c9d 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerDataPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerDataPacket.java @@ -22,6 +22,7 @@ import com.velocitypowered.api.util.Favicon; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; +import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder; import io.netty.buffer.ByteBuf; import org.jetbrains.annotations.Nullable; @@ -121,4 +122,9 @@ public class ServerDataPacket implements MinecraftPacket { public void setSecureChatEnforced(boolean secureChatEnforced) { this.secureChatEnforced = secureChatEnforced; } + + @Override + public int encodeSizeHint(Direction direction, ProtocolVersion version) { + return 8 * 1024; + } } \ No newline at end of file diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerLoginPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerLoginPacket.java index 74eabb5c..65693cd8 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerLoginPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerLoginPacket.java @@ -150,7 +150,7 @@ public class ServerLoginPacket implements MinecraftPacket { } @Override - public int expectedMaxLength(ByteBuf buf, Direction direction, ProtocolVersion version) { + public int decodeExpectedMaxLength(ByteBuf buf, Direction direction, ProtocolVersion version) { // Accommodate the rare (but likely malicious) use of UTF-8 usernames, since it is technically // legal on the protocol level. int base = 1 + (16 * 3); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerLoginSuccessPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerLoginSuccessPacket.java index 1e70568f..322cd9b1 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerLoginSuccessPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerLoginSuccessPacket.java @@ -23,6 +23,7 @@ import com.velocitypowered.api.util.UuidUtils; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; +import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.util.VelocityProperties; import io.netty.buffer.ByteBuf; import java.util.List; @@ -132,4 +133,11 @@ public class ServerLoginSuccessPacket implements MinecraftPacket { public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + @Override + public int encodeSizeHint(Direction direction, ProtocolVersion version) { + // We could compute an exact size, but 4KiB ought to be enough to encode all reasonable + // sizes of this packet. + return 4 * 1024; + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCustomClickActionPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCustomClickActionPacket.java index 2092b260..6b846c23 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCustomClickActionPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/ServerboundCustomClickActionPacket.java @@ -21,6 +21,7 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; +import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder; import io.netty.buffer.ByteBuf; @@ -45,4 +46,8 @@ public class ServerboundCustomClickActionPacket extends DeferredByteBufHolder im return handler.handle(this); } + @Override + public int encodeSizeHint(Direction direction, ProtocolVersion version) { + return content().readableBytes(); + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusPingPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusPingPacket.java index 15999e61..30236704 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusPingPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusPingPacket.java @@ -44,12 +44,12 @@ public class StatusPingPacket implements MinecraftPacket { } @Override - public int expectedMaxLength(ByteBuf buf, Direction direction, ProtocolVersion version) { + public int decodeExpectedMaxLength(ByteBuf buf, Direction direction, ProtocolVersion version) { return 8; } @Override - public int expectedMinLength(ByteBuf buf, Direction direction, ProtocolVersion version) { + public int decodeExpectedMinLength(ByteBuf buf, Direction direction, ProtocolVersion version) { return 8; } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusRequestPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusRequestPacket.java index 46dc0401..870d9909 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusRequestPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusRequestPacket.java @@ -53,7 +53,7 @@ public class StatusRequestPacket implements MinecraftPacket { } @Override - public int expectedMaxLength(ByteBuf buf, Direction direction, ProtocolVersion version) { + public int decodeExpectedMaxLength(ByteBuf buf, Direction direction, ProtocolVersion version) { return 0; } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusResponsePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusResponsePacket.java index 8591fa03..20fada4b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusResponsePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/StatusResponsePacket.java @@ -21,6 +21,7 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; +import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import io.netty.buffer.ByteBuf; import org.checkerframework.checker.nullness.qual.Nullable; @@ -66,4 +67,9 @@ public class StatusResponsePacket implements MinecraftPacket { public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + @Override + public int encodeSizeHint(Direction direction, ProtocolVersion version) { + return ProtocolUtils.stringSizeHint(this.status); + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/UpsertPlayerInfoPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/UpsertPlayerInfoPacket.java index 35bf4a96..9ef40ef0 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/UpsertPlayerInfoPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/UpsertPlayerInfoPacket.java @@ -36,6 +36,8 @@ import org.jetbrains.annotations.Nullable; public class UpsertPlayerInfoPacket implements MinecraftPacket { + private static final Action[] ALL_ACTIONS = Action.class.getEnumConstants(); + private final EnumSet actions; private final List entries; @@ -85,14 +87,13 @@ public class UpsertPlayerInfoPacket implements MinecraftPacket { @Override public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { - Action[] actions = Action.class.getEnumConstants(); - byte[] bytes = new byte[-Math.floorDiv(-actions.length, 8)]; + byte[] bytes = new byte[-Math.floorDiv(-ALL_ACTIONS.length, 8)]; buf.readBytes(bytes); BitSet actionSet = BitSet.valueOf(bytes); - for (int idx = 0; idx < actions.length; idx++) { + for (int idx = 0; idx < ALL_ACTIONS.length; idx++) { if (actionSet.get(idx)) { - addAction(actions[idx]); + addAction(ALL_ACTIONS[idx]); } } @@ -109,14 +110,13 @@ public class UpsertPlayerInfoPacket implements MinecraftPacket { @Override public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) { - Action[] actions = Action.class.getEnumConstants(); - BitSet set = new BitSet(actions.length); - for (int idx = 0; idx < actions.length; idx++) { - set.set(idx, this.actions.contains(actions[idx])); + BitSet set = new BitSet(ALL_ACTIONS.length); + for (int idx = 0; idx < ALL_ACTIONS.length; idx++) { + set.set(idx, this.actions.contains(ALL_ACTIONS[idx])); } byte[] bytes = set.toByteArray(); - buf.writeBytes(Arrays.copyOf(bytes, -Math.floorDiv(-actions.length, 8))); + buf.writeBytes(Arrays.copyOf(bytes, -Math.floorDiv(-ALL_ACTIONS.length, 8))); ProtocolUtils.writeVarInt(buf, this.entries.size()); for (Entry entry : this.entries) { @@ -133,12 +133,6 @@ public class UpsertPlayerInfoPacket implements MinecraftPacket { return handler.handle(this); } - public BitSet readFixedBitSet(ByteBuf buf, int param0) { - byte[] var0 = new byte[-Math.floorDiv(-param0, 8)]; - buf.readBytes(var0); - return BitSet.valueOf(var0); - } - public enum Action { ADD_PLAYER((ignored, buf, info) -> { // read info.profile = new GameProfile( diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/CodeOfConductPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/CodeOfConductPacket.java index 44adb743..41433307 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/CodeOfConductPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/CodeOfConductPacket.java @@ -44,4 +44,9 @@ public class CodeOfConductPacket extends DeferredByteBufHolder implements Minecr public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + @Override + public int encodeSizeHint(Direction direction, ProtocolVersion version) { + return content().readableBytes(); + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/FinishedUpdatePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/FinishedUpdatePacket.java index b4d93ec4..20d40fd4 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/FinishedUpdatePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/FinishedUpdatePacket.java @@ -40,7 +40,7 @@ public class FinishedUpdatePacket implements MinecraftPacket { } @Override - public int expectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, + public int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { return 0; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/RegistrySyncPacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/RegistrySyncPacket.java index ef0ee47a..2d9ed230 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/RegistrySyncPacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/RegistrySyncPacket.java @@ -21,6 +21,7 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; +import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import com.velocitypowered.proxy.protocol.util.DeferredByteBufHolder; import io.netty.buffer.ByteBuf; @@ -47,4 +48,9 @@ public class RegistrySyncPacket extends DeferredByteBufHolder implements Minecra public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + @Override + public int encodeSizeHint(Direction direction, ProtocolVersion version) { + return content().readableBytes(); + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/StartUpdatePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/StartUpdatePacket.java index 8d4585d8..d41265ce 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/StartUpdatePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/StartUpdatePacket.java @@ -40,7 +40,7 @@ public class StartUpdatePacket implements MinecraftPacket { } @Override - public int expectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, + public int decodeExpectedMaxLength(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { return 0; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/TagsUpdatePacket.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/TagsUpdatePacket.java index 6d462fb2..c0cf414e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/TagsUpdatePacket.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/config/TagsUpdatePacket.java @@ -22,6 +22,7 @@ import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.ProtocolUtils; +import com.velocitypowered.proxy.protocol.ProtocolUtils.Direction; import io.netty.buffer.ByteBuf; import java.util.Map; @@ -79,4 +80,22 @@ public class TagsUpdatePacket implements MinecraftPacket { public boolean handle(MinecraftSessionHandler handler) { return handler.handle(this); } + + @Override + public int encodeSizeHint(Direction direction, ProtocolVersion version) { + int size = ProtocolUtils.varIntBytes(tags.size()); + for (Map.Entry> entry : tags.entrySet()) { + size += ProtocolUtils.stringSizeHint(entry.getKey()); + size += ProtocolUtils.varIntBytes(entry.getValue().size()); + for (Map.Entry innerEntry : entry.getValue().entrySet()) { + size += ProtocolUtils.stringSizeHint(innerEntry.getKey()); + size += ProtocolUtils.varIntBytes(innerEntry.getValue().length); + for (int innerEntryValue : innerEntry.getValue()) { + size += ProtocolUtils.varIntBytes(innerEntryValue); + } + } + } + + return size; + } } From 67b988e6d24338e716de1b87584d06af75d974b8 Mon Sep 17 00:00:00 2001 From: Andrew Steinborn Date: Sat, 18 Oct 2025 18:58:21 -0400 Subject: [PATCH 32/36] Update all localizations to use the current year --- .../com/velocitypowered/proxy/l10n/messages_ar_SA.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_bg_BG.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_cs_CZ.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_da_DK.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_de_DE.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_es_ES.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_et_EE.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_fi_FI.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_fr_FR.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_he_IL.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_hu_HU.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_it_IT.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_ja_JP.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_ko_KR.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_nb_NO.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_nl_NL.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_nn_NO.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_pl_PL.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_pt_BR.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_ro_RO.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_ru_RU.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_sk_SK.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_sq_AL.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_sr_CS.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_sr_Latn.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_sv_SE.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_tl_PH.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_tr_TR.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_uk_UA.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_vi_VN.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_zh_CN.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_zh_HK.properties | 2 +- .../com/velocitypowered/proxy/l10n/messages_zh_TW.properties | 2 +- 33 files changed, 33 insertions(+), 33 deletions(-) diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ar_SA.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ar_SA.properties index 103ae08a..8f79f570 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ar_SA.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ar_SA.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural=هناك {0} لاعبين متصلون با velocity.command.glist-view-all=لعرض اللاعبين على جميع السيرفرات استخدم /glist all velocity.command.reload-success=تم إعادة تحميل إعدادات Velocity بنجاح. velocity.command.reload-failure=فشلت إعادة تحميل إعدادات Velocity. تفقد الـconsole للمزيد من التفاصيل. -velocity.command.version-copyright=حقوق الطبع والنشر 2018-2023 {0}. {1} مرخصة بموجب شروط الإصدار الثالث لرخصة GNU العامة (GPLv3). +velocity.command.version-copyright=حقوق الطبع والنشر 2018-{2} {0}. {1} مرخصة بموجب شروط الإصدار الثالث لرخصة GNU العامة (GPLv3). velocity.command.no-plugins=لا توجد إضافات مثبتة على Velocity. velocity.command.plugins-list=الإضافات\: {0} velocity.command.plugin-tooltip-website=موقعها\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_bg_BG.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_bg_BG.properties index b5a12e46..c554fa3d 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_bg_BG.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_bg_BG.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural={0} играчи са свързани къ velocity.command.glist-view-all=За да видите всички играчи, разпределени по сървъри, използвайте /glist all. velocity.command.reload-success=Настройките на Velocity бяха презаредени успешно. velocity.command.reload-failure=Не успяхме да презаредим настройките на Velocity. Моля, проверете конзолата за повече информация. -velocity.command.version-copyright=Авторско право 2018-2023 {0}. {1} е лицензиран под условията на GNU General Public License v3. +velocity.command.version-copyright=Авторско право 2018-{2} {0}. {1} е лицензиран под условията на GNU General Public License v3. velocity.command.no-plugins=За момента няма инсталирани добавки. velocity.command.plugins-list=Добавки\: {0} velocity.command.plugin-tooltip-website=Уебсайт\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_cs_CZ.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_cs_CZ.properties index 99d4f38a..980e4591 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_cs_CZ.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_cs_CZ.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural=Počet hráčů připojených k tomuto prox velocity.command.glist-view-all=Ke zobrazení všech hráčů na všech serverech použij /glist all. velocity.command.reload-success=Konfigurace Velocity úspěšně načtena. velocity.command.reload-failure=Nebylo možné načíst konfiguraci Velocity. Podrobnosti jsou na konzoli. -velocity.command.version-copyright=Copyright 2018-2023 {0}. {1} je licencovaný pod podmínkami GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} je licencovaný pod podmínkami GNU General Public License v3. velocity.command.no-plugins=V tuto chvíli nejsou nainstalovány žádné zásuvné moduly. velocity.command.plugins-list=Zásuvné moduly\: {0} velocity.command.plugin-tooltip-website=Webová stránka\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_da_DK.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_da_DK.properties index c593076e..1a82cbdf 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_da_DK.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_da_DK.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural={0} spillere er i øjeblikket forbundet til velocity.command.glist-view-all=For at se alle spillere på servere, brug /glist all. velocity.command.reload-success=Velocity konfiguration blev genindlæst. velocity.command.reload-failure=Kan ikke genindlæse din Velocity konfiguration. Tjek konsollen for flere detaljer. -velocity.command.version-copyright=Ophavsret 2018-2023 {0}. {1} er licenseret under betingelserne i GNU General Public License v3. +velocity.command.version-copyright=Ophavsret 2018-{2} {0}. {1} er licenseret under betingelserne i GNU General Public License v3. velocity.command.no-plugins=Der er ingen plugins installeret i øjeblikket. velocity.command.plugins-list=Plugins\: {0} velocity.command.plugin-tooltip-website=Hjemmeside\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_de_DE.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_de_DE.properties index f2d6872a..4de00f9e 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_de_DE.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_de_DE.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural={0} Spieler sind derzeit mit dem Proxy verb velocity.command.glist-view-all=Um alle Spieler auf Servern aufzulisten, verwende /glist all. velocity.command.reload-success=Velocity-Konfiguration erfolgreich neu geladen. velocity.command.reload-failure=Die Velocity-Konfiguration konnte nicht neu geladen werden. Prüfe die Konsole für weitere Details. -velocity.command.version-copyright=Copyright 2018-2023 {0}. {1} ist lizenziert unter den Bedingungen der GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} ist lizenziert unter den Bedingungen der GNU General Public License v3. velocity.command.no-plugins=Es sind derzeit keine Plugins installiert. velocity.command.plugins-list=Plugins\: {0} velocity.command.plugin-tooltip-website=Webseite\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_es_ES.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_es_ES.properties index a99efb44..0d484bc9 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_es_ES.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_es_ES.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural={0} jugadores están conectados al proxy. velocity.command.glist-view-all=Para ver todos los jugadores por servidores, usa /glist all. velocity.command.reload-success=La configuración de Velocity ha sido recargada correctamente. velocity.command.reload-failure=No ha sido posible recargar la configuración de Velocity. Para obtener más información, revisa la consola. -velocity.command.version-copyright=Copyright 2018-2023 {0}. {1} está licenciado bajo los términos de la Licencia Pública General de GNU v3. +velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} está licenciado bajo los términos de la Licencia Pública General de GNU v3. velocity.command.no-plugins=Actualmente no hay plugins instalados. velocity.command.plugins-list=Complementos\: {0} velocity.command.plugin-tooltip-website=Página web\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_et_EE.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_et_EE.properties index 246419b9..0e4ea63d 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_et_EE.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_et_EE.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural={0} players are currently connected to the velocity.command.glist-view-all=Et näha kõiki mängijaid kõikides serverites, kasuta käsklust /glist all. velocity.command.reload-success=Velocity seadistus edukalt taaslaetud. velocity.command.reload-failure=Unable to reload your Velocity configuration. Check the console for more details. -velocity.command.version-copyright=Copyright 2018-2023 {0}. {1} is licensed under the terms of the GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} is licensed under the terms of the GNU General Public License v3. velocity.command.no-plugins=There are no plugins currently installed. velocity.command.plugins-list=Pluginad\: {0} velocity.command.plugin-tooltip-website=Veebileht\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_fi_FI.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_fi_FI.properties index 84b6d593..39c8f374 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_fi_FI.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_fi_FI.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural={0} pelaajaa on tällä hetkellä yhdistän velocity.command.glist-view-all=Nähdäksesi pelaajat kaikilla palvelimilla, käytä komentoa /glist all. velocity.command.reload-success=Velocityn konfiguraatio uudelleenladattiin onnistuneesti. velocity.command.reload-failure=Velocityn konfiguraation uudelleenlataus epäonnistui. Katso tarkemmat lisätiedot konsolista. -velocity.command.version-copyright=Tekijänoikeus 2018-2023 {0}. {1} on lisensoitu GNU General Public License v3\:n ehtojen mukaisesti. +velocity.command.version-copyright=Tekijänoikeus 2018-{2} {0}. {1} on lisensoitu GNU General Public License v3\:n ehtojen mukaisesti. velocity.command.no-plugins=Yhtäkään pluginia ei ole asennettu. velocity.command.plugins-list=Pluginit\: {0} velocity.command.plugin-tooltip-website=Verkkosivu\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_fr_FR.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_fr_FR.properties index da990209..64fa63ee 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_fr_FR.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_fr_FR.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural={0} joueurs sont actuellement connectés au velocity.command.glist-view-all=Pour afficher tous les joueurs connectés aux serveurs, utilisez /glist all. velocity.command.reload-success=Configuration de Velocity rechargée avec succès. velocity.command.reload-failure=Impossible de recharger votre configuration de Velocity. Consultez la console pour plus de détails. -velocity.command.version-copyright=Copyright 2018-2023 {0}. {1} est sous la licence GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} est sous la licence GNU General Public License v3. velocity.command.no-plugins=Il n'y a aucun plugin actuellement installé. velocity.command.plugins-list=Plugins \: {0} velocity.command.plugin-tooltip-website=Site Internet \: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_he_IL.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_he_IL.properties index 3b0b8c69..85cd9f99 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_he_IL.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_he_IL.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural={0} שחקנים מחוברים כעת ל- velocity.command.glist-view-all=כדי להציג את כל השחקנים בשרתים, השתמש ב- glist all/. velocity.command.reload-success=תצורת Velocity נטענה מחדש בהצלחה. velocity.command.reload-failure=אין אפשרות לטעון מחדש את תצורת ה- Velocity שלך. עיין בקונסולה לקבלת פרטים נוספים. -velocity.command.version-copyright=זכויות יוצרים 2018-2023 {0}. {1} מורשה על פי תנאי v3 הרישיון הציבורי הכללי של GNU. +velocity.command.version-copyright=זכויות יוצרים 2018-{2} {0}. {1} מורשה על פי תנאי v3 הרישיון הציבורי הכללי של GNU. velocity.command.no-plugins=לא מותקנים כעת תוספים. velocity.command.plugins-list=תוספים\: {0} velocity.command.plugin-tooltip-website=אתר אינטרנט\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_hu_HU.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_hu_HU.properties index e5cb5b1c..b43f69b5 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_hu_HU.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_hu_HU.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural={0} játékos van jelenleg csatlakozva a pr velocity.command.glist-view-all=Hogy megnézd az összes játékost az összes szerveren, használd a /glist all parancsot. velocity.command.reload-success=A Velocity beállításai sikeresen frissítve lettek. velocity.command.reload-failure=Hiba történt a Velocity beállításainak frissítése közben. Több információt a konzolban találsz. -velocity.command.version-copyright=Copyright 2018-2023 {0}. {1} licenszelve van a GNU General Public License v3 alatt. +velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} licenszelve van a GNU General Public License v3 alatt. velocity.command.no-plugins=Jelenleg egyetlen plugin sincs telepítve. velocity.command.plugins-list=Pluginok\: {0} velocity.command.plugin-tooltip-website=Weboldal\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_it_IT.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_it_IT.properties index 0c72f5e1..d2a79886 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_it_IT.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_it_IT.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural={0} giocatori sono attualmente connessi al velocity.command.glist-view-all=Per visualizzare tutti i giocatori sui server, usa /glist all. velocity.command.reload-success=La configurazione di Velocity è stata ricaricata con successo. velocity.command.reload-failure=Impossibile ricaricare la configurazione di Velocity. Controlla la console per maggiori dettagli. -velocity.command.version-copyright=Copyright 2018-2023 {0}. {1} è concesso in licenza secondo i termini della GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} è concesso in licenza secondo i termini della GNU General Public License v3. velocity.command.no-plugins=Non ci sono plugin installati. velocity.command.plugins-list=Plugins\: {0} velocity.command.plugin-tooltip-website=Sito web\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ja_JP.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ja_JP.properties index a16b0605..235de499 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ja_JP.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ja_JP.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural={0} 人が現在プロキシに接続して velocity.command.glist-view-all=サーバー上のすべてのプレイヤーを表示するには、/glist allを使用してください。 velocity.command.reload-success=Velocityの設定が再読み込みされました。 velocity.command.reload-failure=Velocityの設定を再読み込みできません。詳細はコンソールで確認してください。 -velocity.command.version-copyright=Copyright 2018-2023 {0}. {1} は、GNU General Public License v3に基づいてライセンスされています。 +velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} は、GNU General Public License v3に基づいてライセンスされています。 velocity.command.no-plugins=現在インストールされているプラグインはありません。 velocity.command.plugins-list=プラグイン\: {0} velocity.command.plugin-tooltip-website=ウェブサイト\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ko_KR.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ko_KR.properties index b4bbf4c8..e7e528cc 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ko_KR.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ko_KR.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural=플레이어 {0}명이 현재 프록시에 velocity.command.glist-view-all=서버에 있는 모든 플레이어를 보려면, /glist all을 사용하세요. velocity.command.reload-success=Velocity 설정을 성공적으로 다시 불러왔습니다. velocity.command.reload-failure=Velocity 설정을 다시 불러올 수 없습니다. 자세한 내용은 콘솔을 확인하세요. -velocity.command.version-copyright=Copyright 2018-2023 {0}. {1}은(는) GNU General Public License v3 라이센스의 약관을 따릅니다. +velocity.command.version-copyright=Copyright 2018-{2} {0}. {1}은(는) GNU General Public License v3 라이센스의 약관을 따릅니다. velocity.command.no-plugins=설치된 플러그인이 없습니다. velocity.command.plugins-list=플러그인\: {0} velocity.command.plugin-tooltip-website=웹사이트\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_nb_NO.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_nb_NO.properties index ef7794e5..05a17545 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_nb_NO.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_nb_NO.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural={0} spillere er for øyeblikket tilkoblet d velocity.command.glist-view-all=For å se alle tilkoblede spillere, utfør /glist all. velocity.command.reload-success=Velocity konfigurasjonen ble lastet inn på nytt. velocity.command.reload-failure=Kan ikke laste inn Velocity konfigurasjonen din. Sjekk konsollen for mer detaljer. -velocity.command.version-copyright=Opphavsrett 2018-2023 {0}. {1} er lisensiert under betingelsene av GNU General Public License v3. +velocity.command.version-copyright=Opphavsrett 2018-{2} {0}. {1} er lisensiert under betingelsene av GNU General Public License v3. velocity.command.no-plugins=Ingen plugin er installert. velocity.command.plugins-list=Plugins\: {0} velocity.command.plugin-tooltip-website=Hjemmeside\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_nl_NL.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_nl_NL.properties index 44d12b08..cbb5f6b8 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_nl_NL.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_nl_NL.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural={0} spelers zijn momenteel verbonden met de velocity.command.glist-view-all=Om alle spelers op de servers te bekijken, gebruik /glist all. velocity.command.reload-success=Velocity configuratie succesvol herladen. velocity.command.reload-failure=De Velocity-configuratie kan niet opnieuw worden geladen. Kijk in de console voor meer details. -velocity.command.version-copyright=Copyright 2018-2023 {0}. {1} is gelicentieerd onder de voorwaarden van de GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} is gelicentieerd onder de voorwaarden van de GNU General Public License v3. velocity.command.no-plugins=Er zijn momenteel geen plugins geïnstalleerd. velocity.command.plugins-list=Plugins\: {0} velocity.command.plugin-tooltip-website=Website\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_nn_NO.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_nn_NO.properties index eb6bfa38..b23ce960 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_nn_NO.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_nn_NO.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural={0} spelare er for augneblinken tilkopla de velocity.command.glist-view-all=For å sjå alle tilkopla spelarar, gjer /glist all. velocity.command.reload-success=Velocity konfigurasjonen blei lasta inn på nytt. velocity.command.reload-failure=Kan ikkje lasta inn Velocity konfigurasjonen din. Sjekk konsollen for meir detaljar. -velocity.command.version-copyright=Opphavsrett 2018-2023 {0}. {1} er lisensiert under vilkåra av GNU General Public License v3. +velocity.command.version-copyright=Opphavsrett 2018-{2} {0}. {1} er lisensiert under vilkåra av GNU General Public License v3. velocity.command.no-plugins=Det er ingen plugins installert. velocity.command.plugins-list=Plugins\: {0} velocity.command.plugin-tooltip-website=Heimeside\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_pl_PL.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_pl_PL.properties index c3e2cfcf..02a90f87 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_pl_PL.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_pl_PL.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural=Z tym proxy jest obecnie połączonych {0} velocity.command.glist-view-all=Aby zobaczyć wszystkich graczy na wszystkich serwerach, użyj /glist all. velocity.command.reload-success=Konfiguracja Velocity została pomyślnie załadowana ponownie. velocity.command.reload-failure=Nie udało się ponownie załadować twojej konfiguracji Velocity. Sprawdź konsolę po więcej szczegółów. -velocity.command.version-copyright=Copyright 2018-2023 {0}. {1} jest objęty licencją na warunkach GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} jest objęty licencją na warunkach GNU General Public License v3. velocity.command.no-plugins=Obecnie nie ma zainstalowanych żadnych pluginów. velocity.command.plugins-list=Pluginy\: {0} velocity.command.plugin-tooltip-website=Strona internetowa\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_pt_BR.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_pt_BR.properties index 5a915a8d..e0708942 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_pt_BR.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_pt_BR.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural={0} jogadores estão atualmente conectados velocity.command.glist-view-all=Para ver todos os jogadores em todos os servidores, use /glist all. velocity.command.reload-success=Configuração do Velocity recarregada com êxito. velocity.command.reload-failure=Não foi possível recarregar a configuração do Velocity. Verifique o console para mais detalhes. -velocity.command.version-copyright=Copyright 2018-2023 {0}. {1} está licenciado sobre os termos da Licença Pública Geral GNU v3. +velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} está licenciado sobre os termos da Licença Pública Geral GNU v3. velocity.command.no-plugins=Não há plugins instalados atualmente. velocity.command.plugins-list=Plugins\: {0} velocity.command.plugin-tooltip-website=Site\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ro_RO.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ro_RO.properties index 8f28b91b..ea0d4609 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ro_RO.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ro_RO.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural={0} players are currently connected to the velocity.command.glist-view-all=To view all players on servers, use /glist all. velocity.command.reload-success=Velocity configuration successfully reloaded. velocity.command.reload-failure=Unable to reload your Velocity configuration. Check the console for more details. -velocity.command.version-copyright=Copyright 2018-2023 {0}. {1} is licensed under the terms of the GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} is licensed under the terms of the GNU General Public License v3. velocity.command.no-plugins=There are no plugins currently installed. velocity.command.plugins-list=Plugins\: {0} velocity.command.plugin-tooltip-website=Website\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ru_RU.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ru_RU.properties index a2a7afb9..5e5be8e6 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ru_RU.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_ru_RU.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural={0} игрок(а, ов) подключен velocity.command.glist-view-all=Чтобы просмотреть всех игроков на серверах, используйте /glist all. velocity.command.reload-success=Конфигурация Velocity успешно перезагружена. velocity.command.reload-failure=Не удалось перезагрузить конфигурацию Velocity. Проверьте консоль для получения более подробной информации. -velocity.command.version-copyright=Copyright 2018-2023 {0}. {1} лицензирована на условиях GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} лицензирована на условиях GNU General Public License v3. velocity.command.no-plugins=Ни одного плагина не установлено. velocity.command.plugins-list=Плагины\: {0} velocity.command.plugin-tooltip-website=Веб-сайт\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sk_SK.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sk_SK.properties index f7a89585..04e75974 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sk_SK.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sk_SK.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural={0} hráčov je pripojených na tento proxy velocity.command.glist-view-all=Pre zobrazenie všetkých hráčov na všetkých serveroch použi /glist all. velocity.command.reload-success=Konfigurácia Velocity úspešne načítaná. velocity.command.reload-failure=Nepodarilo sa načítať konfiguráciu Velocity. Pozri sa do konzoly pre viac informácií. -velocity.command.version-copyright=Copyright 2018-2023 {0}. {1} je licencovaný pod podmienkami GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} je licencovaný pod podmienkami GNU General Public License v3. velocity.command.no-plugins=Aktuálne nie sú nainštalované žiadne pluginy. velocity.command.plugins-list=Pluginy\: {0} velocity.command.plugin-tooltip-website=Webstránka\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sq_AL.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sq_AL.properties index 64e6b2b1..711b8e84 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sq_AL.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sq_AL.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural={0} lojtarët janë aktualisht të lidhur m velocity.command.glist-view-all=Për të parë të gjithë lojtarët në servera, përdorni /glist all. velocity.command.reload-success=Konfigurimi i shpejtësisë u ringarkua me sukses. velocity.command.reload-failure=Nuk mund të ringarkohet konfigurimi i shpejtësisë. Kontrolloni konsolën për më shumë detaje. -velocity.command.version-copyright=Copyright 2018-2023 {0}. {1} is licensed under the terms of the GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} is licensed under the terms of the GNU General Public License v3. velocity.command.no-plugins=There are no plugins currently installed. velocity.command.plugins-list=Plugins\: {0} velocity.command.plugin-tooltip-website=Website\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sr_CS.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sr_CS.properties index 6313a69b..006a3037 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sr_CS.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sr_CS.properties @@ -46,7 +46,7 @@ velocity.command.glist-player-plural={0} igrača je trenutno povezano na proxy. velocity.command.glist-view-all=Za pregled svih igrača na serverima koristite /glist all. velocity.command.reload-success=Velocity konfiguracija uspešno ponovno učitana. velocity.command.reload-failure=Nije moguće ponovo učitati Vašu Velocity konfiguraciju. Proverite konzolu za više detalja. -velocity.command.version-copyright=Copyright 2018-2023 {0}. {1} je licenciran pod uslovima licence GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} je licenciran pod uslovima licence GNU General Public License v3. velocity.command.no-plugins=Trenutno nema instaliranih plugin-a. velocity.command.plugins-list=Plugin-i\: {0} velocity.command.plugin-tooltip-website=Web stranica\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sr_Latn.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sr_Latn.properties index 313aa913..6106bb7b 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sr_Latn.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sr_Latn.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural={0} igrača je trenutno povezano na proxy. velocity.command.glist-view-all=Za pregled svih igrača na serverima koristite /glist all. velocity.command.reload-success=Velocity konfiguracija uspešno ponovno učitana. velocity.command.reload-failure=Nije moguće ponovo učitati Vašu Velocity konfiguraciju. Proverite konzolu za više detalja. -velocity.command.version-copyright=Copyright 2018-2023 {0}. {1} is licensed under the terms of the GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} is licensed under the terms of the GNU General Public License v3. velocity.command.no-plugins=Trenutno nema instaliranih plugin-a. velocity.command.plugins-list=Plugin-i\: {0} velocity.command.plugin-tooltip-website=Web stranica\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sv_SE.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sv_SE.properties index fca663fa..973506eb 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sv_SE.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_sv_SE.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural={0} spelare är just nu ansluten till proxy velocity.command.glist-view-all=För att se alla spelare på servrar, använd /glist all. velocity.command.reload-success=Velocity konfigurationen har laddats om. velocity.command.reload-failure=Det gick inte att ladda om din Velocity konfiguration. Kontrollera konsolen för mer information. -velocity.command.version-copyright=Copyright 2018-2023 {0}. {1} är licensierad enligt villkoren i GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} är licensierad enligt villkoren i GNU General Public License v3. velocity.command.no-plugins=Det finns inga tillägg installerade. velocity.command.plugins-list=Tillägg\: {0} velocity.command.plugin-tooltip-website=Webbsida\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_tl_PH.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_tl_PH.properties index a9e5c15b..ec4cc9d2 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_tl_PH.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_tl_PH.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural={0} mga manlalaro nasa proxy. velocity.command.glist-view-all=Para sa makita ang mga lahat ng manlalaro sa mga serbidor, magamit ka ng /glist all. velocity.command.reload-success=Matagumpay ang reload ng konfigurasyon ng Velocity. velocity.command.reload-failure=Hindi kaya ang magreload ng konfigurasyon mo sa Velocity. Magsiyasat ang konsole para mas maraming detalye. -velocity.command.version-copyright=Copyright 2018-2023 {0}. {1} is licensed under the terms of the GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} is licensed under the terms of the GNU General Public License v3. velocity.command.no-plugins=Walang plugin nang naka-install. velocity.command.plugins-list=Mga plugin\: {0} velocity.command.plugin-tooltip-website=Website\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_tr_TR.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_tr_TR.properties index 5ae8c98d..3c255d93 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_tr_TR.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_tr_TR.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural=Şu anda sunucuya toplam {0} oyuncu bağlı velocity.command.glist-view-all=Sunucudaki bütün oyuncuları görüntülemek için /glist all komutunu kullan. velocity.command.reload-success=Velocity ayarları başarıyla güncellendi. velocity.command.reload-failure=Velocity ayarlarınız güncellenemiyor. Daha fazla bilgi için konsolu kontrol edin. -velocity.command.version-copyright=Talif hakkı 2018-2023 {0}. {1}, GNU General Public License v3 adı altında lisanslanmıştır. +velocity.command.version-copyright=Talif hakkı 2018-{2} {0}. {1}, GNU General Public License v3 adı altında lisanslanmıştır. velocity.command.no-plugins=Yüklenmiş herhangi bir eklenti yok. velocity.command.plugins-list=Eklentiler\: {0} velocity.command.plugin-tooltip-website=Website\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_uk_UA.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_uk_UA.properties index 18be5e9c..2400bd29 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_uk_UA.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_uk_UA.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural={0} гравець(ця, ців) приєд velocity.command.glist-view-all=Щоб переглянути всіх гравців на серверах, використовуйте /glist all. velocity.command.reload-success=Конфігурація Velocity успішно перезавантажена. velocity.command.reload-failure=Не вдалося перезантажити конфігурацію Velocity. Перевірте консоль для подробиць. -velocity.command.version-copyright=Авторське право 2018-2023 {0}. {1} ліцензовано на умовах GNU General Public License v3. +velocity.command.version-copyright=Авторське право 2018-{2} {0}. {1} ліцензовано на умовах GNU General Public License v3. velocity.command.no-plugins=Плагіни відсутні. velocity.command.plugins-list=Плагіни\: {0} velocity.command.plugin-tooltip-website=Веб-сайт\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_vi_VN.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_vi_VN.properties index 7e46f191..5450e21e 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_vi_VN.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_vi_VN.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural={0} người chơi hiện đang kết nối velocity.command.glist-view-all=Để xem tất cả người chơi trên toàn bộ máy chủ, dùng /glist all. velocity.command.reload-success=Điều chỉnh Velocity đã tải lại thành công. velocity.command.reload-failure=Không thể tải lại điều chỉnh Velocity. Kiểm tra console để xem thêm chi tiết. -velocity.command.version-copyright=Copyright 2018-2023 {0}. {1} is licensed under the terms of the GNU General Public License v3. +velocity.command.version-copyright=Copyright 2018-{2} {0}. {1} is licensed under the terms of the GNU General Public License v3. velocity.command.no-plugins=Hiện tại không có plugin được cài đặt. velocity.command.plugins-list=Plugins\: {0} velocity.command.plugin-tooltip-website=Website\: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_zh_CN.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_zh_CN.properties index f26dea88..4b3b07d3 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_zh_CN.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_zh_CN.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural=共有 {0} 名玩家已连接至此代理 velocity.command.glist-view-all=使用 /glist all 命令来查看所有服务器的全部玩家列表。 velocity.command.reload-success=Velocity 配置已成功重载。 velocity.command.reload-failure=无法重载 Velocity 配置,请查看控制台了解详情。 -velocity.command.version-copyright=Copyright 2018-2023 {0}。{1} 以 GNU 通用公共许可证第三版授权。 +velocity.command.version-copyright=Copyright 2018-{2} {0}。{1} 以 GNU 通用公共许可证第三版授权。 velocity.command.no-plugins=当前没有安装任何插件。 velocity.command.plugins-list=插件:{0} velocity.command.plugin-tooltip-website=网站:{0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_zh_HK.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_zh_HK.properties index dc58d3f8..37468347 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_zh_HK.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_zh_HK.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural=共有 {0} 個玩家已連接至此代理 velocity.command.glist-view-all=使用 /glist all 命令來查看所有伺服器的玩家列表。 velocity.command.reload-success=Velocity 配置重新加載完成。 velocity.command.reload-failure=無法重新加載 Velocity 配置,請查看控制台了解詳情。 -velocity.command.version-copyright=Copyright 2018-2023 {0} ( {1} 的授權條款爲: GNU 通用公共授權條款第三版) +velocity.command.version-copyright=Copyright 2018-{2} {0} ( {1} 的授權條款爲: GNU 通用公共授權條款第三版) velocity.command.no-plugins=目前未有安裝任何 Velocity 插件。 velocity.command.plugins-list=插件: {0} velocity.command.plugin-tooltip-website=網站: {0} diff --git a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_zh_TW.properties b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_zh_TW.properties index 49ed3916..b7fe1b5b 100644 --- a/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_zh_TW.properties +++ b/proxy/src/main/resources/com/velocitypowered/proxy/l10n/messages_zh_TW.properties @@ -48,7 +48,7 @@ velocity.command.glist-player-plural=共有 {0} 名玩家已連線至此代理 velocity.command.glist-view-all=使用 /glist all 命令來查看所有伺服器的全部玩家列表。 velocity.command.reload-success=Velocity 配置已成功重載。 velocity.command.reload-failure=無法重載 Velocity 配置,請查看控制台了解詳情。 -velocity.command.version-copyright=Copyright 2018-2023 {0}。{1} 以 GNU 通用公共許可證第三版授權。 +velocity.command.version-copyright=Copyright 2018-{2} {0}。{1} 以 GNU 通用公共許可證第三版授權。 velocity.command.no-plugins=當前沒有安裝任何插件。 velocity.command.plugins-list=插件:{0} velocity.command.plugin-tooltip-website=網站:{0} From 02cf349075d49509ec0d06951bfe0f90d07ad947 Mon Sep 17 00:00:00 2001 From: Adrian <68704415+4drian3d@users.noreply.github.com> Date: Sun, 19 Oct 2025 09:43:40 -0500 Subject: [PATCH 33/36] Fixed disconnecting players in the middle of a backend server reconfiguration (#1669) --- .../backend/ConfigSessionHandler.java | 8 +++++- .../backend/LoginSessionHandler.java | 2 +- .../connection/client/ConnectedPlayer.java | 27 +++++++++---------- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java index 90bb1a30..8ab38e58 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/ConfigSessionHandler.java @@ -257,7 +257,13 @@ public class ConfigSessionHandler implements MinecraftSessionHandler { @Override public boolean handle(DisconnectPacket packet) { serverConn.disconnect(); - resultFuture.complete(ConnectionRequestResults.forDisconnect(packet, serverConn.getServer())); + // If the player receives a DisconnectPacket without a connection to a server in progress, + // it means that the backend server has kicked the player during reconfiguration + if (serverConn.getPlayer().getConnectionInFlight() != null) { + resultFuture.complete(ConnectionRequestResults.forDisconnect(packet, serverConn.getServer())); + } else { + serverConn.getPlayer().handleConnectionException(serverConn.getServer(), packet, true); + } return true; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java index 612e9c25..14884af4 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java @@ -165,7 +165,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler { } if (player.getConnection().getActiveSessionHandler() instanceof ClientPlaySessionHandler clientPlaySessionHandler) { smc.setAutoReading(false); - clientPlaySessionHandler.doSwitch().thenAcceptAsync((unused) -> smc.setAutoReading(true), smc.eventLoop()); + clientPlaySessionHandler.doSwitch().thenRunAsync(() -> smc.setAutoReading(true), smc.eventLoop()); } else { // Initial login - the player is already in configuration state. server.getEventManager().fireAndForget(new PlayerEnteredConfigurationEvent(player, serverConn)); 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 17f4fb5c..0807789f 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 @@ -817,9 +817,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, createConnectionRequest(res.getServer(), previousConnection).connect() .whenCompleteAsync((status, throwable) -> { if (throwable != null) { - handleConnectionException( - status != null ? status.getAttemptedConnection() : res.getServer(), throwable, - true); + handleConnectionException(res.getServer(), throwable, true); return; } @@ -1497,7 +1495,16 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, VelocityServerConnection con = new VelocityServerConnection(vrs, previousServer, ConnectedPlayer.this, server); connectionInFlight = con; - return con.connect().whenCompleteAsync((result, exception) -> this.resetIfInFlightIs(con), + + return con.connect().whenCompleteAsync((result, exception) -> { + if (result != null && !result.isSuccessful() && !result.isSafe()) { + handleConnectionException(result.getAttemptedConnection(), + // The only way for the reason to be null is if the result is safe + DisconnectPacket.create(result.getReasonComponent().orElseThrow(), + getProtocolVersion(), connection.getState()), false); + } + this.resetIfInFlightIs(con); + }, connection.eventLoop()); }, connection.eventLoop()); }); @@ -1511,22 +1518,14 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, @Override public CompletableFuture connect() { - return this.internalConnect().whenCompleteAsync((status, throwable) -> { - if (status != null && !status.isSuccessful()) { - if (!status.isSafe()) { - handleConnectionException(status.getAttemptedConnection(), throwable, false); - } - } - }, connection.eventLoop()).thenApply(x -> x); + return this.internalConnect().thenApply(x -> x); } @Override public CompletableFuture connectWithIndication() { return internalConnect().whenCompleteAsync((status, throwable) -> { if (throwable != null) { - // TODO: The exception handling from this is not very good. Find a better way. - handleConnectionException(status != null ? status.getAttemptedConnection() : toConnect, - throwable, true); + handleConnectionException(toConnect, throwable, true); return; } From 7412aca81c80c5639e863c2884d232b25316c137 Mon Sep 17 00:00:00 2001 From: Adrian <68704415+4drian3d@users.noreply.github.com> Date: Mon, 20 Oct 2025 19:51:47 -0500 Subject: [PATCH 34/36] Fixed sending ServerData packets if the description component from the backend server is null (#1673) --- api/build.gradle.kts | 2 +- .../api/proxy/server/ServerPing.java | 19 +++++++++++-------- .../util/ServerListPingHandler.java | 8 ++++++++ .../protocol/packet/chat/ComponentHolder.java | 4 ++-- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/api/build.gradle.kts b/api/build.gradle.kts index a4cbd741..b96208c1 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -70,7 +70,7 @@ tasks { "https://jd.advntr.dev/api/${libs.adventure.bom.get().version}/", "https://jd.advntr.dev/text-minimessage/${libs.adventure.bom.get().version}/", "https://jd.advntr.dev/key/${libs.adventure.bom.get().version}/", - "https://javadoc.io/doc/com.github.ben-manes.caffeine/caffeine/${libs.caffeine.get().version}/", + "https://www.javadocs.dev/com.github.ben-manes.caffeine/caffeine/${libs.caffeine.get().version}/", ) o.tags( diff --git a/api/src/main/java/com/velocitypowered/api/proxy/server/ServerPing.java b/api/src/main/java/com/velocitypowered/api/proxy/server/ServerPing.java index 4d906b7b..eb86f93e 100644 --- a/api/src/main/java/com/velocitypowered/api/proxy/server/ServerPing.java +++ b/api/src/main/java/com/velocitypowered/api/proxy/server/ServerPing.java @@ -19,7 +19,9 @@ import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.UUID; -import org.checkerframework.checker.nullness.qual.Nullable; +import net.kyori.adventure.text.Component; +import org.jspecify.annotations.Nullable; + /** * Represents a 1.7 and above server list ping response. This class is immutable. @@ -28,7 +30,7 @@ public final class ServerPing { private final Version version; private final @Nullable Players players; - private final net.kyori.adventure.text.Component description; + private final @Nullable Component description; private final @Nullable Favicon favicon; private final @Nullable ModInfo modinfo; @@ -47,8 +49,8 @@ public final class ServerPing { * @param modinfo the mods this server runs */ public ServerPing(Version version, @Nullable Players players, - net.kyori.adventure.text.Component description, @Nullable Favicon favicon, - @Nullable ModInfo modinfo) { + Component description, @Nullable Favicon favicon, + @Nullable ModInfo modinfo) { this.version = Preconditions.checkNotNull(version, "version"); this.players = players; this.description = Preconditions.checkNotNull(description, "description"); @@ -64,7 +66,8 @@ public final class ServerPing { return Optional.ofNullable(players); } - public net.kyori.adventure.text.Component getDescriptionComponent() { + @Nullable + public Component getDescriptionComponent() { return description; } @@ -151,7 +154,7 @@ public final class ServerPing { private final List samplePlayers = new ArrayList<>(); private String modType = "FML"; private final List mods = new ArrayList<>(); - private net.kyori.adventure.text.Component description; + private Component description; private @Nullable Favicon favicon; private boolean nullOutPlayers; private boolean nullOutModinfo; @@ -299,7 +302,7 @@ public final class ServerPing { * @param description Component to use as the description. * @return this builder, for chaining */ - public Builder description(net.kyori.adventure.text.Component description) { + public Builder description(Component description) { this.description = Preconditions.checkNotNull(description, "description"); return this; } @@ -359,7 +362,7 @@ public final class ServerPing { return samplePlayers; } - public Optional getDescriptionComponent() { + public Optional getDescriptionComponent() { return Optional.ofNullable(description); } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ServerListPingHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ServerListPingHandler.java index d1f2b0db..7740139c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ServerListPingHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/util/ServerListPingHandler.java @@ -36,6 +36,7 @@ import java.util.Locale; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; +import net.kyori.adventure.text.Component; /** * Common utilities for handling server list ping results. @@ -107,6 +108,13 @@ public class ServerListPingHandler { if (response == fallback) { continue; } + + if (response.getDescriptionComponent() == null) { + return response.asBuilder() + .description(Component.empty()) + .build(); + } + return response; } return fallback; diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/ComponentHolder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/ComponentHolder.java index 22b49621..01f36d0e 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/ComponentHolder.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/chat/ComponentHolder.java @@ -87,7 +87,7 @@ public class ComponentHolder { } catch (Exception ex) { logger.error( "Error converting binary component to JSON component! " - + "Binary: " + binaryTag + " JSON: " + json, ex); + + "Binary: " + binaryTag + " JSON: " + json, ex); throw ex; } } @@ -112,7 +112,7 @@ public class ComponentHolder { public static BinaryTag serialize(JsonElement json) { if (json instanceof JsonPrimitive jsonPrimitive) { - if (jsonPrimitive.isNumber()) { + if (jsonPrimitive.isNumber()) { Number number = json.getAsNumber(); if (number instanceof Byte) { From f75b512837b51a68b5c6cafaf63dbdaefedc284a Mon Sep 17 00:00:00 2001 From: Dylan Sperrer Date: Tue, 21 Oct 2025 11:45:04 -0600 Subject: [PATCH 35/36] Moved pre-1.19.1 command argument validation so it prints the faulty identifier (#1675) --- .../packet/brigadier/ArgumentPropertyRegistry.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/brigadier/ArgumentPropertyRegistry.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/brigadier/ArgumentPropertyRegistry.java index 197fddf3..203ab375 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/brigadier/ArgumentPropertyRegistry.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/packet/brigadier/ArgumentPropertyRegistry.java @@ -45,6 +45,8 @@ import com.mojang.brigadier.arguments.StringArgumentType; import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.protocol.ProtocolUtils; import io.netty.buffer.ByteBuf; +import org.jetbrains.annotations.NotNull; + import java.util.HashMap; import java.util.Map; @@ -87,9 +89,6 @@ public class ArgumentPropertyRegistry { ArgumentIdentifier identifier = readIdentifier(buf, protocolVersion); ArgumentPropertySerializer serializer = byIdentifier.get(identifier); - if (serializer == null) { - throw new IllegalArgumentException("Argument type identifier " + identifier + " unknown."); - } Object result = serializer.deserialize(buf, protocolVersion); if (result instanceof ArgumentType) { @@ -156,7 +155,7 @@ public class ArgumentPropertyRegistry { * @param protocolVersion the protocol version to use * @return the identifier read from the buffer */ - public static ArgumentIdentifier readIdentifier(ByteBuf buf, ProtocolVersion protocolVersion) { + public static @NotNull ArgumentIdentifier readIdentifier(ByteBuf buf, ProtocolVersion protocolVersion) { if (protocolVersion.noLessThan(MINECRAFT_1_19)) { int id = ProtocolUtils.readVarInt(buf); for (ArgumentIdentifier i : byIdentifier.keySet()) { @@ -173,8 +172,8 @@ public class ArgumentPropertyRegistry { return i; } } + throw new IllegalArgumentException("Argument type identifier " + identifier + " unknown."); } - return null; } static { From b6b6b20fe97cd9cb0d6b4e817d3e7db72aca2d8d Mon Sep 17 00:00:00 2001 From: Adrian <68704415+4drian3d@users.noreply.github.com> Date: Thu, 23 Oct 2025 11:13:36 -0500 Subject: [PATCH 36/36] Generate a new forwarding secret file if the file is deleted (#1671) * Generate a new forwarding secret file if the file is deleted This allows to generate a new forwarding secret simply by deleting the file if required. The file will only be generated if the forwarding secret is not configured through a system property resolves #1670 * Add file creation message --- .../proxy/config/VelocityConfiguration.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java index dfd007ac..6f0fb61c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java @@ -515,7 +515,7 @@ public class VelocityConfiguration implements ProxyConfig { String forwardingSecretString = System.getenv().getOrDefault( "VELOCITY_FORWARDING_SECRET", ""); - if (forwardingSecretString.isEmpty()) { + if (forwardingSecretString.isBlank()) { final String forwardSecretFile = config.get("forwarding-secret-file"); final Path secretPath = forwardSecretFile == null ? defaultForwardingSecretPath @@ -528,7 +528,11 @@ public class VelocityConfiguration implements ProxyConfig { "The file " + forwardSecretFile + " is not a valid file or it is a directory."); } } else { - throw new RuntimeException("The forwarding-secret-file does not exist."); + Files.createFile(secretPath); + Files.writeString(secretPath, forwardingSecretString = generateRandomString(12), + StandardCharsets.UTF_8); + logger.info("The forwarding-secret-file does not exist. A new file has been created at {}", + forwardSecretFile); } } final byte[] forwardingSecret = forwardingSecretString.getBytes(StandardCharsets.UTF_8);