Handle connection state in reading

This commit is contained in:
themode 2024-09-17 01:38:45 +02:00 committed by Matt Worzala
parent c092e7dd0b
commit 21b21005a2
10 changed files with 49 additions and 53 deletions

View File

@ -5,7 +5,6 @@ import net.minestom.server.event.EventDispatcher;
import net.minestom.server.event.player.PlayerPacketEvent;
import net.minestom.server.listener.*;
import net.minestom.server.listener.common.*;
import net.minestom.server.listener.preplay.ConfigListener;
import net.minestom.server.listener.preplay.HandshakeListener;
import net.minestom.server.listener.preplay.LoginListener;
import net.minestom.server.listener.preplay.StatusListener;
@ -56,8 +55,8 @@ public final class PacketListenerManager {
setConfigurationListener(ClientKeepAlivePacket.class, KeepAliveListener::listener);
setConfigurationListener(ClientPongPacket.class, (packet, player) -> {/* empty */});
setConfigurationListener(ClientResourcePackStatusPacket.class, ResourcePackListener::listener);
setConfigurationListener(ClientSelectKnownPacksPacket.class, ConfigListener::selectKnownPacks);
setConfigurationListener(ClientFinishConfigurationPacket.class, ConfigListener::finishConfigListener);
setConfigurationListener(ClientSelectKnownPacksPacket.class, LoginListener::selectKnownPacks);
setConfigurationListener(ClientFinishConfigurationPacket.class, LoginListener::finishConfigListener);
setListener(ConnectionState.CONFIGURATION, ClientCookieResponsePacket.class, CookieListener::handleCookieResponse);
setPlayListener(ClientKeepAlivePacket.class, KeepAliveListener::listener);

View File

@ -1,20 +0,0 @@
package net.minestom.server.listener.preplay;
import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.Player;
import net.minestom.server.network.ConnectionManager;
import net.minestom.server.network.packet.client.configuration.ClientFinishConfigurationPacket;
import net.minestom.server.network.packet.client.configuration.ClientSelectKnownPacksPacket;
import org.jetbrains.annotations.NotNull;
public final class ConfigListener {
private static final ConnectionManager CONNECTION_MANAGER = MinecraftServer.getConnectionManager();
public static void selectKnownPacks(@NotNull ClientSelectKnownPacksPacket packet, @NotNull Player player) {
player.getPlayerConnection().receiveKnownPacksResponse(packet.entries());
}
public static void finishConfigListener(@NotNull ClientFinishConfigurationPacket packet, @NotNull Player player) {
CONNECTION_MANAGER.transitionConfigToPlay(player);
}
}

View File

@ -8,7 +8,6 @@ import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.MinecraftServer;
import net.minestom.server.extras.bungee.BungeeCordProxy;
import net.minestom.server.network.ConnectionState;
import net.minestom.server.network.packet.client.handshake.ClientHandshakePacket;
import net.minestom.server.network.player.GameProfile;
import net.minestom.server.network.player.PlayerConnection;
@ -40,9 +39,9 @@ public final class HandshakeListener {
public static void listener(@NotNull ClientHandshakePacket packet, @NotNull PlayerConnection connection) {
String address = packet.serverAddress();
switch (packet.intent()) {
case STATUS -> connection.setConnectionState(ConnectionState.STATUS);
case STATUS -> {
}
case LOGIN -> {
connection.setConnectionState(ConnectionState.LOGIN);
if (packet.protocolVersion() != MinecraftServer.PROTOCOL_VERSION) {
// Incorrect client version
connection.kick(INVALID_VERSION_TEXT);
@ -114,9 +113,8 @@ public final class HandshakeListener {
}
}
}
case TRANSFER -> {
throw new UnsupportedOperationException("Transfer intent is not supported in HandshakeListener");
}
case TRANSFER ->
throw new UnsupportedOperationException("Transfer intent is not supported in HandshakeListener");
default -> {
// Unexpected error
}

View File

@ -11,6 +11,8 @@ import net.minestom.server.extras.bungee.BungeeCordProxy;
import net.minestom.server.extras.mojangAuth.MojangCrypt;
import net.minestom.server.extras.velocity.VelocityProxy;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.client.configuration.ClientFinishConfigurationPacket;
import net.minestom.server.network.packet.client.configuration.ClientSelectKnownPacksPacket;
import net.minestom.server.network.packet.client.login.ClientEncryptionResponsePacket;
import net.minestom.server.network.packet.client.login.ClientLoginAcknowledgedPacket;
import net.minestom.server.network.packet.client.login.ClientLoginPluginResponsePacket;
@ -204,6 +206,14 @@ public final class LoginListener {
executeConfig(player, false);
}
public static void selectKnownPacks(@NotNull ClientSelectKnownPacksPacket packet, @NotNull Player player) {
player.getPlayerConnection().receiveKnownPacksResponse(packet.entries());
}
public static void finishConfigListener(@NotNull ClientFinishConfigurationPacket packet, @NotNull Player player) {
MinecraftServer.getConnectionManager().transitionConfigToPlay(player);
}
private static void enterConfig(PlayerConnection connection, GameProfile gameProfile) {
gameProfile = MinecraftServer.getConnectionManager().transitionLoginToConfig(connection, gameProfile);
if (connection instanceof PlayerSocketConnection socketConnection) {

View File

@ -222,13 +222,9 @@ public final class ConnectionManager {
configurationPlayers.add(player);
keepAlivePlayers.add(player);
}
final PlayerConnection connection = player.getPlayerConnection();
connection.setConnectionState(ConnectionState.CONFIGURATION);
player.sendPacket(PluginMessagePacket.brandPacket(MinecraftServer.getBrandName()));
// Request known packs immediately, but don't wait for the response until required (sending registry data).
final var knownPacksFuture = connection.requestKnownPacks(List.of(SelectKnownPacksPacket.MINECRAFT_CORE));
final var knownPacksFuture = player.getPlayerConnection().requestKnownPacks(List.of(SelectKnownPacksPacket.MINECRAFT_CORE));
var event = new AsyncPlayerConfigurationEvent(player, isFirstConfig);
EventDispatcher.call(event);
@ -329,7 +325,6 @@ public final class ConnectionManager {
public void updateWaitingPlayers() {
this.waitingPlayers.drain(player -> {
if (!player.isOnline()) return; // Player disconnected while in queued to join
player.getPlayerConnection().setConnectionState(ConnectionState.PLAY);
playPlayers.add(player);
keepAlivePlayers.add(player);

View File

@ -36,15 +36,16 @@ public final class PacketReading {
* At least one packet was read.
* The buffer may still contain half-read packets and should therefore be compacted for next read.
*/
record Success<T>(List<T> packets, ConnectionState newState) implements Result<T> {
record Success<T>(List<ParsedPacket<T>> packets) implements Result<T> {
public Success {
if (packets.isEmpty()) {
throw new IllegalArgumentException("Empty packets");
}
packets = List.copyOf(packets);
}
public Success(T packet, ConnectionState newState) {
this(List.of(packet), newState);
public Success(ParsedPacket<T> packet) {
this(List.of(packet));
}
}
@ -67,6 +68,9 @@ public final class PacketReading {
}
}
public record ParsedPacket<T>(ConnectionState nextState, T packet) {
}
public static Result<ClientPacket> readClients(
@NotNull NetworkBuffer buffer,
@NotNull ConnectionState state,
@ -90,7 +94,7 @@ public final class PacketReading {
@NotNull BiFunction<T, ConnectionState, ConnectionState> stateUpdater,
boolean compressed
) throws DataFormatException {
List<T> packets = new ArrayList<>();
List<ParsedPacket<T>> packets = new ArrayList<>();
readLoop:
while (buffer.readableBytes() > 0) {
final Result<T> result = readPacket(buffer, parser, state, stateUpdater, compressed);
@ -98,18 +102,19 @@ public final class PacketReading {
switch (result) {
case Result.Success<T> success -> {
assert success.packets().size() == 1;
packets.add(success.packets().getFirst());
state = success.newState();
final ParsedPacket<T> parsedPacket = success.packets().getFirst();
packets.add(parsedPacket);
state = parsedPacket.nextState();
}
case Result.Empty<T> ignored -> {
break readLoop;
}
case Result.Failure<T> failure -> {
return packets.isEmpty() ? failure : new Result.Success<>(packets, state);
return packets.isEmpty() ? failure : new Result.Success<>(packets);
}
}
}
return !packets.isEmpty() ? new Result.Success<>(packets, state) : EMPTY_CLIENT_PACKET;
return !packets.isEmpty() ? new Result.Success<>(packets) : EMPTY_CLIENT_PACKET;
}
public static Result<ClientPacket> readClient(
@ -172,7 +177,7 @@ public final class PacketReading {
final T packet = readFramedPacket(buffer, registry, compressed);
final ConnectionState nextState = stateUpdater.apply(packet, state);
buffer.index(readerEnd, writerEnd);
return new Result.Success<>(packet, nextState);
return new Result.Success<>(new ParsedPacket<>(nextState, packet));
}
private static <T> T readFramedPacket(NetworkBuffer buffer,

View File

@ -18,6 +18,7 @@ import net.minestom.server.network.packet.client.ClientPacket;
import net.minestom.server.network.packet.client.common.ClientCookieResponsePacket;
import net.minestom.server.network.packet.client.common.ClientKeepAlivePacket;
import net.minestom.server.network.packet.client.common.ClientPingRequestPacket;
import net.minestom.server.network.packet.client.configuration.ClientFinishConfigurationPacket;
import net.minestom.server.network.packet.client.configuration.ClientSelectKnownPacksPacket;
import net.minestom.server.network.packet.client.handshake.ClientHandshakePacket;
import net.minestom.server.network.packet.client.login.ClientEncryptionResponsePacket;
@ -64,7 +65,8 @@ public class PlayerSocketConnection extends PlayerConnection {
ClientLoginPluginResponsePacket.class,
ClientSelectKnownPacksPacket.class, // Immediate answer to server request on config
ClientConfigurationAckPacket.class, // Handle config state
ClientLoginAcknowledgedPacket.class // Handle config state
ClientLoginAcknowledgedPacket.class, // Handle config state
ClientFinishConfigurationPacket.class // Enter play state
);
private final SocketChannel channel;
@ -116,12 +118,13 @@ public class PlayerSocketConnection extends PlayerConnection {
private void processPackets(NetworkBuffer readBuffer, PacketParser<ClientPacket> packetParser) {
// Read all packets
final ConnectionState startingState = getConnectionState();
final PacketReading.Result<ClientPacket> result;
try {
result = PacketReading.readPackets(
readBuffer,
packetParser,
getConnectionState(), PacketVanilla::nextClientState,
startingState, PacketVanilla::nextClientState,
compression()
);
} catch (DataFormatException e) {
@ -131,7 +134,8 @@ public class PlayerSocketConnection extends PlayerConnection {
}
switch (result) {
case PacketReading.Result.Success<ClientPacket> success -> {
for (ClientPacket packet : success.packets()) {
for (PacketReading.ParsedPacket<ClientPacket> parsedPacket : success.packets()) {
final ClientPacket packet = parsedPacket.packet();
try {
final boolean processImmediately = IMMEDIATE_PROCESS_PACKETS.contains(packet.getClass());
if (processImmediately) {
@ -145,6 +149,11 @@ public class PlayerSocketConnection extends PlayerConnection {
} catch (Exception e) {
MinecraftServer.getExceptionManager().handleException(e);
}
// Update state to properly interpret next packet
final ConnectionState nextState = parsedPacket.nextState();
if (nextState != getConnectionState()) {
setConnectionState(nextState);
}
}
// Compact in case of incomplete read
readBuffer.compact();

View File

@ -61,7 +61,7 @@ public class SendablePacketTest {
return;
}
assertEquals(1, success.packets().size());
var readPacket = success.packets().getFirst();
ClientPacket readPacket = success.packets().getFirst().packet();
assertEquals(packet, readPacket);
}
}

View File

@ -27,7 +27,7 @@ public class SocketReadTest {
if (!(readResult instanceof PacketReading.Result.Success<ClientPacket> success)) {
throw new AssertionError("Expected a success result, got " + readResult);
}
var packets = success.packets();
List<ClientPacket> packets = success.packets().stream().map(PacketReading.ParsedPacket::packet).toList();
assertEquals(List.of(packet), packets);
}
@ -44,7 +44,7 @@ public class SocketReadTest {
if (!(readResult instanceof PacketReading.Result.Success<ClientPacket> success)) {
throw new AssertionError("Expected a success result, got " + readResult);
}
var packets = success.packets();
List<ClientPacket> packets = success.packets().stream().map(PacketReading.ParsedPacket::packet).toList();
assertEquals(List.of(packet, packet), packets);
}
@ -63,7 +63,7 @@ public class SocketReadTest {
if (!(readResult instanceof PacketReading.Result.Success<ClientPacket> success)) {
throw new AssertionError("Expected a success result, got " + readResult);
}
var packets = success.packets();
List<ClientPacket> packets = success.packets().stream().map(PacketReading.ParsedPacket::packet).toList();
assertEquals(List.of(packet), packets);
readResult = PacketReading.readClients(buffer, ConnectionState.PLAY, compressed);
@ -87,7 +87,7 @@ public class SocketReadTest {
if (!(readResult instanceof PacketReading.Result.Success<ClientPacket> success)) {
throw new AssertionError("Expected a success result, got " + readResult);
}
var packets = success.packets();
List<ClientPacket> packets = success.packets().stream().map(PacketReading.ParsedPacket::packet).toList();
assertEquals(1, buffer.readableBytes());
assertEquals(List.of(packet), packets);

View File

@ -46,7 +46,6 @@ final class TestConnectionImpl implements TestConnection {
throw new IllegalStateException("Already connected");
}
playerConnection.setConnectionState(ConnectionState.LOGIN);
final GameProfile gameProfile = new GameProfile(UUID.randomUUID(), "RandName");
var player = process.connection().createPlayer(playerConnection, gameProfile);
player.eventNode().addListener(AsyncPlayerConfigurationEvent.class, event -> {
@ -65,6 +64,7 @@ final class TestConnectionImpl implements TestConnection {
future.complete(player);
});
future.join();
playerConnection.setConnectionState(ConnectionState.PLAY);
process.connection().updateWaitingPlayers();
return player;
}