#1295: Define native persistent data types for lists
By: Bjarne Koll <lynxplay101@gmail.com>
This commit is contained in:
@@ -5,8 +5,12 @@ import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.lang.reflect.Array;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.stream.Stream;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
@@ -15,12 +19,18 @@ import org.bukkit.configuration.InvalidConfigurationException;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.persistence.ListPersistentDataType;
|
||||
import org.bukkit.persistence.PersistentDataAdapterContext;
|
||||
import org.bukkit.persistence.PersistentDataContainer;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
import org.bukkit.support.AbstractTestingBase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
public class PersistentDataContainerTest extends AbstractTestingBase {
|
||||
|
||||
@@ -31,18 +41,14 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
|
||||
VALID_KEY = new NamespacedKey("test", "validkey");
|
||||
}
|
||||
|
||||
/*
|
||||
Sets a test
|
||||
*/
|
||||
// Sets a test
|
||||
@Test
|
||||
public void testSetNoAdapter() {
|
||||
ItemMeta itemMeta = createNewItemMeta();
|
||||
assertThrows(IllegalArgumentException.class, () -> itemMeta.getPersistentDataContainer().set(VALID_KEY, new PrimitiveTagType<>(boolean.class), true));
|
||||
}
|
||||
|
||||
/*
|
||||
Contains a tag
|
||||
*/
|
||||
// Contains a tag
|
||||
@Test
|
||||
public void testHasNoAdapter() {
|
||||
ItemMeta itemMeta = createNewItemMeta();
|
||||
@@ -57,9 +63,7 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
|
||||
assertTrue(itemMeta.getPersistentDataContainer().has(VALID_KEY));
|
||||
}
|
||||
|
||||
/*
|
||||
Getting a tag
|
||||
*/
|
||||
// Getting a tag
|
||||
@Test
|
||||
public void testGetNoAdapter() {
|
||||
ItemMeta itemMeta = createNewItemMeta();
|
||||
@@ -87,11 +91,11 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
|
||||
assertEquals(160L, (long) meta.getPersistentDataContainer().get(namespacedKeyB, PersistentDataType.LONG));
|
||||
}
|
||||
|
||||
private ItemMeta createNewItemMeta() {
|
||||
private static ItemMeta createNewItemMeta() {
|
||||
return Bukkit.getItemFactory().getItemMeta(Material.DIAMOND_PICKAXE);
|
||||
}
|
||||
|
||||
private NamespacedKey requestKey(String keyName) {
|
||||
private static NamespacedKey requestKey(String keyName) {
|
||||
return new NamespacedKey("test-plugin", keyName.toLowerCase());
|
||||
}
|
||||
|
||||
@@ -117,9 +121,7 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
|
||||
assertEquals(container, target);
|
||||
}
|
||||
|
||||
/*
|
||||
Removing a tag
|
||||
*/
|
||||
// Removing a tag
|
||||
@Test
|
||||
public void testNBTTagStoring() {
|
||||
CraftMetaItem itemMeta = createComplexItemMeta();
|
||||
@@ -188,6 +190,12 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
|
||||
itemMeta.getPersistentDataContainer().set(requestKey("custom-string"), PersistentDataType.STRING, "Hello there world");
|
||||
itemMeta.getPersistentDataContainer().set(requestKey("custom-int"), PersistentDataType.INTEGER, 3);
|
||||
itemMeta.getPersistentDataContainer().set(requestKey("custom-double"), PersistentDataType.DOUBLE, 3.123);
|
||||
itemMeta.getPersistentDataContainer().set(
|
||||
requestKey("custom-list-string"), PersistentDataType.LIST.strings(), List.of("first[]", "second{}", "third()")
|
||||
);
|
||||
itemMeta.getPersistentDataContainer().set(
|
||||
requestKey("custom-list-bytes"), PersistentDataType.LIST.bytes(), List.of((byte) 1, (byte) 2, (byte) 3)
|
||||
);
|
||||
|
||||
PersistentDataContainer innerContainer = itemMeta.getPersistentDataContainer().getAdapterContext().newPersistentDataContainer(); //Add a inner container
|
||||
innerContainer.set(VALID_KEY, PersistentDataType.LONG, 5L);
|
||||
@@ -195,9 +203,7 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
|
||||
return itemMeta;
|
||||
}
|
||||
|
||||
/*
|
||||
Test edge cases with strings
|
||||
*/
|
||||
// Test edge cases with strings
|
||||
@Test
|
||||
public void testStringEdgeCases() throws IOException, InvalidConfigurationException {
|
||||
final ItemStack stack = new ItemStack(Material.DIAMOND);
|
||||
@@ -238,9 +244,7 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
|
||||
assertEquals(jsonLookalike, loadedPdc.get(requestKey("string_json_lookalike"), PersistentDataType.STRING));
|
||||
}
|
||||
|
||||
/*
|
||||
Test complex object storage
|
||||
*/
|
||||
// Test complex object storage
|
||||
@Test
|
||||
public void storeUUIDOnItemTest() {
|
||||
ItemMeta itemMeta = createNewItemMeta();
|
||||
@@ -285,25 +289,29 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
|
||||
class UUIDPersistentDataType implements PersistentDataType<byte[], UUID> {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Class<byte[]> getPrimitiveType() {
|
||||
return byte[].class;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Class<UUID> getComplexType() {
|
||||
return UUID.class;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public byte[] toPrimitive(UUID complex, PersistentDataAdapterContext context) {
|
||||
public byte[] toPrimitive(@NotNull UUID complex, @NotNull PersistentDataAdapterContext context) {
|
||||
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
|
||||
bb.putLong(complex.getMostSignificantBits());
|
||||
bb.putLong(complex.getLeastSignificantBits());
|
||||
return bb.array();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public UUID fromPrimitive(byte[] primitive, PersistentDataAdapterContext context) {
|
||||
public UUID fromPrimitive(@NotNull byte[] primitive, @NotNull PersistentDataAdapterContext context) {
|
||||
ByteBuffer bb = ByteBuffer.wrap(primitive);
|
||||
long firstLong = bb.getLong();
|
||||
long secondLong = bb.getLong();
|
||||
@@ -315,22 +323,22 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
|
||||
public void testPrimitiveCustomTags() {
|
||||
ItemMeta itemMeta = createNewItemMeta();
|
||||
|
||||
testPrimitiveCustomTag(itemMeta, PersistentDataType.BYTE, (byte) 1);
|
||||
testPrimitiveCustomTag(itemMeta, PersistentDataType.SHORT, (short) 1);
|
||||
testPrimitiveCustomTag(itemMeta, PersistentDataType.INTEGER, 1);
|
||||
testPrimitiveCustomTag(itemMeta, PersistentDataType.LONG, 1L);
|
||||
testPrimitiveCustomTag(itemMeta, PersistentDataType.FLOAT, 1.34F);
|
||||
testPrimitiveCustomTag(itemMeta, PersistentDataType.DOUBLE, 151.123);
|
||||
this.testPrimitiveCustomTag(itemMeta, PersistentDataType.BYTE, (byte) 1);
|
||||
this.testPrimitiveCustomTag(itemMeta, PersistentDataType.SHORT, (short) 1);
|
||||
this.testPrimitiveCustomTag(itemMeta, PersistentDataType.INTEGER, 1);
|
||||
this.testPrimitiveCustomTag(itemMeta, PersistentDataType.LONG, 1L);
|
||||
this.testPrimitiveCustomTag(itemMeta, PersistentDataType.FLOAT, 1.34F);
|
||||
this.testPrimitiveCustomTag(itemMeta, PersistentDataType.DOUBLE, 151.123);
|
||||
|
||||
testPrimitiveCustomTag(itemMeta, PersistentDataType.STRING, "test");
|
||||
this.testPrimitiveCustomTag(itemMeta, PersistentDataType.STRING, "test");
|
||||
|
||||
testPrimitiveCustomTag(itemMeta, PersistentDataType.BYTE_ARRAY, new byte[]{
|
||||
this.testPrimitiveCustomTag(itemMeta, PersistentDataType.BYTE_ARRAY, new byte[]{
|
||||
1, 4, 2, Byte.MAX_VALUE
|
||||
});
|
||||
testPrimitiveCustomTag(itemMeta, PersistentDataType.INTEGER_ARRAY, new int[]{
|
||||
this.testPrimitiveCustomTag(itemMeta, PersistentDataType.INTEGER_ARRAY, new int[]{
|
||||
1, 4, 2, Integer.MAX_VALUE
|
||||
});
|
||||
testPrimitiveCustomTag(itemMeta, PersistentDataType.LONG_ARRAY, new long[]{
|
||||
this.testPrimitiveCustomTag(itemMeta, PersistentDataType.LONG_ARRAY, new long[]{
|
||||
1L, 4L, 2L, Long.MAX_VALUE
|
||||
});
|
||||
}
|
||||
@@ -356,31 +364,35 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
|
||||
assertFalse(meta.getPersistentDataContainer().has(tagKey, type));
|
||||
}
|
||||
|
||||
class PrimitiveTagType<T> implements PersistentDataType<T, T> {
|
||||
class PrimitiveTagType<P> implements PersistentDataType<P, P> {
|
||||
|
||||
private final Class<T> primitiveType;
|
||||
private final Class<P> primitiveType;
|
||||
|
||||
PrimitiveTagType(Class<T> primitiveType) {
|
||||
PrimitiveTagType(Class<P> primitiveType) {
|
||||
this.primitiveType = primitiveType;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Class<T> getPrimitiveType() {
|
||||
return primitiveType;
|
||||
public Class<P> getPrimitiveType() {
|
||||
return this.primitiveType;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Class<T> getComplexType() {
|
||||
return primitiveType;
|
||||
public Class<P> getComplexType() {
|
||||
return this.primitiveType;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public T toPrimitive(T complex, PersistentDataAdapterContext context) {
|
||||
public P toPrimitive(@NotNull P complex, @NotNull PersistentDataAdapterContext context) {
|
||||
return complex;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public T fromPrimitive(T primitive, PersistentDataAdapterContext context) {
|
||||
public P fromPrimitive(@NotNull P primitive, @NotNull PersistentDataAdapterContext context) {
|
||||
return primitive;
|
||||
}
|
||||
}
|
||||
@@ -397,7 +409,105 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
|
||||
assertNotSame(container, clonedContainer);
|
||||
assertEquals(container, clonedContainer);
|
||||
|
||||
clonedContainer.set(VALID_KEY, PersistentDataType.STRING, "dinnerbone");
|
||||
clonedContainer.set(PersistentDataContainerTest.VALID_KEY, PersistentDataType.STRING, "dinnerbone");
|
||||
assertNotEquals(container, clonedContainer);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("testListTypeArgumentSource")
|
||||
public <T> void testListType(@NotNull final ListPersistentDataType<T, T> type, @NotNull final List<T> list, @NotNull final BiConsumer<T, T> equalsCheck) {
|
||||
final ItemMeta meta = createNewItemMeta();
|
||||
final PersistentDataContainer container = meta.getPersistentDataContainer();
|
||||
|
||||
container.set(requestKey("list"), type, list);
|
||||
|
||||
final List<T> returnedList = container.get(requestKey("list"), type);
|
||||
|
||||
assertNotNull(returnedList);
|
||||
assertEquals(list.size(), returnedList.size());
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
final T expectedValue = list.get(i);
|
||||
final T foundValue = returnedList.get(i);
|
||||
equalsCheck.accept(expectedValue, foundValue);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Stream<Arguments> testListTypeArgumentSource() {
|
||||
final PersistentDataContainer first = createNewItemMeta().getPersistentDataContainer();
|
||||
final PersistentDataContainer second = first.getAdapterContext().newPersistentDataContainer();
|
||||
first.set(requestKey("a"), PersistentDataType.STRING, "hello world");
|
||||
second.set(requestKey("b"), PersistentDataType.BOOLEAN, true);
|
||||
|
||||
final BiConsumer<Object, Object> objectAssertion = Assertions::assertEquals;
|
||||
final BiConsumer<byte[], byte[]> byteArrayAssertion = Assertions::assertArrayEquals;
|
||||
final BiConsumer<int[], int[]> intArrayAssertion = Assertions::assertArrayEquals;
|
||||
final BiConsumer<long[], long[]> longArrayAssertion = Assertions::assertArrayEquals;
|
||||
|
||||
return Stream.of(
|
||||
Arguments.of(PersistentDataType.LIST.bytes(), List.of((byte) 1, (byte) 2, (byte) 3), objectAssertion),
|
||||
Arguments.of(PersistentDataType.LIST.shorts(), List.of((short) 1, (short) 2, (short) 3), objectAssertion),
|
||||
Arguments.of(PersistentDataType.LIST.integers(), List.of(1, 2, 3), objectAssertion),
|
||||
Arguments.of(PersistentDataType.LIST.longs(), List.of(1L, 2L, 3L), objectAssertion),
|
||||
Arguments.of(PersistentDataType.LIST.floats(), List.of(1F, 2F, 3F), objectAssertion),
|
||||
Arguments.of(PersistentDataType.LIST.doubles(), List.of(1D, 2D, 3D), objectAssertion),
|
||||
Arguments.of(PersistentDataType.LIST.booleans(), List.of(true, true, false), objectAssertion),
|
||||
Arguments.of(PersistentDataType.LIST.strings(), List.of("a", "b", "c"), objectAssertion),
|
||||
Arguments.of(PersistentDataType.LIST.byteArrays(), List.of(new byte[]{1, 2, 3}, new byte[]{4, 5, 6}), byteArrayAssertion),
|
||||
Arguments.of(PersistentDataType.LIST.integerArrays(), List.of(new int[]{1, 2, 3}, new int[]{4, 5, 6}), intArrayAssertion),
|
||||
Arguments.of(PersistentDataType.LIST.longArrays(), List.of(new long[]{1, 2, 3}, new long[]{4, 5, 6}), longArrayAssertion),
|
||||
Arguments.of(PersistentDataType.LIST.dataContainers(), List.of(first, second), objectAssertion));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyListDataMaintainType() {
|
||||
final ItemMeta meta = createNewItemMeta();
|
||||
final PersistentDataContainer container = meta.getPersistentDataContainer();
|
||||
|
||||
container.set(requestKey("list"), PersistentDataType.LIST.strings(), List.of());
|
||||
|
||||
assertTrue(container.has(requestKey("list"), PersistentDataType.LIST.strings()));
|
||||
assertFalse(container.has(requestKey("list"), PersistentDataType.LIST.bytes()));
|
||||
}
|
||||
|
||||
// This is a horrific marriage of tag container array "primitive" types the API offered and the new list types.
|
||||
// We are essentially testing if these two play nice as tag container array was an emulated primitive type
|
||||
// that used lists under the hood, hence this is testing the extra handling of TAG_CONTAINER_ARRAY in combination
|
||||
// with lists. Plain lists in lists are tested above.
|
||||
//
|
||||
// Little faith is to be had when it comes to abominations constructed by plugin developers, this test ensures
|
||||
// even this disgrace of a combination functions in PDCs.
|
||||
@Test
|
||||
public void testListOfListViaContainerArray() {
|
||||
final ListPersistentDataType<PersistentDataContainer[], PersistentDataContainer[]> listPersistentDataType = PersistentDataType.LIST.listTypeFrom(PersistentDataType.TAG_CONTAINER_ARRAY);
|
||||
|
||||
final ItemMeta meta = createNewItemMeta();
|
||||
final PersistentDataContainer container = meta.getPersistentDataContainer();
|
||||
final PersistentDataAdapterContext adapterContext = container.getAdapterContext();
|
||||
|
||||
final PersistentDataContainer first = adapterContext.newPersistentDataContainer();
|
||||
first.set(requestKey("a"), PersistentDataType.STRING, "hi");
|
||||
|
||||
final PersistentDataContainer second = adapterContext.newPersistentDataContainer();
|
||||
second.set(requestKey("a"), PersistentDataType.INTEGER, 2);
|
||||
|
||||
final List<PersistentDataContainer[]> listOfArrays = new ArrayList<>();
|
||||
listOfArrays.add(new PersistentDataContainer[]{first, second});
|
||||
|
||||
container.set(requestKey("containerListList"), listPersistentDataType, listOfArrays);
|
||||
|
||||
assertTrue(container.has(requestKey("containerListList"), listPersistentDataType));
|
||||
|
||||
final List<PersistentDataContainer[]> containerListList = container.get(requestKey("containerListList"), listPersistentDataType);
|
||||
|
||||
assertNotNull(containerListList);
|
||||
assertEquals(1, containerListList.size());
|
||||
|
||||
final PersistentDataContainer[] arrayOfPDC = containerListList.get(0);
|
||||
assertEquals(2, arrayOfPDC.length);
|
||||
|
||||
assertEquals("hi", arrayOfPDC[0].get(requestKey("a"), PersistentDataType.STRING));
|
||||
assertEquals(2, arrayOfPDC[1].get(requestKey("a"), PersistentDataType.INTEGER));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user