From 99bd03099689cbc9c8f92af1cba207e4348afa75 Mon Sep 17 00:00:00 2001 From: Wouter Gritter Date: Wed, 18 Mar 2026 19:23:01 +0100 Subject: [PATCH] Implement missing writabilityChanged() and add backlog logging with BACKPRESSURE_LOG to all writabilityChanged() implementations. (#1745) --- .../backend/ConfigSessionHandler.java | 20 +++++++++++++ .../client/ClientConfigSessionHandler.java | 29 +++++++++++++++++++ .../client/ClientPlaySessionHandler.java | 10 +++++++ 3 files changed, 59 insertions(+) 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 8ab38e58a..e523f0f71 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 @@ -60,6 +60,7 @@ 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 io.netty.channel.Channel; import java.io.IOException; import java.net.InetSocketAddress; import java.util.concurrent.CompletableFuture; @@ -72,6 +73,9 @@ import org.apache.logging.log4j.Logger; * 1.20.2+ switching. Yes, some of this is exceptionally stupid. */ public class ConfigSessionHandler implements MinecraftSessionHandler { + private static final boolean BACKPRESSURE_LOG = + Boolean.getBoolean("velocity.log-server-backpressure"); + private static final Logger logger = LogManager.getLogger(ConfigSessionHandler.class); private final VelocityServer server; private final VelocityServerConnection serverConn; @@ -382,6 +386,22 @@ public class ConfigSessionHandler implements MinecraftSessionHandler { serverConn.getPlayer().getConnection().write(packet); } + @Override + public void writabilityChanged() { + Channel serverChan = serverConn.ensureConnected().getChannel(); + boolean writable = serverChan.isWritable(); + + if (BACKPRESSURE_LOG) { + if (writable) { + logger.info("{} is writable, will auto-read player connection data", this.serverConn); + } else { + logger.info("{} is not writable, not auto-reading player connection data", this.serverConn); + } + } + + serverConn.getPlayer().getConnection().setAutoReading(writable); + } + private void switchFailure(Throwable cause) { logger.error("Unable to switch to new server {} for {}", serverConn.getServerInfo().getName(), serverConn.getPlayer().getUsername(), cause); 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 776f99d68..299285826 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 @@ -60,6 +60,8 @@ import org.apache.logging.log4j.Logger; * Handles the client config stage. */ public class ClientConfigSessionHandler implements MinecraftSessionHandler { + private static final boolean BACKPRESSURE_LOG = + Boolean.getBoolean("velocity.log-server-backpressure"); private static final Logger logger = LogManager.getLogger(ClientConfigSessionHandler.class); private final VelocityServer server; @@ -268,6 +270,33 @@ public class ClientConfigSessionHandler implements MinecraftSessionHandler { player.disconnect(Component.translatable("velocity.error.player-connection-error", NamedTextColor.RED)); } + @Override + public void writabilityChanged() { + final boolean writable = player.getConnection().getChannel().isWritable(); + + if (BACKPRESSURE_LOG) { + if (writable) { + logger.info("{} is writable, will auto-read backend connection data", player); + } else { + logger.info("{} is not writable, not auto-reading backend connection data", player); + } + } + + if (!writable) { + // Flush pending packets to free up memory. Schedule on a future event loop invocation + // to avoid disabling auto-read while the flush resolves backpressure. + player.getConnection().eventLoop().execute(() -> player.getConnection().flush()); + } + + final VelocityServerConnection serverConn = player.getConnectionInFlightOrConnectedServer(); + if (serverConn != null) { + final MinecraftConnection smc = serverConn.getConnection(); + if (smc != null) { + smc.setAutoReading(writable); + } + } + } + /** * Calls the {@link PlayerConfigurationEvent}. * For 1.20.5+ backends this is done when the client responds to 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 a4ddacc90..fb13271be 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 @@ -98,6 +98,8 @@ import org.checkerframework.checker.nullness.qual.Nullable; * center that joins backend servers with players. */ public class ClientPlaySessionHandler implements MinecraftSessionHandler { + private static final boolean BACKPRESSURE_LOG = + Boolean.getBoolean("velocity.log-server-backpressure"); private static final Logger logger = LogManager.getLogger(ClientPlaySessionHandler.class); @@ -505,6 +507,14 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { public void writabilityChanged() { boolean writable = player.getConnection().getChannel().isWritable(); + if (BACKPRESSURE_LOG) { + if (writable) { + logger.info("{} is writable, will auto-read backend connection data", player); + } else { + logger.info("{} is not writable, not auto-reading backend connection data", player); + } + } + if (!writable) { // We might have packets queued from the server, so flush them now to free up memory. Make // sure to do it on a future invocation of the event loop, otherwise while the issue will