From 528468a3421be5741e637f3c20c769dec7786af8 Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Tue, 13 Nov 2012 16:10:56 +0100 Subject: [PATCH] Fixed a bug preventing the entity modifier from reading entities. --- .../protocol/CleanupStaticMembers.java | 4 +- .../comphenix/protocol/ProtocolManager.java | 10 ++ .../protocol/injector/EntityUtilities.java | 101 ++++++++++++------ .../injector/PacketFilterManager.java | 6 ++ .../protocol/wrappers/BukkitConverters.java | 50 +++------ .../protocol/wrappers/WrappedDataWatcher.java | 8 +- 6 files changed, 106 insertions(+), 73 deletions(-) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/CleanupStaticMembers.java b/ProtocolLib/src/main/java/com/comphenix/protocol/CleanupStaticMembers.java index 1d6fa6da..f04aebdc 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/CleanupStaticMembers.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/CleanupStaticMembers.java @@ -19,7 +19,6 @@ import com.comphenix.protocol.reflect.compiler.StructureCompiler; import com.comphenix.protocol.reflect.instances.CollectionGenerator; import com.comphenix.protocol.reflect.instances.DefaultInstances; import com.comphenix.protocol.reflect.instances.PrimitiveGenerator; -import com.comphenix.protocol.wrappers.BukkitConverters; import com.comphenix.protocol.wrappers.ChunkPosition; import com.comphenix.protocol.wrappers.WrappedDataWatcher; import com.comphenix.protocol.wrappers.WrappedWatchableObject; @@ -50,8 +49,7 @@ class CleanupStaticMembers { PrimitiveGenerator.class, FuzzyReflection.class, MethodUtils.class, BackgroundCompiler.class, StructureCompiler.class, ObjectCloner.class, Packets.Server.class, Packets.Client.class, - ChunkPosition.class, WrappedDataWatcher.class, WrappedWatchableObject.class, - BukkitConverters.class + ChunkPosition.class, WrappedDataWatcher.class, WrappedWatchableObject.class }; String[] internalClasses = { diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolManager.java b/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolManager.java index f23e20c7..cbdc93f1 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolManager.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/ProtocolManager.java @@ -20,6 +20,7 @@ package com.comphenix.protocol; import java.util.List; import java.util.Set; +import org.bukkit.World; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; @@ -105,6 +106,15 @@ public interface ProtocolManager extends PacketStream { */ public void updateEntity(Entity entity, List observers) throws FieldAccessException; + /** + * Retrieve the associated entity. + * @param container - the world the entity belongs to. + * @param id - the unique ID of the entity. + * @return The associated entity. + * @throws FieldAccessException Reflection failed. + */ + public Entity getEntityFromID(World container, int id) throws FieldAccessException; + /** * Retrieves a immutable set containing the ID of the sent server packets that will be observed by listeners. * @return Every filtered server packet. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java index 8dbd1567..9671c6ce 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java @@ -27,6 +27,8 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import net.minecraft.server.EntityTrackerEntry; + import org.bukkit.World; import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.entity.Entity; @@ -83,8 +85,51 @@ class EntityUtilities { * */ public static void updateEntity(Entity entity, List observers) throws FieldAccessException { - - World world = entity.getWorld(); + try { + //EntityTrackerEntry trackEntity = (EntityTrackerEntry) tracker.trackedEntities.get(entity.getEntityId()); + Object trackerEntry = getEntityTrackerEntry(entity.getWorld(), entity.getEntityId()); + + if (trackedPlayersField == null) { + // This one is fairly easy + trackedPlayersField = FuzzyReflection.fromObject(trackerEntry).getFieldByType("java\\.util\\..*"); + } + + // Phew, finally there. + Collection trackedPlayers = (Collection) FieldUtils.readField(trackedPlayersField, trackerEntry, false); + List nmsPlayers = unwrapBukkit(observers); + + // trackEntity.trackedPlayers.clear(); + trackedPlayers.removeAll(nmsPlayers); + + // We have to rely on a NAME once again. Damn it. + if (scanPlayersMethod == null) { + scanPlayersMethod = trackerEntry.getClass().getMethod("scanPlayers", List.class); + } + + //trackEntity.scanPlayers(server.players); + scanPlayersMethod.invoke(trackerEntry, nmsPlayers); + + } catch (IllegalArgumentException e) { + throw e; + } catch (IllegalAccessException e) { + throw new FieldAccessException("Security limitation prevents access to 'get' method in IntHashMap", e); + } catch (InvocationTargetException e) { + throw new RuntimeException("Exception occurred in Minecraft.", e); + } catch (SecurityException e) { + throw new FieldAccessException("Security limitation prevents access to 'scanPlayers' method in trackerEntry.", e); + } catch (NoSuchMethodException e) { + throw new FieldAccessException("Canot find 'scanPlayers' method. Is ProtocolLib up to date?", e); + } + } + + /** + * Retrieve the entity tracker entry given a ID. + * @param world - world server. + * @param entityID - entity ID. + * @return The entity tracker entry. + * @throws FieldAccessException + */ + private static Object getEntityTrackerEntry(World world, int entityID) throws FieldAccessException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { Object worldServer = ((CraftWorld) world).getHandle(); // We have to rely on the class naming here. @@ -148,41 +193,37 @@ class EntityUtilities { } } } - + + // Wrap exceptions try { - //EntityTrackerEntry trackEntity = (EntityTrackerEntry) tracker.trackedEntities.get(entity.getEntityId()); - Object trackerEntry = hashGetMethod.invoke(trackedEntities, entity.getEntityId()); - - if (trackedPlayersField == null) { - // This one is fairly easy - trackedPlayersField = FuzzyReflection.fromObject(trackerEntry).getFieldByType("java\\.util\\..*"); - } - - // Phew, finally there. - Collection trackedPlayers = (Collection) FieldUtils.readField(trackedPlayersField, trackerEntry, false); - List nmsPlayers = unwrapBukkit(observers); - - // trackEntity.trackedPlayers.clear(); - trackedPlayers.removeAll(nmsPlayers); - - // We have to rely on a NAME once again. Damn it. - if (scanPlayersMethod == null) { - scanPlayersMethod = trackerEntry.getClass().getMethod("scanPlayers", List.class); - } - - //trackEntity.scanPlayers(server.players); - scanPlayersMethod.invoke(trackerEntry, nmsPlayers); - + return hashGetMethod.invoke(trackedEntities, entityID); } catch (IllegalArgumentException e) { throw e; } catch (IllegalAccessException e) { throw new FieldAccessException("Security limitation prevents access to 'get' method in IntHashMap", e); } catch (InvocationTargetException e) { throw new RuntimeException("Exception occurred in Minecraft.", e); - } catch (SecurityException e) { - throw new FieldAccessException("Security limitation prevents access to 'scanPlayers' method in trackerEntry.", e); - } catch (NoSuchMethodException e) { - throw new FieldAccessException("Canot find 'scanPlayers' method. Is ProtocolLib up to date?", e); + } + } + + /** + * Retrieve entity from a ID, even it it's newly created. + * @return The asssociated entity. + * @throws FieldAccessException Reflection error. + */ + public static Entity getEntityFromID(World world, int entityID) throws FieldAccessException { + try { + EntityTrackerEntry trackerEntry = (EntityTrackerEntry) getEntityTrackerEntry(world, entityID); + + // Handle NULL cases + if (trackerEntry != null && trackerEntry.tracker != null) { + return trackerEntry.tracker.getBukkitEntity(); + } else { + return null; + } + + } catch (Exception e) { + throw new FieldAccessException("Cannot find entity from ID.", e); } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java index 29ee6f9d..988e773b 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/PacketFilterManager.java @@ -34,6 +34,7 @@ import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; import org.bukkit.Server; +import org.bukkit.World; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -565,6 +566,11 @@ public final class PacketFilterManager implements ProtocolManager, ListenerInvok EntityUtilities.updateEntity(entity, observers); } + @Override + public Entity getEntityFromID(World container, int id) throws FieldAccessException { + return EntityUtilities.getEntityFromID(container, id); + } + /** * Initialize the packet injection for every player. * @param players - list of players to inject. diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java index ee7faf9f..c8bc5935 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java @@ -1,7 +1,6 @@ package com.comphenix.protocol.wrappers; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -9,18 +8,16 @@ import java.util.List; import net.minecraft.server.DataWatcher; import net.minecraft.server.WatchableObject; -import org.bukkit.Bukkit; -import org.bukkit.Server; import org.bukkit.World; import org.bukkit.WorldType; -import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.inventory.CraftItemStack; import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.ProtocolManager; import com.comphenix.protocol.reflect.EquivalentConverter; -import com.comphenix.protocol.reflect.FuzzyReflection; +import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.instances.DefaultInstances; /** @@ -32,9 +29,6 @@ public class BukkitConverters { // Check whether or not certain classes exists private static boolean hasWorldType = false; - // The getEntity method - private static Method getEntity; - static { try { Class.forName("net.minecraft.server.WorldType"); @@ -183,13 +177,10 @@ public class BukkitConverters { * @return A converter between the underlying NMS entity and Bukkit's wrapper. */ public static EquivalentConverter getEntityConverter(World world) { - final Object worldServer = ((CraftWorld) world).getHandle(); - final Class nmsEntityClass = net.minecraft.server.Entity.class; + final World container = world; + final WeakReference managerRef = + new WeakReference(ProtocolLibrary.getProtocolManager()); - if (getEntity == null) - getEntity = FuzzyReflection.fromObject(worldServer).getMethodByParameters( - "getEntity", nmsEntityClass, new Class[] { int.class }); - return getIgnoreNull(new EquivalentConverter() { @Override @@ -201,34 +192,17 @@ public class BukkitConverters { @Override public Entity getSpecific(Object generic) { try { - net.minecraft.server.Entity nmsEntity = (net.minecraft.server.Entity) - getEntity.invoke(worldServer, generic); Integer id = (Integer) generic; - // Attempt to get the Bukkit entity - if (nmsEntity != null) { - return nmsEntity.getBukkitEntity(); + // Use the + if (id != null && managerRef.get() != null) { + return managerRef.get().getEntityFromID(container, id); } else { - Server server = Bukkit.getServer(); - - // Maybe it's a player that has just logged in? Try a search - if (server != null) { - for (Player player : server.getOnlinePlayers()) { - if (player.getEntityId() == id) { - return player; - } - } - } - return null; } - } catch (IllegalArgumentException e) { - throw new RuntimeException("Incorrect arguments detected.", e); - } catch (IllegalAccessException e) { - throw new RuntimeException("Cannot read field due to a security limitation.", e); - } catch (InvocationTargetException e) { - throw new RuntimeException("Error occured in Minecraft method.", e.getCause()); + } catch (FieldAccessException e) { + throw new RuntimeException("Cannot retrieve entity from ID.", e); } } diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java index b5451328..f8326c6c 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java @@ -14,6 +14,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import org.bukkit.entity.Entity; import org.bukkit.inventory.ItemStack; +import com.comphenix.protocol.injector.BukkitUnwrapper; import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FieldUtils; import com.comphenix.protocol.reflect.FuzzyReflection; @@ -414,10 +415,13 @@ public class WrappedDataWatcher { */ public static WrappedDataWatcher getEntityWatcher(Entity entity) throws FieldAccessException { if (entityDataField == null) - entityDataField = FuzzyReflection.fromClass(Entity.class, true).getFieldByType("datawatcher", DataWatcher.class); + entityDataField = FuzzyReflection.fromClass(net.minecraft.server.Entity.class, true). + getFieldByType("datawatcher", DataWatcher.class); + BukkitUnwrapper unwrapper = new BukkitUnwrapper(); + try { - Object nsmWatcher = FieldUtils.readField(entityDataField, entity, true); + Object nsmWatcher = FieldUtils.readField(entityDataField, unwrapper.unwrapItem(entity), true); if (nsmWatcher != null) return new WrappedDataWatcher((DataWatcher) nsmWatcher);