mirror of
https://github.com/PaperMC/Paper.git
synced 2025-01-19 23:01:29 +01:00
#1295: Define native persistent data types for lists
By: Bjarne Koll <lynxplay101@gmail.com>
This commit is contained in:
parent
71ca5a7bdf
commit
8cd8851498
11
paper-server/nms-patches/net/minecraft/nbt/NBTTagList.patch
Normal file
11
paper-server/nms-patches/net/minecraft/nbt/NBTTagList.patch
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
--- a/net/minecraft/nbt/NBTTagList.java
|
||||||
|
+++ b/net/minecraft/nbt/NBTTagList.java
|
||||||
|
@@ -145,7 +145,7 @@
|
||||||
|
private final List<NBTBase> list;
|
||||||
|
private byte type;
|
||||||
|
|
||||||
|
- NBTTagList(List<NBTBase> list, byte b0) {
|
||||||
|
+ public NBTTagList(List<NBTBase> list, byte b0) { // PAIL: package-private -> public
|
||||||
|
this.list = list;
|
||||||
|
this.type = b0;
|
||||||
|
}
|
@ -7,27 +7,31 @@ import org.bukkit.inventory.meta.tags.ItemTagType;
|
|||||||
import org.bukkit.persistence.PersistentDataAdapterContext;
|
import org.bukkit.persistence.PersistentDataAdapterContext;
|
||||||
import org.bukkit.persistence.PersistentDataContainer;
|
import org.bukkit.persistence.PersistentDataContainer;
|
||||||
import org.bukkit.persistence.PersistentDataType;
|
import org.bukkit.persistence.PersistentDataType;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public final class DeprecatedContainerTagType<Z> implements PersistentDataType<PersistentDataContainer, Z> {
|
public final class DeprecatedContainerTagType<C> implements PersistentDataType<PersistentDataContainer, C> {
|
||||||
|
|
||||||
private final ItemTagType<CustomItemTagContainer, Z> deprecated;
|
private final ItemTagType<CustomItemTagContainer, C> deprecated;
|
||||||
|
|
||||||
DeprecatedContainerTagType(ItemTagType<CustomItemTagContainer, Z> deprecated) {
|
DeprecatedContainerTagType(ItemTagType<CustomItemTagContainer, C> deprecated) {
|
||||||
this.deprecated = deprecated;
|
this.deprecated = deprecated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Class<PersistentDataContainer> getPrimitiveType() {
|
public Class<PersistentDataContainer> getPrimitiveType() {
|
||||||
return PersistentDataContainer.class;
|
return PersistentDataContainer.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Class<Z> getComplexType() {
|
public Class<C> getComplexType() {
|
||||||
return deprecated.getComplexType();
|
return deprecated.getComplexType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public PersistentDataContainer toPrimitive(Z complex, PersistentDataAdapterContext context) {
|
public PersistentDataContainer toPrimitive(@NotNull C complex, @NotNull PersistentDataAdapterContext context) {
|
||||||
CustomItemTagContainer deprecated = this.deprecated.toPrimitive(complex, new DeprecatedItemAdapterContext(context));
|
CustomItemTagContainer deprecated = this.deprecated.toPrimitive(complex, new DeprecatedItemAdapterContext(context));
|
||||||
Preconditions.checkArgument(deprecated instanceof DeprecatedCustomTagContainer, "Could not wrap deprecated API due to foreign CustomItemTagContainer implementation %s", deprecated.getClass().getSimpleName());
|
Preconditions.checkArgument(deprecated instanceof DeprecatedCustomTagContainer, "Could not wrap deprecated API due to foreign CustomItemTagContainer implementation %s", deprecated.getClass().getSimpleName());
|
||||||
|
|
||||||
@ -39,8 +43,9 @@ public final class DeprecatedContainerTagType<Z> implements PersistentDataType<P
|
|||||||
return new CraftPersistentDataContainer(craftTagContainer.getRaw(), craftTagContainer.getDataTagTypeRegistry());
|
return new CraftPersistentDataContainer(craftTagContainer.getRaw(), craftTagContainer.getDataTagTypeRegistry());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Z fromPrimitive(PersistentDataContainer primitive, PersistentDataAdapterContext context) {
|
public C fromPrimitive(@NotNull PersistentDataContainer primitive, @NotNull PersistentDataAdapterContext context) {
|
||||||
Preconditions.checkArgument(primitive instanceof CraftPersistentDataContainer, "Could not wrap deprecated API due to foreign PersistentMetadataContainer implementation %s", primitive.getClass().getSimpleName());
|
Preconditions.checkArgument(primitive instanceof CraftPersistentDataContainer, "Could not wrap deprecated API due to foreign PersistentMetadataContainer implementation %s", primitive.getClass().getSimpleName());
|
||||||
|
|
||||||
return this.deprecated.fromPrimitive(new DeprecatedCustomTagContainer(primitive), new DeprecatedItemAdapterContext(context));
|
return this.deprecated.fromPrimitive(new DeprecatedCustomTagContainer(primitive), new DeprecatedItemAdapterContext(context));
|
||||||
|
@ -3,32 +3,37 @@ package org.bukkit.craftbukkit.inventory.tags;
|
|||||||
import org.bukkit.inventory.meta.tags.ItemTagType;
|
import org.bukkit.inventory.meta.tags.ItemTagType;
|
||||||
import org.bukkit.persistence.PersistentDataAdapterContext;
|
import org.bukkit.persistence.PersistentDataAdapterContext;
|
||||||
import org.bukkit.persistence.PersistentDataType;
|
import org.bukkit.persistence.PersistentDataType;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public final class DeprecatedItemTagType<T, Z> implements PersistentDataType<T, Z> {
|
public final class DeprecatedItemTagType<P, C> implements PersistentDataType<P, C> {
|
||||||
|
|
||||||
private final ItemTagType<T, Z> deprecated;
|
private final ItemTagType<P, C> deprecated;
|
||||||
|
|
||||||
public DeprecatedItemTagType(ItemTagType<T, Z> deprecated) {
|
public DeprecatedItemTagType(ItemTagType<P, C> deprecated) {
|
||||||
this.deprecated = deprecated;
|
this.deprecated = deprecated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Class<T> getPrimitiveType() {
|
public Class<P> getPrimitiveType() {
|
||||||
return deprecated.getPrimitiveType();
|
return deprecated.getPrimitiveType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Class<Z> getComplexType() {
|
public Class<C> getComplexType() {
|
||||||
return deprecated.getComplexType();
|
return deprecated.getComplexType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public T toPrimitive(Z complex, PersistentDataAdapterContext context) {
|
public P toPrimitive(@NotNull C complex, @NotNull PersistentDataAdapterContext context) {
|
||||||
return this.deprecated.toPrimitive(complex, new DeprecatedItemAdapterContext(context));
|
return this.deprecated.toPrimitive(complex, new DeprecatedItemAdapterContext(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Z fromPrimitive(T primitive, PersistentDataAdapterContext context) {
|
public C fromPrimitive(@NotNull P primitive, @NotNull PersistentDataAdapterContext context) {
|
||||||
return this.deprecated.fromPrimitive(primitive, new DeprecatedItemAdapterContext(context));
|
return this.deprecated.fromPrimitive(primitive, new DeprecatedItemAdapterContext(context));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import org.bukkit.craftbukkit.util.CraftNBTTagConfigSerializer;
|
|||||||
import org.bukkit.persistence.PersistentDataAdapterContext;
|
import org.bukkit.persistence.PersistentDataAdapterContext;
|
||||||
import org.bukkit.persistence.PersistentDataContainer;
|
import org.bukkit.persistence.PersistentDataContainer;
|
||||||
import org.bukkit.persistence.PersistentDataType;
|
import org.bukkit.persistence.PersistentDataType;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public class CraftPersistentDataContainer implements PersistentDataContainer {
|
public class CraftPersistentDataContainer implements PersistentDataContainer {
|
||||||
|
|
||||||
@ -33,16 +34,16 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T, Z> void set(NamespacedKey key, PersistentDataType<T, Z> type, Z value) {
|
public <T, Z> void set(@NotNull NamespacedKey key, @NotNull PersistentDataType<T, Z> type, @NotNull Z value) {
|
||||||
Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null");
|
Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null");
|
||||||
Preconditions.checkArgument(type != null, "The provided type cannot be null");
|
Preconditions.checkArgument(type != null, "The provided type cannot be null");
|
||||||
Preconditions.checkArgument(value != null, "The provided value cannot be null");
|
Preconditions.checkArgument(value != null, "The provided value cannot be null");
|
||||||
|
|
||||||
this.customDataTags.put(key.toString(), registry.wrap(type.getPrimitiveType(), type.toPrimitive(value, adapterContext)));
|
this.customDataTags.put(key.toString(), this.registry.wrap(type, type.toPrimitive(value, adapterContext)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T, Z> boolean has(NamespacedKey key, PersistentDataType<T, Z> type) {
|
public <T, Z> boolean has(@NotNull NamespacedKey key, @NotNull PersistentDataType<T, Z> type) {
|
||||||
Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null");
|
Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null");
|
||||||
Preconditions.checkArgument(type != null, "The provided type cannot be null");
|
Preconditions.checkArgument(type != null, "The provided type cannot be null");
|
||||||
|
|
||||||
@ -51,7 +52,7 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return registry.isInstanceOf(type.getPrimitiveType(), value);
|
return this.registry.isInstanceOf(type, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -60,7 +61,7 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T, Z> Z get(NamespacedKey key, PersistentDataType<T, Z> type) {
|
public <T, Z> Z get(@NotNull NamespacedKey key, @NotNull PersistentDataType<T, Z> type) {
|
||||||
Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null");
|
Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null");
|
||||||
Preconditions.checkArgument(type != null, "The provided type cannot be null");
|
Preconditions.checkArgument(type != null, "The provided type cannot be null");
|
||||||
|
|
||||||
@ -69,15 +70,17 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return type.fromPrimitive(registry.extract(type.getPrimitiveType(), value), adapterContext);
|
return type.fromPrimitive(this.registry.extract(type, value), adapterContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public <T, Z> Z getOrDefault(NamespacedKey key, PersistentDataType<T, Z> type, Z defaultValue) {
|
public <T, Z> Z getOrDefault(@NotNull NamespacedKey key, @NotNull PersistentDataType<T, Z> type, @NotNull Z defaultValue) {
|
||||||
Z z = get(key, type);
|
Z z = this.get(key, type);
|
||||||
return z != null ? z : defaultValue;
|
return z != null ? z : defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Set<NamespacedKey> getKeys() {
|
public Set<NamespacedKey> getKeys() {
|
||||||
Set<NamespacedKey> keys = new HashSet<>();
|
Set<NamespacedKey> keys = new HashSet<>();
|
||||||
@ -93,7 +96,7 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void remove(NamespacedKey key) {
|
public void remove(@NotNull NamespacedKey key) {
|
||||||
Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null");
|
Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null");
|
||||||
|
|
||||||
this.customDataTags.remove(key.toString());
|
this.customDataTags.remove(key.toString());
|
||||||
@ -104,6 +107,7 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
|
|||||||
return this.customDataTags.isEmpty();
|
return this.customDataTags.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public void copyTo(PersistentDataContainer other, boolean replace) {
|
public void copyTo(PersistentDataContainer other, boolean replace) {
|
||||||
Preconditions.checkArgument(other != null, "The target container cannot be null");
|
Preconditions.checkArgument(other != null, "The target container cannot be null");
|
||||||
@ -127,7 +131,7 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, NBTBase> myRawMap = getRaw();
|
Map<String, NBTBase> myRawMap = this.getRaw();
|
||||||
Map<String, NBTBase> theirRawMap = ((CraftPersistentDataContainer) obj).getRaw();
|
Map<String, NBTBase> theirRawMap = ((CraftPersistentDataContainer) obj).getRaw();
|
||||||
|
|
||||||
return Objects.equals(myRawMap, theirRawMap);
|
return Objects.equals(myRawMap, theirRawMap);
|
||||||
@ -160,7 +164,7 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public CraftPersistentDataTypeRegistry getDataTagTypeRegistry() {
|
public CraftPersistentDataTypeRegistry getDataTagTypeRegistry() {
|
||||||
return registry;
|
return this.registry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
package org.bukkit.craftbukkit.persistence;
|
package org.bukkit.craftbukkit.persistence;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.primitives.Primitives;
|
import com.google.common.primitives.Primitives;
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.BiPredicate;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import net.minecraft.nbt.NBTBase;
|
import net.minecraft.nbt.NBTBase;
|
||||||
import net.minecraft.nbt.NBTTagByte;
|
import net.minecraft.nbt.NBTTagByte;
|
||||||
@ -20,85 +25,110 @@ import net.minecraft.nbt.NBTTagLong;
|
|||||||
import net.minecraft.nbt.NBTTagLongArray;
|
import net.minecraft.nbt.NBTTagLongArray;
|
||||||
import net.minecraft.nbt.NBTTagShort;
|
import net.minecraft.nbt.NBTTagShort;
|
||||||
import net.minecraft.nbt.NBTTagString;
|
import net.minecraft.nbt.NBTTagString;
|
||||||
|
import org.bukkit.persistence.ListPersistentDataType;
|
||||||
import org.bukkit.persistence.PersistentDataContainer;
|
import org.bukkit.persistence.PersistentDataContainer;
|
||||||
|
import org.bukkit.persistence.PersistentDataType;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents a registry that contains the used adapters for.
|
* The craft persistent data type registry, at its core, is responsible for the
|
||||||
|
* conversion process between a {@link PersistentDataType} and a respective
|
||||||
|
* {@link NBTBase} instance.
|
||||||
|
* <p>
|
||||||
|
* It does so by creating {@link TagAdapter} instances that are capable of
|
||||||
|
* mappings the supported "primitive types" of {@link PersistentDataType}s to
|
||||||
|
* their respective {@link NBTBase} instances.
|
||||||
|
* <p>
|
||||||
|
* To accomplish this, the class makes <b>heavy</b> use of raw arguments. Their
|
||||||
|
* validity is enforced by the mapping of class to {@link TagAdapter}
|
||||||
|
* internally.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
public final class CraftPersistentDataTypeRegistry {
|
public final class CraftPersistentDataTypeRegistry {
|
||||||
|
|
||||||
private final Function<Class, TagAdapter> CREATE_ADAPTER = this::createAdapter;
|
private final Function<Class, TagAdapter> CREATE_ADAPTER = this::createAdapter;
|
||||||
|
|
||||||
private class TagAdapter<T, Z extends NBTBase> {
|
/**
|
||||||
|
* A tag adapter is a closely related type to a specific implementation of
|
||||||
private final Function<T, Z> builder;
|
* the {@link NBTBase} interface. It exists to convert from and to the
|
||||||
private final Function<Z, T> extractor;
|
* respective value of a {@link NBTBase} to a "primitive type" for later
|
||||||
|
* usage in {@link PersistentDataType}.
|
||||||
private final Class<T> primitiveType;
|
*
|
||||||
private final Class<Z> nbtBaseType;
|
* @param primitiveType the class of the primitive type, e.g.
|
||||||
|
* {@link String}.
|
||||||
public TagAdapter(Class<T> primitiveType, Class<Z> nbtBaseType, Function<T, Z> builder, Function<Z, T> extractor) {
|
* @param nbtBaseType the class of the tag implementation that is used to
|
||||||
this.primitiveType = primitiveType;
|
* store this primitive type, e.g {@link NBTTagString}.
|
||||||
this.nbtBaseType = nbtBaseType;
|
* @param nmsTypeByte the byte identifier of the tag as defined by
|
||||||
this.builder = builder;
|
* {@link NBTBase#getId()}.
|
||||||
this.extractor = extractor;
|
* @param builder a bi function that is responsible for mapping a "primitive
|
||||||
}
|
* type" and its respective {@link PersistentDataType} to a {@link NBTBase}.
|
||||||
|
* @param extractor a bi function that is responsible for extracting a
|
||||||
|
* "primitive type" from a {@link NBTBase} given a
|
||||||
|
* {@link PersistentDataType}.
|
||||||
|
* @param matcher a bi predicate that is responsible for computing if the
|
||||||
|
* passed {@link NBTBase} holds a value that the {@link PersistentDataType}
|
||||||
|
* can extract.
|
||||||
|
* @param <P> the generic type of the primitive the persistent data type
|
||||||
|
* expects.
|
||||||
|
* @param <T> the generic type of the concrete {@link NBTBase}
|
||||||
|
* implementation that the primitive type is mapped into.
|
||||||
|
*/
|
||||||
|
private record TagAdapter<P, T extends NBTBase>(
|
||||||
|
Class<P> primitiveType,
|
||||||
|
Class<T> nbtBaseType,
|
||||||
|
byte nmsTypeByte,
|
||||||
|
BiFunction<PersistentDataType<P, ?>, P, T> builder,
|
||||||
|
BiFunction<PersistentDataType<P, ?>, T, P> extractor,
|
||||||
|
BiPredicate<PersistentDataType<P, ?>, NBTBase> matcher) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method will extract the value stored in the tag, according to
|
* Extract the primitive value from the {@link NBTBase}.
|
||||||
* the expected primitive type.
|
|
||||||
*
|
*
|
||||||
* @param base the base to extract from
|
* @param base the base to extract from
|
||||||
*
|
* @return the value stored inside the tag
|
||||||
* @return the value stored inside of the tag
|
|
||||||
*
|
|
||||||
* @throws ClassCastException if the passed base is not an instanced of
|
* @throws ClassCastException if the passed base is not an instanced of
|
||||||
* the defined base type and therefore is not applicable to the
|
* the defined base type and therefore is not applicable to the
|
||||||
* extractor function
|
* extractor function.
|
||||||
*/
|
*/
|
||||||
T extract(NBTBase base) {
|
private P extract(final PersistentDataType<P, ?> dataType, final NBTBase base) {
|
||||||
Preconditions.checkArgument(nbtBaseType.isInstance(base), "The provided NBTBase was of the type %s. Expected type %s", base.getClass().getSimpleName(), nbtBaseType.getSimpleName());
|
Preconditions.checkArgument(this.nbtBaseType.isInstance(base), "The provided NBTBase was of the type %s. Expected type %s", base.getClass().getSimpleName(), this.nbtBaseType.getSimpleName());
|
||||||
return this.extractor.apply(nbtBaseType.cast(base));
|
return this.extractor.apply(dataType, this.nbtBaseType.cast(base));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a tag instance wrapping around the provided value object.
|
* Builds a tag instance wrapping around the provided primitive value.
|
||||||
*
|
*
|
||||||
* @param value the value to store inside the created tag
|
* @param value the value to store inside the created tag
|
||||||
*
|
|
||||||
* @return the new tag instance
|
* @return the new tag instance
|
||||||
*
|
|
||||||
* @throws ClassCastException if the passed value object is not of the
|
* @throws ClassCastException if the passed value object is not of the
|
||||||
* defined primitive type and therefore is not applicable to the builder
|
* defined primitive type and therefore is not applicable to the builder
|
||||||
* function
|
* function.
|
||||||
*/
|
*/
|
||||||
Z build(Object value) {
|
private T build(final PersistentDataType<P, ?> dataType, final Object value) {
|
||||||
Preconditions.checkArgument(primitiveType.isInstance(value), "The provided value was of the type %s. Expected type %s", value.getClass().getSimpleName(), primitiveType.getSimpleName());
|
Preconditions.checkArgument(this.primitiveType.isInstance(value), "The provided value was of the type %s. Expected type %s", value.getClass().getSimpleName(), this.primitiveType.getSimpleName());
|
||||||
return this.builder.apply(primitiveType.cast(value));
|
return this.builder.apply(dataType, this.primitiveType.cast(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns if the tag instance matches the adapters one.
|
* Computes if the provided persistent data type's primitive type is a
|
||||||
*
|
* representation of the {@link NBTBase}.
|
||||||
* @param base the base to check
|
|
||||||
*
|
*
|
||||||
|
* @param base the base tag instance to check against
|
||||||
* @return if the tag was an instance of the set type
|
* @return if the tag was an instance of the set type
|
||||||
*/
|
*/
|
||||||
boolean isInstance(NBTBase base) {
|
private boolean isInstance(final PersistentDataType<P, ?> persistentDataType, final NBTBase base) {
|
||||||
return this.nbtBaseType.isInstance(base);
|
return this.matcher.test(persistentDataType, base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Map<Class, TagAdapter> adapters = new HashMap<>();
|
private final Map<Class, TagAdapter> adapters = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a suitable adapter instance for the primitive class type
|
* Creates a suitable adapter instance for the primitive class type.
|
||||||
*
|
*
|
||||||
* @param type the type to create an adapter for
|
* @param type the type to create an adapter for
|
||||||
* @param <T> the generic type of that class
|
* @param <T> the generic type of the primitive type
|
||||||
*
|
|
||||||
* @return the created adapter instance
|
* @return the created adapter instance
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException if no suitable tag type adapter for this
|
* @throws IllegalArgumentException if no suitable tag type adapter for this
|
||||||
* type was found
|
* type was found
|
||||||
*/
|
*/
|
||||||
@ -107,109 +137,163 @@ public final class CraftPersistentDataTypeRegistry {
|
|||||||
type = Primitives.wrap(type); //Make sure we will always "switch" over the wrapper types
|
type = Primitives.wrap(type); //Make sure we will always "switch" over the wrapper types
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Primitives
|
||||||
Primitives
|
|
||||||
*/
|
|
||||||
if (Objects.equals(Byte.class, type)) {
|
if (Objects.equals(Byte.class, type)) {
|
||||||
return createAdapter(Byte.class, NBTTagByte.class, NBTTagByte::valueOf, NBTTagByte::getAsByte);
|
return this.createAdapter(
|
||||||
|
Byte.class, NBTTagByte.class, NBTBase.TAG_BYTE,
|
||||||
|
NBTTagByte::valueOf, NBTTagByte::getAsByte
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (Objects.equals(Short.class, type)) {
|
if (Objects.equals(Short.class, type)) {
|
||||||
return createAdapter(Short.class, NBTTagShort.class, NBTTagShort::valueOf, NBTTagShort::getAsShort);
|
return this.createAdapter(
|
||||||
|
Short.class, NBTTagShort.class, NBTBase.TAG_SHORT, NBTTagShort::valueOf, NBTTagShort::getAsShort
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (Objects.equals(Integer.class, type)) {
|
if (Objects.equals(Integer.class, type)) {
|
||||||
return createAdapter(Integer.class, NBTTagInt.class, NBTTagInt::valueOf, NBTTagInt::getAsInt);
|
return this.createAdapter(
|
||||||
|
Integer.class, NBTTagInt.class, NBTBase.TAG_INT, NBTTagInt::valueOf, NBTTagInt::getAsInt
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (Objects.equals(Long.class, type)) {
|
if (Objects.equals(Long.class, type)) {
|
||||||
return createAdapter(Long.class, NBTTagLong.class, NBTTagLong::valueOf, NBTTagLong::getAsLong);
|
return this.createAdapter(
|
||||||
|
Long.class, NBTTagLong.class, NBTBase.TAG_LONG, NBTTagLong::valueOf, NBTTagLong::getAsLong
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (Objects.equals(Float.class, type)) {
|
if (Objects.equals(Float.class, type)) {
|
||||||
return createAdapter(Float.class, NBTTagFloat.class, NBTTagFloat::valueOf, NBTTagFloat::getAsFloat);
|
return this.createAdapter(
|
||||||
|
Float.class, NBTTagFloat.class, NBTBase.TAG_FLOAT,
|
||||||
|
NBTTagFloat::valueOf, NBTTagFloat::getAsFloat
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (Objects.equals(Double.class, type)) {
|
if (Objects.equals(Double.class, type)) {
|
||||||
return createAdapter(Double.class, NBTTagDouble.class, NBTTagDouble::valueOf, NBTTagDouble::getAsDouble);
|
return this.createAdapter(
|
||||||
|
Double.class, NBTTagDouble.class, NBTBase.TAG_DOUBLE,
|
||||||
|
NBTTagDouble::valueOf, NBTTagDouble::getAsDouble
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
String
|
|
||||||
*/
|
|
||||||
if (Objects.equals(String.class, type)) {
|
if (Objects.equals(String.class, type)) {
|
||||||
return createAdapter(String.class, NBTTagString.class, NBTTagString::valueOf, NBTTagString::getAsString);
|
return this.createAdapter(
|
||||||
|
String.class, NBTTagString.class, NBTBase.TAG_STRING,
|
||||||
|
NBTTagString::valueOf, NBTTagString::getAsString
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Primitive non-list arrays
|
||||||
Primitive Arrays
|
|
||||||
*/
|
|
||||||
if (Objects.equals(byte[].class, type)) {
|
if (Objects.equals(byte[].class, type)) {
|
||||||
return createAdapter(byte[].class, NBTTagByteArray.class, array -> new NBTTagByteArray(Arrays.copyOf(array, array.length)), n -> Arrays.copyOf(n.getAsByteArray(), n.size()));
|
return this.createAdapter(
|
||||||
|
byte[].class, NBTTagByteArray.class, NBTBase.TAG_BYTE_ARRAY,
|
||||||
|
array -> new NBTTagByteArray(Arrays.copyOf(array, array.length)),
|
||||||
|
n -> Arrays.copyOf(n.getAsByteArray(), n.size())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (Objects.equals(int[].class, type)) {
|
if (Objects.equals(int[].class, type)) {
|
||||||
return createAdapter(int[].class, NBTTagIntArray.class, array -> new NBTTagIntArray(Arrays.copyOf(array, array.length)), n -> Arrays.copyOf(n.getAsIntArray(), n.size()));
|
return this.createAdapter(
|
||||||
|
int[].class, NBTTagIntArray.class, NBTBase.TAG_INT_ARRAY,
|
||||||
|
array -> new NBTTagIntArray(Arrays.copyOf(array, array.length)),
|
||||||
|
n -> Arrays.copyOf(n.getAsIntArray(), n.size())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (Objects.equals(long[].class, type)) {
|
if (Objects.equals(long[].class, type)) {
|
||||||
return createAdapter(long[].class, NBTTagLongArray.class, array -> new NBTTagLongArray(Arrays.copyOf(array, array.length)), n -> Arrays.copyOf(n.getAsLongArray(), n.size()));
|
return this.createAdapter(
|
||||||
|
long[].class, NBTTagLongArray.class, NBTBase.TAG_LONG_ARRAY,
|
||||||
|
array -> new NBTTagLongArray(Arrays.copyOf(array, array.length)),
|
||||||
|
n -> Arrays.copyOf(n.getAsLongArray(), n.size())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Previously "emulated" compound lists, now useless as a proper list type exists.
|
||||||
Complex Arrays
|
|
||||||
*/
|
|
||||||
if (Objects.equals(PersistentDataContainer[].class, type)) {
|
if (Objects.equals(PersistentDataContainer[].class, type)) {
|
||||||
return createAdapter(PersistentDataContainer[].class, NBTTagList.class,
|
return this.createAdapter(
|
||||||
|
PersistentDataContainer[].class, NBTTagList.class, NBTBase.TAG_LIST,
|
||||||
(containerArray) -> {
|
(containerArray) -> {
|
||||||
NBTTagList list = new NBTTagList();
|
final NBTTagList list = new NBTTagList();
|
||||||
for (int i = 0; i < containerArray.length; i++) {
|
for (final PersistentDataContainer persistentDataContainer : containerArray) {
|
||||||
list.add(((CraftPersistentDataContainer) containerArray[i]).toTagCompound());
|
list.add(((CraftPersistentDataContainer) persistentDataContainer).toTagCompound());
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
},
|
},
|
||||||
(tag) -> {
|
(tag) -> {
|
||||||
PersistentDataContainer[] containerArray = new CraftPersistentDataContainer[tag.size()];
|
final PersistentDataContainer[] containerArray = new CraftPersistentDataContainer[tag.size()];
|
||||||
for (int i = 0; i < tag.size(); i++) {
|
for (int i = 0; i < tag.size(); i++) {
|
||||||
CraftPersistentDataContainer container = new CraftPersistentDataContainer(this);
|
final CraftPersistentDataContainer container = new CraftPersistentDataContainer(this);
|
||||||
NBTTagCompound compound = tag.getCompound(i);
|
final NBTTagCompound compound = tag.getCompound(i);
|
||||||
for (String key : compound.getAllKeys()) {
|
for (final String key : compound.getAllKeys()) {
|
||||||
container.put(key, compound.get(key));
|
container.put(key, compound.get(key));
|
||||||
}
|
}
|
||||||
containerArray[i] = container;
|
containerArray[i] = container;
|
||||||
}
|
}
|
||||||
return containerArray;
|
return containerArray;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that this will map the interface PersistentMetadataContainer directly to the CraftBukkit implementation
|
||||||
|
// Passing any other instance of this form to the tag type registry will throw a ClassCastException
|
||||||
|
// as defined in TagAdapter#build.
|
||||||
|
if (Objects.equals(PersistentDataContainer.class, type)) {
|
||||||
|
return this.createAdapter(
|
||||||
|
CraftPersistentDataContainer.class, NBTTagCompound.class, NBTBase.TAG_COMPOUND,
|
||||||
|
CraftPersistentDataContainer::toTagCompound,
|
||||||
|
tag -> {
|
||||||
|
final CraftPersistentDataContainer container = new CraftPersistentDataContainer(this);
|
||||||
|
for (final String key : tag.getAllKeys()) {
|
||||||
|
container.put(key, tag.get(key));
|
||||||
|
}
|
||||||
|
return container;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
if (Objects.equals(List.class, type)) {
|
||||||
Note that this will map the interface PersistentMetadataContainer directly to the CraftBukkit implementation
|
return createAdapter(
|
||||||
Passing any other instance of this form to the tag type registry will throw a ClassCastException as defined in TagAdapter#build
|
List.class,
|
||||||
*/
|
net.minecraft.nbt.NBTTagList.class,
|
||||||
if (Objects.equals(PersistentDataContainer.class, type)) {
|
NBTBase.TAG_LIST,
|
||||||
return createAdapter(CraftPersistentDataContainer.class, NBTTagCompound.class, CraftPersistentDataContainer::toTagCompound, tag -> {
|
this::constructList,
|
||||||
CraftPersistentDataContainer container = new CraftPersistentDataContainer(this);
|
this::extractList,
|
||||||
for (String key : tag.getAllKeys()) {
|
this::matchesListTag
|
||||||
container.put(key, tag.get(key));
|
);
|
||||||
}
|
|
||||||
return container;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new IllegalArgumentException("Could not find a valid TagAdapter implementation for the requested type " + type.getSimpleName());
|
throw new IllegalArgumentException("Could not find a valid TagAdapter implementation for the requested type " + type.getSimpleName());
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T, Z extends NBTBase> TagAdapter<T, Z> createAdapter(Class<T> primitiveType, Class<Z> nbtBaseType, Function<T, Z> builder, Function<Z, T> extractor) {
|
// Plain constructor helper method.
|
||||||
return new TagAdapter<>(primitiveType, nbtBaseType, builder, extractor);
|
private <T, Z extends NBTBase> TagAdapter<T, Z> createAdapter(
|
||||||
|
final Class<T> primitiveType, final Class<Z> nbtBaseType, final byte nmsTypeByte,
|
||||||
|
final Function<T, Z> builder, final Function<Z, T> extractor
|
||||||
|
) {
|
||||||
|
return createAdapter(
|
||||||
|
primitiveType,
|
||||||
|
nbtBaseType,
|
||||||
|
nmsTypeByte,
|
||||||
|
(type, t) -> builder.apply(t),
|
||||||
|
(type, z) -> extractor.apply(z),
|
||||||
|
(type, t) -> nbtBaseType.isInstance(t)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plain constructor helper method.
|
||||||
|
private <T, Z extends NBTBase> TagAdapter<T, Z> createAdapter(
|
||||||
|
final Class<T> primitiveType, final Class<Z> nbtBaseType, final byte nmsTypeByte,
|
||||||
|
final BiFunction<PersistentDataType<T, ?>, T, Z> builder,
|
||||||
|
final BiFunction<PersistentDataType<T, ?>, Z, T> extractor,
|
||||||
|
final BiPredicate<PersistentDataType<T, ?>, NBTBase> matcher
|
||||||
|
) {
|
||||||
|
return new TagAdapter<>(primitiveType, nbtBaseType, nmsTypeByte, builder, extractor, matcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps the passed value into a tag instance.
|
* Wraps the passed primitive value into a tag instance.
|
||||||
*
|
*
|
||||||
* @param type the type of the passed value
|
* @param type the type of the passed value
|
||||||
* @param value the value to be stored in the tag
|
* @param value the value to be stored in the tag
|
||||||
* @param <T> the generic type of the value
|
* @param <T> the generic type of the value
|
||||||
*
|
|
||||||
* @return the created tag instance
|
* @return the created tag instance
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException if no suitable tag type adapter for this
|
* @throws IllegalArgumentException if no suitable tag type adapter for this
|
||||||
* type was found
|
* type was found.
|
||||||
*/
|
*/
|
||||||
public <T> NBTBase wrap(Class<T> type, T value) {
|
public <T> NBTBase wrap(final PersistentDataType<T, ?> type, final T value) {
|
||||||
return this.adapters.computeIfAbsent(type, CREATE_ADAPTER).build(value);
|
return this.getOrCreateAdapter(type).build(type, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -218,14 +302,29 @@ public final class CraftPersistentDataTypeRegistry {
|
|||||||
* @param type the type of the primitive value
|
* @param type the type of the primitive value
|
||||||
* @param base the base instance to check
|
* @param base the base instance to check
|
||||||
* @param <T> the generic type of the type
|
* @param <T> the generic type of the type
|
||||||
*
|
|
||||||
* @return if the base stores values of the primitive type passed
|
* @return if the base stores values of the primitive type passed
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException if no suitable tag type adapter for this
|
* @throws IllegalArgumentException if no suitable tag type adapter for this
|
||||||
* type was found
|
* type was found.
|
||||||
*/
|
*/
|
||||||
public <T> boolean isInstanceOf(Class<T> type, NBTBase base) {
|
public <T> boolean isInstanceOf(final PersistentDataType<T, ?> type, final NBTBase base) {
|
||||||
return this.adapters.computeIfAbsent(type, CREATE_ADAPTER).isInstance(base);
|
return this.getOrCreateAdapter(type).isInstance(type, base);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches or creates an adapter for the requested persistent data type.
|
||||||
|
*
|
||||||
|
* @param type the persistent data type to find or create an adapter for.
|
||||||
|
* @param <T> the generic type of the primitive type of the persistent data
|
||||||
|
* type.
|
||||||
|
* @param <Z> the generic type of the complex type of the persistent data
|
||||||
|
* type.
|
||||||
|
* @return the tag adapter instance that was found or created.
|
||||||
|
* @throws IllegalArgumentException if no adapter can be created for the
|
||||||
|
* persistent data type.
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
private <T, Z extends NBTBase> TagAdapter<T, Z> getOrCreateAdapter(@NotNull final PersistentDataType<T, ?> type) {
|
||||||
|
return this.adapters.computeIfAbsent(type.getPrimitiveType(), CREATE_ADAPTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -234,23 +333,95 @@ public final class CraftPersistentDataTypeRegistry {
|
|||||||
* @param type the type of the value to extract
|
* @param type the type of the value to extract
|
||||||
* @param tag the tag to extract the value from
|
* @param tag the tag to extract the value from
|
||||||
* @param <T> the generic type of the value stored inside the tag
|
* @param <T> the generic type of the value stored inside the tag
|
||||||
*
|
|
||||||
* @return the extracted value
|
* @return the extracted value
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException if the passed base is not an instanced
|
* @throws IllegalArgumentException if the passed base is not an instanced
|
||||||
* of the defined base type and therefore is not applicable to the extractor
|
* of the defined base type and therefore is not applicable to the extractor
|
||||||
* function
|
* function.
|
||||||
* @throws IllegalArgumentException if the found object is not of type
|
* @throws IllegalArgumentException if the found object is not of type
|
||||||
* passed
|
* passed.
|
||||||
* @throws IllegalArgumentException if no suitable tag type adapter for this
|
* @throws IllegalArgumentException if no suitable tag type adapter for this
|
||||||
* type was found
|
* type was found.
|
||||||
*/
|
*/
|
||||||
public <T> T extract(Class<T> type, NBTBase tag) throws ClassCastException, IllegalArgumentException {
|
public <T, Z extends NBTBase> T extract(final PersistentDataType<T, ?> type, final NBTBase tag) throws ClassCastException, IllegalArgumentException {
|
||||||
TagAdapter adapter = this.adapters.computeIfAbsent(type, CREATE_ADAPTER);
|
final Class<T> primitiveType = type.getPrimitiveType();
|
||||||
Preconditions.checkArgument(adapter.isInstance(tag), "The found tag instance (%s) cannot store %s", tag.getClass().getSimpleName(), type.getSimpleName());
|
final TagAdapter<T, Z> adapter = this.getOrCreateAdapter(type);
|
||||||
|
Preconditions.checkArgument(adapter.isInstance(type, tag), "The found tag instance (%s) cannot store %s", tag.getClass().getSimpleName(), primitiveType.getSimpleName());
|
||||||
|
|
||||||
Object foundValue = adapter.extract(tag);
|
final Object foundValue = adapter.extract(type, tag);
|
||||||
Preconditions.checkArgument(type.isInstance(foundValue), "The found object is of the type %s. Expected type %s", foundValue.getClass().getSimpleName(), type.getSimpleName());
|
Preconditions.checkArgument(primitiveType.isInstance(foundValue), "The found object is of the type %s. Expected type %s", foundValue.getClass().getSimpleName(), primitiveType.getSimpleName());
|
||||||
return type.cast(foundValue);
|
return primitiveType.cast(foundValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a {@link NBTTagList} from a {@link List} instance by using the
|
||||||
|
* passed persistent data type.
|
||||||
|
*
|
||||||
|
* @param type the persistent data type of the list.
|
||||||
|
* @param list the list or primitive values.
|
||||||
|
* @param <P> the generic type of the primitive values in the list.
|
||||||
|
* @return the constructed {@link NBTTagList}.
|
||||||
|
*/
|
||||||
|
private <P, T extends List<P>> NBTTagList constructList(@NotNull final PersistentDataType<T, ?> type, @NotNull final List<P> list) {
|
||||||
|
Preconditions.checkArgument(type instanceof ListPersistentDataType<?, ?>, "The passed list cannot be written to the PDC with a %s (expected a list data type)", type.getClass().getSimpleName());
|
||||||
|
final ListPersistentDataType<P, ?> listPersistentDataType = (ListPersistentDataType<P, ?>) type;
|
||||||
|
|
||||||
|
final TagAdapter<P, NBTBase> elementAdapter = this.getOrCreateAdapter(listPersistentDataType.elementType());
|
||||||
|
|
||||||
|
final List<NBTBase> values = Lists.newArrayListWithCapacity(list.size());
|
||||||
|
for (final P primitiveValue : list) {
|
||||||
|
values.add(this.wrap(listPersistentDataType.elementType(), primitiveValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new NBTTagList(values, elementAdapter.nmsTypeByte());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts a {@link List} from a {@link NBTTagList} and a respective
|
||||||
|
* {@link PersistentDataType}.
|
||||||
|
*
|
||||||
|
* @param type the persistent data type of the list.
|
||||||
|
* @param listTag the list tag to extract the {@link List} from.
|
||||||
|
* @param <P> the generic type of the primitive values stored in the
|
||||||
|
* {@link List}.
|
||||||
|
* @return the extracted {@link List} instance.
|
||||||
|
* @throws IllegalArgumentException if the passed {@link PersistentDataType}
|
||||||
|
* is not a {@link ListPersistentDataType} and can hence not be used to
|
||||||
|
* extract a {@link List}.
|
||||||
|
*/
|
||||||
|
private <P> List<P> extractList(@NotNull final PersistentDataType<P, ?> type,
|
||||||
|
@NotNull final NBTTagList listTag) {
|
||||||
|
Preconditions.checkArgument(type instanceof ListPersistentDataType<?, ?>, "The found list tag cannot be read with a %s (expected a list data type)", type.getClass().getSimpleName());
|
||||||
|
final ListPersistentDataType<P, ?> listPersistentDataType = (ListPersistentDataType<P, ?>) type;
|
||||||
|
|
||||||
|
final List<P> output = new ObjectArrayList<>(listTag.size());
|
||||||
|
for (final NBTBase tag : listTag) {
|
||||||
|
output.add(this.extract(listPersistentDataType.elementType(), tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes if the passed {@link NBTBase} is a {@link NBTTagList} and it,
|
||||||
|
* including its elements, can be read/written via the passed
|
||||||
|
* {@link PersistentDataType}.
|
||||||
|
*
|
||||||
|
* @param type the persistent data type for which to check if the tag
|
||||||
|
* matches.
|
||||||
|
* @param tag the tag that is to be checked if it matches the data type.
|
||||||
|
* @return whether the passed tag can be read/written via the passed type.
|
||||||
|
*/
|
||||||
|
private boolean matchesListTag(final PersistentDataType<List, ?> type, final NBTBase tag) {
|
||||||
|
if ((!(type instanceof final ListPersistentDataType listPersistentDataType))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!(tag instanceof final NBTTagList listTag)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte elementType = listTag.getElementType();
|
||||||
|
final TagAdapter elementAdapter = this.getOrCreateAdapter(listPersistentDataType.elementType());
|
||||||
|
|
||||||
|
return elementAdapter.nmsTypeByte() == elementType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import net.minecraft.nbt.NBTBase;
|
|||||||
import net.minecraft.nbt.NBTTagCompound;
|
import net.minecraft.nbt.NBTTagCompound;
|
||||||
import org.bukkit.NamespacedKey;
|
import org.bukkit.NamespacedKey;
|
||||||
import org.bukkit.persistence.PersistentDataType;
|
import org.bukkit.persistence.PersistentDataType;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A child class of the persistent data container that recalls if it has been
|
* A child class of the persistent data container that recalls if it has been
|
||||||
@ -31,13 +32,13 @@ public final class DirtyCraftPersistentDataContainer extends CraftPersistentData
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T, Z> void set(NamespacedKey key, PersistentDataType<T, Z> type, Z value) {
|
public <T, Z> void set(@NotNull NamespacedKey key, @NotNull PersistentDataType<T, Z> type, @NotNull Z value) {
|
||||||
super.set(key, type, value);
|
super.set(key, type, value);
|
||||||
this.dirty(true);
|
this.dirty(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void remove(NamespacedKey key) {
|
public void remove(@NotNull NamespacedKey key) {
|
||||||
super.remove(key);
|
super.remove(key);
|
||||||
this.dirty(true);
|
this.dirty(true);
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,12 @@ import java.io.IOException;
|
|||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.stream.Stream;
|
||||||
import net.minecraft.nbt.NBTTagCompound;
|
import net.minecraft.nbt.NBTTagCompound;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
@ -15,12 +19,18 @@ import org.bukkit.configuration.InvalidConfigurationException;
|
|||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.inventory.meta.ItemMeta;
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
|
import org.bukkit.persistence.ListPersistentDataType;
|
||||||
import org.bukkit.persistence.PersistentDataAdapterContext;
|
import org.bukkit.persistence.PersistentDataAdapterContext;
|
||||||
import org.bukkit.persistence.PersistentDataContainer;
|
import org.bukkit.persistence.PersistentDataContainer;
|
||||||
import org.bukkit.persistence.PersistentDataType;
|
import org.bukkit.persistence.PersistentDataType;
|
||||||
import org.bukkit.support.AbstractTestingBase;
|
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.BeforeAll;
|
||||||
import org.junit.jupiter.api.Test;
|
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 {
|
public class PersistentDataContainerTest extends AbstractTestingBase {
|
||||||
|
|
||||||
@ -31,18 +41,14 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
|
|||||||
VALID_KEY = new NamespacedKey("test", "validkey");
|
VALID_KEY = new NamespacedKey("test", "validkey");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Sets a test
|
||||||
Sets a test
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetNoAdapter() {
|
public void testSetNoAdapter() {
|
||||||
ItemMeta itemMeta = createNewItemMeta();
|
ItemMeta itemMeta = createNewItemMeta();
|
||||||
assertThrows(IllegalArgumentException.class, () -> itemMeta.getPersistentDataContainer().set(VALID_KEY, new PrimitiveTagType<>(boolean.class), true));
|
assertThrows(IllegalArgumentException.class, () -> itemMeta.getPersistentDataContainer().set(VALID_KEY, new PrimitiveTagType<>(boolean.class), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Contains a tag
|
||||||
Contains a tag
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testHasNoAdapter() {
|
public void testHasNoAdapter() {
|
||||||
ItemMeta itemMeta = createNewItemMeta();
|
ItemMeta itemMeta = createNewItemMeta();
|
||||||
@ -57,9 +63,7 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
|
|||||||
assertTrue(itemMeta.getPersistentDataContainer().has(VALID_KEY));
|
assertTrue(itemMeta.getPersistentDataContainer().has(VALID_KEY));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Getting a tag
|
||||||
Getting a tag
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetNoAdapter() {
|
public void testGetNoAdapter() {
|
||||||
ItemMeta itemMeta = createNewItemMeta();
|
ItemMeta itemMeta = createNewItemMeta();
|
||||||
@ -87,11 +91,11 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
|
|||||||
assertEquals(160L, (long) meta.getPersistentDataContainer().get(namespacedKeyB, PersistentDataType.LONG));
|
assertEquals(160L, (long) meta.getPersistentDataContainer().get(namespacedKeyB, PersistentDataType.LONG));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ItemMeta createNewItemMeta() {
|
private static ItemMeta createNewItemMeta() {
|
||||||
return Bukkit.getItemFactory().getItemMeta(Material.DIAMOND_PICKAXE);
|
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());
|
return new NamespacedKey("test-plugin", keyName.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,9 +121,7 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
|
|||||||
assertEquals(container, target);
|
assertEquals(container, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Removing a tag
|
||||||
Removing a tag
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testNBTTagStoring() {
|
public void testNBTTagStoring() {
|
||||||
CraftMetaItem itemMeta = createComplexItemMeta();
|
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-string"), PersistentDataType.STRING, "Hello there world");
|
||||||
itemMeta.getPersistentDataContainer().set(requestKey("custom-int"), PersistentDataType.INTEGER, 3);
|
itemMeta.getPersistentDataContainer().set(requestKey("custom-int"), PersistentDataType.INTEGER, 3);
|
||||||
itemMeta.getPersistentDataContainer().set(requestKey("custom-double"), PersistentDataType.DOUBLE, 3.123);
|
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
|
PersistentDataContainer innerContainer = itemMeta.getPersistentDataContainer().getAdapterContext().newPersistentDataContainer(); //Add a inner container
|
||||||
innerContainer.set(VALID_KEY, PersistentDataType.LONG, 5L);
|
innerContainer.set(VALID_KEY, PersistentDataType.LONG, 5L);
|
||||||
@ -195,9 +203,7 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
|
|||||||
return itemMeta;
|
return itemMeta;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Test edge cases with strings
|
||||||
Test edge cases with strings
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testStringEdgeCases() throws IOException, InvalidConfigurationException {
|
public void testStringEdgeCases() throws IOException, InvalidConfigurationException {
|
||||||
final ItemStack stack = new ItemStack(Material.DIAMOND);
|
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));
|
assertEquals(jsonLookalike, loadedPdc.get(requestKey("string_json_lookalike"), PersistentDataType.STRING));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Test complex object storage
|
||||||
Test complex object storage
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void storeUUIDOnItemTest() {
|
public void storeUUIDOnItemTest() {
|
||||||
ItemMeta itemMeta = createNewItemMeta();
|
ItemMeta itemMeta = createNewItemMeta();
|
||||||
@ -285,25 +289,29 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
|
|||||||
class UUIDPersistentDataType implements PersistentDataType<byte[], UUID> {
|
class UUIDPersistentDataType implements PersistentDataType<byte[], UUID> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@NotNull
|
||||||
public Class<byte[]> getPrimitiveType() {
|
public Class<byte[]> getPrimitiveType() {
|
||||||
return byte[].class;
|
return byte[].class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Class<UUID> getComplexType() {
|
public Class<UUID> getComplexType() {
|
||||||
return UUID.class;
|
return UUID.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public byte[] toPrimitive(UUID complex, PersistentDataAdapterContext context) {
|
public byte[] toPrimitive(@NotNull UUID complex, @NotNull PersistentDataAdapterContext context) {
|
||||||
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
|
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
|
||||||
bb.putLong(complex.getMostSignificantBits());
|
bb.putLong(complex.getMostSignificantBits());
|
||||||
bb.putLong(complex.getLeastSignificantBits());
|
bb.putLong(complex.getLeastSignificantBits());
|
||||||
return bb.array();
|
return bb.array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public UUID fromPrimitive(byte[] primitive, PersistentDataAdapterContext context) {
|
public UUID fromPrimitive(@NotNull byte[] primitive, @NotNull PersistentDataAdapterContext context) {
|
||||||
ByteBuffer bb = ByteBuffer.wrap(primitive);
|
ByteBuffer bb = ByteBuffer.wrap(primitive);
|
||||||
long firstLong = bb.getLong();
|
long firstLong = bb.getLong();
|
||||||
long secondLong = bb.getLong();
|
long secondLong = bb.getLong();
|
||||||
@ -315,22 +323,22 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
|
|||||||
public void testPrimitiveCustomTags() {
|
public void testPrimitiveCustomTags() {
|
||||||
ItemMeta itemMeta = createNewItemMeta();
|
ItemMeta itemMeta = createNewItemMeta();
|
||||||
|
|
||||||
testPrimitiveCustomTag(itemMeta, PersistentDataType.BYTE, (byte) 1);
|
this.testPrimitiveCustomTag(itemMeta, PersistentDataType.BYTE, (byte) 1);
|
||||||
testPrimitiveCustomTag(itemMeta, PersistentDataType.SHORT, (short) 1);
|
this.testPrimitiveCustomTag(itemMeta, PersistentDataType.SHORT, (short) 1);
|
||||||
testPrimitiveCustomTag(itemMeta, PersistentDataType.INTEGER, 1);
|
this.testPrimitiveCustomTag(itemMeta, PersistentDataType.INTEGER, 1);
|
||||||
testPrimitiveCustomTag(itemMeta, PersistentDataType.LONG, 1L);
|
this.testPrimitiveCustomTag(itemMeta, PersistentDataType.LONG, 1L);
|
||||||
testPrimitiveCustomTag(itemMeta, PersistentDataType.FLOAT, 1.34F);
|
this.testPrimitiveCustomTag(itemMeta, PersistentDataType.FLOAT, 1.34F);
|
||||||
testPrimitiveCustomTag(itemMeta, PersistentDataType.DOUBLE, 151.123);
|
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
|
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
|
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
|
1L, 4L, 2L, Long.MAX_VALUE
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -356,31 +364,35 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
|
|||||||
assertFalse(meta.getPersistentDataContainer().has(tagKey, type));
|
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;
|
this.primitiveType = primitiveType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Class<T> getPrimitiveType() {
|
public Class<P> getPrimitiveType() {
|
||||||
return primitiveType;
|
return this.primitiveType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Class<T> getComplexType() {
|
public Class<P> getComplexType() {
|
||||||
return primitiveType;
|
return this.primitiveType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public T toPrimitive(T complex, PersistentDataAdapterContext context) {
|
public P toPrimitive(@NotNull P complex, @NotNull PersistentDataAdapterContext context) {
|
||||||
return complex;
|
return complex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public T fromPrimitive(T primitive, PersistentDataAdapterContext context) {
|
public P fromPrimitive(@NotNull P primitive, @NotNull PersistentDataAdapterContext context) {
|
||||||
return primitive;
|
return primitive;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -397,7 +409,105 @@ public class PersistentDataContainerTest extends AbstractTestingBase {
|
|||||||
assertNotSame(container, clonedContainer);
|
assertNotSame(container, clonedContainer);
|
||||||
assertEquals(container, clonedContainer);
|
assertEquals(container, clonedContainer);
|
||||||
|
|
||||||
clonedContainer.set(VALID_KEY, PersistentDataType.STRING, "dinnerbone");
|
clonedContainer.set(PersistentDataContainerTest.VALID_KEY, PersistentDataType.STRING, "dinnerbone");
|
||||||
assertNotEquals(container, clonedContainer);
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user