From 9d1332d3a3444814a77e7539935f27bd3d2755d4 Mon Sep 17 00:00:00 2001 From: Shane Freeder Date: Tue, 11 Feb 2025 17:19:37 +0000 Subject: [PATCH 1/2] Add PluginMessageEvents for configuration phase --- .../backend/ConfigSessionHandler.java | 28 ++++++++++++++++- .../client/ClientConfigSessionHandler.java | 30 ++++++++++++++++++- 2 files changed, 56 insertions(+), 2 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 74f0576c17..1860093d9e 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 @@ -17,6 +17,7 @@ package com.velocitypowered.proxy.connection.backend; +import com.velocitypowered.api.event.connection.PluginMessageEvent; import com.velocitypowered.api.event.connection.PreTransferEvent; import com.velocitypowered.api.event.player.CookieRequestEvent; import com.velocitypowered.api.event.player.CookieStoreEvent; @@ -24,6 +25,7 @@ import com.velocitypowered.api.event.player.ServerResourcePackRemoveEvent; import com.velocitypowered.api.event.player.ServerResourcePackSendEvent; import com.velocitypowered.api.network.ProtocolVersion; +import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.api.proxy.player.ResourcePackInfo; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.MinecraftConnection; @@ -54,6 +56,8 @@ import com.velocitypowered.proxy.protocol.packet.config.StartUpdatePacket; import com.velocitypowered.proxy.protocol.packet.config.TagsUpdatePacket; import com.velocitypowered.proxy.protocol.util.PluginMessageUtil; +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.Unpooled; import java.io.IOException; import java.net.InetSocketAddress; import java.util.concurrent.CompletableFuture; @@ -261,7 +265,29 @@ public boolean handle(PluginMessagePacket packet) { PluginMessageUtil.rewriteMinecraftBrand(packet, server.getVersion(), serverConn.getPlayer().getProtocolVersion())); } else { - serverConn.getPlayer().getConnection().write(packet.retain()); + byte[] bytes = ByteBufUtil.getBytes(packet.content()); + ChannelIdentifier id = this.server.getChannelRegistrar().getFromId(packet.getChannel()); + + if (id == null) { + serverConn.getPlayer().getConnection().write(packet.retain()); + return true; + } + + // Handling this stuff async means that we should probably pause + // the connection while we toss this off into another pool + this.serverConn.getConnection().setAutoReading(false); + this.server.getEventManager() + .fire(new PluginMessageEvent(serverConn, serverConn.getPlayer(), id, bytes)) + .thenAcceptAsync(pme -> { + if (pme.getResult().isAllowed() && !serverConn.getPlayer().getConnection().isClosed()) { + serverConn.getPlayer().getConnection().write(new PluginMessagePacket( + pme.getIdentifier().getId(), Unpooled.wrappedBuffer(bytes))); + } + this.serverConn.getConnection().setAutoReading(true); + }, serverConn.ensureConnected().eventLoop()).exceptionally((ex) -> { + logger.error("Exception while handling plugin message {}", packet, ex); + return null; + }); } return true; } 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 7bb7bedfa2..a4e734ed3e 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 @@ -17,14 +17,17 @@ package com.velocitypowered.proxy.connection.client; +import com.velocitypowered.api.event.connection.PluginMessageEvent; import com.velocitypowered.api.event.player.CookieReceiveEvent; import com.velocitypowered.api.event.player.PlayerClientBrandEvent; import com.velocitypowered.api.event.player.configuration.PlayerConfigurationEvent; import com.velocitypowered.api.event.player.configuration.PlayerFinishConfigurationEvent; import com.velocitypowered.api.event.player.configuration.PlayerFinishedConfigurationEvent; +import com.velocitypowered.api.proxy.messages.ChannelIdentifier; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.connection.MinecraftConnection; import com.velocitypowered.proxy.connection.MinecraftSessionHandler; +import com.velocitypowered.proxy.connection.backend.BungeeCordMessageResponder; import com.velocitypowered.proxy.connection.backend.VelocityServerConnection; import com.velocitypowered.proxy.connection.player.resourcepack.ResourcePackResponseBundle; import com.velocitypowered.proxy.protocol.MinecraftPacket; @@ -41,6 +44,7 @@ import com.velocitypowered.proxy.protocol.packet.config.KnownPacksPacket; import com.velocitypowered.proxy.protocol.util.PluginMessageUtil; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @@ -123,8 +127,32 @@ public boolean handle(final PluginMessagePacket packet) { brandChannel = packet.getChannel(); // Client sends `minecraft:brand` packet immediately after Login, // but at this time the backend server may not be ready + } else if (BungeeCordMessageResponder.isBungeeCordMessage(packet)) { + return true; } else if (serverConn != null) { - serverConn.ensureConnected().write(packet.retain()); + byte[] bytes = ByteBufUtil.getBytes(packet.content()); + ChannelIdentifier id = this.server.getChannelRegistrar().getFromId(packet.getChannel()); + + if (id == null) { + serverConn.getPlayer().getConnection().write(packet.retain()); + return true; + } + + // Handling this stuff async means that we should probably pause + // the connection while we toss this off into another pool + serverConn.getPlayer().getConnection().setAutoReading(false); + this.server.getEventManager() + .fire(new PluginMessageEvent(serverConn.getPlayer(), serverConn, id, bytes)) + .thenAcceptAsync(pme -> { + if (pme.getResult().isAllowed() && serverConn.getConnection() != null) { + serverConn.ensureConnected().write(new PluginMessagePacket( + pme.getIdentifier().getId(), Unpooled.wrappedBuffer(bytes))); + } + serverConn.getPlayer().getConnection().setAutoReading(true); + }, player.getConnection().eventLoop()).exceptionally((ex) -> { + logger.error("Exception while handling plugin message packet for {}", player, ex); + return null; + }); } return true; } From f986eb51ecf341fe3eec2a14a1f3563f4a58af21 Mon Sep 17 00:00:00 2001 From: Shane Freeder Date: Thu, 13 Feb 2025 12:12:33 +0000 Subject: [PATCH 2/2] Do not print an exception if a client closed before switching to config state --- .../proxy/connection/client/ConnectedPlayer.java | 5 +++++ 1 file changed, 5 insertions(+) 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 a77170550e..be7a82b2ae 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 @@ -1287,6 +1287,11 @@ private boolean sendKeepAliveToBackend(final @Nullable VelocityServerConnection public void switchToConfigState() { server.getEventManager().fire(new PlayerEnterConfigurationEvent(this, getConnectionInFlightOrConnectedServer())) .completeOnTimeout(null, 5, TimeUnit.SECONDS).thenRunAsync(() -> { + // if the connection was closed earlier, there is a risk that the player is no longer connected + if (!connection.getChannel().isActive()) { + return; + } + if (bundleHandler.isInBundleSession()) { bundleHandler.toggleBundleSession(); connection.write(BundleDelimiterPacket.INSTANCE);