diff --git a/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java b/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java index 2b3f5e7d..e3f2fde6 100644 --- a/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java +++ b/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java @@ -28,6 +28,7 @@ import com.comphenix.protocol.reflect.accessors.FieldAccessor; import com.comphenix.protocol.reflect.accessors.MethodAccessor; import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract; import com.comphenix.protocol.reflect.fuzzy.FuzzyMethodContract; +import com.comphenix.protocol.utility.MinecraftFields; import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftVersion; import com.comphenix.protocol.wrappers.WrappedIntHashMap; @@ -67,7 +68,10 @@ class EntityUtilities { Collection trackedPlayers = getTrackedPlayers(entity); List nmsPlayers = unwrapBukkit(observers); - trackedPlayers.removeAll(nmsPlayers); + List removingEntries = MinecraftVersion.CAVES_CLIFFS_1.atOrAbove() ? + getPlayerConnections(nmsPlayers) : nmsPlayers; + + trackedPlayers.removeAll(removingEntries); Object trackerEntry = getEntityTrackerEntry(entity.getWorld(), entity.getEntityId()); @@ -103,7 +107,9 @@ class EntityUtilities { // Wrap every player - we also ensure that the underlying tracker list is immutable for (Object tracker : trackedPlayers) { - if (MinecraftReflection.isMinecraftPlayer(tracker)) { + if (MinecraftVersion.CAVES_CLIFFS_1.atOrAbove() && MinecraftReflection.isServerHandler(tracker)) { + result.add(MinecraftReflection.getBukkitPlayerFromConnection(tracker)); + } else if (MinecraftReflection.isMinecraftPlayer(tracker)) { result.add((Player) MinecraftReflection.getBukkitEntity(tracker)); } } @@ -266,6 +272,12 @@ class EntityUtilities { } } + private List getPlayerConnections(List nmsPlayers) { + List connections = new ArrayList<>(nmsPlayers.size()); + nmsPlayers.forEach(nmsPlayer -> connections.add(MinecraftFields.getPlayerConnection(nmsPlayer))); + return connections; + } + private List unwrapBukkit(List players) { List output = Lists.newArrayList(); BukkitUnwrapper unwrapper = new BukkitUnwrapper(); diff --git a/src/main/java/com/comphenix/protocol/utility/MinecraftFields.java b/src/main/java/com/comphenix/protocol/utility/MinecraftFields.java index 0c4378c0..625e46ef 100644 --- a/src/main/java/com/comphenix/protocol/utility/MinecraftFields.java +++ b/src/main/java/com/comphenix/protocol/utility/MinecraftFields.java @@ -15,15 +15,16 @@ public class MinecraftFields { // Cached accessors private static volatile FieldAccessor CONNECTION_ACCESSOR; private static volatile FieldAccessor NETWORK_ACCESSOR; - + private static volatile FieldAccessor CONNECTION_ENTITY_ACCESSOR; + private MinecraftFields() { // Not constructable } /** - * Retrieve the network mananger associated with a particular player. + * Retrieve the network manager associated with a particular player. * @param player - the player. - * @return The network manager, or NULL if no network manager has been asssociated yet. + * @return The network manager, or NULL if no network manager has been associated yet. */ public static Object getNetworkManager(Player player) { Object nmsPlayer = BukkitUnwrapper.getInstance().unwrapItem(player); @@ -50,9 +51,13 @@ public class MinecraftFields { Preconditions.checkNotNull(player, "player cannot be null!"); return getPlayerConnection(BukkitUnwrapper.getInstance().unwrapItem(player)); } - - // Retrieve player connection from a native instance - private static Object getPlayerConnection(Object nmsPlayer) { + + /** + * Retrieve the PlayerConnection (or NetServerHandler) associated with a player. + * @param nmsPlayer - the NMS player. + * @return The player connection. + */ + public static Object getPlayerConnection(Object nmsPlayer) { Preconditions.checkNotNull(nmsPlayer, "nmsPlayer cannot be null!"); if (CONNECTION_ACCESSOR == null) { @@ -61,4 +66,21 @@ public class MinecraftFields { } return CONNECTION_ACCESSOR.get(nmsPlayer); } + + /** + * Retrieves the EntityPlayer player field from a PlayerConnection. + * + * @param playerConnection The PlayerConnection object from which to retrieve the EntityPlayer field. + * @return The value of the EntityPlayer field in the PlayerConnection. + */ + public static Object getPlayerFromConnection(Object playerConnection) { + Preconditions.checkNotNull(playerConnection, "playerConnection cannot be null!"); + + if (CONNECTION_ENTITY_ACCESSOR == null) { + Class connectionClass = MinecraftReflection.getPlayerConnectionClass(); + Class entityPlayerClass = MinecraftReflection.getEntityPlayerClass(); + CONNECTION_ENTITY_ACCESSOR = Accessors.getFieldAccessor(connectionClass, entityPlayerClass, true); + } + return CONNECTION_ENTITY_ACCESSOR.get(playerConnection); + } } diff --git a/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java b/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java index f2026048..94c92bf2 100644 --- a/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java +++ b/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java @@ -41,6 +41,7 @@ import javax.annotation.Nonnull; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Server; +import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import com.comphenix.protocol.PacketType; @@ -376,6 +377,21 @@ public class MinecraftReflection { } } + /** + * Retrieve the Bukkit player from a given PlayerConnection. + * @param playerConnection The PlayerConnection. + * @return A bukkit player. + * @throws RuntimeException If we were unable to retrieve the Bukkit player. + */ + public static Player getBukkitPlayerFromConnection(Object playerConnection) + { + try { + return (Player) getBukkitEntity(MinecraftFields.getPlayerFromConnection(playerConnection)); + } catch (Exception e) { + throw new IllegalArgumentException("Cannot get Bukkit entity from connection " + playerConnection, e); + } + } + /** * Determine if a given object can be found within the package net.minecraft.server. * @param obj - the object to test.