Fixed disconnecting players in the middle of a backend server reconfiguration (#1669)

This commit is contained in:
Adrian
2025-10-19 09:43:40 -05:00
committed by GitHub
parent 67b988e6d2
commit 02cf349075
3 changed files with 21 additions and 16 deletions

View File

@@ -257,7 +257,13 @@ public class ConfigSessionHandler implements MinecraftSessionHandler {
@Override @Override
public boolean handle(DisconnectPacket packet) { public boolean handle(DisconnectPacket packet) {
serverConn.disconnect(); 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; return true;
} }

View File

@@ -165,7 +165,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
} }
if (player.getConnection().getActiveSessionHandler() instanceof ClientPlaySessionHandler clientPlaySessionHandler) { if (player.getConnection().getActiveSessionHandler() instanceof ClientPlaySessionHandler clientPlaySessionHandler) {
smc.setAutoReading(false); smc.setAutoReading(false);
clientPlaySessionHandler.doSwitch().thenAcceptAsync((unused) -> smc.setAutoReading(true), smc.eventLoop()); clientPlaySessionHandler.doSwitch().thenRunAsync(() -> smc.setAutoReading(true), smc.eventLoop());
} else { } else {
// Initial login - the player is already in configuration state. // Initial login - the player is already in configuration state.
server.getEventManager().fireAndForget(new PlayerEnteredConfigurationEvent(player, serverConn)); server.getEventManager().fireAndForget(new PlayerEnteredConfigurationEvent(player, serverConn));

View File

@@ -817,9 +817,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
createConnectionRequest(res.getServer(), previousConnection).connect() createConnectionRequest(res.getServer(), previousConnection).connect()
.whenCompleteAsync((status, throwable) -> { .whenCompleteAsync((status, throwable) -> {
if (throwable != null) { if (throwable != null) {
handleConnectionException( handleConnectionException(res.getServer(), throwable, true);
status != null ? status.getAttemptedConnection() : res.getServer(), throwable,
true);
return; return;
} }
@@ -1497,7 +1495,16 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
VelocityServerConnection con = VelocityServerConnection con =
new VelocityServerConnection(vrs, previousServer, ConnectedPlayer.this, server); new VelocityServerConnection(vrs, previousServer, ConnectedPlayer.this, server);
connectionInFlight = con; 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());
}, connection.eventLoop()); }, connection.eventLoop());
}); });
@@ -1511,22 +1518,14 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
@Override @Override
public CompletableFuture<Result> connect() { public CompletableFuture<Result> connect() {
return this.internalConnect().whenCompleteAsync((status, throwable) -> { return this.internalConnect().thenApply(x -> x);
if (status != null && !status.isSuccessful()) {
if (!status.isSafe()) {
handleConnectionException(status.getAttemptedConnection(), throwable, false);
}
}
}, connection.eventLoop()).thenApply(x -> x);
} }
@Override @Override
public CompletableFuture<Boolean> connectWithIndication() { public CompletableFuture<Boolean> connectWithIndication() {
return internalConnect().whenCompleteAsync((status, throwable) -> { return internalConnect().whenCompleteAsync((status, throwable) -> {
if (throwable != null) { if (throwable != null) {
// TODO: The exception handling from this is not very good. Find a better way. handleConnectionException(toConnect, throwable, true);
handleConnectionException(status != null ? status.getAttemptedConnection() : toConnect,
throwable, true);
return; return;
} }