diff --git a/api/src/main/java/com/velocitypowered/api/event/player/configuration/PlayerEnterConfigurationEvent.java b/api/src/main/java/com/velocitypowered/api/event/player/configuration/PlayerEnterConfigurationEvent.java
index f0148ef5ac..05d6c2af02 100644
--- a/api/src/main/java/com/velocitypowered/api/event/player/configuration/PlayerEnterConfigurationEvent.java
+++ b/api/src/main/java/com/velocitypowered/api/event/player/configuration/PlayerEnterConfigurationEvent.java
@@ -15,7 +15,9 @@
/**
* This event is executed when a player is about to enter the configuration state.
* It is not called for the initial configuration of a player after login.
- *
Velocity will wait for this event before asking the client to enter configuration state.
+ * Velocity will wait for this event before asking the client to enter configuration state.
+ * However due to backend server being unable to keep the connection alive during state changes,
+ * Velocity will only wait for a maximum of 5 seconds.
*
* @param player The player who is about to enter configuration state.
* @param server The server that wants to reconfigure the player.
diff --git a/api/src/main/java/com/velocitypowered/api/event/player/configuration/PlayerFinishConfigurationEvent.java b/api/src/main/java/com/velocitypowered/api/event/player/configuration/PlayerFinishConfigurationEvent.java
index 6b924d445f..50df5a8abe 100644
--- a/api/src/main/java/com/velocitypowered/api/event/player/configuration/PlayerFinishConfigurationEvent.java
+++ b/api/src/main/java/com/velocitypowered/api/event/player/configuration/PlayerFinishConfigurationEvent.java
@@ -15,9 +15,9 @@
/**
* This event is executed when a player is about to finish the configuration state.
* Velocity will wait for this event before asking the client to finish the configuration state.
- * However due to backend server being unable to keep the connection alive for more than 15 seconds,
+ * However due to backend server being unable to keep the connection alive during state changes,
* Velocity will only wait for a maximum of 5 seconds. If you need to hold a player in configuration
- * state it is recommended to use the {@link PlayerConfigurationEvent}.
+ * state, use the {@link PlayerConfigurationEvent}.
*
* @param player The player who is about to finish the configuration phase.
* @param server The server that has (re-)configured the player.
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 008eaaba5b..7d232b09f8 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
@@ -244,14 +244,14 @@ public CompletableFuture handleBackendFinishUpdate(VelocityServerConnectio
smc.write(brandPacket);
}
- callConfigurationEvent().thenRun(() -> {
- server.getEventManager().fire(new PlayerFinishConfigurationEvent(player, serverConn))
- .completeOnTimeout(null, 5, TimeUnit.SECONDS).thenRunAsync(() -> {
- player.getConnection().write(FinishedUpdatePacket.INSTANCE);
- player.getConnection().getChannel().pipeline().get(MinecraftEncoder.class).setState(StateRegistry.PLAY);
- server.getEventManager().fireAndForget(new PlayerFinishedConfigurationEvent(player, serverConn));
- }, player.getConnection().eventLoop());
- }).exceptionally(ex -> {
+ callConfigurationEvent().thenCompose(v -> {
+ return server.getEventManager().fire(new PlayerFinishConfigurationEvent(player, serverConn))
+ .completeOnTimeout(null, 5, TimeUnit.SECONDS);
+ }).thenRunAsync(() -> {
+ player.getConnection().write(FinishedUpdatePacket.INSTANCE);
+ player.getConnection().getChannel().pipeline().get(MinecraftEncoder.class).setState(StateRegistry.PLAY);
+ server.getEventManager().fireAndForget(new PlayerFinishedConfigurationEvent(player, serverConn));
+ }, player.getConnection().eventLoop()).exceptionally(ex -> {
logger.error("Error finishing configuration state:", ex);
return null;
});
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 b855b54c93..11f6b52982 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
@@ -1274,15 +1274,16 @@ private boolean sendKeepAliveToBackend(final @Nullable VelocityServerConnection
* Switches the connection to the client into config state.
*/
public void switchToConfigState() {
- server.getEventManager().fire(new PlayerEnterConfigurationEvent(this, getConnectionInFlightOrConnectedServer())).thenRunAsync(() -> {
- connection.write(StartUpdatePacket.INSTANCE);
- 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.eventLoop()).exceptionally((ex) -> {
- logger.error("Error switching player connection to config state", ex);
- return null;
- });
+ server.getEventManager().fire(new PlayerEnterConfigurationEvent(this, getConnectionInFlightOrConnectedServer()))
+ .completeOnTimeout(null, 5, TimeUnit.SECONDS).thenRunAsync(() -> {
+ connection.write(StartUpdatePacket.INSTANCE);
+ 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.eventLoop()).exceptionally((ex) -> {
+ logger.error("Error switching player connection to config state", ex);
+ return null;
+ });
}
/**