13
0
geforkt von Mirrors/Velocity

Implement a proper login state machine for logins

Fixes #688 and probably a host of other potential problems.
Dieser Commit ist enthalten in:
Andrew Steinborn 2022-04-23 18:07:34 -04:00
Ursprung 5ee96606f6
Commit aa38d3e561
5 geänderte Dateien mit 288 neuen und 202 gelöschten Zeilen

Datei anzeigen

@ -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;

Datei anzeigen

@ -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 <https://www.gnu.org/licenses/>.
*/
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<Component> 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<Void> connectToInitialServer(ConnectedPlayer player) {
Optional<RegisteredServer> initialFromConfig = player.getNextServerToTry();
PlayerChooseInitialServerEvent event = new PlayerChooseInitialServerEvent(player,
initialFromConfig.orElse(null));
return server.getEventManager().fire(event)
.thenRunAsync(() -> {
Optional<RegisteredServer> 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();
}
}

Datei anzeigen

@ -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) {

Datei anzeigen

@ -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<Component> 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<Component> 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<Component> 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<Void> connectToInitialServer(ConnectedPlayer player) {
Optional<RegisteredServer> initialFromConfig = player.getNextServerToTry();
PlayerChooseInitialServerEvent event = new PlayerChooseInitialServerEvent(player,
initialFromConfig.orElse(null));
return server.getEventManager().fire(event)
.thenRunAsync(() -> {
Optional<RegisteredServer> 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
}
}

Datei anzeigen

@ -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();
}
}