diff --git a/Spigot-API-Patches/0171-Add-PlayerConnectionCloseEvent.patch b/Spigot-API-Patches/0171-Add-PlayerConnectionCloseEvent.patch new file mode 100644 index 0000000000..d09d1436ed --- /dev/null +++ b/Spigot-API-Patches/0171-Add-PlayerConnectionCloseEvent.patch @@ -0,0 +1,133 @@ +From c82f656c16764f1c42045bc102ad89c43d43a1db Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Sun, 7 Oct 2018 12:05:06 -0700 +Subject: [PATCH] Add PlayerConnectionCloseEvent + +This event is invoked when a player has disconnected. It is guaranteed that, +if the server is in online-mode, that the provided uuid and username have been +validated. + +The event is invoked for players who have not yet logged into the world, whereas +PlayerQuitEvent is only invoked on players who have logged into the world. + +The event is invoked for players who have already logged into the world, +although whether or not the player exists in the world at the time of +firing is undefined. (That is, whether the plugin can retrieve a Player object +using the event parameters is undefined). However, it is guaranteed that this +event is invoked AFTER PlayerQuitEvent, if the player has already logged into +the world. + +This event is guaranteed to never fire unless AsyncPlayerPreLoginEvent has +been called beforehand, and this event may not be called in parallel with +AsyncPlayerPreLoginEvent for the same connection. + +Cancelling the AsyncPlayerPreLoginEvent guarantees the corresponding +PlayerConnectionCloseEvent is never called. + +The event may be invoked asynchronously or synchronously. As it stands, +it is never invoked asynchronously. However, plugins should check +Event#isAsynchronous to be future-proof. + +On purpose, the deprecated PlayerPreLoginEvent event is left out of the +API spec for this event. Plugins should not be using that event, and +how PlayerPreLoginEvent interacts with PlayerConnectionCloseEvent +is undefined. + +diff --git a/src/main/java/com/destroystokyo/paper/event/player/PlayerConnectionCloseEvent.java b/src/main/java/com/destroystokyo/paper/event/player/PlayerConnectionCloseEvent.java +new file mode 100644 +index 00000000..80896bbf +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerConnectionCloseEvent.java +@@ -0,0 +1,89 @@ ++package com.destroystokyo.paper.event.player; ++ ++import org.bukkit.event.Event; ++import org.bukkit.event.HandlerList; ++ ++import java.net.InetAddress; ++import java.util.UUID; ++ ++/** ++ *

++ * This event is invoked when a player has disconnected. It is guaranteed that, ++ * if the server is in online-mode, that the provided uuid and username have been ++ * validated. ++ *

++ * ++ *

++ * The event is invoked for players who have not yet logged into the world, whereas ++ * {@link org.bukkit.event.player.PlayerQuitEvent} is only invoked on players who have logged into the world. ++ *

++ * ++ *

++ * The event is invoked for players who have already logged into the world, ++ * although whether or not the player exists in the world at the time of ++ * firing is undefined. (That is, whether the plugin can retrieve a Player object ++ * using the event parameters is undefined). However, it is guaranteed that this ++ * event is invoked AFTER {@link org.bukkit.event.player.PlayerQuitEvent}, if the player has already logged into the world. ++ *

++ * ++ *

++ * This event is guaranteed to never fire unless {@link org.bukkit.event.player.AsyncPlayerPreLoginEvent} has ++ * been fired beforehand, and this event may not be called in parallel with ++ * {@link org.bukkit.event.player.AsyncPlayerPreLoginEvent} for the same connection. ++ *

++ * ++ *

++ * Cancelling the {@link org.bukkit.event.player.AsyncPlayerPreLoginEvent} guarantees the corresponding ++ * {@code PlayerConnectionCloseEvent} is never called. ++ *

++ * ++ *

