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 e35ad3299..a2ee67655 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java @@ -24,6 +24,7 @@ import com.velocitypowered.proxy.protocol.netty.MinecraftCompressDecoder; import com.velocitypowered.proxy.protocol.netty.MinecraftCompressEncoder; import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder; import com.velocitypowered.proxy.protocol.netty.MinecraftEncoder; +import com.velocitypowered.proxy.protocol.packet.Disconnect; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelFutureListener; @@ -58,6 +59,7 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { private @Nullable MinecraftConnectionAssociation association; private final VelocityServer server; private ConnectionType connectionType = ConnectionTypes.UNDETERMINED; + private boolean knownDisconnect = false; /** * Initializes a new {@link MinecraftConnection} instance. @@ -88,7 +90,7 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { sessionHandler.disconnected(); } - if (association != null) { + if (association != null && !knownDisconnect) { logger.info("{} has disconnected", association); } } @@ -191,6 +193,7 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { */ public void closeWith(Object msg) { if (channel.isActive()) { + knownDisconnect = true; channel.writeAndFlush(msg).addListener(ChannelFutureListener.CLOSE); } } 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 2c7984e10..f6f210964 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 @@ -235,6 +235,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { @Override public void disconnect(Component reason) { + logger.info("{} has disconnected: {}", this, ComponentSerializers.LEGACY.serialize(reason)); connection.closeWith(Disconnect.create(reason)); } @@ -351,7 +352,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { if (nextServer.isPresent()) { createConnectionRequest(nextServer.get()).fireAndForget(); } else { - connection.closeWith(Disconnect.create(friendlyReason)); + disconnect(friendlyReason); } } else { KickedFromServerEvent originalEvent = new KickedFromServerEvent(this, rs, kickReason, @@ -360,7 +361,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { .thenAcceptAsync(event -> { if (event.getResult() instanceof DisconnectPlayer) { DisconnectPlayer res = (DisconnectPlayer) event.getResult(); - connection.closeWith(Disconnect.create(res.getReason())); + disconnect(res.getReason()); } else if (event.getResult() instanceof RedirectPlayer) { RedirectPlayer res = (RedirectPlayer) event.getResult(); createConnectionRequest(res.getServer()).fireAndForget(); @@ -369,11 +370,11 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { if (event.kickedDuringServerConnect()) { sendMessage(res.getMessage()); } else { - connection.closeWith(Disconnect.create(res.getMessage())); + disconnect(res.getMessage()); } } else { // In case someone gets creative, assume we want to disconnect the player. - connection.closeWith(Disconnect.create(friendlyReason)); + disconnect(friendlyReason); } }, connection.eventLoop()); } 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 865d3fb2a..64d24aba4 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 @@ -4,9 +4,9 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.velocitypowered.api.event.connection.ConnectionHandshakeEvent; import com.velocitypowered.api.event.proxy.ProxyPingEvent; +import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.proxy.InboundConnection; import com.velocitypowered.api.proxy.server.ServerPing; -import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.VelocityServer; import com.velocitypowered.proxy.config.PlayerInfoForwarding; import com.velocitypowered.proxy.config.VelocityConfiguration; @@ -75,6 +75,7 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler { public boolean handle(Handshake handshake) { InitialInboundConnection ic = new InitialInboundConnection(connection, cleanVhost(handshake.getServerAddress()), handshake); + connection.setAssociation(ic); switch (handshake.getNextStatus()) { case StateRegistry.STATUS_ID: connection.setState(StateRegistry.STATUS); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialInboundConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialInboundConnection.java index c24f46591..c280e9579 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialInboundConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialInboundConnection.java @@ -3,11 +3,19 @@ package com.velocitypowered.proxy.connection.client; import com.velocitypowered.api.proxy.InboundConnection; import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.proxy.connection.MinecraftConnection; +import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation; +import com.velocitypowered.proxy.protocol.packet.Disconnect; import com.velocitypowered.proxy.protocol.packet.Handshake; import java.net.InetSocketAddress; import java.util.Optional; +import net.kyori.text.Component; +import net.kyori.text.serializer.ComponentSerializers; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -class InitialInboundConnection implements InboundConnection { +class InitialInboundConnection implements InboundConnection, MinecraftConnectionAssociation { + + private static final Logger logger = LogManager.getLogger(InitialInboundConnection.class); private final MinecraftConnection connection; private final String cleanedAddress; @@ -39,4 +47,14 @@ class InitialInboundConnection implements InboundConnection { public ProtocolVersion getProtocolVersion() { return connection.getProtocolVersion(); } + + @Override + public String toString() { + return "[initial connection] " + connection.getRemoteAddress().toString(); + } + + public void disconnect(Component reason) { + logger.info("{} has disconnected: {}", this, ComponentSerializers.LEGACY.serialize(reason)); + connection.closeWith(Disconnect.create(reason)); + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java index 04a798750..1c1911aa0 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java @@ -31,7 +31,6 @@ 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 com.velocitypowered.proxy.util.EncryptionUtils; import com.velocitypowered.proxy.util.VelocityMessages; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; @@ -57,26 +56,26 @@ public class LoginSessionHandler implements MinecraftSessionHandler { "https://sessionserver.mojang.com/session/minecraft/hasJoined?username=%s&serverId=%s&ip=%s"; private final VelocityServer server; - private final MinecraftConnection inbound; - private final InboundConnection apiInbound; + private final MinecraftConnection mcConnection; + private final InitialInboundConnection inbound; private @MonotonicNonNull ServerLogin login; private byte[] verify = EMPTY_BYTE_ARRAY; private int playerInfoId; private @MonotonicNonNull ConnectedPlayer connectedPlayer; - LoginSessionHandler(VelocityServer server, MinecraftConnection inbound, - InboundConnection apiInbound) { + LoginSessionHandler(VelocityServer server, MinecraftConnection mcConnection, + InitialInboundConnection inbound) { this.server = Preconditions.checkNotNull(server, "server"); + this.mcConnection = Preconditions.checkNotNull(mcConnection, "mcConnection"); this.inbound = Preconditions.checkNotNull(inbound, "inbound"); - this.apiInbound = Preconditions.checkNotNull(apiInbound, "apiInbound"); } @Override public boolean handle(ServerLogin packet) { this.login = packet; - if (inbound.getProtocolVersion().compareTo(MINECRAFT_1_13) >= 0) { + if (mcConnection.getProtocolVersion().compareTo(MINECRAFT_1_13) >= 0) { playerInfoId = ThreadLocalRandom.current().nextInt(); - inbound.write(new LoginPluginMessage(playerInfoId, VELOCITY_IP_FORWARDING_CHANNEL, + mcConnection.write(new LoginPluginMessage(playerInfoId, VELOCITY_IP_FORWARDING_CHANNEL, Unpooled.EMPTY_BUFFER)); } else { beginPreLogin(); @@ -89,7 +88,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler { if (packet.getId() == playerInfoId) { if (packet.isSuccess()) { // Uh oh, someone's trying to run Velocity behind Velocity. We don't want that happening. - inbound.closeWith(Disconnect.create(VelocityMessages.NO_PROXY_BEHIND_PROXY)); + inbound.disconnect(VelocityMessages.NO_PROXY_BEHIND_PROXY); } else { // Proceed with the regular login process. beginPreLogin(); @@ -119,14 +118,14 @@ public class LoginSessionHandler implements MinecraftSessionHandler { byte[] decryptedSharedSecret = decryptRsa(serverKeyPair, packet.getSharedSecret()); String serverId = generateServerId(decryptedSharedSecret, serverKeyPair.getPublic()); - String playerIp = ((InetSocketAddress) inbound.getRemoteAddress()).getHostString(); + String playerIp = ((InetSocketAddress) mcConnection.getRemoteAddress()).getHostString(); String url = String.format(MOJANG_HASJOINED_URL, UrlEscapers.urlFormParameterEscaper().escape(login.getUsername()), serverId, UrlEscapers.urlFormParameterEscaper().escape(playerIp)); server.getHttpClient() .get(new URL(url)) .thenAcceptAsync(profileResponse -> { - if (inbound.isClosed()) { + if (mcConnection.isClosed()) { // The player disconnected after we authenticated them. return; } @@ -134,7 +133,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler { // Go ahead and enable encryption. Once the client sends EncryptionResponse, encryption // is enabled. try { - inbound.enableEncryption(decryptedSharedSecret); + mcConnection.enableEncryption(decryptedSharedSecret); } catch (GeneralSecurityException e) { throw new RuntimeException(e); } @@ -144,25 +143,23 @@ public class LoginSessionHandler implements MinecraftSessionHandler { initializePlayer(GSON.fromJson(profileResponse.getBody(), GameProfile.class), true); } else if (profileResponse.getCode() == 204) { // Apparently an offline-mode user logged onto this online-mode proxy. - logger.warn("An offline-mode client ({} from {}) tried to connect!", - login.getUsername(), playerIp); - inbound.closeWith(Disconnect.create(VelocityMessages.ONLINE_MODE_ONLY)); + inbound.disconnect(VelocityMessages.ONLINE_MODE_ONLY); } else { // Something else went wrong logger.error( "Got an unexpected error code {} whilst contacting Mojang to log in {} ({})", profileResponse.getCode(), login.getUsername(), playerIp); - inbound.close(); + mcConnection.close(); } - }, inbound.eventLoop()) + }, mcConnection.eventLoop()) .exceptionally(exception -> { logger.error("Unable to enable encryption", exception); - inbound.close(); + mcConnection.close(); return null; }); } catch (GeneralSecurityException e) { logger.error("Unable to enable encryption", e); - inbound.close(); + mcConnection.close(); } catch (MalformedURLException e) { throw new AssertionError(e); } @@ -174,10 +171,10 @@ public class LoginSessionHandler implements MinecraftSessionHandler { if (login == null) { throw new IllegalStateException("No ServerLogin packet received yet."); } - PreLoginEvent event = new PreLoginEvent(apiInbound, login.getUsername()); + PreLoginEvent event = new PreLoginEvent(inbound, login.getUsername()); server.getEventManager().fire(event) .thenRunAsync(() -> { - if (inbound.isClosed()) { + if (mcConnection.isClosed()) { // The player was disconnected return; } @@ -185,7 +182,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler { Optional disconnectReason = result.getReason(); if (disconnectReason.isPresent()) { // The component is guaranteed to be provided if the connection was denied. - inbound.closeWith(Disconnect.create(disconnectReason.get())); + mcConnection.closeWith(Disconnect.create(disconnectReason.get())); return; } @@ -194,11 +191,11 @@ public class LoginSessionHandler implements MinecraftSessionHandler { // Request encryption. EncryptionRequest request = generateEncryptionRequest(); this.verify = Arrays.copyOf(request.getVerifyToken(), 4); - inbound.write(request); + mcConnection.write(request); } else { initializePlayer(GameProfile.forOfflinePlayer(login.getUsername()), false); } - }, inbound.eventLoop()); + }, mcConnection.eventLoop()); } private EncryptionRequest generateEncryptionRequest() { @@ -213,15 +210,16 @@ public class LoginSessionHandler implements MinecraftSessionHandler { private void initializePlayer(GameProfile profile, boolean onlineMode) { // Some connection types may need to alter the game profile. - profile = inbound.getType().addGameProfileTokensIfRequired(profile, + profile = mcConnection.getType().addGameProfileTokensIfRequired(profile, server.getConfiguration().getPlayerInfoForwardingMode()); - GameProfileRequestEvent profileRequestEvent = new GameProfileRequestEvent(apiInbound, profile, + GameProfileRequestEvent profileRequestEvent = new GameProfileRequestEvent(inbound, profile, onlineMode); server.getEventManager().fire(profileRequestEvent).thenCompose(profileEvent -> { // Initiate a regular connection and move over to it. - ConnectedPlayer player = new ConnectedPlayer(server, profileEvent.getGameProfile(), inbound, - apiInbound.getVirtualHost().orElse(null)); + ConnectedPlayer player = new ConnectedPlayer(server, profileEvent.getGameProfile(), + mcConnection, + inbound.getVirtualHost().orElse(null)); this.connectedPlayer = player; if (!server.registerConnection(player)) { @@ -229,6 +227,8 @@ public class LoginSessionHandler implements MinecraftSessionHandler { return CompletableFuture.completedFuture(null); } + logger.info("{} has connected", player); + return server.getEventManager() .fire(new PermissionsSetupEvent(player, ConnectedPlayer.DEFAULT_PERMISSIONS)) .thenCompose(event -> { @@ -239,7 +239,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler { }) // then complete the connection .thenAcceptAsync(event -> { - if (inbound.isClosed()) { + if (mcConnection.isClosed()) { // The player was disconnected return; } @@ -250,7 +250,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler { } else { finishLogin(player); } - }, inbound.eventLoop()); + }, mcConnection.eventLoop()); }); } @@ -264,27 +264,26 @@ public class LoginSessionHandler implements MinecraftSessionHandler { int threshold = server.getConfiguration().getCompressionThreshold(); if (threshold >= 0) { - inbound.write(new SetCompression(threshold)); - inbound.setCompressionThreshold(threshold); + mcConnection.write(new SetCompression(threshold)); + mcConnection.setCompressionThreshold(threshold); } ServerLoginSuccess success = new ServerLoginSuccess(); success.setUsername(player.getUsername()); success.setUuid(player.getUniqueId()); - inbound.write(success); + mcConnection.write(success); - inbound.setAssociation(player); - inbound.setState(StateRegistry.PLAY); + mcConnection.setAssociation(player); + mcConnection.setState(StateRegistry.PLAY); - logger.info("{} has connected", player); - inbound.setSessionHandler(new InitialConnectSessionHandler(player)); + mcConnection.setSessionHandler(new InitialConnectSessionHandler(player)); server.getEventManager().fire(new PostLoginEvent(player)) .thenRun(() -> player.createConnectionRequest(toTry.get()).fireAndForget()); } @Override public void handleUnknown(ByteBuf buf) { - throw new IllegalStateException("Unknown data " + ByteBufUtil.hexDump(buf)); + mcConnection.close(); } @Override diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/VelocityMessages.java b/proxy/src/main/java/com/velocitypowered/proxy/util/VelocityMessages.java index a6022b0b3..621003e4c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/VelocityMessages.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/VelocityMessages.java @@ -14,8 +14,6 @@ public class VelocityMessages { .of("No available servers", TextColor.RED); public static final Component ALREADY_CONNECTED = TextComponent .of("You are already connected to this proxy!", TextColor.RED); - public static final Component INVALID_USERNAME = TextComponent - .of("Trying to login with invalid username", TextColor.RED); private VelocityMessages() { throw new AssertionError();