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
new file mode 100644
index 000000000..3b108c6a7
--- /dev/null
+++ b/api/src/main/java/com/velocitypowered/api/event/player/configuration/PlayerEnterConfigurationEvent.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 Velocity Contributors
+ *
+ * The Velocity API is licensed under the terms of the MIT License. For more details,
+ * reference the LICENSE file in the api top-level directory.
+ */
+
+package com.velocitypowered.api.event.player.configuration;
+
+import com.velocitypowered.api.network.ProtocolState;
+import com.velocitypowered.api.proxy.Player;
+import com.velocitypowered.api.proxy.ServerConnection;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * This event is executed when a player with version 1.20.2 or higher enters the configuration phase.
+ *
From this moment on, until the {@link PlayerFinishedConfigurationEvent} is executed,
+ * the {@linkplain Player#getProtocolState()} method is guaranteed
+ * to return {@link ProtocolState#CONFIGURATION}.
+ *
+ * @param player The player that has entered the configuration phase.
+ * @param server The server that will now (re-)configure the player.
+ * @since 3.3.0
+ * @sinceMinecraft 1.20.2
+ */
+public record PlayerEnterConfigurationEvent(@NotNull Player player, ServerConnection server) {
+}
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
new file mode 100644
index 000000000..f6249b897
--- /dev/null
+++ b/api/src/main/java/com/velocitypowered/api/event/player/configuration/PlayerFinishConfigurationEvent.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 Velocity Contributors
+ *
+ * The Velocity API is licensed under the terms of the MIT License. For more details,
+ * reference the LICENSE file in the api top-level directory.
+ */
+
+package com.velocitypowered.api.event.player.configuration;
+
+import com.velocitypowered.api.event.annotation.AwaitingEvent;
+import com.velocitypowered.api.proxy.Player;
+import com.velocitypowered.api.proxy.ServerConnection;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * This event is executed when the player is about to finish the Configuration state.
+ *
Velocity will wait for this event to finish the configuration phase on the client.
+ *
+ * @param player The player who is about to complete the configuration phase.
+ * @param server The server that is currently (re-)configuring the player.
+ * @since 3.3.0
+ * @sinceMinecraft 1.20.2
+ */
+@AwaitingEvent
+public record PlayerFinishConfigurationEvent(@NotNull Player player, @NotNull ServerConnection server) {
+}
diff --git a/api/src/main/java/com/velocitypowered/api/event/player/configuration/PlayerFinishedConfigurationEvent.java b/api/src/main/java/com/velocitypowered/api/event/player/configuration/PlayerFinishedConfigurationEvent.java
new file mode 100644
index 000000000..09e76104f
--- /dev/null
+++ b/api/src/main/java/com/velocitypowered/api/event/player/configuration/PlayerFinishedConfigurationEvent.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 Velocity Contributors
+ *
+ * The Velocity API is licensed under the terms of the MIT License. For more details,
+ * reference the LICENSE file in the api top-level directory.
+ */
+
+package com.velocitypowered.api.event.player.configuration;
+
+import com.velocitypowered.api.network.ProtocolState;
+import com.velocitypowered.api.proxy.Player;
+import com.velocitypowered.api.proxy.ServerConnection;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Event executed when a player of version 1.20.2 or higher finishes the Configuration state.
+ *
From this moment on, the {@link Player#getProtocolState()} method
+ * will return {@link ProtocolState#PLAY}.
+ *
+ * @param player The player who has completed the Configuration state
+ * @param server The server that has (re-)configured the player.
+ * @since 3.3.0
+ * @sinceMinecraft 1.20.2
+ */
+public record PlayerFinishedConfigurationEvent(@NotNull Player player, @NotNull ServerConnection server) {
+}
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java
index ef8217c69..004243616 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java
@@ -45,7 +45,7 @@ import com.velocitypowered.proxy.command.builtin.ShutdownCommand;
import com.velocitypowered.proxy.command.builtin.VelocityCommand;
import com.velocitypowered.proxy.config.VelocityConfiguration;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
-import com.velocitypowered.proxy.connection.player.VelocityResourcePackInfo;
+import com.velocitypowered.proxy.connection.player.resourcepack.VelocityResourcePackInfo;
import com.velocitypowered.proxy.connection.util.ServerListPingHandler;
import com.velocitypowered.proxy.console.VelocityConsole;
import com.velocitypowered.proxy.crypto.EncryptionUtils;
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 b1c91ebdb..c91739078 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java
@@ -47,7 +47,8 @@ import com.velocitypowered.proxy.protocol.netty.MinecraftCompressorAndLengthEnco
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
import com.velocitypowered.proxy.protocol.netty.MinecraftEncoder;
import com.velocitypowered.proxy.protocol.netty.MinecraftVarintLengthEncoder;
-import com.velocitypowered.proxy.protocol.netty.PlayPacketQueueHandler;
+import com.velocitypowered.proxy.protocol.netty.PlayPacketQueueInboundHandler;
+import com.velocitypowered.proxy.protocol.netty.PlayPacketQueueOutboundHandler;
import com.velocitypowered.proxy.protocol.packet.SetCompressionPacket;
import com.velocitypowered.proxy.util.except.QuietDecoderException;
import io.netty.buffer.ByteBuf;
@@ -148,13 +149,11 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
return;
}
- if (msg instanceof MinecraftPacket) {
- MinecraftPacket pkt = (MinecraftPacket) msg;
+ if (msg instanceof MinecraftPacket pkt) {
if (!pkt.handle(activeSessionHandler)) {
activeSessionHandler.handleGeneric((MinecraftPacket) msg);
}
- } else if (msg instanceof HAProxyMessage) {
- HAProxyMessage proxyMessage = (HAProxyMessage) msg;
+ } else if (msg instanceof HAProxyMessage proxyMessage) {
this.remoteAddress = new InetSocketAddress(proxyMessage.sourceAddress(),
proxyMessage.sourcePort());
} else if (msg instanceof ByteBuf) {
@@ -383,9 +382,14 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
if (state == StateRegistry.CONFIG) {
// Activate the play packet queue
addPlayPacketQueueHandler();
- } else if (this.channel.pipeline().get(Connections.PLAY_PACKET_QUEUE) != null) {
+ } else {
// Remove the queue
- this.channel.pipeline().remove(Connections.PLAY_PACKET_QUEUE);
+ if (this.channel.pipeline().get(Connections.PLAY_PACKET_QUEUE_OUTBOUND) != null) {
+ this.channel.pipeline().remove(Connections.PLAY_PACKET_QUEUE_OUTBOUND);
+ }
+ if (this.channel.pipeline().get(Connections.PLAY_PACKET_QUEUE_INBOUND) != null) {
+ this.channel.pipeline().remove(Connections.PLAY_PACKET_QUEUE_INBOUND);
+ }
}
}
@@ -393,10 +397,13 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
* Adds the play packet queue handler.
*/
public void addPlayPacketQueueHandler() {
- if (this.channel.pipeline().get(Connections.PLAY_PACKET_QUEUE) == null) {
- this.channel.pipeline().addAfter(Connections.MINECRAFT_ENCODER, Connections.PLAY_PACKET_QUEUE,
- new PlayPacketQueueHandler(this.protocolVersion,
- channel.pipeline().get(MinecraftEncoder.class).getDirection()));
+ 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()));
+ }
+ 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()));
}
}
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java
index 5b83e6549..14989b1a3 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BackendPlaySessionHandler.java
@@ -40,8 +40,8 @@ import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
-import com.velocitypowered.proxy.connection.player.VelocityResourcePackInfo;
-import com.velocitypowered.proxy.connection.player.resourcepack.ResourcePackHandler;
+import com.velocitypowered.proxy.connection.player.resourcepack.VelocityResourcePackInfo;
+import com.velocitypowered.proxy.connection.player.resourcepack.handler.ResourcePackHandler;
import com.velocitypowered.proxy.connection.util.ConnectionMessages;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.StateRegistry;
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java
index 3c2691781..ea28d2c97 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/BungeeCordMessageResponder.java
@@ -37,7 +37,6 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.util.Optional;
import java.util.StringJoiner;
-import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.ComponentSerializer;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
@@ -143,7 +142,7 @@ public class BungeeCordMessageResponder {
out.writeUTF("PlayerList");
out.writeUTF(info.getServerInfo().getName());
- StringJoiner joiner = new StringJoiner(", ");
+ final StringJoiner joiner = new StringJoiner(", ");
for (Player online : info.getPlayersConnected()) {
joiner.add(online.getUsername());
}
@@ -187,10 +186,9 @@ public class BungeeCordMessageResponder {
Component messageComponent = serializer.deserialize(message);
if (target.equals("ALL")) {
- proxy.sendMessage(Identity.nil(), messageComponent);
+ proxy.sendMessage(messageComponent);
} else {
- proxy.getPlayer(target).ifPresent(player -> player.sendMessage(Identity.nil(),
- messageComponent));
+ proxy.getPlayer(target).ifPresent(player -> player.sendMessage(messageComponent));
}
}
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 3f4325e52..a19854eb9 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
@@ -29,7 +29,7 @@ import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.client.ClientConfigSessionHandler;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
-import com.velocitypowered.proxy.connection.player.VelocityResourcePackInfo;
+import com.velocitypowered.proxy.connection.player.resourcepack.VelocityResourcePackInfo;
import com.velocitypowered.proxy.connection.util.ConnectionMessages;
import com.velocitypowered.proxy.connection.util.ConnectionRequestResults;
import com.velocitypowered.proxy.connection.util.ConnectionRequestResults.Impl;
@@ -132,7 +132,8 @@ public class ConfigSessionHandler implements MinecraftSessionHandler {
@Override
public boolean handle(KeepAlivePacket packet) {
- serverConn.ensureConnected().write(packet);
+ serverConn.getPendingPings().put(packet.getRandomId(), System.nanoTime());
+ serverConn.getPlayer().getConnection().write(packet);
return true;
}
@@ -193,30 +194,25 @@ public class ConfigSessionHandler implements MinecraftSessionHandler {
@Override
public boolean handle(FinishedUpdatePacket packet) {
- MinecraftConnection smc = serverConn.ensureConnected();
- ConnectedPlayer player = serverConn.getPlayer();
- ClientConfigSessionHandler configHandler =
- (ClientConfigSessionHandler) player.getConnection().getActiveSessionHandler();
+ final MinecraftConnection smc = serverConn.ensureConnected();
+ final ConnectedPlayer player = serverConn.getPlayer();
+ final ClientConfigSessionHandler configHandler = (ClientConfigSessionHandler) player.getConnection().getActiveSessionHandler();
- smc.setAutoReading(false);
- // Even when not auto reading messages are still decoded. Decode them with the correct state
smc.getChannel().pipeline().get(MinecraftDecoder.class).setState(StateRegistry.PLAY);
- configHandler.handleBackendFinishUpdate(serverConn).thenAcceptAsync((unused) -> {
+ //noinspection DataFlowIssue
+ configHandler.handleBackendFinishUpdate(serverConn).thenRunAsync(() -> {
+ smc.write(FinishedUpdatePacket.INSTANCE);
if (serverConn == player.getConnectedServer()) {
smc.setActiveSessionHandler(StateRegistry.PLAY);
- player.sendPlayerListHeaderAndFooter(
- player.getPlayerListHeader(), player.getPlayerListFooter());
+ player.sendPlayerListHeaderAndFooter(player.getPlayerListHeader(), player.getPlayerListFooter());
// The client cleared the tab list. TODO: Restore changes done via TabList API
player.getTabList().clearAllSilent();
} else {
- smc.setActiveSessionHandler(StateRegistry.PLAY,
- new TransitionSessionHandler(server, serverConn, resultFuture));
+ smc.setActiveSessionHandler(StateRegistry.PLAY, new TransitionSessionHandler(server, serverConn, resultFuture));
}
- if (player.resourcePackHandler().getFirstAppliedPack() == null
- && resourcePackToApply != null) {
+ if (player.resourcePackHandler().getFirstAppliedPack() == null && resourcePackToApply != null) {
player.resourcePackHandler().queueResourcePack(resourcePackToApply);
}
- smc.setAutoReading(true);
}, smc.eventLoop());
return true;
}
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java
index a672c9174..a2d2205ef 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java
@@ -166,12 +166,9 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
if (player.getClientSettingsPacket() != null) {
smc.write(player.getClientSettingsPacket());
}
- if (player.getConnection().getActiveSessionHandler() instanceof ClientPlaySessionHandler) {
+ if (player.getConnection().getActiveSessionHandler() instanceof ClientPlaySessionHandler clientPlaySessionHandler) {
smc.setAutoReading(false);
- ((ClientPlaySessionHandler) player.getConnection()
- .getActiveSessionHandler()).doSwitch().thenAcceptAsync((unused) -> {
- smc.setAutoReading(true);
- }, smc.eventLoop());
+ clientPlaySessionHandler.doSwitch().thenAcceptAsync((unused) -> smc.setAutoReading(true), smc.eventLoop());
}
}
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 52b67f7b5..3d955e903 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
@@ -19,6 +19,8 @@ package com.velocitypowered.proxy.connection.client;
import com.velocitypowered.api.event.player.CookieReceiveEvent;
import com.velocitypowered.api.event.player.PlayerClientBrandEvent;
+import com.velocitypowered.api.event.player.configuration.PlayerFinishConfigurationEvent;
+import com.velocitypowered.api.event.player.configuration.PlayerFinishedConfigurationEvent;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
@@ -246,13 +248,11 @@ public class ClientConfigSessionHandler implements MinecraftSessionHandler {
smc.write(brandPacket);
}
- player.getConnection().eventLoop().execute(() -> {
+ server.getEventManager().fire(new PlayerFinishConfigurationEvent(player, serverConn)).thenAcceptAsync(event -> {
player.getConnection().write(FinishedUpdatePacket.INSTANCE);
player.getConnection().getChannel().pipeline().get(MinecraftEncoder.class).setState(StateRegistry.PLAY);
- });
-
- smc.write(FinishedUpdatePacket.INSTANCE);
- smc.getChannel().pipeline().get(MinecraftEncoder.class).setState(StateRegistry.PLAY);
+ server.getEventManager().fireAndForget(new PlayerFinishedConfigurationEvent(player, serverConn));
+ }, player.getConnection().eventLoop());
return configSwitchFuture;
}
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 2a595b167..a76f054e7 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
@@ -27,6 +27,7 @@ import com.velocitypowered.api.event.player.CookieReceiveEvent;
import com.velocitypowered.api.event.player.PlayerChannelRegisterEvent;
import com.velocitypowered.api.event.player.PlayerClientBrandEvent;
import com.velocitypowered.api.event.player.TabCompleteEvent;
+import com.velocitypowered.api.event.player.configuration.PlayerEnterConfigurationEvent;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
@@ -406,6 +407,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
// Complete client switch
player.getConnection().setActiveSessionHandler(StateRegistry.CONFIG);
VelocityServerConnection serverConnection = player.getConnectedServer();
+ server.getEventManager().fireAndForget(new PlayerEnterConfigurationEvent(player, serverConnection));
if (serverConnection != null) {
MinecraftConnection smc = serverConnection.ensureConnected();
CompletableFuture.runAsync(() -> {
@@ -512,7 +514,7 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
* @return a future that completes when the switch is complete
*/
public CompletableFuture doSwitch() {
- VelocityServerConnection existingConnection = player.getConnectedServer();
+ final VelocityServerConnection existingConnection = player.getConnectedServer();
if (existingConnection != null) {
// Shut down the existing server connection.
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 a471e2b53..de828382e 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
@@ -37,6 +37,7 @@ import com.velocitypowered.api.event.player.KickedFromServerEvent.ServerKickResu
import com.velocitypowered.api.event.player.PlayerModInfoEvent;
import com.velocitypowered.api.event.player.PlayerSettingsChangedEvent;
import com.velocitypowered.api.event.player.ServerPreConnectEvent;
+import com.velocitypowered.api.event.player.configuration.PlayerEnterConfigurationEvent;
import com.velocitypowered.api.network.ProtocolState;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.permission.PermissionFunction;
@@ -59,8 +60,9 @@ import com.velocitypowered.proxy.adventure.VelocityBossBarImplementation;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
-import com.velocitypowered.proxy.connection.player.VelocityResourcePackInfo;
-import com.velocitypowered.proxy.connection.player.resourcepack.ResourcePackHandler;
+import com.velocitypowered.proxy.connection.player.bundle.BundleDelimiterHandler;
+import com.velocitypowered.proxy.connection.player.resourcepack.VelocityResourcePackInfo;
+import com.velocitypowered.proxy.connection.player.resourcepack.handler.ResourcePackHandler;
import com.velocitypowered.proxy.connection.util.ConnectionMessages;
import com.velocitypowered.proxy.connection.util.ConnectionRequestResults.Impl;
import com.velocitypowered.proxy.connection.util.VelocityInboundConnection;
@@ -806,7 +808,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
}, connection.eventLoop());
} else if (event.getResult() instanceof final Notify res) {
if (event.kickedDuringServerConnect() && previousConnection != null) {
- sendMessage(Identity.nil(), res.getMessageComponent());
+ sendMessage(res.getMessageComponent());
} else {
disconnect(res.getMessageComponent());
}
@@ -1224,11 +1226,12 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
CompletableFuture.runAsync(() -> {
connection.write(StartUpdatePacket.INSTANCE);
connection.getChannel().pipeline()
- .get(MinecraftEncoder.class).setState(StateRegistry.CONFIG);
+ .get(MinecraftEncoder.class).setState(StateRegistry.CONFIG);
// Make sure we don't send any play packets to the player after update start
connection.addPlayPacketQueueHandler();
+ server.getEventManager().fireAndForget(new PlayerEnterConfigurationEvent(this, connectionInFlight));
}, connection.eventLoop()).exceptionally((ex) -> {
- logger.error("Error switching player connection to config state:", ex);
+ logger.error("Error switching player connection to config state", ex);
return null;
});
}
@@ -1363,24 +1366,20 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
}
switch (status.getStatus()) {
- case ALREADY_CONNECTED:
- sendMessage(Identity.nil(), ConnectionMessages.ALREADY_CONNECTED);
- break;
- case CONNECTION_IN_PROGRESS:
- sendMessage(Identity.nil(), ConnectionMessages.IN_PROGRESS);
- break;
- case CONNECTION_CANCELLED:
+ case ALREADY_CONNECTED -> sendMessage(ConnectionMessages.ALREADY_CONNECTED);
+ case CONNECTION_IN_PROGRESS -> sendMessage(ConnectionMessages.IN_PROGRESS);
+ case CONNECTION_CANCELLED -> {
// Ignored; the plugin probably already handled this.
- break;
- case SERVER_DISCONNECTED:
- Component reason = status.getReasonComponent()
- .orElse(ConnectionMessages.INTERNAL_SERVER_CONNECTION_ERROR);
+ }
+ case SERVER_DISCONNECTED -> {
+ final Component reason = status.getReasonComponent()
+ .orElse(ConnectionMessages.INTERNAL_SERVER_CONNECTION_ERROR);
handleConnectionException(toConnect,
- DisconnectPacket.create(reason, getProtocolVersion(), connection.getState()), status.isSafe());
- break;
- default:
+ DisconnectPacket.create(reason, getProtocolVersion(), connection.getState()), status.isSafe());
+ }
+ default -> {
// The only remaining value is successful (no need to do anything!)
- break;
+ }
}
}, connection.eventLoop()).thenApply(Result::isSuccessful);
}
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/BundleDelimiterHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/player/bundle/BundleDelimiterHandler.java
similarity index 95%
rename from proxy/src/main/java/com/velocitypowered/proxy/connection/client/BundleDelimiterHandler.java
rename to proxy/src/main/java/com/velocitypowered/proxy/connection/player/bundle/BundleDelimiterHandler.java
index e92d10587..d5d52ef03 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/BundleDelimiterHandler.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/player/bundle/BundleDelimiterHandler.java
@@ -15,10 +15,11 @@
* along with this program. If not, see .
*/
-package com.velocitypowered.proxy.connection.client;
+package com.velocitypowered.proxy.connection.player.bundle;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
+import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.proxy.protocol.packet.BundleDelimiterPacket;
import java.util.concurrent.CompletableFuture;
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/player/VelocityResourcePackInfo.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/VelocityResourcePackInfo.java
similarity index 98%
rename from proxy/src/main/java/com/velocitypowered/proxy/connection/player/VelocityResourcePackInfo.java
rename to proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/VelocityResourcePackInfo.java
index 67f90fd15..4292710e0 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/connection/player/VelocityResourcePackInfo.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/VelocityResourcePackInfo.java
@@ -15,7 +15,7 @@
* along with this program. If not, see .
*/
-package com.velocitypowered.proxy.connection.player;
+package com.velocitypowered.proxy.connection.player.resourcepack;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.proxy.player.ResourcePackInfo;
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/Legacy117ResourcePackHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/handler/Legacy117ResourcePackHandler.java
similarity index 99%
rename from proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/Legacy117ResourcePackHandler.java
rename to proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/handler/Legacy117ResourcePackHandler.java
index 385bdce4c..a1af567dd 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/Legacy117ResourcePackHandler.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/handler/Legacy117ResourcePackHandler.java
@@ -15,7 +15,7 @@
* along with this program. If not, see .
*/
-package com.velocitypowered.proxy.connection.player.resourcepack;
+package com.velocitypowered.proxy.connection.player.resourcepack.handler;
import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent;
import com.velocitypowered.proxy.VelocityServer;
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/LegacyResourcePackHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/handler/LegacyResourcePackHandler.java
similarity index 98%
rename from proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/LegacyResourcePackHandler.java
rename to proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/handler/LegacyResourcePackHandler.java
index b1b325484..53fa2421c 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/LegacyResourcePackHandler.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/handler/LegacyResourcePackHandler.java
@@ -15,13 +15,14 @@
* along with this program. If not, see .
*/
-package com.velocitypowered.proxy.connection.player.resourcepack;
+package com.velocitypowered.proxy.connection.player.resourcepack.handler;
import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.player.ResourcePackInfo;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
+import com.velocitypowered.proxy.connection.player.resourcepack.ResourcePackResponseBundle;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collection;
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/ModernResourcePackHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/handler/ModernResourcePackHandler.java
similarity index 98%
rename from proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/ModernResourcePackHandler.java
rename to proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/handler/ModernResourcePackHandler.java
index 5ba74a273..077ce701d 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/ModernResourcePackHandler.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/handler/ModernResourcePackHandler.java
@@ -15,7 +15,7 @@
* along with this program. If not, see .
*/
-package com.velocitypowered.proxy.connection.player.resourcepack;
+package com.velocitypowered.proxy.connection.player.resourcepack.handler;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimaps;
@@ -23,6 +23,7 @@ import com.velocitypowered.api.event.player.PlayerResourcePackStatusEvent;
import com.velocitypowered.api.proxy.player.ResourcePackInfo;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
+import com.velocitypowered.proxy.connection.player.resourcepack.ResourcePackResponseBundle;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/ResourcePackHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/handler/ResourcePackHandler.java
similarity index 97%
rename from proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/ResourcePackHandler.java
rename to proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/handler/ResourcePackHandler.java
index 4e6e72505..e5df9f659 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/ResourcePackHandler.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/player/resourcepack/handler/ResourcePackHandler.java
@@ -15,14 +15,15 @@
* along with this program. If not, see .
*/
-package com.velocitypowered.proxy.connection.player.resourcepack;
+package com.velocitypowered.proxy.connection.player.resourcepack.handler;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.player.ResourcePackInfo;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.backend.VelocityServerConnection;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
-import com.velocitypowered.proxy.connection.player.VelocityResourcePackInfo;
+import com.velocitypowered.proxy.connection.player.resourcepack.ResourcePackResponseBundle;
+import com.velocitypowered.proxy.connection.player.resourcepack.VelocityResourcePackInfo;
import com.velocitypowered.proxy.protocol.packet.ResourcePackRequestPacket;
import com.velocitypowered.proxy.protocol.packet.ResourcePackResponsePacket;
import com.velocitypowered.proxy.protocol.packet.chat.ComponentHolder;
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/Connections.java b/proxy/src/main/java/com/velocitypowered/proxy/network/Connections.java
index 27ec4ba8b..ca4e6b1b2 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/network/Connections.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/network/Connections.java
@@ -35,7 +35,8 @@ public class Connections {
public static final String MINECRAFT_DECODER = "minecraft-decoder";
public static final String MINECRAFT_ENCODER = "minecraft-encoder";
public static final String READ_TIMEOUT = "read-timeout";
- public static final String PLAY_PACKET_QUEUE = "play-packet-queue";
+ public static final String PLAY_PACKET_QUEUE_OUTBOUND = "play-packet-queue-outbound";
+ public static final String PLAY_PACKET_QUEUE_INBOUND = "play-packet-queue-inbound";
private Connections() {
throw new AssertionError();
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java
index f8362cc09..d75cb46ca 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/MinecraftDecoder.java
@@ -56,8 +56,7 @@ public class MinecraftDecoder extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
- if (msg instanceof ByteBuf) {
- ByteBuf buf = (ByteBuf) msg;
+ if (msg instanceof ByteBuf buf) {
tryDecode(ctx, buf);
} else {
ctx.fireChannelRead(msg);
@@ -147,4 +146,8 @@ public class MinecraftDecoder extends ChannelInboundHandlerAdapter {
this.state = state;
this.setProtocolVersion(registry.version);
}
+
+ public ProtocolUtils.Direction getDirection() {
+ return direction;
+ }
}
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/PlayPacketQueueInboundHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/PlayPacketQueueInboundHandler.java
new file mode 100644
index 000000000..fe553f76a
--- /dev/null
+++ b/proxy/src/main/java/com/velocitypowered/proxy/protocol/netty/PlayPacketQueueInboundHandler.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2023 Velocity Contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.velocitypowered.proxy.protocol.netty;
+
+import com.velocitypowered.api.network.ProtocolVersion;
+import com.velocitypowered.proxy.protocol.MinecraftPacket;
+import com.velocitypowered.proxy.protocol.ProtocolUtils;
+import com.velocitypowered.proxy.protocol.StateRegistry;
+import io.netty.channel.ChannelDuplexHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.util.ReferenceCountUtil;
+import io.netty.util.internal.PlatformDependent;
+import java.util.Queue;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Queues up any pending PLAY packets while the client is in the CONFIG state.
+ *
+ *
Much of the Velocity API (i.e. chat messages) utilize PLAY packets, however the client is
+ * incapable of receiving these packets during the CONFIG state. Certain events such as the
+ * ServerPreConnectEvent may be called during this time, and we need to ensure that any API that
+ * uses these packets will work as expected.
+ *
+ *
This handler will queue up any packets that are sent to the client during this time, and send
+ * them once the client has (re)entered the PLAY state.
+ */
+public class PlayPacketQueueInboundHandler extends ChannelDuplexHandler {
+
+ private final StateRegistry.PacketRegistry.ProtocolRegistry registry;
+ private final Queue