Skip to content

Commit 7b61796

Browse files
committed
Add mechanism to clone NonNullLists
1 parent 684b687 commit 7b61796

File tree

3 files changed

+70
-0
lines changed

3 files changed

+70
-0
lines changed

modules/API/src/main/java/com/comphenix/protocol/reflect/cloning/BukkitCloner.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,14 @@
1616
*/
1717
package com.comphenix.protocol.reflect.cloning;
1818

19+
import java.lang.reflect.Constructor;
20+
import java.util.ArrayList;
21+
import java.util.List;
1922
import java.util.Map;
2023
import java.util.Map.Entry;
2124

2225
import com.comphenix.protocol.reflect.EquivalentConverter;
26+
import com.comphenix.protocol.reflect.StructureModifier;
2327
import com.comphenix.protocol.utility.MinecraftReflection;
2428
import com.comphenix.protocol.wrappers.BlockPosition;
2529
import com.comphenix.protocol.wrappers.BukkitConverters;
@@ -67,6 +71,11 @@ public BukkitCloner() {
6771
addClass(7, MinecraftReflection.getIBlockDataClass());
6872
} catch (Throwable ex) {
6973
}
74+
75+
try {
76+
addClass(8, MinecraftReflection.getNonNullListClass());
77+
} catch (Throwable ex) {
78+
}
7079
}
7180

7281
private void addClass(int id, Class<?> clazz) {
@@ -122,8 +131,43 @@ public Object clone(Object source) {
122131
case 7:
123132
EquivalentConverter<WrappedBlockData> blockDataConverter = BukkitConverters.getWrappedBlockDataConverter();
124133
return blockDataConverter.getGeneric(clonableClasses.get(7), blockDataConverter.getSpecific(source).deepClone());
134+
case 8:
135+
return nonNullListCloner().clone(source);
125136
default:
126137
throw new IllegalArgumentException("Cannot clone objects of type " + source.getClass());
127138
}
128139
}
140+
141+
private static Constructor<?> nonNullList = null;
142+
143+
private static final Cloner nonNullListCloner() {
144+
return new Cloner() {
145+
@Override
146+
public boolean canClone(Object source) {
147+
return MinecraftReflection.is(MinecraftReflection.getNonNullListClass(), source);
148+
}
149+
150+
@Override
151+
public Object clone(Object source) {
152+
StructureModifier<Object> modifier = new StructureModifier<>(source.getClass(), true).withTarget(source);
153+
List<?> list = (List<?>) modifier.read(0);
154+
Object empty = modifier.read(1);
155+
156+
if (nonNullList == null) {
157+
try {
158+
nonNullList = source.getClass().getDeclaredConstructor(List.class, Object.class);
159+
nonNullList.setAccessible(true);
160+
} catch (ReflectiveOperationException ex) {
161+
throw new RuntimeException("Could not find NonNullList constructor", ex);
162+
}
163+
}
164+
165+
try {
166+
return nonNullList.newInstance(new ArrayList<>(list), empty);
167+
} catch (ReflectiveOperationException ex) {
168+
throw new RuntimeException("Could not create new NonNullList", ex);
169+
}
170+
}
171+
};
172+
}
129173
}

modules/API/src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1836,6 +1836,10 @@ public static boolean signUpdateExists() {
18361836
}
18371837
}
18381838

1839+
public static Class<?> getNonNullListClass() {
1840+
return getMinecraftClass("NonNullList");
1841+
}
1842+
18391843
/**
18401844
* Retrieve a CraftItemStack from a given ItemStack.
18411845
* @param bukkitItemStack - the Bukkit ItemStack to convert.

modules/ProtocolLib/src/test/java/com/comphenix/protocol/reflect/cloning/AggregateClonerTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
package com.comphenix.protocol.reflect.cloning;
22

33
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.assertArrayEquals;
45

56
import java.util.Arrays;
67
import java.util.List;
78

9+
import net.minecraft.server.v1_11_R1.ItemStack;
10+
import net.minecraft.server.v1_11_R1.NonNullList;
11+
812
import org.junit.BeforeClass;
913
import org.junit.Test;
1014

1115
import com.comphenix.protocol.BukkitInitialization;
16+
import com.comphenix.protocol.PacketType;
17+
import com.comphenix.protocol.events.PacketContainer;
1218

1319
public class AggregateClonerTest {
1420

@@ -22,4 +28,20 @@ public void testArrays() {
2228
List<Integer> input = Arrays.asList(1, 2, 3);
2329
assertEquals(input, AggregateCloner.DEFAULT.clone(input));
2430
}
31+
32+
@Test
33+
public void testNonNullList() {
34+
PacketContainer packet = new PacketContainer(PacketType.Play.Server.WINDOW_ITEMS);
35+
36+
NonNullList<ItemStack> list = NonNullList.a(16, ItemStack.a);
37+
packet.getModifier().write(1, list);
38+
39+
PacketContainer cloned = packet.deepClone();
40+
41+
@SuppressWarnings("unchecked")
42+
NonNullList<ItemStack> list1 = (NonNullList<ItemStack>) cloned.getModifier().read(1);
43+
44+
assertEquals(list.size(), list1.size());
45+
assertArrayEquals(list.toArray(), list1.toArray());
46+
}
2547
}

0 commit comments

Comments
 (0)