Add mechanism to clone NonNullLists

This commit is contained in:
Dan Mulloy 2016-11-22 17:36:49 -05:00
parent 684b687e42
commit 7b61796506
3 changed files with 70 additions and 0 deletions

View File

@ -16,10 +16,14 @@
*/ */
package com.comphenix.protocol.reflect.cloning; package com.comphenix.protocol.reflect.cloning;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import com.comphenix.protocol.reflect.EquivalentConverter; import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.BlockPosition; import com.comphenix.protocol.wrappers.BlockPosition;
import com.comphenix.protocol.wrappers.BukkitConverters; import com.comphenix.protocol.wrappers.BukkitConverters;
@ -67,6 +71,11 @@ public class BukkitCloner implements Cloner {
addClass(7, MinecraftReflection.getIBlockDataClass()); addClass(7, MinecraftReflection.getIBlockDataClass());
} catch (Throwable ex) { } catch (Throwable ex) {
} }
try {
addClass(8, MinecraftReflection.getNonNullListClass());
} catch (Throwable ex) {
}
} }
private void addClass(int id, Class<?> clazz) { private void addClass(int id, Class<?> clazz) {
@ -122,8 +131,43 @@ public class BukkitCloner implements Cloner {
case 7: case 7:
EquivalentConverter<WrappedBlockData> blockDataConverter = BukkitConverters.getWrappedBlockDataConverter(); EquivalentConverter<WrappedBlockData> blockDataConverter = BukkitConverters.getWrappedBlockDataConverter();
return blockDataConverter.getGeneric(clonableClasses.get(7), blockDataConverter.getSpecific(source).deepClone()); return blockDataConverter.getGeneric(clonableClasses.get(7), blockDataConverter.getSpecific(source).deepClone());
case 8:
return nonNullListCloner().clone(source);
default: default:
throw new IllegalArgumentException("Cannot clone objects of type " + source.getClass()); throw new IllegalArgumentException("Cannot clone objects of type " + source.getClass());
} }
} }
private static Constructor<?> nonNullList = null;
private static final Cloner nonNullListCloner() {
return new Cloner() {
@Override
public boolean canClone(Object source) {
return MinecraftReflection.is(MinecraftReflection.getNonNullListClass(), source);
}
@Override
public Object clone(Object source) {
StructureModifier<Object> modifier = new StructureModifier<>(source.getClass(), true).withTarget(source);
List<?> list = (List<?>) modifier.read(0);
Object empty = modifier.read(1);
if (nonNullList == null) {
try {
nonNullList = source.getClass().getDeclaredConstructor(List.class, Object.class);
nonNullList.setAccessible(true);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Could not find NonNullList constructor", ex);
}
}
try {
return nonNullList.newInstance(new ArrayList<>(list), empty);
} catch (ReflectiveOperationException ex) {
throw new RuntimeException("Could not create new NonNullList", ex);
}
}
};
}
} }

View File

@ -1836,6 +1836,10 @@ public class MinecraftReflection {
} }
} }
public static Class<?> getNonNullListClass() {
return getMinecraftClass("NonNullList");
}
/** /**
* Retrieve a CraftItemStack from a given ItemStack. * Retrieve a CraftItemStack from a given ItemStack.
* @param bukkitItemStack - the Bukkit ItemStack to convert. * @param bukkitItemStack - the Bukkit ItemStack to convert.

View File

@ -1,14 +1,20 @@
package com.comphenix.protocol.reflect.cloning; package com.comphenix.protocol.reflect.cloning;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertArrayEquals;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import net.minecraft.server.v1_11_R1.ItemStack;
import net.minecraft.server.v1_11_R1.NonNullList;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import com.comphenix.protocol.BukkitInitialization; import com.comphenix.protocol.BukkitInitialization;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
public class AggregateClonerTest { public class AggregateClonerTest {
@ -22,4 +28,20 @@ public class AggregateClonerTest {
List<Integer> input = Arrays.asList(1, 2, 3); List<Integer> input = Arrays.asList(1, 2, 3);
assertEquals(input, AggregateCloner.DEFAULT.clone(input)); assertEquals(input, AggregateCloner.DEFAULT.clone(input));
} }
@Test
public void testNonNullList() {
PacketContainer packet = new PacketContainer(PacketType.Play.Server.WINDOW_ITEMS);
NonNullList<ItemStack> list = NonNullList.a(16, ItemStack.a);
packet.getModifier().write(1, list);
PacketContainer cloned = packet.deepClone();
@SuppressWarnings("unchecked")
NonNullList<ItemStack> list1 = (NonNullList<ItemStack>) cloned.getModifier().read(1);
assertEquals(list.size(), list1.size());
assertArrayEquals(list.toArray(), list1.toArray());
}
} }