mirror of
https://github.com/Minestom/Minestom.git
synced 2025-02-22 23:31:37 +01:00
Generate Tag from record type (#883)
This commit is contained in:
parent
b6ba6901ed
commit
bbd9e58d35
38
src/main/java/net/minestom/server/tag/Serializers.java
Normal file
38
src/main/java/net/minestom/server/tag/Serializers.java
Normal file
@ -0,0 +1,38 @@
|
||||
package net.minestom.server.tag;
|
||||
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import org.jglrxavpok.hephaistos.nbt.*;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
final class Serializers {
|
||||
static final Entry<?, ?> VOID = new Entry<>(null, null);
|
||||
|
||||
static final Entry<Byte, NBTByte> BYTE = new Entry<>(NBTByte::getValue, NBT::Byte);
|
||||
static final Entry<Short, NBTShort> SHORT = new Entry<>(NBTShort::getValue, NBT::Short);
|
||||
static final Entry<Integer, NBTInt> INT = new Entry<>(NBTInt::getValue, NBT::Int);
|
||||
static final Entry<Long, NBTLong> LONG = new Entry<>(NBTLong::getValue, NBT::Long);
|
||||
static final Entry<Float, NBTFloat> FLOAT = new Entry<>(NBTFloat::getValue, NBT::Float);
|
||||
static final Entry<Double, NBTDouble> DOUBLE = new Entry<>(NBTDouble::getValue, NBT::Double);
|
||||
static final Entry<String, NBTString> STRING = new Entry<>(NBTString::getValue, NBT::String);
|
||||
static final Entry<NBT, NBT> NBT_ENTRY = new Entry<>(Function.identity(), Function.identity());
|
||||
|
||||
static final Entry<ItemStack, NBTCompound> ITEM = new Entry<>(ItemStack::fromItemNBT, ItemStack::toItemNBT);
|
||||
|
||||
static <T> Entry<T,?> fromTagSerializer(TagSerializer<T> serializer) {
|
||||
return new Serializers.Entry<>(
|
||||
(NBTCompound compound) -> {
|
||||
if (compound.isEmpty()) return null;
|
||||
return serializer.read(TagHandler.fromCompound(compound));
|
||||
},
|
||||
(value) -> {
|
||||
if (value == null) return NBTCompound.EMPTY;
|
||||
TagHandler handler = TagHandler.newHandler();
|
||||
serializer.write(handler, value);
|
||||
return handler.asCompound();
|
||||
});
|
||||
}
|
||||
|
||||
record Entry<T, N extends NBT>(Function<N, T> read, Function<T, N> write) {
|
||||
}
|
||||
}
|
@ -6,7 +6,10 @@ import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.*;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBT;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompoundLike;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTList;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTType;
|
||||
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
|
||||
|
||||
import java.util.List;
|
||||
@ -30,40 +33,43 @@ public class Tag<T> {
|
||||
|
||||
final int index;
|
||||
private final String key;
|
||||
final Function<NBT, T> readFunction;
|
||||
final Function<T, NBT> writeFunction;
|
||||
final Serializers.Entry<T, NBT> entry;
|
||||
private final Supplier<T> defaultValue;
|
||||
|
||||
final Function<?, ?> originalRead;
|
||||
final Function<?, ?> readComparator;
|
||||
// Optional properties
|
||||
final PathEntry[] path;
|
||||
final UnaryOperator<T> copy;
|
||||
final int listScope;
|
||||
|
||||
Tag(int index, String key,
|
||||
Function<?, ?> originalRead,
|
||||
Function<NBT, T> readFunction, Function<T, NBT> writeFunction,
|
||||
Function<?, ?> readComparator,
|
||||
Serializers.Entry<T, NBT> entry,
|
||||
Supplier<T> defaultValue, PathEntry[] path, UnaryOperator<T> copy, int listScope) {
|
||||
assert index == INDEX_MAP.get(key);
|
||||
this.index = index;
|
||||
this.key = key;
|
||||
this.originalRead = originalRead;
|
||||
this.readFunction = readFunction;
|
||||
this.writeFunction = writeFunction;
|
||||
this.readComparator = readComparator;
|
||||
this.entry = entry;
|
||||
this.defaultValue = defaultValue;
|
||||
this.path = path;
|
||||
this.copy = copy;
|
||||
this.listScope = listScope;
|
||||
}
|
||||
|
||||
static <T, N extends NBT> Tag<T> tag(@NotNull String key,
|
||||
@NotNull Function<N, T> readFunction,
|
||||
@NotNull Function<T, N> writeFunction) {
|
||||
return new Tag<>(INDEX_MAP.get(key), key, readFunction,
|
||||
(Function<NBT, T>) readFunction, (Function<T, NBT>) writeFunction,
|
||||
static <T, N extends NBT> Tag<T> tag(@NotNull String key, @NotNull Serializers.Entry<T, N> entry) {
|
||||
return new Tag<>(INDEX_MAP.get(key), key, entry.read(), (Serializers.Entry<T, NBT>) entry,
|
||||
null, null, null, 0);
|
||||
}
|
||||
|
||||
static <T> Tag<T> fromSerializer(@NotNull String key, @NotNull TagSerializer<T> serializer) {
|
||||
if (serializer instanceof TagRecord.Serializer recordSerializer) {
|
||||
// Allow fast retrieval
|
||||
return tag(key, recordSerializer.serializerEntry);
|
||||
}
|
||||
return tag(key, Serializers.fromTagSerializer(serializer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key used to navigate inside the holder nbt.
|
||||
*
|
||||
@ -75,7 +81,7 @@ public class Tag<T> {
|
||||
|
||||
@Contract(value = "_ -> new", pure = true)
|
||||
public Tag<T> defaultValue(@NotNull Supplier<T> defaultValue) {
|
||||
return new Tag<>(index, key, originalRead, readFunction, writeFunction, defaultValue, path, copy, listScope);
|
||||
return new Tag<>(index, key, readComparator, entry, defaultValue, path, copy, listScope);
|
||||
}
|
||||
|
||||
@Contract(value = "_ -> new", pure = true)
|
||||
@ -86,15 +92,14 @@ public class Tag<T> {
|
||||
@Contract(value = "_, _ -> new", pure = true)
|
||||
public <R> Tag<R> map(@NotNull Function<T, R> readMap,
|
||||
@NotNull Function<R, T> writeMap) {
|
||||
return new Tag<>(index, key,
|
||||
readMap,
|
||||
// Read
|
||||
readFunction.andThen(t -> {
|
||||
if (t == null) return null;
|
||||
return readMap.apply(t);
|
||||
}),
|
||||
// Write
|
||||
writeMap.andThen(writeFunction),
|
||||
var entry = this.entry;
|
||||
final Function<NBT, R> readFunction = entry.read().andThen(t -> {
|
||||
if (t == null) return null;
|
||||
return readMap.apply(t);
|
||||
});
|
||||
final Function<R, NBT> writeFunction = writeMap.andThen(entry.write());
|
||||
return new Tag<>(index, key, readMap,
|
||||
new Serializers.Entry<>(readFunction, writeFunction),
|
||||
// Default value
|
||||
() -> readMap.apply(createDefault()),
|
||||
path, null, listScope);
|
||||
@ -103,7 +108,10 @@ public class Tag<T> {
|
||||
@ApiStatus.Experimental
|
||||
@Contract(value = "-> new", pure = true)
|
||||
public Tag<List<T>> list() {
|
||||
return new Tag<>(index, key, originalRead,
|
||||
var entry = this.entry;
|
||||
var readFunction = entry.read();
|
||||
var writeFunction = entry.write();
|
||||
var listEntry = new Serializers.Entry<List<T>, NBT>(
|
||||
read -> {
|
||||
var list = (NBTList<?>) read;
|
||||
final int size = list.getSize();
|
||||
@ -131,35 +139,34 @@ public class Tag<T> {
|
||||
array[i] = nbt;
|
||||
}
|
||||
return NBT.List(type, List.of(array));
|
||||
}, null, path,
|
||||
copy != null ? ts -> {
|
||||
final int size = ts.size();
|
||||
T[] array = (T[]) new Object[size];
|
||||
boolean shallowCopy = true;
|
||||
for (int i = 0; i < size; i++) {
|
||||
final T t = ts.get(i);
|
||||
final T copy = this.copy.apply(t);
|
||||
if (shallowCopy && copy != t) shallowCopy = false;
|
||||
array[i] = copy;
|
||||
}
|
||||
return shallowCopy ? List.copyOf(ts) : List.of(array);
|
||||
} : List::copyOf, listScope + 1);
|
||||
});
|
||||
UnaryOperator<List<T>> co = this.copy != null ? ts -> {
|
||||
final int size = ts.size();
|
||||
T[] array = (T[]) new Object[size];
|
||||
boolean shallowCopy = true;
|
||||
for (int i = 0; i < size; i++) {
|
||||
final T t = ts.get(i);
|
||||
final T copy = this.copy.apply(t);
|
||||
if (shallowCopy && copy != t) shallowCopy = false;
|
||||
array[i] = copy;
|
||||
}
|
||||
return shallowCopy ? List.copyOf(ts) : List.of(array);
|
||||
} : List::copyOf;
|
||||
return new Tag<>(index, key, readComparator, listEntry, null, path, co, listScope + 1);
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
@Contract(value = "_ -> new", pure = true)
|
||||
public Tag<T> path(@NotNull String @Nullable ... path) {
|
||||
if (path == null || path.length == 0) {
|
||||
return new Tag<>(index, key, originalRead,
|
||||
readFunction, writeFunction, defaultValue, null, copy, listScope);
|
||||
return new Tag<>(index, key, readComparator, entry, defaultValue, null, copy, listScope);
|
||||
}
|
||||
PathEntry[] entries = new PathEntry[path.length];
|
||||
PathEntry[] pathEntries = new PathEntry[path.length];
|
||||
for (int i = 0; i < path.length; i++) {
|
||||
var name = path[i];
|
||||
entries[i] = new PathEntry(name, INDEX_MAP.get(name));
|
||||
pathEntries[i] = new PathEntry(name, INDEX_MAP.get(name));
|
||||
}
|
||||
return new Tag<>(index, key, originalRead,
|
||||
readFunction, writeFunction, defaultValue, entries, copy, listScope);
|
||||
return new Tag<>(index, key, readComparator, entry, defaultValue, pathEntries, copy, listScope);
|
||||
}
|
||||
|
||||
public @Nullable T read(@NotNull NBTCompoundLike nbt) {
|
||||
@ -167,7 +174,7 @@ public class Tag<T> {
|
||||
final NBT readable = key.isEmpty() ? nbt.toCompound() : nbt.get(key);
|
||||
final T result;
|
||||
try {
|
||||
if (readable == null || (result = readFunction.apply(readable)) == null)
|
||||
if (readable == null || (result = entry.read().apply(readable)) == null)
|
||||
return createDefault();
|
||||
return result;
|
||||
} catch (ClassCastException e) {
|
||||
@ -183,7 +190,7 @@ public class Tag<T> {
|
||||
public void write(@NotNull MutableNBTCompound nbtCompound, @Nullable T value) {
|
||||
final String key = this.key;
|
||||
if (value != null) {
|
||||
final NBT nbt = writeFunction.apply(value);
|
||||
final NBT nbt = entry.write().apply(value);
|
||||
if (key.isEmpty()) nbtCompound.copyFrom((NBTCompoundLike) nbt);
|
||||
else nbtCompound.set(key, nbt);
|
||||
} else {
|
||||
@ -198,41 +205,43 @@ public class Tag<T> {
|
||||
}
|
||||
|
||||
final boolean shareValue(@NotNull Tag<?> other) {
|
||||
// Verify if these 2 tags can share the same cached value
|
||||
// Key/Default value/Path are ignored
|
||||
return this == other || (this.originalRead == other.originalRead && this.listScope == other.listScope);
|
||||
if (this == other) return true;
|
||||
// Tags are not strictly the same, compare readers
|
||||
if (this.listScope != other.listScope)
|
||||
return false;
|
||||
return this.readComparator == other.readComparator;
|
||||
}
|
||||
|
||||
public static @NotNull Tag<Byte> Byte(@NotNull String key) {
|
||||
return tag(key, NBTByte::getValue, NBT::Byte);
|
||||
return tag(key, Serializers.BYTE);
|
||||
}
|
||||
|
||||
public static @NotNull Tag<Short> Short(@NotNull String key) {
|
||||
return tag(key, NBTShort::getValue, NBT::Short);
|
||||
return tag(key, Serializers.SHORT);
|
||||
}
|
||||
|
||||
public static @NotNull Tag<Integer> Integer(@NotNull String key) {
|
||||
return tag(key, NBTInt::getValue, NBT::Int);
|
||||
return tag(key, Serializers.INT);
|
||||
}
|
||||
|
||||
public static @NotNull Tag<Long> Long(@NotNull String key) {
|
||||
return tag(key, NBTLong::getValue, NBT::Long);
|
||||
return tag(key, Serializers.LONG);
|
||||
}
|
||||
|
||||
public static @NotNull Tag<Float> Float(@NotNull String key) {
|
||||
return tag(key, NBTFloat::getValue, NBT::Float);
|
||||
return tag(key, Serializers.FLOAT);
|
||||
}
|
||||
|
||||
public static @NotNull Tag<Double> Double(@NotNull String key) {
|
||||
return tag(key, NBTDouble::getValue, NBT::Double);
|
||||
return tag(key, Serializers.DOUBLE);
|
||||
}
|
||||
|
||||
public static @NotNull Tag<String> String(@NotNull String key) {
|
||||
return tag(key, NBTString::getValue, NBT::String);
|
||||
return tag(key, Serializers.STRING);
|
||||
}
|
||||
|
||||
public static <T extends NBT> @NotNull Tag<T> NBT(@NotNull String key) {
|
||||
return Tag.<T, T>tag(key, nbt -> nbt, t -> t);
|
||||
return tag(key, (Serializers.Entry<T, ? extends NBT>) Serializers.NBT_ENTRY);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -244,26 +253,20 @@ public class Tag<T> {
|
||||
* @return the created tag
|
||||
*/
|
||||
public static <T> @NotNull Tag<T> Structure(@NotNull String key, @NotNull TagSerializer<T> serializer) {
|
||||
return tag(key,
|
||||
(NBTCompound compound) -> serializer.read(TagHandler.fromCompound(compound)),
|
||||
(value) -> {
|
||||
TagHandler handler = TagHandler.newHandler();
|
||||
serializer.write(handler, value);
|
||||
return handler.asCompound();
|
||||
});
|
||||
return fromSerializer(key, serializer);
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
public static <T extends Record> @NotNull Tag<T> Structure(@NotNull String key, @NotNull Class<T> type) {
|
||||
assert type.isRecord();
|
||||
return fromSerializer(key, TagRecord.serializer(type));
|
||||
}
|
||||
|
||||
public static <T> @NotNull Tag<T> View(@NotNull TagSerializer<T> serializer) {
|
||||
return tag("",
|
||||
(NBTCompound compound) -> serializer.read(TagHandler.fromCompound(compound)),
|
||||
(value) -> {
|
||||
TagHandler handler = TagHandler.newHandler();
|
||||
serializer.write(handler, value);
|
||||
return handler.asCompound();
|
||||
});
|
||||
return Structure("", serializer);
|
||||
}
|
||||
|
||||
public static @NotNull Tag<ItemStack> ItemStack(@NotNull String key) {
|
||||
return tag(key, ItemStack::fromItemNBT, ItemStack::toItemNBT);
|
||||
return tag(key, Serializers.ITEM);
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ final class TagHandlerImpl implements TagHandler {
|
||||
if (value == null) return;
|
||||
// Empty path, create a new handler
|
||||
local = new TagHandlerImpl();
|
||||
entries[pathIndex] = new Entry<>(Tag.tag(path.name(), null, null), local);
|
||||
entries[pathIndex] = new Entry(Tag.tag(path.name(), Serializers.VOID), local);
|
||||
} else if (entry.value instanceof TagHandlerImpl handler) {
|
||||
// Existing path, continue navigating
|
||||
local = handler;
|
||||
@ -161,7 +161,7 @@ final class TagHandlerImpl implements TagHandler {
|
||||
|
||||
NBT updatedNbt() {
|
||||
NBT nbt = this.nbt;
|
||||
if (nbt == null) this.nbt = nbt = tag.writeFunction.apply(value);
|
||||
if (nbt == null) this.nbt = nbt = tag.entry.write().apply(value);
|
||||
return nbt;
|
||||
}
|
||||
}
|
||||
@ -210,7 +210,7 @@ final class TagHandlerImpl implements TagHandler {
|
||||
// Value must be parsed from nbt if the tag is different
|
||||
final NBT nbt = entry.updatedNbt();
|
||||
try {
|
||||
return tag.readFunction.apply(nbt);
|
||||
return tag.entry.read().apply(nbt);
|
||||
} catch (ClassCastException e) {
|
||||
return tag.createDefault();
|
||||
}
|
||||
|
108
src/main/java/net/minestom/server/tag/TagRecord.java
Normal file
108
src/main/java/net/minestom/server/tag/TagRecord.java
Normal file
@ -0,0 +1,108 @@
|
||||
package net.minestom.server.tag;
|
||||
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBT;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.RecordComponent;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static java.util.Map.entry;
|
||||
|
||||
final class TagRecord {
|
||||
static final Map<Class<?>, Function<String, Tag<?>>> SUPPORTED_TYPES = Map.ofEntries(
|
||||
entry(Byte.class, Tag::Byte), entry(byte.class, Tag::Byte),
|
||||
entry(Short.class, Tag::Short), entry(short.class, Tag::Short),
|
||||
entry(Integer.class, Tag::Integer), entry(int.class, Tag::Integer),
|
||||
entry(Long.class, Tag::Long), entry(long.class, Tag::Long),
|
||||
entry(Float.class, Tag::Float), entry(float.class, Tag::Float),
|
||||
entry(Double.class, Tag::Double), entry(double.class, Tag::Double),
|
||||
entry(String.class, Tag::String),
|
||||
|
||||
entry(ItemStack.class, Tag::ItemStack));
|
||||
|
||||
static final ClassValue<Serializer<? extends Record>> serializers = new ClassValue<>() {
|
||||
@Override
|
||||
protected Serializer<? extends Record> computeValue(Class<?> type) {
|
||||
assert type.isRecord();
|
||||
final RecordComponent[] components = type.getRecordComponents();
|
||||
final Entry[] entries = Arrays.stream(components)
|
||||
.map(recordComponent -> {
|
||||
final String componentName = recordComponent.getName();
|
||||
final Class<?> componentType = recordComponent.getType();
|
||||
final Tag<?> tag;
|
||||
if (componentType.isRecord()) {
|
||||
tag = Tag.Structure(componentName, serializers.get(componentType));
|
||||
} else if (NBT.class.isAssignableFrom(componentType)) {
|
||||
tag = Tag.NBT(componentName);
|
||||
} else {
|
||||
final var fun = SUPPORTED_TYPES.get(componentType);
|
||||
if (fun == null)
|
||||
throw new IllegalArgumentException("Unsupported type: " + componentType);
|
||||
tag = fun.apply(componentName);
|
||||
}
|
||||
return new Entry(recordComponent, (Tag<Object>) tag);
|
||||
}).toArray(Entry[]::new);
|
||||
Constructor<?> constructor;
|
||||
try {
|
||||
constructor = type.getDeclaredConstructor(Arrays.stream(components).map(RecordComponent::getType).toArray(Class[]::new));
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return new Serializer<>(Constructor.class.cast(constructor), entries);
|
||||
}
|
||||
};
|
||||
|
||||
public static <T extends Record> @NotNull Serializer<T> serializer(@NotNull Class<T> type) {
|
||||
//noinspection unchecked
|
||||
return (Serializer<T>) serializers.get(type);
|
||||
}
|
||||
|
||||
static final class Serializer<T extends Record> implements TagSerializer<T> {
|
||||
Constructor<T> constructor;
|
||||
Entry[] entries;
|
||||
Serializers.Entry<T, ?> serializerEntry;
|
||||
|
||||
Serializer(Constructor<T> constructor, Entry[] entries) {
|
||||
this.constructor = constructor;
|
||||
this.entries = entries;
|
||||
this.serializerEntry = Serializers.fromTagSerializer(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable T read(@NotNull TagReadable reader) {
|
||||
Object[] components = new Object[entries.length];
|
||||
for (int i = 0; i < components.length; i++) {
|
||||
final Entry entry = entries[i];
|
||||
Object component = reader.getTag(entry.tag);
|
||||
if (component == null) return null;
|
||||
components[i] = component;
|
||||
}
|
||||
try {
|
||||
return constructor.newInstance(components);
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull TagWritable writer, @NotNull T value) {
|
||||
try {
|
||||
for (Entry entry : entries) {
|
||||
final Object component = entry.component.getAccessor().invoke(value);
|
||||
writer.setTag(entry.tag, component);
|
||||
}
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
record Entry(RecordComponent component, Tag<Object> tag) {
|
||||
}
|
||||
}
|
@ -22,7 +22,7 @@ public interface TagSerializer<T> {
|
||||
* Writes the custom tag to a {@link TagWritable}.
|
||||
*
|
||||
* @param writer the writer
|
||||
* @param value the value to serialize, null to remove
|
||||
* @param value the value to serialize
|
||||
*/
|
||||
void write(@NotNull TagWritable writer, @Nullable T value);
|
||||
void write(@NotNull TagWritable writer, @NotNull T value);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.minestom.server.tag;
|
||||
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.function.Function;
|
||||
@ -96,4 +97,19 @@ public class TagEqualityTest {
|
||||
assertFalse(tag.shareValue(tag2));
|
||||
assertFalse(tag.list().shareValue(tag2.list()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void recordStructure() {
|
||||
var tag = Tag.Structure("test", Vec.class);
|
||||
var tag2 = Tag.Structure("test", Vec.class);
|
||||
assertTrue(tag.shareValue(tag2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void recordStructureList() {
|
||||
var tag = Tag.Structure("test", Vec.class).list();
|
||||
var tag2 = Tag.Structure("test", Vec.class).list();
|
||||
assertTrue(tag.shareValue(tag2));
|
||||
assertTrue(tag.list().shareValue(tag2.list()));
|
||||
}
|
||||
}
|
||||
|
@ -223,8 +223,8 @@ public class TagPathTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull TagWritable writer, @Nullable Entry value) {
|
||||
writer.setTag(VALUE_TAG, value != null ? value.value : null);
|
||||
public void write(@NotNull TagWritable writer, @NotNull Entry value) {
|
||||
writer.setTag(VALUE_TAG, value.value);
|
||||
}
|
||||
});
|
||||
|
||||
|
91
src/test/java/net/minestom/server/tag/TagRecordTest.java
Normal file
91
src/test/java/net/minestom/server/tag/TagRecordTest.java
Normal file
@ -0,0 +1,91 @@
|
||||
package net.minestom.server.tag;
|
||||
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBT;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static net.minestom.server.api.TestUtils.assertEqualsSNBT;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TagRecordTest {
|
||||
|
||||
@Test
|
||||
public void basic() {
|
||||
var handler = TagHandler.newHandler();
|
||||
var tag = Tag.Structure("vec", Vec.class);
|
||||
var vec = new Vec(1, 2, 3);
|
||||
assertNull(handler.getTag(tag));
|
||||
handler.setTag(tag, vec);
|
||||
assertEquals(vec, handler.getTag(tag));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicSerializer() {
|
||||
var handler = TagHandler.newHandler();
|
||||
var serializer = TagRecord.serializer(Vec.class);
|
||||
serializer.write(handler, new Vec(1, 2, 3));
|
||||
assertEquals(new Vec(1, 2, 3), serializer.read(handler));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicSnbt() {
|
||||
var handler = TagHandler.newHandler();
|
||||
var tag = Tag.Structure("vec", Vec.class);
|
||||
var vec = new Vec(1, 2, 3);
|
||||
handler.setTag(tag, vec);
|
||||
assertEqualsSNBT("""
|
||||
{
|
||||
"vec": {
|
||||
"x":1D,
|
||||
"y":2D,
|
||||
"z":3D
|
||||
}
|
||||
}
|
||||
""", handler.asCompound());
|
||||
handler.removeTag(tag);
|
||||
assertEqualsSNBT("{}", handler.asCompound());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nbtSerializer() {
|
||||
record CompoundRecord(NBTCompound compound) {
|
||||
}
|
||||
var test = new CompoundRecord(NBT.Compound(Map.of("key", NBT.String("value"))));
|
||||
var handler = TagHandler.newHandler();
|
||||
var serializer = TagRecord.serializer(CompoundRecord.class);
|
||||
serializer.write(handler, test);
|
||||
assertEquals(test, serializer.read(handler));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unsupportedList() {
|
||||
record Test(List<Object> list) {
|
||||
}
|
||||
assertThrows(IllegalArgumentException.class, () -> Tag.Structure("test", Test.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unsupportedArray() {
|
||||
record Test(Object[] array) {
|
||||
}
|
||||
assertThrows(IllegalArgumentException.class, () -> Tag.Structure("test", Test.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forceRecord() {
|
||||
assertThrows(Throwable.class, () -> Tag.Structure("entity", Class.class.cast(Entity.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidItem() {
|
||||
// ItemStack cannot become a record due to `ItemStack#toItemNBT` being serialized differently, and independently of
|
||||
// the item record components
|
||||
assertThrows(Throwable.class, () -> Tag.Structure("item", Class.class.cast(ItemStack.class)));
|
||||
}
|
||||
}
|
@ -19,12 +19,8 @@ public class TagStructureTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull TagWritable writer, @Nullable Entry value) {
|
||||
if (value != null) {
|
||||
writer.setTag(VALUE_TAG, value.value);
|
||||
} else {
|
||||
writer.removeTag(VALUE_TAG);
|
||||
}
|
||||
public void write(@NotNull TagWritable writer, @NotNull Entry value) {
|
||||
writer.setTag(VALUE_TAG, value.value);
|
||||
}
|
||||
});
|
||||
|
||||
@ -38,12 +34,8 @@ public class TagStructureTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull TagWritable writer, @Nullable Entry value) {
|
||||
if (value != null) {
|
||||
writer.setTag(VALUE_TAG, value.value);
|
||||
} else {
|
||||
writer.removeTag(VALUE_TAG);
|
||||
}
|
||||
public void write(@NotNull TagWritable writer, @NotNull Entry value) {
|
||||
writer.setTag(VALUE_TAG, value.value);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -19,12 +19,8 @@ public class TagViewTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull TagWritable writer, @Nullable Entry value) {
|
||||
if (value != null) {
|
||||
writer.setTag(VALUE_TAG, value.value);
|
||||
} else {
|
||||
writer.removeTag(VALUE_TAG);
|
||||
}
|
||||
public void write(@NotNull TagWritable writer, @NotNull Entry value) {
|
||||
writer.setTag(VALUE_TAG, value.value);
|
||||
}
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user