diff --git a/api/build.gradle b/api/build.gradle index 3b8fa7c37..e7169618f 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -103,10 +103,15 @@ publishing { repositories { maven { - name = 'myRepo' - def base = 'file:///maven-repo' - def releasesRepoUrl = "$base/releases" - def snapshotsRepoUrl = "$base/snapshots" + credentials { + username System.getenv("NEXUS_USERNAME") + password System.getenv("NEXUS_PASSWORD") + } + + name = 'velocity-nexus' + def base = 'https://nexus.velocitypowered.com/repository/velocity-artifacts' + def releasesRepoUrl = "$base-releases/" + def snapshotsRepoUrl = "$base-snapshots/" url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl } } diff --git a/api/src/main/java/com/velocitypowered/api/event/connection/DisconnectEvent.java b/api/src/main/java/com/velocitypowered/api/event/connection/DisconnectEvent.java index 2442077a8..85b5fae86 100644 --- a/api/src/main/java/com/velocitypowered/api/event/connection/DisconnectEvent.java +++ b/api/src/main/java/com/velocitypowered/api/event/connection/DisconnectEvent.java @@ -1,5 +1,9 @@ package com.velocitypowered.api.event.connection; +import static com.velocitypowered.api.event.connection.DisconnectEvent.LoginStatus.CANCELLED_BY_PROXY; +import static com.velocitypowered.api.event.connection.DisconnectEvent.LoginStatus.CONFLICTING_LOGIN; +import static com.velocitypowered.api.event.connection.DisconnectEvent.LoginStatus.SUCCESSFUL_LOGIN; + import com.google.common.base.Preconditions; import com.velocitypowered.api.proxy.Player; @@ -10,31 +14,51 @@ import com.velocitypowered.api.proxy.Player; public final class DisconnectEvent { private final Player player; - private final boolean disconnectedDuringLogin; + private final LoginStatus loginStatus; + @Deprecated public DisconnectEvent(Player player) { this(player, false); } + @Deprecated public DisconnectEvent(Player player, boolean disconnectedDuringLogin) { + this(player, disconnectedDuringLogin ? CANCELLED_BY_PROXY : SUCCESSFUL_LOGIN); + } + + public DisconnectEvent(Player player, LoginStatus loginStatus) { this.player = Preconditions.checkNotNull(player, "player"); - this.disconnectedDuringLogin = disconnectedDuringLogin; + this.loginStatus = Preconditions.checkNotNull(loginStatus, "loginStatus"); } public Player getPlayer() { return player; } + @Deprecated public boolean disconnectedDuringLogin() { - return this.disconnectedDuringLogin; + return this.loginStatus == CANCELLED_BY_PROXY || this.loginStatus == CONFLICTING_LOGIN; + } + + public LoginStatus getLoginStatus() { + return loginStatus; } @Override public String toString() { return "DisconnectEvent{" + "player=" + player + ", " - + "disconnectedDuringLogin=" + disconnectedDuringLogin + + "loginStatus=" + loginStatus + '}'; } + + public enum LoginStatus { + + SUCCESSFUL_LOGIN, + CONFLICTING_LOGIN, + CANCELLED_BY_USER, + CANCELLED_BY_PROXY, + CANCELLED_BY_USER_BEFORE_COMPLETE + } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index d60c8999a..7a817089f 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -421,6 +421,8 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { timedOut = true; } + eventManager.fireShutdownEvent(); + timedOut = !eventManager.shutdown() || timedOut; timedOut = !scheduler.shutdown() || timedOut; @@ -432,8 +434,6 @@ public class VelocityServer implements ProxyServer, ForwardingAudience { Thread.currentThread().interrupt(); } - eventManager.fireShutdownEvent(); - shutdown = true; if (explicitExit) { 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 ecf8c87c3..c29ec7274 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java @@ -209,8 +209,10 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { */ public void closeWith(Object msg) { if (channel.isActive()) { - if (channel.eventLoop().inEventLoop() - && this.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0) { + boolean is1Point8 = this.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_8) >= 0; + boolean isLegacyPing = this.getState() == StateRegistry.HANDSHAKE + || this.getState() == StateRegistry.STATUS; + if (channel.eventLoop().inEventLoop() && (is1Point8 || isLegacyPing)) { knownDisconnect = true; channel.writeAndFlush(msg).addListener(ChannelFutureListener.CLOSE); } else { @@ -271,6 +273,10 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { return channel.config().isAutoRead(); } + public boolean isKnownDisconnect() { + return knownDisconnect; + } + /** * Determines whether or not the channel should continue reading data automaticaly. * @param autoReading whether or not we should read data automatically 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 c505b489d..b3aa72c46 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 @@ -6,6 +6,7 @@ import static java.util.concurrent.CompletableFuture.completedFuture; import com.google.common.base.Preconditions; import com.google.gson.JsonObject; import com.velocitypowered.api.event.connection.DisconnectEvent; +import com.velocitypowered.api.event.connection.DisconnectEvent.LoginStatus; import com.velocitypowered.api.event.player.KickedFromServerEvent; import com.velocitypowered.api.event.player.KickedFromServerEvent.DisconnectPlayer; import com.velocitypowered.api.event.player.KickedFromServerEvent.Notify; @@ -683,10 +684,21 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { if (connectedServer != null) { connectedServer.disconnect(); } - boolean isConnected = server.getPlayer(this.getUniqueId()).isPresent(); + + Optional connectedPlayer = server.getPlayer(this.getUniqueId()); server.unregisterConnection(this); - server.getEventManager().fire(new DisconnectEvent(this, !isConnected)) - .thenRun(() -> this.teardownFuture.complete(null)); + + DisconnectEvent.LoginStatus status; + if (connectedPlayer.isPresent()) { + status = connectedPlayer.get() == this ? LoginStatus.SUCCESSFUL_LOGIN + : LoginStatus.CONFLICTING_LOGIN; + } else { + status = connection.isKnownDisconnect() ? LoginStatus.CANCELLED_BY_PROXY : + LoginStatus.CANCELLED_BY_USER; + } + + DisconnectEvent event = new DisconnectEvent(this, status); + server.getEventManager().fire(event).thenRun(() -> this.teardownFuture.complete(null)); } public CompletableFuture getTeardownFuture() { 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 5cc033224..991954731 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 @@ -8,6 +8,8 @@ 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; @@ -202,10 +204,14 @@ public class LoginSessionHandler implements MinecraftSessionHandler { onlineMode); server.getEventManager().fire(profileRequestEvent).thenCompose(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); + mcConnection, inbound.getVirtualHost().orElse(null), onlineMode); this.connectedPlayer = player; if (!server.canRegisterConnection(player)) { player.disconnect0(VelocityMessages.ALREADY_CONNECTED, true); @@ -220,13 +226,13 @@ public class LoginSessionHandler implements MinecraftSessionHandler { if (!mcConnection.isClosed()) { // wait for permissions to load, then set the players permission function player.setPermissionFunction(event.createFunction(player)); - finishLogin(player); + completeLoginProtocolPhaseAndInitialize(player); } }, mcConnection.eventLoop()); }); } - private void finishLogin(ConnectedPlayer player) { + 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)); @@ -249,6 +255,8 @@ public class LoginSessionHandler implements MinecraftSessionHandler { .thenAcceptAsync(event -> { if (mcConnection.isClosed()) { // The player was disconnected + server.getEventManager().fireAndForget(new DisconnectEvent(player, + LoginStatus.CANCELLED_BY_USER_BEFORE_COMPLETE)); return; }