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 9de076f5..e402ab44 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java @@ -16,8 +16,11 @@ */ package com.comphenix.protocol.wrappers; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -32,16 +35,16 @@ import com.comphenix.protocol.injector.BukkitUnwrapper; import com.comphenix.protocol.reflect.FieldAccessException; import com.comphenix.protocol.reflect.FieldUtils; import com.comphenix.protocol.reflect.FuzzyReflection; +import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.reflect.accessors.Accessors; -import com.comphenix.protocol.reflect.accessors.ConstructorAccessor; import com.comphenix.protocol.reflect.accessors.MethodAccessor; import com.comphenix.protocol.utility.MinecraftReflection; public class WrappedDataWatcher extends AbstractWrapper implements Iterable { - private static ConstructorAccessor dataWatcherObjectConstructor = null; private static MethodAccessor getter = null; private static MethodAccessor setter = null; + private static Field MAP_FIELD = null; private static Field ENTITY_DATA_FIELD = null; private static Field ENTITY_FIELD = null; @@ -78,29 +81,26 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable candidates = fuzzy.getFieldListByType(Map.class); - Field match = null; for (Field candidate : candidates) { if (Modifier.isStatic(candidate.getModifiers())) { // This is the entity class to current index map, which we really don't have a use for } else { // This is the map we're looking for - match = candidate; + MAP_FIELD = candidate; + MAP_FIELD.setAccessible(true); } } - if (match == null) { + if (MAP_FIELD == null) { throw new FieldAccessException("Could not find index -> object map."); } Map map = null; try { - match.setAccessible(true); - map = (Map) match.get(handle); - } catch (IllegalArgumentException e) { - throw new FieldAccessException(e); - } catch (IllegalAccessException e) { - throw new FieldAccessException(e); + map = (Map) MAP_FIELD.get(handle); + } catch (ReflectiveOperationException e) { + throw new FieldAccessException("Failed to access index -> object map", e); } Map ret = new HashMap<>(); @@ -111,6 +111,10 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable HANDLE_TYPE = MinecraftReflection.getDataWatcherSerializerClass(); + private static Constructor constructor = null; + + private StructureModifier modifier = new StructureModifier(HANDLE_TYPE); + + public WrappedDataWatcherObject(Object handle) { + super(HANDLE_TYPE); + setHandle(handle); + modifier.withTarget(handle); + } + + public WrappedDataWatcherObject(int index, Serializer serializer) { + this(newHandle(index, serializer.getHandle())); + } + + private static Object newHandle(int index, Object serializer) { + if (constructor == null) { + constructor = HANDLE_TYPE.getConstructors()[0]; + } + + try { + return constructor.newInstance(index, serializer); + } catch (ReflectiveOperationException e) { + throw new RuntimeException("Failed to create new DataWatcherObject", e); + } + } + + public int getIndex() { + return (int) modifier.read(0); + } + + public Serializer getSerializer() { + // TODO this + return null; + } + } + + public static class Serializer extends AbstractWrapper { + private static final Class HANDLE_TYPE = MinecraftReflection.getDataWatcherSerializerClass(); + + private final Class type; + private final boolean optional; + + public Serializer(Class type, Object handle, boolean optional) { + super(HANDLE_TYPE); + this.type = type; + this.optional = optional; + + setHandle(handle); + } + + public Class getType() { + return type; + } + + public boolean isOptional() { + return optional; + } + } + + public static class Registry { + private static boolean INITIALIZED = false; + private static Map, Serializer> REGISTRY = new HashMap<>(); + + public static Serializer get(Class clazz) { + if (! INITIALIZED) { + initialize(); + INITIALIZED = true; + } + + return REGISTRY.get(clazz); + } + + private static void initialize() { + List candidates = FuzzyReflection.fromClass(MinecraftReflection.getMinecraftClass("DataWatcherRegistry"), true) + .getFieldListByType(MinecraftReflection.getDataWatcherSerializerClass()); + for (Field candidate : candidates) { + Type generic = candidate.getGenericType(); + if (generic instanceof ParameterizedType) { + ParameterizedType type = (ParameterizedType) generic; + Type[] args = type.getActualTypeArguments(); + Type arg = args[0]; + + Class innerClass = null; + boolean optional = false; + + if (arg instanceof Class) { + innerClass = (Class) arg; + } else if (arg instanceof ParameterizedType) { + innerClass = (Class) ((ParameterizedType) arg).getActualTypeArguments()[0]; + optional = true; + } else { + throw new IllegalStateException("Failed to find inner class of field " + candidate); + } + + Object serializer; + + try { + serializer = candidate.get(null); + } catch (ReflectiveOperationException e) { + throw new IllegalStateException("Failed to read field " + candidate); + } + + REGISTRY.put(innerClass, new Serializer(innerClass, serializer, optional)); + } + } + } + } } \ No newline at end of file diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java index 08d2aa2b..ab40eaaf 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedWatchableObject.java @@ -7,6 +7,7 @@ import org.bukkit.inventory.ItemStack; import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.utility.MinecraftReflection; +import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject; /** * @author dmulloy2 @@ -32,6 +33,14 @@ public class WrappedWatchableObject extends AbstractWrapper { // ---- Getter methods + public WrappedDataWatcherObject getWatcherObject() { + return new WrappedDataWatcherObject(modifier.read(0)); + } + + public int getIndex() { + return getWatcherObject().getIndex(); + } + /** * Gets the wrapped value of this data watcher item. * @return The wrapped value diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/BukkitInitialization.java b/ProtocolLib/src/test/java/com/comphenix/protocol/BukkitInitialization.java index 022005c5..fb775fd6 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/BukkitInitialization.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/BukkitInitialization.java @@ -60,6 +60,6 @@ public class BukkitInitialization { public static void initializePackage() { // Initialize reflection MinecraftReflection.setMinecraftPackage(Constants.NMS, Constants.OBC); - MinecraftVersion.setCurrentVersion(MinecraftVersion.BOUNTIFUL_UPDATE); + MinecraftVersion.setCurrentVersion(MinecraftVersion.COMBAT_UPDATE); } }