#1295: Define native persistent data types for lists

By: Bjarne Koll <lynxplay101@gmail.com>
This commit is contained in:
CraftBukkit/Spigot
2024-01-06 16:03:58 +11:00
parent 71ca5a7bdf
commit 8cd8851498
7 changed files with 482 additions and 175 deletions

View File

@@ -7,27 +7,31 @@ import org.bukkit.inventory.meta.tags.ItemTagType;
import org.bukkit.persistence.PersistentDataAdapterContext;
import org.bukkit.persistence.PersistentDataContainer;
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;
}
@NotNull
@Override
public Class<PersistentDataContainer> getPrimitiveType() {
return PersistentDataContainer.class;
}
@NotNull
@Override
public Class<Z> getComplexType() {
public Class<C> getComplexType() {
return deprecated.getComplexType();
}
@NotNull
@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));
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());
}
@NotNull
@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());
return this.deprecated.fromPrimitive(new DeprecatedCustomTagContainer(primitive), new DeprecatedItemAdapterContext(context));

View File

@@ -3,32 +3,37 @@ package org.bukkit.craftbukkit.inventory.tags;
import org.bukkit.inventory.meta.tags.ItemTagType;
import org.bukkit.persistence.PersistentDataAdapterContext;
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;
}
@NotNull
@Override
public Class<T> getPrimitiveType() {
public Class<P> getPrimitiveType() {
return deprecated.getPrimitiveType();
}
@NotNull
@Override
public Class<Z> getComplexType() {
public Class<C> getComplexType() {
return deprecated.getComplexType();
}
@NotNull
@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));
}
@NotNull
@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));
}
}

View File

