Fix block positions in watchers and issues with components

Fixes #170
This commit is contained in:
Dan Mulloy 2016-03-12 15:35:34 -05:00
parent 9b2c8826b1
commit 513a83c4b0
5 changed files with 133 additions and 23 deletions

View File

@ -1,5 +1,8 @@
package com.comphenix.protocol.wrappers;
import java.lang.reflect.Method;
import java.util.List;
import org.bukkit.ChatColor;
import com.comphenix.protocol.reflect.FuzzyReflection;
@ -27,8 +30,7 @@ public class WrappedChatComponent extends AbstractWrapper {
// Retrieve the correct methods
SERIALIZE_COMPONENT = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("serialize", /* a */
String.class, new Class<?>[] { COMPONENT }));
DESERIALIZE_COMPONENT = Accessors.getMethodAccessor(fuzzy.getMethodByParameters("deserialize", /* a */
COMPONENT, new Class<?>[] { String.class }));
DESERIALIZE_COMPONENT = findDeserialize(fuzzy);
// Get a component from a standard Minecraft message
CONSTRUCT_COMPONENT = Accessors.getMethodAccessor(MinecraftReflection.getCraftChatMessage(), "fromString", String.class);
@ -36,6 +38,23 @@ public class WrappedChatComponent extends AbstractWrapper {
// And the component text constructor
CONSTRUCT_TEXT_COMPONENT = Accessors.getConstructorAccessor(MinecraftReflection.getChatComponentTextClass(), String.class);
}
private static MethodAccessor findDeserialize(FuzzyReflection fuzzy) {
List<Method> methods = fuzzy.getMethodListByParameters(COMPONENT, new Class<?>[] { String.class });
if (methods.isEmpty()) {
throw new IllegalArgumentException("Unable to find deserialize method in " + fuzzy.getSource().getName());
}
// Try to find b, we want leniency
for (Method method : methods) {
if (method.getName().equals("b")) {
return Accessors.getMethodAccessor(method);
}
}
// Oh well
return Accessors.getMethodAccessor(methods.get(0));
}
private transient String cache;
@ -89,7 +108,7 @@ public class WrappedChatComponent extends AbstractWrapper {
}
return result;
}
/**
* Retrieve a copy of this component as a JSON string.
* <p>
@ -134,4 +153,9 @@ public class WrappedChatComponent extends AbstractWrapper {
public int hashCode() {
return handle.hashCode();
}
}
@Override
public String toString() {
return "WrappedChatComponent[json=" + getJson() + "]";
}
}

View File

@ -22,7 +22,6 @@ 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;
import java.util.List;
import java.util.Map;
@ -646,12 +645,28 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
/**
* Represents a DataWatcherRegistry containing the supported {@link Serializer}s in 1.9.
*
*
* <ul>
* <li>Byte</li>
* <li>Integer</li>
* <li>Float</li>
* <li>String</li>
* <li>IChatBaseComponent</li>
* <li>Optional&lt;ItemStack&gt;</li>
* <li>Optional&lt;IBlockData&gt;</li>
* <li>Boolean</li>
* <li>Vector3f</li>
* <li>BlockPosition</li>
* <li>Optional&lt;BlockPosition&gt;</li>
* <li>EnumDirection</li>
* <li>Optional&lt;UUID&gt;</li>
* </ul>
*
* @author dmulloy2
*/
public static class Registry {
private static boolean INITIALIZED = false;
private static Map<Class<?>, Serializer> REGISTRY = new HashMap<>();
private static List<Serializer> REGISTRY = new ArrayList<>();
/**
* Gets the serializer associated with a given class. </br>
@ -660,11 +675,53 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
* @param clazz Class to find serializer for
* @return The serializer, or null if none exists
*/
/**
* Gets the first serializer associated with a given class.
*
* <p><b>Note</b>: If {@link Serializer#isOptional() the serializer is optional},
* values <i>must</i> be wrapped in an {@link Optional}.</p>
*
* <p>If there are multiple serializers for a given class (i.e. BlockPosition),
* you should use {@link #get(Class, boolean)} for more precision.</p>
*
* @param clazz Class to find serializer for
* @return The serializer, or null if none exists
*/
public static Serializer get(Class<?> clazz) {
Validate.notNull("Class cannot be null!");
initialize();
return REGISTRY.get(clazz);
for (Serializer serializer : REGISTRY) {
if (serializer.getType().equals(clazz)) {
return serializer;
}
}
return null;
}
/**
* Gets the first serializer associated with a given class and optional state.
*
* <p><b>Note</b>: If the serializer is optional, values <i>must<i> be wrapped in an {@link Optional}
*
* @param clazz Class to find serializer for
* @param optional Optional state
* @return The serializer, or null if none exists
*/
public static Serializer get(Class<?> clazz, boolean optional) {
Validate.notNull(clazz, "Class cannot be null!");
initialize();
for (Serializer serializer : REGISTRY) {
if (serializer.getType().equals(clazz)
&& serializer.isOptional() == optional) {
return serializer;
}
}
return null;
}
/**
@ -676,7 +733,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
Validate.notNull("Handle cannot be null!");
initialize();
for (Serializer serializer : REGISTRY.values()) {
for (Serializer serializer : REGISTRY) {
if (serializer.getHandle().equals(handle)) {
return serializer;
}
@ -721,7 +778,7 @@ public class WrappedDataWatcher extends AbstractWrapper implements Iterable<Wrap
throw new IllegalStateException("Failed to read field " + candidate);
}
REGISTRY.put(innerClass, new Serializer(innerClass, serializer, optional));
REGISTRY.add(new Serializer(innerClass, serializer, optional));
}
}
}

View File

@ -32,6 +32,8 @@ import net.minecraft.server.v1_9_R1.AttributeModifier;
import net.minecraft.server.v1_9_R1.DataWatcher;
import net.minecraft.server.v1_9_R1.Entity;
import net.minecraft.server.v1_9_R1.EntityLightning;
import net.minecraft.server.v1_9_R1.MobEffect;
import net.minecraft.server.v1_9_R1.MobEffectList;
import net.minecraft.server.v1_9_R1.PacketPlayOutUpdateAttributes;
import net.minecraft.server.v1_9_R1.PacketPlayOutUpdateAttributes.AttributeSnapshot;
@ -43,6 +45,8 @@ import org.bukkit.Material;
import org.bukkit.WorldType;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -51,6 +55,7 @@ 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.injector.PacketConstructor;
import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection;
@ -179,12 +184,10 @@ public class PacketContainerTest {
WrappedChatComponent.fromChatMessage("hello world"));
}
// TODO Find a packet with integer arrays
/*@Test
@Test
public void testGetIntegerArrays() {
// Contains a byte array we will test
PacketContainer mapChunkBulk = new PacketContainer(PacketType.Play.Server.MAP_CHUNK_BULK);
PacketContainer mapChunkBulk = new PacketContainer(PacketType.Play.Server.WORLD_PARTICLES);
StructureModifier<int[]> integers = mapChunkBulk.getIntegerArrays();
int[] testArray = new int[] { 1, 2, 3 };
@ -195,7 +198,7 @@ public class PacketContainerTest {
integers.write(0, testArray);
assertArrayEquals(testArray, integers.read(0));
}*/
}
@Test
public void testGetItemModifier() {
@ -447,24 +450,30 @@ public class PacketContainerTest {
assertEquals(material, read.getType());
}
/*@Test
@Test
@SuppressWarnings("deprecation")
public void testPotionEffect() {
PotionEffect effect = new PotionEffect(PotionEffectType.FIRE_RESISTANCE, 20 * 60, 1);
MobEffect mobEffect = new MobEffect(effect.getType().getId(), effect.getDuration(), effect.getAmplifier(), effect.isAmbient(),
MobEffect mobEffect = new MobEffect(MobEffectList.fromId(effect.getType().getId()), effect.getDuration(), effect.getAmplifier(), effect.isAmbient(),
effect.hasParticles());
int entityId = 42;
// The constructor we want to call
PacketConstructor creator = PacketConstructor.DEFAULT.withPacket(
PacketType.Play.Server.ENTITY_EFFECT, new Class<?>[] { int.class, MobEffect.class });
PacketContainer packet = creator.createPacket(1, mobEffect);
PacketContainer packet = creator.createPacket(entityId, mobEffect);
assertEquals(1, (int) packet.getIntegers().read(0));
assertEquals(entityId, (int) packet.getIntegers().read(0));
assertEquals(effect.getType().getId(), (byte) packet.getBytes().read(0));
assertEquals(effect.getAmplifier(), (byte) packet.getBytes().read(1));
assertEquals(effect.getDuration(), (int) packet.getIntegers().read(1));
assertEquals(effect.hasParticles(), packet.getBytes().read(2) == (effect.hasParticles() ? 1 : 0));
}*/
int e = 0;
if (effect.isAmbient()) e |= 1;
if (effect.hasParticles()) e |= 2;
assertEquals(e, (byte) packet.getBytes().read(2));
}
private static final List<PacketType> BLACKLISTED = Util.asList(
PacketType.Play.Client.CUSTOM_PAYLOAD, PacketType.Play.Server.CUSTOM_PAYLOAD,

View File

@ -1,5 +1,6 @@
package com.comphenix.protocol.wrappers;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.BeforeClass;
@ -18,7 +19,9 @@ public class WrappedChatComponentTest {
public void testText() {
WrappedChatComponent test = WrappedChatComponent.fromText("Hello.");
String json = test.getJson();
assertNotNull(json);
WrappedChatComponent clone = WrappedChatComponent.fromJson(json);
assertEquals(json, clone.getJson());
}
}

View File

@ -4,10 +4,17 @@
package com.comphenix.protocol.wrappers;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.UUID;
import net.minecraft.server.v1_9_R1.DataWatcher;
import net.minecraft.server.v1_9_R1.Entity;
import net.minecraft.server.v1_9_R1.EntityLightning;
import net.minecraft.server.v1_9_R1.ItemStack;
import org.junit.BeforeClass;
import org.junit.Test;
@ -71,4 +78,14 @@ public class WrappedDataWatcherTest {
DataWatcher handle = entity.getDataWatcher();
return new WrappedDataWatcher(handle);
}
@Test
public void testSerializers() {
Serializer blockPos = Registry.get(net.minecraft.server.v1_9_R1.BlockPosition.class, false);
Serializer optionalBlockPos = Registry.get(net.minecraft.server.v1_9_R1.BlockPosition.class, true);
assertNotSame(blockPos, optionalBlockPos);
assertNull(Registry.get(ItemStack.class, false));
assertNotNull(Registry.get(UUID.class, true));
}
}