diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java index c455e4271..8a6a22e90 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java @@ -33,6 +33,7 @@ import com.velocitypowered.natives.encryption.VelocityCipher; import com.velocitypowered.natives.encryption.VelocityCipherFactory; import com.velocitypowered.natives.util.Natives; import com.velocitypowered.proxy.VelocityServer; +import com.velocitypowered.proxy.connection.client.ConnectedPlayer; import com.velocitypowered.proxy.connection.client.HandshakeSessionHandler; import com.velocitypowered.proxy.connection.client.InitialLoginSessionHandler; import com.velocitypowered.proxy.connection.client.StatusSessionHandler; @@ -368,6 +369,7 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { public void setState(StateRegistry state) { ensureInEventLoop(); + final StateRegistry previousState = this.state; this.state = state; final MinecraftVarintFrameDecoder frameDecoder = this.channel.pipeline() .get(MinecraftVarintFrameDecoder.class); @@ -388,7 +390,13 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { if (state == StateRegistry.CONFIG) { // Activate the play packet queue - addPlayPacketQueueHandler(); + if (previousState == StateRegistry.PLAY + && this.pendingConfigurationSwitch + && this.association instanceof ConnectedPlayer) { + addPlayPacketQueueOutboundHandler(); + } else { + addPlayPacketQueueHandler(); + } } else { // Remove the queue if (this.channel.pipeline().get(Connections.PLAY_PACKET_QUEUE_OUTBOUND) != null) { @@ -404,13 +412,23 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { * Adds the play packet queue handler. */ public void addPlayPacketQueueHandler() { - if (this.channel.pipeline().get(Connections.PLAY_PACKET_QUEUE_OUTBOUND) == null) { - this.channel.pipeline().addAfter(Connections.MINECRAFT_ENCODER, Connections.PLAY_PACKET_QUEUE_OUTBOUND, - new PlayPacketQueueOutboundHandler(this.protocolVersion, channel.pipeline().get(MinecraftEncoder.class).getDirection())); - } + addPlayPacketQueueOutboundHandler(); + if (this.channel.pipeline().get(Connections.PLAY_PACKET_QUEUE_INBOUND) == null) { this.channel.pipeline().addAfter(Connections.MINECRAFT_DECODER, Connections.PLAY_PACKET_QUEUE_INBOUND, - new PlayPacketQueueInboundHandler(this.protocolVersion, channel.pipeline().get(MinecraftDecoder.class).getDirection())); + new PlayPacketQueueInboundHandler(this.protocolVersion, + channel.pipeline().get(MinecraftDecoder.class).getDirection())); + } + } + + /** + * Adds only the outbound play packet queue handler. + */ + public void addPlayPacketQueueOutboundHandler() { + if (this.channel.pipeline().get(Connections.PLAY_PACKET_QUEUE_OUTBOUND) == null) { + this.channel.pipeline().addAfter(Connections.MINECRAFT_ENCODER, Connections.PLAY_PACKET_QUEUE_OUTBOUND, + new PlayPacketQueueOutboundHandler(this.protocolVersion, + channel.pipeline().get(MinecraftEncoder.class).getDirection())); } } 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 fb13271be..a3485983f 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 @@ -470,7 +470,11 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { } MinecraftConnection smc = serverConnection.getConnection(); - if (smc != null && serverConnection.getPhase().consideredComplete()) { + final boolean stateAllowsForward = smc != null + && !smc.isClosed() + && serverConnection.getPhase().consideredComplete() + && smc.getState() == StateRegistry.PLAY; + if (stateAllowsForward) { if (packet instanceof PluginMessagePacket) { ((PluginMessagePacket) packet).retain(); } @@ -487,7 +491,11 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler { } MinecraftConnection smc = serverConnection.getConnection(); - if (smc != null && !smc.isClosed() && serverConnection.getPhase().consideredComplete()) { + final boolean stateAllowsForward = smc != null + && !smc.isClosed() + && serverConnection.getPhase().consideredComplete() + && smc.getState() == StateRegistry.PLAY; + if (stateAllowsForward) { smc.write(buf.retain()); } } 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 d6deeaef7..47d59a718 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 @@ -1349,11 +1349,17 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, final Long sentTime = serverConnection.getPendingPings().remove(packet.getRandomId()); if (sentTime != null) { final MinecraftConnection smc = serverConnection.getConnection(); - if (smc != null) { + final StateRegistry clientState = connection.getState(); + final boolean stateAllowsForward = smc != null + && !smc.isClosed() + && clientState == smc.getState() + && (clientState == StateRegistry.CONFIG || clientState == StateRegistry.PLAY); + if (stateAllowsForward) { setPing(TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - sentTime)); smc.write(packet); - return true; } + // We removed this, and so this is ours + return true; } } return false; @@ -1363,7 +1369,8 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, * Switches the connection to the client into config state. */ public void switchToConfigState() { - server.getEventManager().fire(new PlayerEnterConfigurationEvent(this, getConnectionInFlightOrConnectedServer())) + final VelocityServerConnection targetServer = getConnectionInFlightOrConnectedServer(); + server.getEventManager().fire(new PlayerEnterConfigurationEvent(this, targetServer)) .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()) { @@ -1378,7 +1385,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player, connection.pendingConfigurationSwitch = true; connection.getChannel().pipeline().get(MinecraftEncoder.class).setState(StateRegistry.CONFIG); // Make sure we don't send any play packets to the player after update start - connection.addPlayPacketQueueHandler(); + connection.addPlayPacketQueueOutboundHandler(); }, connection.eventLoop()).exceptionally((ex) -> { logger.error("Error switching player connection to config state", ex); return null;