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 2d9e5004e..7d08a3037 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java
@@ -34,9 +34,8 @@ import com.velocitypowered.natives.encryption.VelocityCipherFactory;
import com.velocitypowered.natives.util.Natives;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.connection.client.HandshakeSessionHandler;
-import com.velocitypowered.proxy.connection.client.LoginSessionHandler;
+import com.velocitypowered.proxy.connection.client.InitialLoginSessionHandler;
import com.velocitypowered.proxy.connection.client.StatusSessionHandler;
-import com.velocitypowered.proxy.network.Connections;
import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.StateRegistry;
import com.velocitypowered.proxy.protocol.VelocityConnectionEvent;
@@ -178,7 +177,7 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter {
if (cause instanceof ReadTimeoutException) {
logger.error("{}: read timed out", association);
} else {
- boolean frontlineHandler = sessionHandler instanceof LoginSessionHandler
+ boolean frontlineHandler = sessionHandler instanceof InitialLoginSessionHandler
|| sessionHandler instanceof HandshakeSessionHandler
|| sessionHandler instanceof StatusSessionHandler;
boolean isQuietDecoderException = cause instanceof QuietDecoderException;
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/AuthSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/AuthSessionHandler.java
new file mode 100644
index 000000000..4ba401f96
--- /dev/null
+++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/AuthSessionHandler.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2018 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.connection.client;
+
+import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8;
+
+import com.google.common.base.Preconditions;
+import com.velocitypowered.api.event.connection.DisconnectEvent;
+import com.velocitypowered.api.event.connection.LoginEvent;
+import com.velocitypowered.api.event.connection.PostLoginEvent;
+import com.velocitypowered.api.event.permission.PermissionsSetupEvent;
+import com.velocitypowered.api.event.player.GameProfileRequestEvent;
+import com.velocitypowered.api.event.player.PlayerChooseInitialServerEvent;
+import com.velocitypowered.api.permission.PermissionFunction;
+import com.velocitypowered.api.proxy.server.RegisteredServer;
+import com.velocitypowered.api.util.GameProfile;
+import com.velocitypowered.api.util.UuidUtils;
+import com.velocitypowered.proxy.VelocityServer;
+import com.velocitypowered.proxy.config.PlayerInfoForwarding;
+import com.velocitypowered.proxy.config.VelocityConfiguration;
+import com.velocitypowered.proxy.connection.MinecraftConnection;
+import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
+import com.velocitypowered.proxy.protocol.StateRegistry;
+import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess;
+import com.velocitypowered.proxy.protocol.packet.SetCompression;
+import io.netty.buffer.ByteBuf;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
+
+public class AuthSessionHandler implements MinecraftSessionHandler {
+
+ private static final Logger logger = LogManager.getLogger(AuthSessionHandler.class);
+
+ private final VelocityServer server;
+ private final MinecraftConnection mcConnection;
+ private final LoginInboundConnection inbound;
+ private GameProfile profile;
+ private @MonotonicNonNull ConnectedPlayer connectedPlayer;
+ private final boolean onlineMode;
+
+ AuthSessionHandler(VelocityServer server, LoginInboundConnection inbound,
+ GameProfile profile, boolean onlineMode) {
+ this.server = Preconditions.checkNotNull(server, "server");
+ this.inbound = Preconditions.checkNotNull(inbound, "inbound");
+ this.profile = Preconditions.checkNotNull(profile, "profile");
+ this.onlineMode = onlineMode;
+ this.mcConnection = inbound.delegatedConnection();
+ }
+
+ @Override
+ public void activated() {
+ // Some connection types may need to alter the game profile.
+ profile = mcConnection.getType().addGameProfileTokensIfRequired(profile,
+ server.getConfiguration().getPlayerInfoForwardingMode());
+ GameProfileRequestEvent profileRequestEvent = new GameProfileRequestEvent(inbound, profile,
+ onlineMode);
+ final GameProfile finalProfile = profile;
+
+ server.getEventManager().fire(profileRequestEvent).thenComposeAsync(profileEvent -> {
+ if (mcConnection.isClosed()) {
+ // The player disconnected after we authenticated them.
+ return CompletableFuture.completedFuture(null);
+ }
+
+ // Initiate a regular connection and move over to it.
+ ConnectedPlayer player = new ConnectedPlayer(server, profileEvent.getGameProfile(),
+ mcConnection, inbound.getVirtualHost().orElse(null), onlineMode);
+ this.connectedPlayer = player;
+ if (!server.canRegisterConnection(player)) {
+ player.disconnect0(Component.translatable("velocity.error.already-connected-proxy",
+ NamedTextColor.RED), true);
+ return CompletableFuture.completedFuture(null);
+ }
+
+ logger.info("{} has connected", player);
+
+ return server.getEventManager()
+ .fire(new PermissionsSetupEvent(player, ConnectedPlayer.DEFAULT_PERMISSIONS))
+ .thenAcceptAsync(event -> {
+ if (!mcConnection.isClosed()) {
+ // wait for permissions to load, then set the players permission function
+ final PermissionFunction function = event.createFunction(player);
+ if (function == null) {
+ logger.error(
+ "A plugin permission provider {} provided an invalid permission function"
+ + " for player {}. This is a bug in the plugin, not in Velocity. Falling"
+ + " back to the default permission function.",
+ event.getProvider().getClass().getName(),
+ player.getUsername());
+ } else {
+ player.setPermissionFunction(function);
+ }
+ completeLoginProtocolPhaseAndInitialize(player);
+ }
+ }, mcConnection.eventLoop());
+ }, mcConnection.eventLoop()).exceptionally((ex) -> {
+ logger.error("Exception during connection of {}", finalProfile, ex);
+ return null;
+ });
+ }
+
+ private void completeLoginProtocolPhaseAndInitialize(ConnectedPlayer player) {
+ int threshold = server.getConfiguration().getCompressionThreshold();
+ if (threshold >= 0 && mcConnection.getProtocolVersion().compareTo(MINECRAFT_1_8) >= 0) {
+ mcConnection.write(new SetCompression(threshold));
+ mcConnection.setCompressionThreshold(threshold);
+ }
+ VelocityConfiguration configuration = server.getConfiguration();
+ UUID playerUniqueId = player.getUniqueId();
+ if (configuration.getPlayerInfoForwardingMode() == PlayerInfoForwarding.NONE) {
+ playerUniqueId = UuidUtils.generateOfflinePlayerUuid(player.getUsername());
+ }
+ ServerLoginSuccess success = new ServerLoginSuccess();
+ success.setUsername(player.getUsername());
+ success.setUuid(playerUniqueId);
+ mcConnection.write(success);
+
+ mcConnection.setAssociation(player);
+ mcConnection.setState(StateRegistry.PLAY);
+
+ server.getEventManager().fire(new LoginEvent(player))
+ .thenAcceptAsync(event -> {
+ if (mcConnection.isClosed()) {
+ // The player was disconnected
+ server.getEventManager().fireAndForget(new DisconnectEvent(player,
+ DisconnectEvent.LoginStatus.CANCELLED_BY_USER_BEFORE_COMPLETE));
+ return;
+ }
+
+ Optional reason = event.getResult().getReasonComponent();
+ if (reason.isPresent()) {
+ player.disconnect0(reason.get(), true);
+ } else {
+ if (!server.registerConnection(player)) {
+ player.disconnect0(Component.translatable("velocity.error.already-connected-proxy"),
+ true);
+ return;
+ }
+
+ mcConnection.setSessionHandler(new InitialConnectSessionHandler(player));
+ server.getEventManager().fire(new PostLoginEvent(player))
+ .thenCompose((ignored) -> connectToInitialServer(player))
+ .exceptionally((ex) -> {
+ logger.error("Exception while connecting {} to initial server", player, ex);
+ return null;
+ });
+ }
+ }, mcConnection.eventLoop())
+ .exceptionally((ex) -> {
+ logger.error("Exception while completing login initialisation phase for {}", player, ex);
+ return null;
+ });
+ }
+
+ private CompletableFuture connectToInitialServer(ConnectedPlayer player) {
+ Optional initialFromConfig = player.getNextServerToTry();
+ PlayerChooseInitialServerEvent event = new PlayerChooseInitialServerEvent(player,
+ initialFromConfig.orElse(null));
+
+ return server.getEventManager().fire(event)
+ .thenRunAsync(() -> {
+ Optional toTry = event.getInitialServer();
+ if (!toTry.isPresent()) {
+ player.disconnect0(Component.translatable("velocity.error.no-available-servers",
+ NamedTextColor.RED), true);
+ return;
+ }
+ player.createConnectionRequest(toTry.get()).fireAndForget();
+ }, mcConnection.eventLoop());
+ }
+
+ @Override
+ public void handleUnknown(ByteBuf buf) {
+ mcConnection.close(true);
+ }
+
+ @Override
+ public void disconnected() {
+ if (connectedPlayer != null) {
+ connectedPlayer.teardown();
+ }
+ this.inbound.cleanup();
+ }
+}
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java
index 74e7ad080..2870b64bc 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java
@@ -40,8 +40,6 @@ import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Optional;
import net.kyori.adventure.text.Component;
-import net.kyori.adventure.text.TextComponent;
-import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.format.NamedTextColor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -144,7 +142,7 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler {
LoginInboundConnection lic = new LoginInboundConnection(ic);
server.getEventManager().fireAndForget(new ConnectionHandshakeEvent(lic));
- connection.setSessionHandler(new LoginSessionHandler(server, connection, lic));
+ connection.setSessionHandler(new InitialLoginSessionHandler(server, connection, lic));
}
private ConnectionType getHandshakeConnectionType(Handshake handshake) {
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialLoginSessionHandler.java
similarity index 54%
rename from proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java
rename to proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialLoginSessionHandler.java
index 4ab7fe628..cd20fb2a9 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialLoginSessionHandler.java
@@ -18,38 +18,23 @@
package com.velocitypowered.proxy.connection.client;
import static com.google.common.net.UrlEscapers.urlFormParameterEscaper;
-import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8;
import static com.velocitypowered.proxy.VelocityServer.GENERAL_GSON;
import static com.velocitypowered.proxy.connection.VelocityConstants.EMPTY_BYTE_ARRAY;
import static com.velocitypowered.proxy.util.EncryptionUtils.decryptRsa;
import static com.velocitypowered.proxy.util.EncryptionUtils.generateServerId;
import com.google.common.base.Preconditions;
-import com.velocitypowered.api.event.connection.DisconnectEvent;
-import com.velocitypowered.api.event.connection.DisconnectEvent.LoginStatus;
-import com.velocitypowered.api.event.connection.LoginEvent;
-import com.velocitypowered.api.event.connection.PostLoginEvent;
import com.velocitypowered.api.event.connection.PreLoginEvent;
import com.velocitypowered.api.event.connection.PreLoginEvent.PreLoginComponentResult;
-import com.velocitypowered.api.event.permission.PermissionsSetupEvent;
-import com.velocitypowered.api.event.player.GameProfileRequestEvent;
-import com.velocitypowered.api.event.player.PlayerChooseInitialServerEvent;
-import com.velocitypowered.api.permission.PermissionFunction;
-import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.util.GameProfile;
-import com.velocitypowered.api.util.UuidUtils;
import com.velocitypowered.proxy.VelocityServer;
-import com.velocitypowered.proxy.config.PlayerInfoForwarding;
-import com.velocitypowered.proxy.config.VelocityConfiguration;
import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
-import com.velocitypowered.proxy.protocol.StateRegistry;
+import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
import com.velocitypowered.proxy.protocol.packet.EncryptionRequest;
import com.velocitypowered.proxy.protocol.packet.EncryptionResponse;
import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse;
import com.velocitypowered.proxy.protocol.packet.ServerLogin;
-import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess;
-import com.velocitypowered.proxy.protocol.packet.SetCompression;
import io.netty.buffer.ByteBuf;
import java.net.InetSocketAddress;
import java.security.GeneralSecurityException;
@@ -57,8 +42,6 @@ import java.security.KeyPair;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Optional;
-import java.util.UUID;
-import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadLocalRandom;
import net.kyori.adventure.text.Component;
@@ -69,9 +52,9 @@ import org.asynchttpclient.ListenableFuture;
import org.asynchttpclient.Response;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
-public class LoginSessionHandler implements MinecraftSessionHandler {
+public class InitialLoginSessionHandler implements MinecraftSessionHandler {
- private static final Logger logger = LogManager.getLogger(LoginSessionHandler.class);
+ private static final Logger logger = LogManager.getLogger(InitialLoginSessionHandler.class);
private static final String MOJANG_HASJOINED_URL =
System.getProperty("mojang.sessionserver", "https://sessionserver.mojang.com/session/minecraft/hasJoined")
.concat("?username=%s&serverId=%s");
@@ -81,10 +64,10 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
private final LoginInboundConnection inbound;
private @MonotonicNonNull ServerLogin login;
private byte[] verify = EMPTY_BYTE_ARRAY;
- private @MonotonicNonNull ConnectedPlayer connectedPlayer;
+ private LoginState currentState = LoginState.LOGIN_PACKET_EXPECTED;
- LoginSessionHandler(VelocityServer server, MinecraftConnection mcConnection,
- LoginInboundConnection inbound) {
+ InitialLoginSessionHandler(VelocityServer server, MinecraftConnection mcConnection,
+ LoginInboundConnection inbound) {
this.server = Preconditions.checkNotNull(server, "server");
this.mcConnection = Preconditions.checkNotNull(mcConnection, "mcConnection");
this.inbound = Preconditions.checkNotNull(inbound, "inbound");
@@ -92,8 +75,53 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
@Override
public boolean handle(ServerLogin packet) {
+ assertState(LoginState.LOGIN_PACKET_EXPECTED);
+ this.currentState = LoginState.LOGIN_PACKET_RECEIVED;
this.login = packet;
- beginPreLogin();
+
+ PreLoginEvent event = new PreLoginEvent(inbound, login.getUsername());
+ server.getEventManager().fire(event)
+ .thenRunAsync(() -> {
+ if (mcConnection.isClosed()) {
+ // The player was disconnected
+ return;
+ }
+
+ PreLoginComponentResult result = event.getResult();
+ Optional disconnectReason = result.getReasonComponent();
+ if (disconnectReason.isPresent()) {
+ // The component is guaranteed to be provided if the connection was denied.
+ inbound.disconnect(disconnectReason.get());
+ return;
+ }
+
+ inbound.loginEventFired(() -> {
+ if (mcConnection.isClosed()) {
+ // The player was disconnected
+ return;
+ }
+
+ mcConnection.eventLoop().execute(() -> {
+ if (!result.isForceOfflineMode() && (server.getConfiguration().isOnlineMode()
+ || result.isOnlineModeAllowed())) {
+ // Request encryption.
+ EncryptionRequest request = generateEncryptionRequest();
+ this.verify = Arrays.copyOf(request.getVerifyToken(), 4);
+ mcConnection.write(request);
+ this.currentState = LoginState.ENCRYPTION_REQUEST_SENT;
+ } else {
+ mcConnection.setSessionHandler(new AuthSessionHandler(
+ server, inbound, GameProfile.forOfflinePlayer(login.getUsername()), false
+ ));
+ }
+ });
+ });
+ }, mcConnection.eventLoop())
+ .exceptionally((ex) -> {
+ logger.error("Exception in pre-login stage", ex);
+ return null;
+ });
+
return true;
}
@@ -105,6 +133,9 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
@Override
public boolean handle(EncryptionResponse packet) {
+ assertState(LoginState.ENCRYPTION_REQUEST_SENT);
+ this.currentState = LoginState.ENCRYPTION_RESPONSE_RECEIVED;
+
ServerLogin login = this.login;
if (login == null) {
throw new IllegalStateException("No ServerLogin packet received yet.");
@@ -156,8 +187,9 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
Response profileResponse = hasJoinedResponse.get();
if (profileResponse.getStatusCode() == 200) {
// All went well, initialize the session.
- initializePlayer(GENERAL_GSON.fromJson(profileResponse.getResponseBody(),
- GameProfile.class), true);
+ mcConnection.setSessionHandler(new AuthSessionHandler(
+ server, inbound, GENERAL_GSON.fromJson(profileResponse.getResponseBody(), GameProfile.class), true
+ ));
} else if (profileResponse.getStatusCode() == 204) {
// Apparently an offline-mode user logged onto this online-mode proxy.
inbound.disconnect(Component.translatable("velocity.error.online-mode-only",
@@ -184,52 +216,6 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
return true;
}
- private void beginPreLogin() {
- ServerLogin login = this.login;
- if (login == null) {
- throw new IllegalStateException("No ServerLogin packet received yet.");
- }
- PreLoginEvent event = new PreLoginEvent(inbound, login.getUsername());
- server.getEventManager().fire(event)
- .thenRunAsync(() -> {
- if (mcConnection.isClosed()) {
- // The player was disconnected
- return;
- }
-
- PreLoginComponentResult result = event.getResult();
- Optional disconnectReason = result.getReasonComponent();
- if (disconnectReason.isPresent()) {
- // The component is guaranteed to be provided if the connection was denied.
- inbound.disconnect(disconnectReason.get());
- return;
- }
-
- inbound.loginEventFired(() -> {
- if (mcConnection.isClosed()) {
- // The player was disconnected
- return;
- }
-
- mcConnection.eventLoop().execute(() -> {
- if (!result.isForceOfflineMode() && (server.getConfiguration().isOnlineMode()
- || result.isOnlineModeAllowed())) {
- // Request encryption.
- EncryptionRequest request = generateEncryptionRequest();
- this.verify = Arrays.copyOf(request.getVerifyToken(), 4);
- mcConnection.write(request);
- } else {
- initializePlayer(GameProfile.forOfflinePlayer(login.getUsername()), false);
- }
- });
- });
- }, mcConnection.eventLoop())
- .exceptionally((ex) -> {
- logger.error("Exception in pre-login stage", ex);
- return null;
- });
- }
-
private EncryptionRequest generateEncryptionRequest() {
byte[] verify = new byte[4];
ThreadLocalRandom.current().nextBytes(verify);
@@ -240,127 +226,6 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
return request;
}
- private void initializePlayer(GameProfile profile, boolean onlineMode) {
- // Some connection types may need to alter the game profile.
- profile = mcConnection.getType().addGameProfileTokensIfRequired(profile,
- server.getConfiguration().getPlayerInfoForwardingMode());
- GameProfileRequestEvent profileRequestEvent = new GameProfileRequestEvent(inbound, profile,
- onlineMode);
- final GameProfile finalProfile = profile;
-
- server.getEventManager().fire(profileRequestEvent).thenComposeAsync(profileEvent -> {
- if (mcConnection.isClosed()) {
- // The player disconnected after we authenticated them.
- return CompletableFuture.completedFuture(null);
- }
-
- // Initiate a regular connection and move over to it.
- ConnectedPlayer player = new ConnectedPlayer(server, profileEvent.getGameProfile(),
- mcConnection, inbound.getVirtualHost().orElse(null), onlineMode);
- this.connectedPlayer = player;
- if (!server.canRegisterConnection(player)) {
- player.disconnect0(Component.translatable("velocity.error.already-connected-proxy",
- NamedTextColor.RED), true);
- return CompletableFuture.completedFuture(null);
- }
-
- logger.info("{} has connected", player);
-
- return server.getEventManager()
- .fire(new PermissionsSetupEvent(player, ConnectedPlayer.DEFAULT_PERMISSIONS))
- .thenAcceptAsync(event -> {
- if (!mcConnection.isClosed()) {
- // wait for permissions to load, then set the players permission function
- final PermissionFunction function = event.createFunction(player);
- if (function == null) {
- logger.error(
- "A plugin permission provider {} provided an invalid permission function"
- + " for player {}. This is a bug in the plugin, not in Velocity. Falling"
- + " back to the default permission function.",
- event.getProvider().getClass().getName(),
- player.getUsername());
- } else {
- player.setPermissionFunction(function);
- }
- completeLoginProtocolPhaseAndInitialize(player);
- }
- }, mcConnection.eventLoop());
- }, mcConnection.eventLoop()).exceptionally((ex) -> {
- logger.error("Exception during connection of {}", finalProfile, ex);
- return null;
- });
- }
-
- private void completeLoginProtocolPhaseAndInitialize(ConnectedPlayer player) {
- int threshold = server.getConfiguration().getCompressionThreshold();
- if (threshold >= 0 && mcConnection.getProtocolVersion().compareTo(MINECRAFT_1_8) >= 0) {
- mcConnection.write(new SetCompression(threshold));
- mcConnection.setCompressionThreshold(threshold);
- }
- VelocityConfiguration configuration = server.getConfiguration();
- UUID playerUniqueId = player.getUniqueId();
- if (configuration.getPlayerInfoForwardingMode() == PlayerInfoForwarding.NONE) {
- playerUniqueId = UuidUtils.generateOfflinePlayerUuid(player.getUsername());
- }
- ServerLoginSuccess success = new ServerLoginSuccess();
- success.setUsername(player.getUsername());
- success.setUuid(playerUniqueId);
- mcConnection.write(success);
-
- mcConnection.setAssociation(player);
- mcConnection.setState(StateRegistry.PLAY);
-
- server.getEventManager().fire(new LoginEvent(player))
- .thenAcceptAsync(event -> {
- if (mcConnection.isClosed()) {
- // The player was disconnected
- server.getEventManager().fireAndForget(new DisconnectEvent(player,
- LoginStatus.CANCELLED_BY_USER_BEFORE_COMPLETE));
- return;
- }
-
- Optional reason = event.getResult().getReasonComponent();
- if (reason.isPresent()) {
- player.disconnect0(reason.get(), true);
- } else {
- if (!server.registerConnection(player)) {
- player.disconnect0(Component.translatable("velocity.error.already-connected-proxy"),
- true);
- return;
- }
-
- mcConnection.setSessionHandler(new InitialConnectSessionHandler(player));
- server.getEventManager().fire(new PostLoginEvent(player))
- .thenCompose((ignored) -> connectToInitialServer(player))
- .exceptionally((ex) -> {
- logger.error("Exception while connecting {} to initial server", player, ex);
- return null;
- });
- }
- }, mcConnection.eventLoop())
- .exceptionally((ex) -> {
- logger.error("Exception while completing login initialisation phase for {}", player, ex);
- return null;
- });
- }
-
- private CompletableFuture connectToInitialServer(ConnectedPlayer player) {
- Optional initialFromConfig = player.getNextServerToTry();
- PlayerChooseInitialServerEvent event = new PlayerChooseInitialServerEvent(player,
- initialFromConfig.orElse(null));
-
- return server.getEventManager().fire(event)
- .thenRunAsync(() -> {
- Optional toTry = event.getInitialServer();
- if (!toTry.isPresent()) {
- player.disconnect0(Component.translatable("velocity.error.no-available-servers",
- NamedTextColor.RED), true);
- return;
- }
- player.createConnectionRequest(toTry.get()).fireAndForget();
- }, mcConnection.eventLoop());
- }
-
@Override
public void handleUnknown(ByteBuf buf) {
mcConnection.close(true);
@@ -368,9 +233,23 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
@Override
public void disconnected() {
- if (connectedPlayer != null) {
- connectedPlayer.teardown();
- }
this.inbound.cleanup();
}
+
+ private void assertState(LoginState expectedState) {
+ if (this.currentState != expectedState) {
+ if (MinecraftDecoder.DEBUG) {
+ logger.error("{} Received an unexpected packet requiring state {}, but we are in {}", inbound,
+ expectedState, this.currentState);
+ }
+ mcConnection.close(true);
+ }
+ }
+
+ private enum LoginState {
+ LOGIN_PACKET_EXPECTED,
+ LOGIN_PACKET_RECEIVED,
+ ENCRYPTION_REQUEST_SENT,
+ ENCRYPTION_RESPONSE_RECEIVED
+ }
}
diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginInboundConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginInboundConnection.java
index a9efb8d3d..f707c4cc7 100644
--- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginInboundConnection.java
+++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginInboundConnection.java
@@ -20,6 +20,7 @@ package com.velocitypowered.proxy.connection.client;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.LoginPhaseConnection;
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
+import com.velocitypowered.proxy.connection.MinecraftConnection;
import com.velocitypowered.proxy.protocol.packet.LoginPluginMessage;
import com.velocitypowered.proxy.protocol.packet.LoginPluginResponse;
import io.netty.buffer.ByteBufUtil;
@@ -144,4 +145,8 @@ public class LoginInboundConnection implements LoginPhaseConnection {
onAllMessagesHandled.run();
}
}
+
+ MinecraftConnection delegatedConnection() {
+ return delegate.getConnection();
+ }
}