diff --git a/ProtocolLib/src/com/comphenix/protocol/events/PacketContainer.java b/ProtocolLib/src/com/comphenix/protocol/events/PacketContainer.java index 33ee5627..e2a53d8e 100644 --- a/ProtocolLib/src/com/comphenix/protocol/events/PacketContainer.java +++ b/ProtocolLib/src/com/comphenix/protocol/events/PacketContainer.java @@ -17,12 +17,19 @@ package com.comphenix.protocol.events; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +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.inventory.ItemStack; import com.comphenix.protocol.injector.StructureCache; import com.comphenix.protocol.reflect.EquivalentConverter; +import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.StructureModifier; import net.minecraft.server.Packet; @@ -39,10 +46,13 @@ public class PacketContainer { // Current structure modifier protected StructureModifier structureModifier; - + // 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"); @@ -192,6 +202,57 @@ public class PacketContainer { }); } + /** + * Retrieves a read/write structure for entity objects. + *

+ * Note that entities are transmitted by integer ID, and the type may not be enough + * to distinguish between entities and other values. Thus, this structure modifier + * MAY return null or invalid entities for certain fields. Using the correct index + * is essential. + * + * @return A modifier entity types. + */ + public StructureModifier getEntityModifier(World world) { + + final Object worldServer = ((CraftWorld) world).getHandle(); + final Class nmsEntityClass = net.minecraft.server.Entity.class; + + if (getEntity == null) + getEntity = FuzzyReflection.fromObject(worldServer).getMethodByParameters( + "getEntity", nmsEntityClass, new Class[] { int.class }); + + // Convert to and from the Bukkit wrapper + return structureModifier.withType(int.class, new EquivalentConverter() { + @Override + public Object getGeneric(Entity specific) { + // Simple enough + return specific.getEntityId(); + } + + @Override + public Entity getSpecific(Object generic) { + try { + net.minecraft.server.Entity nmsEntity = (net.minecraft.server.Entity) + getEntity.invoke(worldServer, generic); + + // Attempt to get the Bukkit entity + if (nmsEntity != null) { + return nmsEntity.getBukkitEntity(); + } else { + 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()); + } + } + }); + } + /** * Retrieves the ID of this packet. * @return Packet ID. diff --git a/ProtocolLib/src/com/comphenix/protocol/reflect/FuzzyReflection.java b/ProtocolLib/src/com/comphenix/protocol/reflect/FuzzyReflection.java index 32e783b3..cc26d1db 100644 --- a/ProtocolLib/src/com/comphenix/protocol/reflect/FuzzyReflection.java +++ b/ProtocolLib/src/com/comphenix/protocol/reflect/FuzzyReflection.java @@ -131,6 +131,26 @@ public class FuzzyReflection { throw new RuntimeException("Unable to find " + name + " in " + source.getName()); } + /** + * Retrieves a method by looking at the parameter types and return type only. + * @param name - potential name of the method. Only used by the error mechanism. + * @param returnType - return type of the method to find. + * @param args - parameter types of the method to find. + * @return The first method that satisfies the parameter types. + */ + public Method getMethodByParameters(String name, Class returnType, Class[] args) { + + // Find the correct method to call + for (Method method : getMethods()) { + if (method.getReturnType().equals(returnType) && Arrays.equals(method.getParameterTypes(), args)) { + return method; + } + } + + // That sucks + throw new RuntimeException("Unable to find " + name + " in " + source.getName()); + } + /** * Retrieves a field by name. * @param nameRegex - regular expression that will match a field name.