++ * The event may be invoked asynchronously or synchronously. Plugins should check ++ * {@link Event#isAsynchronous()} and handle accordingly. ++ *

++ */ ++public class PlayerConnectionCloseEvent extends Event { ++ ++ private static final HandlerList HANDLERS = new HandlerList(); ++ ++ private final UUID playerUniqueId; ++ private final String playerName; ++ private final InetAddress ipAddress; ++ ++ public PlayerConnectionCloseEvent(final UUID playerUniqueId, final String playerName, final InetAddress ipAddress, final boolean async) { ++ super(async); ++ this.playerUniqueId = playerUniqueId; ++ this.playerName = playerName; ++ this.ipAddress = ipAddress; ++ } ++ ++ /** ++ * Returns the {@code UUID} of the player disconnecting. ++ */ ++ public UUID getPlayerUniqueId() { ++ return this.playerUniqueId; ++ } ++ ++ /** ++ * Returns the name of the player disconnecting. ++ */ ++ public String getPlayerName() { ++ return this.playerName; ++ } ++ ++ /** ++ * Returns the player's IP address. ++ */ ++ public InetAddress getIpAddress() { ++ return this.ipAddress; ++ } ++ ++ @Override ++ public HandlerList getHandlers() { ++ return HANDLERS; ++ } ++ ++ public static HandlerList getHandlerList() { ++ return HANDLERS; ++ } ++} +-- +2.20.0 + diff --git a/Spigot-Server-Patches/0416-Add-PlayerConnectionCloseEvent.patch b/Spigot-Server-Patches/0416-Add-PlayerConnectionCloseEvent.patch new file mode 100644 index 0000000000..7c454e5fcf --- /dev/null +++ b/Spigot-Server-Patches/0416-Add-PlayerConnectionCloseEvent.patch @@ -0,0 +1,93 @@ +From a6fc124f1734c2bcac9058c5746a1127bf58bb54 Mon Sep 17 00:00:00 2001 +From: Spottedleaf +Date: Sun, 7 Oct 2018 12:05:28 -0700 +Subject: [PATCH] Add PlayerConnectionCloseEvent + +This event is invoked when a player has disconnected. It is guaranteed that, +if the server is in online-mode, that the provided uuid and username have been +validated. + +The event is invoked for players who have not yet logged into the world, whereas +PlayerQuitEvent is only invoked on players who have logged into the world. + +The event is invoked for players who have already logged into the world, +although whether or not the player exists in the world at the time of +firing is undefined. (That is, whether the plugin can retrieve a Player object +using the event parameters is undefined). However, it is guaranteed that this +event is invoked AFTER PlayerQuitEvent, if the player has already logged into +the world. + +This event is guaranteed to never fire unless AsyncPlayerPreLoginEvent has +been called beforehand, and this event may not be called in parallel with +AsyncPlayerPreLoginEvent for the same connection. + +Cancelling the AsyncPlayerPreLoginEvent guarantees the corresponding +PlayerConnectionCloseEvent is never called. + +The event may be invoked asynchronously or synchronously. As it stands, +it is never invoked asynchronously. However, plugins should check +Event#isAsynchronous to be future-proof. + +On purpose, the deprecated PlayerPreLoginEvent event is left out of the +API spec for this event. Plugins should not be using that event, and +how PlayerPreLoginEvent interacts with PlayerConnectionCloseEvent +is undefined. + +diff --git a/src/main/java/net/minecraft/server/LoginListener.java b/src/main/java/net/minecraft/server/LoginListener.java +index 4df31c828..78ea9ad18 100644 +--- a/src/main/java/net/minecraft/server/LoginListener.java ++++ b/src/main/java/net/minecraft/server/LoginListener.java +@@ -36,9 +36,9 @@ public class LoginListener implements PacketLoginInListener, ITickable { + private final byte[] e = new byte[4]; + private final MinecraftServer server; + public final NetworkManager networkManager; +- private LoginListener.EnumProtocolState g; ++ private LoginListener.EnumProtocolState g; public final LoginListener.EnumProtocolState getLoginState() { return this.g; }; // Paper - OBFHELPER + private int h; +- private GameProfile i; private void setGameProfile(GameProfile profile) { i = profile; } private GameProfile getGameProfile() { return i; } // Paper - OBFHELPER ++ private GameProfile i; private void setGameProfile(GameProfile profile) { i = profile; } public final GameProfile getGameProfile() { return i; } // Paper - OBFHELPER + private final String j; + private SecretKey loginKey; + private EntityPlayer l; +diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java +index 2f887932b..1e5dfd3f7 100644 +--- a/src/main/java/net/minecraft/server/NetworkManager.java ++++ b/src/main/java/net/minecraft/server/NetworkManager.java +@@ -16,6 +16,7 @@ import io.netty.handler.timeout.TimeoutException; + import io.netty.util.AttributeKey; + import io.netty.util.concurrent.Future; + import io.netty.util.concurrent.GenericFutureListener; ++ + import java.net.SocketAddress; + import java.util.Queue; + import java.util.concurrent.locks.ReentrantReadWriteLock; +@@ -358,6 +359,26 @@ public class NetworkManager extends SimpleChannelInboundHandler> { + this.i().a(new ChatMessage("multiplayer.disconnect.generic", new Object[0])); + } + this.i.clear(); // Free up packet queue. ++ // Paper start - Add PlayerConnectionCloseEvent ++ final PacketListener packetListener = this.i(); ++ if (packetListener instanceof PlayerConnection) { ++ /* Player was logged in */ ++ final PlayerConnection playerConnection = (PlayerConnection) packetListener; ++ new com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent(playerConnection.player.uniqueID, ++ playerConnection.player.getName(), ((java.net.InetSocketAddress)l).getAddress(), false).callEvent(); ++ } else if (packetListener instanceof LoginListener) { ++ /* Player is login stage */ ++ final LoginListener loginListener = (LoginListener) packetListener; ++ switch (loginListener.getLoginState()) { ++ case READY_TO_ACCEPT: ++ case DELAY_ACCEPT: ++ case ACCEPTED: ++ final com.mojang.authlib.GameProfile profile = loginListener.getGameProfile(); /* Should be non-null at this stage */ ++ new com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent(profile.getId(), profile.getName(), ++ ((java.net.InetSocketAddress)l).getAddress(), false).callEvent(); ++ } ++ } ++ // Paper end + } + + } +-- +2.20.0 +