From ec8fa0d1fe268c5c2b436e7c94bd67b9a44ff136 Mon Sep 17 00:00:00 2001 From: Dan Mulloy Date: Fri, 4 Mar 2016 21:24:12 -0500 Subject: [PATCH] Add wrapper for MinecraftKey, more work on data watchers --- .../protocol/injector/EntityUtilities.java | 10 +-- .../protocol/wrappers/BukkitConverters.java | 40 +++++++++++- .../protocol/wrappers/MinecraftKey.java | 62 +++++++++++++++++++ .../protocol/wrappers/WrappedDataWatcher.java | 32 +++++++--- .../wrappers/WrappedWatchableObject.java | 25 +++++++- .../protocol/events/PacketContainerTest.java | 32 +++++----- 6 files changed, 171 insertions(+), 30 deletions(-) create mode 100644 ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/MinecraftKey.java 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 9f591b6a..bef960db 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/EntityUtilities.java @@ -209,16 +209,18 @@ class EntityUtilities { // Read the entity hashmap Object trackedEntities = null; - + try { trackedEntities = FieldUtils.readField(trackedEntitiesField, tracker, true); } catch (IllegalAccessException e) { throw new FieldAccessException("Cannot access 'trackedEntities' field due to security limitations.", e); } - - return WrappedIntHashMap.fromHandle(trackedEntities).get(entityID); + + Object trackerEntry = WrappedIntHashMap.fromHandle(trackedEntities).get(entityID); + Class entryClass = MinecraftReflection.getEntityTrackerClass(); + return entryClass.cast(trackerEntry); } - + /** * Retrieve entity from a ID, even it it's newly created. * @return The asssociated entity. 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 7d43945b..dd2baa96 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/BukkitConverters.java @@ -31,6 +31,7 @@ import java.util.Map.Entry; import java.util.Set; import org.bukkit.Material; +import org.bukkit.Sound; import org.bukkit.World; import org.bukkit.WorldType; import org.bukkit.entity.Entity; @@ -962,7 +963,44 @@ public class BukkitConverters { }; } - + + private static MethodAccessor soundGetter = null; + private static FieldAccessor soundKey = null; + + public static EquivalentConverter getSoundConverter() { + return new IgnoreNullConverter() { + + @Override + public Class getSpecificType() { + return Sound.class; + } + + @Override + protected Object getGenericValue(Class genericType, Sound specific) { + if (soundGetter == null) { + Class soundEffects = MinecraftReflection.getMinecraftClass("SoundEffects"); + FuzzyReflection fuzzy = FuzzyReflection.fromClass(soundEffects, true); + soundGetter = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("getSound", MinecraftReflection.getMinecraftClass("SoundEffect"), String.class)); + } + + MinecraftKey key = MinecraftKey.fromEnum(specific); + return soundGetter.invoke(null, key.getFullKey()); + } + + @Override + protected Sound getSpecificValue(Object generic) { + if (soundKey == null) { + Class soundEffect = generic.getClass(); + FuzzyReflection fuzzy = FuzzyReflection.fromClass(soundEffect, true); + soundKey = Accessors.getFieldAccessor(fuzzy.getFieldByType("key", MinecraftReflection.getMinecraftClass("MinecraftKey"))); + } + + MinecraftKey key = MinecraftKey.fromHandle(soundKey.get(generic)); + return Sound.valueOf(key.getEnumFormat()); + } + }; + } + /** * Wraps a given equivalent converter in NULL checks, ensuring that such values are ignored. * @param Type diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/MinecraftKey.java b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/MinecraftKey.java new file mode 100644 index 00000000..79823572 --- /dev/null +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/MinecraftKey.java @@ -0,0 +1,62 @@ +/** + * ProtocolLib - Bukkit server library that allows access to the Minecraft protocol. + * Copyright (C) 2016 dmulloy2 + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ +package com.comphenix.protocol.wrappers; + +import com.comphenix.protocol.reflect.StructureModifier; + +/** + * @author dmulloy2 + */ + +public class MinecraftKey { + private final String prefix; + private final String key; + + public MinecraftKey(String prefix, String key) { + this.prefix = prefix; + this.key = key; + } + + public MinecraftKey(String key) { + this("minecraft", key); + } + + public static MinecraftKey fromHandle(Object handle) { + StructureModifier modifier = new StructureModifier(handle.getClass()).withTarget(handle).withType(String.class); + return new MinecraftKey(modifier.read(0), modifier.read(1)); + } + + public static MinecraftKey fromEnum(Enum value) { + return new MinecraftKey(value.name().toLowerCase().replace("_", ".")); + } + + public String getPrefix() { + return prefix; + } + + public String getKey() { + return key; + } + + public String getFullKey() { + return prefix + ":" + key; + } + + public String getEnumFormat() { + return key.toUpperCase().replace(".", "_"); + } +} \ No newline at end of file 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 3e8d31aa..205b5145 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/wrappers/WrappedDataWatcher.java @@ -52,9 +52,9 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable entry : asMap().entrySet()) { + clone.setObject(entry.getKey(), entry.getValue()); + } + + return clone; } /** @@ -354,6 +362,10 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable HANDLE_TYPE = MinecraftReflection.getDataWatcherItemClass(); + private static ConstructorAccessor constructor; + private final StructureModifier modifier; /** @@ -35,12 +40,28 @@ public class WrappedWatchableObject extends AbstractWrapper { * @param handle Data watcher item */ public WrappedWatchableObject(Object handle) { - super(MinecraftReflection.getDataWatcherItemClass()); - + super(HANDLE_TYPE); setHandle(handle); this.modifier = new StructureModifier(handleType).withTarget(handle); } + /** + * Constructs a wrapped watchable object with a given watcher object and initial value. + * @param watcherObject Watcher object + * @param value Initial value + */ + public WrappedWatchableObject(WrappedDataWatcherObject watcherObject, Object value) { + this(newHandle(watcherObject, value)); + } + + private static Object newHandle(WrappedDataWatcherObject watcherObject, Object value) { + if (constructor == null) { + constructor = Accessors.getConstructorAccessor(HANDLE_TYPE.getConstructors()[0]); + } + + return constructor.invoke(watcherObject.getHandle(), value); + } + // ---- Getter methods /** diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java index 1e25c88b..c4b62a5d 100644 --- a/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java +++ b/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java @@ -20,9 +20,12 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import java.lang.reflect.Array; import java.lang.reflect.Field; import java.util.List; +import java.util.Objects; import java.util.UUID; import net.minecraft.server.v1_9_R1.AttributeModifier; @@ -47,10 +50,12 @@ import org.powermock.core.classloader.annotations.PowerMockIgnore; import com.comphenix.protocol.BukkitInitialization; import com.comphenix.protocol.PacketType; import com.comphenix.protocol.PacketType.Sender; +import com.comphenix.protocol.reflect.EquivalentConverter; import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.Util; import com.comphenix.protocol.wrappers.BlockPosition; +import com.comphenix.protocol.wrappers.BukkitConverters; import com.comphenix.protocol.wrappers.WrappedBlockData; import com.comphenix.protocol.wrappers.WrappedChatComponent; import com.comphenix.protocol.wrappers.WrappedDataWatcher; @@ -66,8 +71,8 @@ import com.google.common.collect.Lists; //@PrepareForTest(CraftItemFactory.class) public class PacketContainerTest { // Helper converters - // private EquivalentConverter watchConvert = BukkitConverters.getDataWatcherConverter(); - // private EquivalentConverter itemConvert = BukkitConverters.getItemStackConverter(); + private EquivalentConverter watchConvert = BukkitConverters.getDataWatcherConverter(); + private EquivalentConverter itemConvert = BukkitConverters.getItemStackConverter(); @BeforeClass public static void initializeBukkit() throws IllegalAccessException { @@ -458,14 +463,11 @@ public class PacketContainerTest { assertEquals(effect.hasParticles(), packet.getBytes().read(2) == (effect.hasParticles() ? 1 : 0)); }*/ - // This is usually the last one, since it requires all the API stuff to be worked out - - /*private static final List BLACKLISTED = Util.asList( - PacketType.Play.Client.CUSTOM_PAYLOAD, PacketType.Play.Server.CUSTOM_PAYLOAD, PacketType.Play.Server.MAP_CHUNK, - PacketType.Play.Server.UPDATE_ATTRIBUTES + private static final List BLACKLISTED = Util.asList( + PacketType.Play.Client.CUSTOM_PAYLOAD, PacketType.Play.Server.CUSTOM_PAYLOAD, + PacketType.Play.Server.SET_COOLDOWN, PacketType.Play.Server.NAMED_SOUND_EFFECT ); - @Test public void testDeepClone() { // Try constructing all the packets @@ -505,7 +507,7 @@ public class PacketContainerTest { testEquality(firstMod.read(i), secondMod.read(i)); } } - } catch (IllegalArgumentException e) { + } catch (IllegalArgumentException e) { if (!registered) { // Let the test pass System.err.println("The packet ID " + type + " is not registered."); @@ -518,7 +520,7 @@ public class PacketContainerTest { throw new RuntimeException("Failed to serialize packet " + type, e); } } - }*/ + } @Test public void testPacketType() { @@ -526,7 +528,7 @@ public class PacketContainerTest { } // Convert to objects that support equals() - /*private void testEquality(Object a, Object b) { + private void testEquality(Object a, Object b) { if (a != null && b != null) { if (MinecraftReflection.isDataWatcher(a)) { a = watchConvert.getSpecific(a); @@ -542,20 +544,20 @@ public class PacketContainerTest { return; } } else { - if (a.equals(b) || Objects.equal(a, b) || a.toString().equals(b.toString())) { + if (a.equals(b) || Objects.equals(a, b) || a.toString().equals(b.toString())) { return; } } assertEquals(a, b); - }*/ + } /** * Get the underlying array as an object array. * @param val - array wrapped as an Object. * @return An object array. */ - /*private Object[] getArray(Object val) { + private Object[] getArray(Object val) { if (val instanceof Object[]) return (Object[]) val; if (val == null) @@ -567,5 +569,5 @@ public class PacketContainerTest { for (int i = 0; i < arrlength; ++i) outputArray[i] = Array.get(val, i); return outputArray; - }*/ + } }