use outbound only queueing when reentering configuration (#1747)

* use outbound only queueing and guard serverbound forwarding during reconfiguration
This commit is contained in:
Harold
2026-04-08 18:27:02 -04:00
committed by GitHub
parent 6f01587318
commit b1a1b8bda3
3 changed files with 45 additions and 12 deletions

View File

@@ -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
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()));
}
}

View File

@@ -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());
}
}

View File

@@ -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;