From 558c5a348ca73d6ba8c13d2b68a7df85ed02fafa Mon Sep 17 00:00:00 2001
From: Chris Cowan <agentme49@gmail.com>
Date: Sat, 2 Dec 2017 16:58:41 -0800
Subject: [PATCH] SPIGOT-3663: Add plugin parameter to hidePlayer+showPlayer.

---
 .../craftbukkit/entity/CraftPlayer.java       | 69 +++++++++++++++++--
 1 file changed, 62 insertions(+), 7 deletions(-)

diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index 9747a8866b..505c58db49 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -8,16 +8,19 @@ import io.netty.buffer.Unpooled;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.lang.ref.WeakReference;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
+import java.util.WeakHashMap;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -63,10 +66,13 @@ import org.bukkit.inventory.InventoryView.Property;
 import org.bukkit.map.MapCursor;
 import org.bukkit.map.MapView;
 import org.bukkit.metadata.MetadataValue;
+import org.bukkit.plugin.IllegalPluginAccessException;
 import org.bukkit.plugin.Plugin;
 import org.bukkit.plugin.messaging.StandardMessenger;
 import org.bukkit.scoreboard.Scoreboard;
 
+import javax.annotation.Nullable;
+
 @DelegateDeserialization(CraftOfflinePlayer.class)
 public class CraftPlayer extends CraftHumanEntity implements Player {
     private long firstPlayed = 0;
@@ -74,7 +80,8 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
     private boolean hasPlayedBefore = false;
     private final ConversationTracker conversationTracker = new ConversationTracker();
     private final Set<String> channels = new HashSet<String>();
-    private final Set<UUID> hiddenPlayers = new HashSet<UUID>();
+    private final Map<UUID, Set<WeakReference<Plugin>>> hiddenPlayers = new HashMap<>();
+    private static final WeakHashMap<Plugin, WeakReference<Plugin>> pluginWeakReferences = new WeakHashMap<>();
     private int hash = 0;
     private double health = 20;
     private boolean scaledHealth = false;
@@ -905,18 +912,45 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
         }
     }
 
+    @Nullable
+    private static WeakReference<Plugin> getPluginWeakReference(@Nullable Plugin plugin) {
+        return (plugin == null) ? null : pluginWeakReferences.computeIfAbsent(plugin, WeakReference::new);
+    }
+
     @Override
+    @Deprecated
     public void hidePlayer(Player player) {
+        hidePlayer0(null, player);
+    }
+
+    @Override
+    public void hidePlayer(Plugin plugin, Player player) {
+        Validate.notNull(plugin, "Plugin cannot be null");
+        Validate.isTrue(plugin.isEnabled(), "Plugin attempted to hide player while disabled");
+
+        hidePlayer0(plugin, player);
+    }
+
+    private void hidePlayer0(@Nullable Plugin plugin, Player player) {
         Validate.notNull(player, "hidden player cannot be null");
         if (getHandle().playerConnection == null) return;
         if (equals(player)) return;
-        if (hiddenPlayers.contains(player.getUniqueId())) return;
-        hiddenPlayers.add(player.getUniqueId());
+
+        Set<WeakReference<Plugin>> hidingPlugins = hiddenPlayers.get(player.getUniqueId());
+        if (hidingPlugins != null) {
+            // Some plugins are already hiding the player. Just mark that this
+            // plugin wants the player hidden too and end.
+            hidingPlugins.add(getPluginWeakReference(plugin));
+            return;
+        }
+        hidingPlugins = new HashSet<>();
+        hidingPlugins.add(getPluginWeakReference(plugin));
+        hiddenPlayers.put(player.getUniqueId(), hidingPlugins);
 
         // Remove this player from the hidden player's EntityTrackerEntry
         EntityTracker tracker = ((WorldServer) entity.world).tracker;
         EntityPlayer other = ((CraftPlayer) player).getHandle();
-        EntityTrackerEntry entry = (EntityTrackerEntry) tracker.trackedEntities.get(other.getId());
+        EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId());
         if (entry != null) {
             entry.clear(getHandle());
         }
@@ -928,11 +962,32 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
     }
 
     @Override
+    @Deprecated
     public void showPlayer(Player player) {
+        showPlayer0(null, player);
+    }
+
+    @Override
+    public void showPlayer(Plugin plugin, Player player) {
+        Validate.notNull(plugin, "Plugin cannot be null");
+        // Don't require that plugin be enabled. A plugin must be allowed to call
+        // showPlayer during its onDisable() method.
+        showPlayer0(plugin, player);
+    }
+
+    private void showPlayer0(@Nullable Plugin plugin, Player player) {
         Validate.notNull(player, "shown player cannot be null");
         if (getHandle().playerConnection == null) return;
         if (equals(player)) return;
-        if (!hiddenPlayers.contains(player.getUniqueId())) return;
+
+        Set<WeakReference<Plugin>> hidingPlugins = hiddenPlayers.get(player.getUniqueId());
+        if (hidingPlugins == null) {
+            return; // Player isn't hidden
+        }
+        hidingPlugins.remove(getPluginWeakReference(plugin));
+        if (!hidingPlugins.isEmpty()) {
+            return; // Some other plugins still want the player hidden
+        }
         hiddenPlayers.remove(player.getUniqueId());
 
         EntityTracker tracker = ((WorldServer) entity.world).tracker;
@@ -940,7 +995,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
 
         getHandle().playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, other));
 
-        EntityTrackerEntry entry = (EntityTrackerEntry) tracker.trackedEntities.get(other.getId());
+        EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId());
         if (entry != null && !entry.trackedPlayers.contains(getHandle())) {
             entry.updatePlayer(getHandle());
         }
@@ -952,7 +1007,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
 
     @Override
     public boolean canSee(Player player) {
-        return !hiddenPlayers.contains(player.getUniqueId());
+        return !hiddenPlayers.containsKey(player.getUniqueId());
     }
 
     @Override