@@ -14,6 +14,7 @@ import org.bukkit.craftbukkit.util.CraftNBTTagConfigSerializer;
import org.bukkit.persistence.PersistentDataAdapterContext;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
public class CraftPersistentDataContainer implements PersistentDataContainer {
@@ -33,16 +34,16 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
@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(type != null, "The provided type 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
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(type != null, "The provided type cannot be null");
@@ -51,7 +52,7 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
return false;
}
return registry.isInstanceOf(type.getPrimitiveType(), value);
return this.registry.isInstanceOf(type, value);
}
@Override
@@ -60,7 +61,7 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
}
@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(type != null, "The provided type cannot be null");
@@ -69,15 +70,17 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
return null;
}
return type.fromPrimitive(registry.extract(type.getPrimitiveType(), value), adapterContext);
return type.fromPrimitive(this.registry.extract(type, value), adapterContext);
}
@NotNull
@Override
public <T, Z> Z getOrDefault(NamespacedKey key, PersistentDataType<T, Z> type, Z defaultValue) {
Z z = get(key, type);
public <T, Z> Z getOrDefault(@NotNull NamespacedKey key, @NotNull PersistentDataType<T, Z> type, @NotNull Z defaultValue) {
Z z = this.get(key, type);
return z != null ? z : defaultValue;
}
@NotNull
@Override
public Set<NamespacedKey> getKeys() {
Set<NamespacedKey> keys = new HashSet<>();
@@ -93,7 +96,7 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
}
@Override
public void remove(NamespacedKey key) {
public void remove(@NotNull NamespacedKey key) {
Preconditions.checkArgument(key != null, "The NamespacedKey key cannot be null");
this.customDataTags.remove(key.toString());
@@ -104,6 +107,7 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
return this.customDataTags.isEmpty();
}
@NotNull
@Override
public void copyTo(PersistentDataContainer other, boolean replace) {
Preconditions.checkArgument(other != null, "The target container cannot be null");
@@ -127,7 +131,7 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
return false;
}
Map<String, NBTBase> myRawMap = getRaw();
Map<String, NBTBase> myRawMap = this.getRaw();
Map<String, NBTBase> theirRawMap = ((CraftPersistentDataContainer) obj).getRaw();
return Objects.equals(myRawMap, theirRawMap);
@@ -160,7 +164,7 @@ public class CraftPersistentDataContainer implements PersistentDataContainer {
}
public CraftPersistentDataTypeRegistry getDataTagTypeRegistry() {
return registry;
return this.registry;
}
@Override

View File

@@ -1,11 +1,16 @@
package org.bukkit.craftbukkit.persistence;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.primitives.Primitives;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Function;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagByte;
@@ -20,85 +25,110 @@ import net.minecraft.nbt.NBTTagLong;
import net.minecraft.nbt.NBTTagLongArray;
import net.minecraft.nbt.NBTTagShort;
import net.minecraft.nbt.NBTTagString;
import org.bukkit.persistence.ListPersistentDataType;
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 {
private final Function<Class, TagAdapter> CREATE_ADAPTER = this::createAdapter;
private class TagAdapter<T, Z extends NBTBase> {
private final Function<T, Z> builder;
private final Function<Z, T> extractor;
private final Class<T> primitiveType;
private final Class<Z> nbtBaseType;
public TagAdapter(Class<T> primitiveType, Class<Z> nbtBaseType, Function<T, Z> builder, Function<Z, T> extractor) {
this.primitiveType = primitiveType;
this.nbtBaseType = nbtBaseType;
this.builder = builder;
this.extractor = extractor;
}
/**
* A tag adapter is a closely related type to a specific implementation of
* the {@link NBTBase} interface. It exists to convert from and to the
* respective value of a {@link NBTBase} to a "primitive type" for later
* usage in {@link PersistentDataType}.
*
* @param primitiveType the class of the primitive type, e.g.
* {@link String}.
* @param nbtBaseType the class of the tag implementation that is used to
* store this primitive type, e.g {@link NBTTagString}.
* @param nmsTypeByte the byte identifier of the tag as defined by
* {@link NBTBase#getId()}.
* @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
* the expected primitive type.
* Extract the primitive value from the {@link NBTBase}.
*
* @param base the base to extract from
*
* @return the value stored inside of the tag
*
* @return the value stored inside the tag
* @throws ClassCastException if the passed base is not an instanced of
* the defined base type and therefore is not applicable to the
* extractor function
* extractor function.
*/
T extract(NBTBase base) {
Preconditions.checkArgument(nbtBaseType.isInstance(base), "The provided NBTBase was of the type %s. Expected type %s", base.getClass().getSimpleName(), nbtBaseType.getSimpleName());
return this.extractor.apply(nbtBaseType.cast(base));
private P extract(final PersistentDataType<P, ?> dataType, final NBTBase base) {
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(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
*
* @return the new tag instance
*
* @throws ClassCastException if the passed value object is not of the
* defined primitive type and therefore is not applicable to the builder
* function
* function.
*/
Z build(Object value) {
Preconditions.checkArgument(primitiveType.isInstance(value), "The provided value was of the type %s. Expected type %s", value.getClass().getSimpleName(), primitiveType.getSimpleName());
return this.builder.apply(primitiveType.cast(value));
private T build(final PersistentDataType<P, ?> dataType, final Object value) {
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(dataType, this.primitiveType.cast(value));
}
/**
* Returns if the tag instance matches the adapters one.
*
* @param base the base to check
* Computes if the provided persistent data type's primitive type is a
* representation of the {@link NBTBase}.
*
* @param base the base tag instance to check against
* @return if the tag was an instance of the set type
*/
boolean isInstance(NBTBase base) {
return this.nbtBaseType.isInstance(base);
private boolean isInstance(final PersistentDataType<P, ?> persistentDataType, final NBTBase base) {
return this.matcher.test(persistentDataType, base);
}
}
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 <T> the generic type of that class
*
* @param <T> the generic type of the primitive type
* @return the created adapter instance
*
* @throws IllegalArgumentException if no suitable tag type adapter for this
* 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
}
/*
Primitives
*/
// Primitives
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)) {
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)) {
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)) {
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)) {
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)) {
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)) {
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 Arrays
*/
// Primitive non-list arrays
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)) {
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)) {
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())
);
}
/*
Complex Arrays
*/
// Previously "emulated" compound lists, now useless as a proper list type exists.
if (Objects.equals(PersistentDataContainer[].class, type)) {
return createAdapter(PersistentDataContainer[].class, NBTTagList.class,
return this.createAdapter(
PersistentDataContainer[].class, NBTTagList.class, NBTBase.TAG_LIST,
(containerArray) -> {
NBTTagList list = new NBTTagList();
for (int i = 0; i < containerArray.length; i++) {
list.add(((CraftPersistentDataContainer) containerArray[i]).toTagCompound());
final NBTTagList list = new NBTTagList();
for (final PersistentDataContainer persistentDataContainer : containerArray) {
list.add(((CraftPersistentDataContainer) persistentDataContainer).toTagCompound());
}
return list;
},
(tag) -> {
PersistentDataContainer[] containerArray = new CraftPersistentDataContainer[tag.size()];
final PersistentDataContainer[] containerArray = new CraftPersistentDataContainer[tag.size()];
for (int i = 0; i < tag.size(); i++) {
CraftPersistentDataContainer container = new CraftPersistentDataContainer(this);
NBTTagCompound compound = tag.getCompound(i);
for (String key : compound.getAllKeys()) {
final CraftPersistentDataContainer container = new CraftPersistentDataContainer(this);
final NBTTagCompound compound = tag.getCompound(i);
for (final String key : compound.getAllKeys()) {
container.put(key, compound.get(key));
}
containerArray[i] = container;
}
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;
});
}
/*
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 createAdapter(CraftPersistentDataContainer.class, NBTTagCompound.class, CraftPersistentDataContainer::toTagCompound, tag -> {
CraftPersistentDataContainer container = new CraftPersistentDataContainer(this);
for (String key : tag.getAllKeys()) {
container.put(key, tag.get(key));
}
return container;
});
if (Objects.equals(List.class, type)) {
return createAdapter(
List.class,
net.minecraft.nbt.NBTTagList.class,
NBTBase.TAG_LIST,
this::constructList,
this::extractList,
this::matchesListTag
);
}
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) {
return new TagAdapter<>(primitiveType, nbtBaseType, builder, extractor);
// 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 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 value the value to be stored in the tag
* @param <T> the generic type of the value
*
* @return the created tag instance
*
* @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) {
return this.adapters.computeIfAbsent(type, CREATE_ADAPTER).build(value);
public <T> NBTBase wrap(final PersistentDataType<T, ?> type, final T 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 base the base instance to check
* @param <T> the generic type of the type
*
* @return if the base stores values of the primitive type passed
*
* @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) {
return this.adapters.computeIfAbsent(type, CREATE_ADAPTER).isInstance(base);
public <T> boolean isInstanceOf(final PersistentDataType<T, ?> type, final NBTBase 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 tag the tag to extract the value from
* @param <T> the generic type of the value stored inside the tag
*
* @return the extracted value
*
* @throws IllegalArgumentException if the passed base is not an instanced
* 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
* passed
* passed.
* @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 {
TagAdapter adapter = this.adapters.computeIfAbsent(type, CREATE_ADAPTER);
Preconditions.checkArgument(adapter.isInstance(tag), "The found tag instance (%s) cannot store %s", tag.getClass().getSimpleName(), type.getSimpleName());
public <T, Z extends NBTBase> T extract(final PersistentDataType<T, ?> type, final NBTBase tag) throws ClassCastException, IllegalArgumentException {
final Class<T> primitiveType = type.getPrimitiveType();
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);
Preconditions.checkArgument(type.isInstance(foundValue), "The found object is of the type %s. Expected type %s", foundValue.getClass().getSimpleName(), type.getSimpleName());
return type.cast(foundValue);
final Object foundValue = adapter.extract(type, tag);
Preconditions.checkArgument(primitiveType.isInstance(foundValue), "The found object is of the type %s. Expected type %s", foundValue.getClass().getSimpleName(), primitiveType.getSimpleName());
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;
}
}

View File

@@ -5,6 +5,7 @@ import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import org.bukkit.NamespacedKey;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
/**
* 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
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);
this.dirty(true);
}
@Override
public void remove(NamespacedKey key) {
public void remove(@NotNull NamespacedKey key) {
super.remove(key);
this.dirty(true);
}