mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-23 16:41:35 +01:00
Tag internal rework (#782)
This commit is contained in:
parent
84871ea93e
commit
f2fec73202
@ -0,0 +1,48 @@
|
||||
package net.minestom.jmh.tag;
|
||||
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import org.openjdk.jmh.annotations.*;
|
||||
import org.openjdk.jmh.infra.Blackhole;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Warmup(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
|
||||
@Measurement(iterations = 10, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
|
||||
@Fork(3)
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
@State(Scope.Benchmark)
|
||||
public class TagReadBenchmark {
|
||||
static final Tag<String> TAG = Tag.String("key");
|
||||
|
||||
@Param({"false", "true"})
|
||||
public boolean present;
|
||||
|
||||
TagHandler tagHandler;
|
||||
Tag<String> secondTag;
|
||||
|
||||
@Setup
|
||||
public void setup() {
|
||||
this.tagHandler = TagHandler.newHandler();
|
||||
if (present) {
|
||||
tagHandler.setTag(TAG, "value");
|
||||
}
|
||||
secondTag = Tag.String("key");
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void readConstantTag(Blackhole blackhole) {
|
||||
blackhole.consume(tagHandler.getTag(TAG));
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void readDifferentTag(Blackhole blackhole) {
|
||||
blackhole.consume(tagHandler.getTag(secondTag));
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void readNewTag(Blackhole blackhole) {
|
||||
blackhole.consume(tagHandler.getTag(Tag.String("key")));
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.permission.PermissionHandler;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import net.minestom.server.tag.Taggable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@ -12,7 +12,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
* <p>
|
||||
* Main implementations are {@link Player} and {@link ConsoleSender}.
|
||||
*/
|
||||
public interface CommandSender extends PermissionHandler, Audience, TagHandler {
|
||||
public interface CommandSender extends PermissionHandler, Audience, Taggable {
|
||||
|
||||
/**
|
||||
* Sends a raw string message.
|
||||
|
@ -5,11 +5,8 @@ import net.kyori.adventure.identity.Identity;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import net.minestom.server.permission.Permission;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -24,7 +21,7 @@ public class ConsoleSender implements CommandSender {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ConsoleSender.class);
|
||||
|
||||
private final Set<Permission> permissions = new CopyOnWriteArraySet<>();
|
||||
private final MutableNBTCompound nbtCompound = new MutableNBTCompound();
|
||||
private final TagHandler tagHandler = TagHandler.newHandler();
|
||||
|
||||
@Override
|
||||
public void sendMessage(@NotNull String message) {
|
||||
@ -54,12 +51,7 @@ public class ConsoleSender implements CommandSender {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return tag.read(nbtCompound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
tag.write(nbtCompound, value);
|
||||
public @NotNull TagHandler tagHandler() {
|
||||
return tagHandler;
|
||||
}
|
||||
}
|
||||
|
@ -3,11 +3,8 @@ package net.minestom.server.command;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.minestom.server.command.builder.CommandContext;
|
||||
import net.minestom.server.permission.Permission;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
@ -23,7 +20,7 @@ import java.util.Set;
|
||||
public class ServerSender implements CommandSender {
|
||||
|
||||
private final Set<Permission> permissions = Collections.unmodifiableSet(new HashSet<>());
|
||||
private final MutableNBTCompound nbtCompound = new MutableNBTCompound();
|
||||
private final TagHandler tagHandler = TagHandler.newHandler();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
@ -32,12 +29,7 @@ public class ServerSender implements CommandSender {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return tag.read(nbtCompound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
tag.write(nbtCompound, value);
|
||||
public @NotNull TagHandler tagHandler() {
|
||||
return tagHandler;
|
||||
}
|
||||
}
|
||||
|
@ -42,9 +42,8 @@ import net.minestom.server.potion.TimedPotion;
|
||||
import net.minestom.server.snapshot.EntitySnapshot;
|
||||
import net.minestom.server.snapshot.SnapshotUpdater;
|
||||
import net.minestom.server.snapshot.Snapshotable;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import net.minestom.server.tag.TagReadable;
|
||||
import net.minestom.server.tag.Taggable;
|
||||
import net.minestom.server.thread.Acquirable;
|
||||
import net.minestom.server.timer.Schedulable;
|
||||
import net.minestom.server.timer.Scheduler;
|
||||
@ -64,8 +63,6 @@ import net.minestom.server.utils.validate.Check;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
|
||||
import space.vectrix.flare.fastutil.Int2ObjectSyncMap;
|
||||
|
||||
import java.time.Duration;
|
||||
@ -85,7 +82,7 @@ import java.util.function.UnaryOperator;
|
||||
* <p>
|
||||
* To create your own entity you probably want to extends {@link LivingEntity} or {@link EntityCreature} instead.
|
||||
*/
|
||||
public class Entity implements Viewable, Tickable, Schedulable, Snapshotable, EventHandler<EntityEvent>, TagHandler, PermissionHandler, HoverEventSource<ShowEntity>, Sound.Emitter {
|
||||
public class Entity implements Viewable, Tickable, Schedulable, Snapshotable, EventHandler<EntityEvent>, Taggable, PermissionHandler, HoverEventSource<ShowEntity>, Sound.Emitter {
|
||||
|
||||
private static final Int2ObjectSyncMap<Entity> ENTITY_BY_ID = Int2ObjectSyncMap.hashmap();
|
||||
private static final Map<UUID, Entity> ENTITY_BY_UUID = new ConcurrentHashMap<>();
|
||||
@ -150,7 +147,7 @@ public class Entity implements Viewable, Tickable, Schedulable, Snapshotable, Ev
|
||||
|
||||
protected final EntityView viewEngine = new EntityView(this);
|
||||
protected final Set<Player> viewers = viewEngine.set;
|
||||
private final MutableNBTCompound nbtCompound = new MutableNBTCompound();
|
||||
private final TagHandler tagHandler = TagHandler.newHandler();
|
||||
private final Scheduler scheduler = Scheduler.newScheduler();
|
||||
private final EventNode<EntityEvent> eventNode;
|
||||
private final Set<Permission> permissions = new CopyOnWriteArraySet<>();
|
||||
@ -1538,13 +1535,8 @@ public class Entity implements Viewable, Tickable, Schedulable, Snapshotable, Ev
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return tag.read(nbtCompound);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
tag.write(nbtCompound, value);
|
||||
public @NotNull TagHandler tagHandler() {
|
||||
return tagHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1561,7 +1553,7 @@ public class Entity implements Viewable, Tickable, Schedulable, Snapshotable, Ev
|
||||
return new EntitySnapshotImpl.Entity(entityType, uuid, id, position, velocity,
|
||||
updater.reference(instance), chunk.getChunkX(), chunk.getChunkZ(),
|
||||
viewersId, passengersId, vehicle == null ? -1 : vehicle.getEntityId(),
|
||||
TagReadable.fromCompound(nbtCompound.toCompound()));
|
||||
tagHandler.readableCopy());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -5,12 +5,10 @@ import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.entity.LivingEntity;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.sound.SoundEvent;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import net.minestom.server.tag.Taggable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
|
||||
|
||||
/**
|
||||
* Represents a type of damage, required when calling {@link LivingEntity#damage(DamageType, float)}
|
||||
@ -18,7 +16,7 @@ import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
|
||||
* <p>
|
||||
* This class can be extended if you need to include custom fields and/or methods.
|
||||
*/
|
||||
public class DamageType implements TagHandler {
|
||||
public class DamageType implements Taggable {
|
||||
|
||||
public static final DamageType VOID = new DamageType("attack.outOfWorld");
|
||||
public static final DamageType GRAVITY = new DamageType("attack.fall");
|
||||
@ -29,8 +27,7 @@ public class DamageType implements TagHandler {
|
||||
}
|
||||
};
|
||||
private final String identifier;
|
||||
private final Object nbtLock = new Object();
|
||||
private final MutableNBTCompound nbt = new MutableNBTCompound();
|
||||
private final TagHandler tagHandler = TagHandler.newHandler();
|
||||
|
||||
/**
|
||||
* Creates a new damage type.
|
||||
@ -128,16 +125,7 @@ public class DamageType implements TagHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
synchronized (nbtLock) {
|
||||
return tag.read(nbt);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
synchronized (nbtLock) {
|
||||
tag.write(nbt, value);
|
||||
}
|
||||
public @NotNull TagHandler tagHandler() {
|
||||
return tagHandler;
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package net.minestom.server.instance;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.instance.block.BlockHandler;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.utils.NamespaceID;
|
||||
import net.minestom.server.utils.async.AsyncUtils;
|
||||
import net.minestom.server.world.biomes.Biome;
|
||||
@ -52,7 +51,7 @@ public class AnvilLoader implements IChunkLoader {
|
||||
try (var reader = new NBTReader(Files.newInputStream(levelPath))) {
|
||||
final NBTCompound tag = (NBTCompound) reader.read();
|
||||
Files.copy(levelPath, path.resolve("level.dat_old"), StandardCopyOption.REPLACE_EXISTING);
|
||||
instance.setTag(Tag.NBT, tag);
|
||||
instance.tagHandler().updateContent(tag);
|
||||
} catch (IOException | NBTException e) {
|
||||
MinecraftServer.getExceptionManager().handleException(e);
|
||||
}
|
||||
@ -213,8 +212,8 @@ public class AnvilLoader implements IChunkLoader {
|
||||
|
||||
@Override
|
||||
public @NotNull CompletableFuture<Void> saveInstance(@NotNull Instance instance) {
|
||||
final var nbt = instance.getTag(Tag.NBT);
|
||||
if (nbt == null) {
|
||||
final var nbt = instance.tagHandler().asCompound();
|
||||
if (nbt.isEmpty()) {
|
||||
// Instance has no data
|
||||
return AsyncUtils.VOID_FUTURE;
|
||||
}
|
||||
|
@ -9,15 +9,12 @@ import net.minestom.server.entity.pathfinding.PFColumnarSpace;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.network.packet.server.play.ChunkDataPacket;
|
||||
import net.minestom.server.snapshot.Snapshotable;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import net.minestom.server.tag.Taggable;
|
||||
import net.minestom.server.utils.chunk.ChunkSupplier;
|
||||
import net.minestom.server.utils.chunk.ChunkUtils;
|
||||
import net.minestom.server.world.biomes.Biome;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@ -36,7 +33,7 @@ import java.util.UUID;
|
||||
* You generally want to avoid storing references of this object as this could lead to a huge memory leak,
|
||||
* you should store the chunk coordinates instead.
|
||||
*/
|
||||
public abstract class Chunk implements Block.Getter, Block.Setter, Biome.Getter, Biome.Setter, Viewable, Tickable, TagHandler, Snapshotable {
|
||||
public abstract class Chunk implements Block.Getter, Block.Setter, Biome.Getter, Biome.Setter, Viewable, Tickable, Taggable, Snapshotable {
|
||||
public static final int CHUNK_SIZE_X = 16;
|
||||
public static final int CHUNK_SIZE_Z = 16;
|
||||
public static final int CHUNK_SECTION_SIZE = 16;
|
||||
@ -58,7 +55,7 @@ public abstract class Chunk implements Block.Getter, Block.Setter, Biome.Getter,
|
||||
protected PFColumnarSpace columnarSpace;
|
||||
|
||||
// Data
|
||||
private final MutableNBTCompound nbt = new MutableNBTCompound();
|
||||
private final TagHandler tagHandler = TagHandler.newHandler();
|
||||
|
||||
public Chunk(@NotNull Instance instance, int chunkX, int chunkZ, boolean shouldGenerate) {
|
||||
this.identifier = UUID.randomUUID();
|
||||
@ -284,13 +281,8 @@ public abstract class Chunk implements Block.Getter, Block.Setter, Biome.Getter,
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return tag.read(nbt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
tag.write(nbt, value);
|
||||
public @NotNull TagHandler tagHandler() {
|
||||
return tagHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -16,8 +16,6 @@ import net.minestom.server.network.packet.server.play.data.ChunkData;
|
||||
import net.minestom.server.network.packet.server.play.data.LightData;
|
||||
import net.minestom.server.snapshot.ChunkSnapshot;
|
||||
import net.minestom.server.snapshot.SnapshotUpdater;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagReadable;
|
||||
import net.minestom.server.utils.ArrayUtils;
|
||||
import net.minestom.server.utils.MathUtils;
|
||||
import net.minestom.server.utils.Utils;
|
||||
@ -257,7 +255,7 @@ public class DynamicChunk extends Chunk {
|
||||
final int[] entityIds = ArrayUtils.mapToIntArray(entities, Entity::getEntityId);
|
||||
return new InstanceSnapshotImpl.Chunk(minSection, chunkX, chunkZ,
|
||||
clonedSections, entries.clone(), entityIds, updater.reference(instance),
|
||||
TagReadable.fromCompound(Objects.requireNonNull(getTag(Tag.NBT))));
|
||||
tagHandler().readableCopy());
|
||||
}
|
||||
|
||||
private void assertLock() {
|
||||
|
@ -22,9 +22,8 @@ import net.minestom.server.snapshot.ChunkSnapshot;
|
||||
import net.minestom.server.snapshot.InstanceSnapshot;
|
||||
import net.minestom.server.snapshot.SnapshotUpdater;
|
||||
import net.minestom.server.snapshot.Snapshotable;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import net.minestom.server.tag.TagReadable;
|
||||
import net.minestom.server.tag.Taggable;
|
||||
import net.minestom.server.thread.ThreadDispatcher;
|
||||
import net.minestom.server.timer.Schedulable;
|
||||
import net.minestom.server.timer.Scheduler;
|
||||
@ -38,9 +37,7 @@ import net.minestom.server.world.DimensionType;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
@ -59,7 +56,7 @@ import java.util.stream.Collectors;
|
||||
* with {@link InstanceManager#registerInstance(Instance)}, and
|
||||
* you need to be sure to signal the {@link ThreadDispatcher} of every partition/element changes.
|
||||
*/
|
||||
public abstract class Instance implements Block.Getter, Block.Setter, Tickable, Schedulable, Snapshotable, TagHandler, PacketGroupingAudience {
|
||||
public abstract class Instance implements Block.Getter, Block.Setter, Tickable, Schedulable, Snapshotable, Taggable, PacketGroupingAudience {
|
||||
|
||||
private boolean registered;
|
||||
|
||||
@ -85,8 +82,7 @@ public abstract class Instance implements Block.Getter, Block.Setter, Tickable,
|
||||
protected UUID uniqueId;
|
||||
|
||||
// instance custom data
|
||||
private final Object nbtLock = new Object();
|
||||
private final MutableNBTCompound nbt = new MutableNBTCompound();
|
||||
private final TagHandler tagHandler = TagHandler.newHandler();
|
||||
|
||||
private final Scheduler scheduler = Scheduler.newScheduler();
|
||||
|
||||
@ -599,17 +595,8 @@ public abstract class Instance implements Block.Getter, Block.Setter, Tickable,
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
synchronized (nbtLock) {
|
||||
return tag.read(nbt);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
synchronized (nbtLock) {
|
||||
tag.write(nbt, value);
|
||||
}
|
||||
public @NotNull TagHandler tagHandler() {
|
||||
return tagHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -623,7 +610,7 @@ public abstract class Instance implements Block.Getter, Block.Setter, Tickable,
|
||||
final int[] entities = ArrayUtils.mapToIntArray(entityTracker.entities(), Entity::getEntityId);
|
||||
return new InstanceSnapshotImpl.Instance(updater.reference(MinecraftServer.process()),
|
||||
getDimensionType(), getWorldAge(), getTime(), chunksMap, entities,
|
||||
TagReadable.fromCompound(Objects.requireNonNull(getTag(Tag.NBT))));
|
||||
tagHandler.readableCopy());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -67,9 +67,7 @@ public sealed interface Block extends ProtocolObject, TagReadable, Blocks permit
|
||||
* @return a new block with different nbt
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
default @NotNull Block withNbt(@Nullable NBTCompound compound) {
|
||||
return withTag(Tag.NBT, compound);
|
||||
}
|
||||
@NotNull Block withNbt(@Nullable NBTCompound compound);
|
||||
|
||||
/**
|
||||
* Creates a new block with the specified {@link BlockHandler handler}.
|
||||
@ -88,9 +86,7 @@ public sealed interface Block extends ProtocolObject, TagReadable, Blocks permit
|
||||
* @return the block nbt, null if not present
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
default @Nullable NBTCompound nbt() {
|
||||
return getTag(Tag.NBT);
|
||||
}
|
||||
@Nullable NBTCompound nbt();
|
||||
|
||||
@Contract(pure = true)
|
||||
default boolean hasNbt() {
|
||||
|
@ -150,6 +150,11 @@ record BlockImpl(@NotNull Registry.BlockEntry registry,
|
||||
return new BlockImpl(registry, propertiesArray, finalNbt, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Block withNbt(@Nullable NBTCompound compound) {
|
||||
return new BlockImpl(registry, propertiesArray, compound, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Block withHandler(@Nullable BlockHandler handler) {
|
||||
return new BlockImpl(registry, propertiesArray, nbt, handler);
|
||||
|
@ -6,14 +6,11 @@ import net.minestom.server.event.inventory.PlayerInventoryItemChangeEvent;
|
||||
import net.minestom.server.inventory.click.InventoryClickProcessor;
|
||||
import net.minestom.server.inventory.condition.InventoryCondition;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import net.minestom.server.tag.Taggable;
|
||||
import net.minestom.server.utils.MathUtils;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
@ -26,7 +23,7 @@ import java.util.function.UnaryOperator;
|
||||
/**
|
||||
* Represents an inventory where items can be modified/retrieved.
|
||||
*/
|
||||
public sealed abstract class AbstractInventory implements InventoryClickHandler, TagHandler
|
||||
public sealed abstract class AbstractInventory implements InventoryClickHandler, Taggable
|
||||
permits Inventory, PlayerInventory {
|
||||
|
||||
private static final VarHandle ITEM_UPDATER = MethodHandles.arrayElementVarHandle(ItemStack[].class);
|
||||
@ -39,8 +36,7 @@ public sealed abstract class AbstractInventory implements InventoryClickHandler,
|
||||
// the click processor which process all the clicks in the inventory
|
||||
protected final InventoryClickProcessor clickProcessor = new InventoryClickProcessor();
|
||||
|
||||
private final Object nbtLock = new Object();
|
||||
private final MutableNBTCompound nbt = new MutableNBTCompound();
|
||||
private final TagHandler tagHandler = TagHandler.newHandler();
|
||||
|
||||
protected AbstractInventory(int size) {
|
||||
this.size = size;
|
||||
@ -251,16 +247,7 @@ public sealed abstract class AbstractInventory implements InventoryClickHandler,
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
synchronized (nbtLock) {
|
||||
return tag.read(nbt);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
synchronized (nbtLock) {
|
||||
tag.write(nbt, value);
|
||||
}
|
||||
public @NotNull TagHandler tagHandler() {
|
||||
return tagHandler;
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,16 @@
|
||||
package net.minestom.server.tag;
|
||||
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
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.NBT;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompoundLike;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTException;
|
||||
import org.jglrxavpok.hephaistos.nbt.*;
|
||||
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
|
||||
import org.jglrxavpok.hephaistos.parser.SNBTParser;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@ -26,74 +23,47 @@ import java.util.function.Supplier;
|
||||
*/
|
||||
@ApiStatus.NonExtendable
|
||||
public class Tag<T> {
|
||||
|
||||
/**
|
||||
* Handles the snbt of the tag holder.
|
||||
* <p>
|
||||
* Writing will override all tags. Proceed with caution.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
public static final Tag<String> SNBT = new Tag<>(null, NBTCompoundLike::toSNBT, (original, snbt) -> {
|
||||
try {
|
||||
final var updated = new SNBTParser(new StringReader(snbt)).parse();
|
||||
if (!(updated instanceof NBTCompound updatedCompound))
|
||||
throw new IllegalArgumentException("'" + snbt + "' is not a compound!");
|
||||
original.copyFrom(updatedCompound);
|
||||
} catch (NBTException e) {
|
||||
MinecraftServer.getExceptionManager().handleException(e);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Handles the complete tag holder compound.
|
||||
* <p>
|
||||
* Writing will override all tags. Proceed with caution.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
public static final Tag<NBTCompound> NBT = new Tag<>(null, NBTCompoundLike::toCompound,
|
||||
(original, updated) -> {
|
||||
if (updated == null) {
|
||||
original.clear();
|
||||
return;
|
||||
}
|
||||
original.copyFrom(updated);
|
||||
});
|
||||
private static final Map<String, Integer> INDEX_MAP = new ConcurrentHashMap<>();
|
||||
private static final AtomicInteger INDEX = new AtomicInteger();
|
||||
|
||||
private final String key;
|
||||
private final Function<NBTCompoundLike, T> readFunction;
|
||||
private final BiConsumer<MutableNBTCompound, T> writeConsumer;
|
||||
private final Function<NBT, T> readFunction;
|
||||
private final Function<T, NBT> writeFunction;
|
||||
private final Supplier<T> defaultValue;
|
||||
|
||||
protected Tag(@Nullable String key,
|
||||
@NotNull Function<NBTCompoundLike, T> readFunction,
|
||||
@NotNull BiConsumer<MutableNBTCompound, T> writeConsumer,
|
||||
final int index;
|
||||
|
||||
protected Tag(@NotNull String key,
|
||||
@NotNull Function<NBT, T> readFunction,
|
||||
@NotNull Function<T, NBT> writeFunction,
|
||||
@Nullable Supplier<T> defaultValue) {
|
||||
this.key = key;
|
||||
this.readFunction = readFunction;
|
||||
this.writeConsumer = writeConsumer;
|
||||
this.writeFunction = writeFunction;
|
||||
this.defaultValue = defaultValue;
|
||||
|
||||
this.index = INDEX_MAP.computeIfAbsent(key, k -> INDEX.getAndIncrement());
|
||||
}
|
||||
|
||||
protected Tag(@Nullable String key,
|
||||
@NotNull Function<NBTCompoundLike, T> readFunction,
|
||||
@NotNull BiConsumer<MutableNBTCompound, T> writeConsumer) {
|
||||
this(key, readFunction, writeConsumer, null);
|
||||
static <T, N extends NBT> Tag<T> tag(@NotNull String key,
|
||||
@NotNull Class<N> nbtClass,
|
||||
@NotNull Function<N, T> readFunction,
|
||||
@NotNull Function<T, N> writeFunction) {
|
||||
return new Tag<T>(key, (Function<NBT, T>) readFunction, (Function<T, NBT>) writeFunction, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the key used to navigate inside the holder nbt.
|
||||
* <p>
|
||||
* Can be null if unused (e.g. {@link #View(TagSerializer)}, {@link #SNBT} and {@link #NBT}).
|
||||
*
|
||||
* @return the tag key
|
||||
*/
|
||||
public @Nullable String getKey() {
|
||||
public @NotNull String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
@Contract(value = "_ -> new", pure = true)
|
||||
public Tag<T> defaultValue(@NotNull Supplier<T> defaultValue) {
|
||||
return new Tag<>(key, readFunction, writeConsumer, defaultValue);
|
||||
return new Tag<>(key, readFunction, writeFunction, defaultValue);
|
||||
}
|
||||
|
||||
@Contract(value = "_ -> new", pure = true)
|
||||
@ -106,42 +76,40 @@ public class Tag<T> {
|
||||
@NotNull Function<R, T> writeMap) {
|
||||
return new Tag<>(key,
|
||||
// Read
|
||||
nbtCompound -> {
|
||||
final var old = readFunction.apply(nbtCompound);
|
||||
if (old == null) {
|
||||
return null;
|
||||
}
|
||||
return readMap.apply(old);
|
||||
},
|
||||
readFunction.andThen(t -> {
|
||||
if (t == null) return null;
|
||||
return readMap.apply(t);
|
||||
}),
|
||||
// Write
|
||||
(nbtCompound, r) -> {
|
||||
var n = writeMap.apply(r);
|
||||
writeConsumer.accept(nbtCompound, n);
|
||||
},
|
||||
writeMap.andThen(writeFunction),
|
||||
// Default value
|
||||
() -> {
|
||||
if (defaultValue == null) {
|
||||
return null;
|
||||
}
|
||||
var old = defaultValue.get();
|
||||
return readMap.apply(old);
|
||||
});
|
||||
() -> readMap.apply(createDefault()));
|
||||
}
|
||||
|
||||
public @Nullable T read(@NotNull NBTCompoundLike nbtCompound) {
|
||||
T result = readFunction.apply(nbtCompound);
|
||||
if (result == null) {
|
||||
final var supplier = defaultValue;
|
||||
result = supplier != null ? supplier.get() : null;
|
||||
public @Nullable T read(@NotNull NBTCompoundLike nbt) {
|
||||
final String key = this.key;
|
||||
if (key.isEmpty()) {
|
||||
// Special handling for view tag
|
||||
return convertToValue(nbt.toCompound());
|
||||
}
|
||||
return result;
|
||||
final NBT subTag = nbt.get(key);
|
||||
return convertToValue(subTag);
|
||||
}
|
||||
|
||||
T createDefault() {
|
||||
final var supplier = defaultValue;
|
||||
return supplier != null ? supplier.get() : null;
|
||||
}
|
||||
|
||||
public void write(@NotNull MutableNBTCompound nbtCompound, @Nullable T value) {
|
||||
if (key == null || value != null) {
|
||||
this.writeConsumer.accept(nbtCompound, value);
|
||||
final String key = this.key;
|
||||
if (value != null) {
|
||||
final NBT nbt = writeFunction.apply(value);
|
||||
if (key.isEmpty()) nbtCompound.copyFrom((NBTCompoundLike) nbt);
|
||||
else nbtCompound.set(key, nbt);
|
||||
} else {
|
||||
nbtCompound.remove(key);
|
||||
if (key.isEmpty()) nbtCompound.clear();
|
||||
else nbtCompound.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,53 +118,52 @@ public class Tag<T> {
|
||||
write(nbtCompound, (T) value);
|
||||
}
|
||||
|
||||
T convertToValue(NBT nbt) {
|
||||
final T result;
|
||||
try {
|
||||
if (nbt == null || (result = readFunction.apply(nbt)) == null)
|
||||
return createDefault();
|
||||
return result;
|
||||
} catch (ClassCastException e) {
|
||||
return createDefault();
|
||||
}
|
||||
}
|
||||
|
||||
NBT convertToNbt(T value) {
|
||||
return writeFunction.apply(value);
|
||||
}
|
||||
|
||||
public static @NotNull Tag<Byte> Byte(@NotNull String key) {
|
||||
return new Tag<>(key,
|
||||
nbtCompound -> nbtCompound.getByte(key),
|
||||
(nbtCompound, value) -> nbtCompound.setByte(key, value));
|
||||
return tag(key, NBTByte.class, NBTByte::getValue, NBT::Byte);
|
||||
}
|
||||
|
||||
public static @NotNull Tag<Short> Short(@NotNull String key) {
|
||||
return new Tag<>(key,
|
||||
nbtCompound -> nbtCompound.getShort(key),
|
||||
(nbtCompound, value) -> nbtCompound.setShort(key, value));
|
||||
return tag(key, NBTShort.class, NBTShort::getValue, NBT::Short);
|
||||
}
|
||||
|
||||
public static @NotNull Tag<Integer> Integer(@NotNull String key) {
|
||||
return new Tag<>(key,
|
||||
nbtCompound -> nbtCompound.getInt(key),
|
||||
(nbtCompound, integer) -> nbtCompound.setInt(key, integer));
|
||||
return tag(key, NBTInt.class, NBTInt::getValue, NBT::Int);
|
||||
}
|
||||
|
||||
public static @NotNull Tag<Long> Long(@NotNull String key) {
|
||||
return new Tag<>(key,
|
||||
nbtCompound -> nbtCompound.getLong(key),
|
||||
(nbtCompound, value) -> nbtCompound.setLong(key, value));
|
||||
return tag(key, NBTLong.class, NBTLong::getValue, NBT::Long);
|
||||
}
|
||||
|
||||
public static @NotNull Tag<Float> Float(@NotNull String key) {
|
||||
return new Tag<>(key,
|
||||
nbtCompound -> nbtCompound.getFloat(key),
|
||||
(nbtCompound, value) -> nbtCompound.setFloat(key, value));
|
||||
return tag(key, NBTFloat.class, NBTFloat::getValue, NBT::Float);
|
||||
}
|
||||
|
||||
public static @NotNull Tag<Double> Double(@NotNull String key) {
|
||||
return new Tag<>(key,
|
||||
nbtCompound -> nbtCompound.getDouble(key),
|
||||
(nbtCompound, value) -> nbtCompound.setDouble(key, value));
|
||||
return tag(key, NBTDouble.class, NBTDouble::getValue, NBT::Double);
|
||||
}
|
||||
|
||||
public static @NotNull Tag<String> String(@NotNull String key) {
|
||||
return new Tag<>(key,
|
||||
nbtCompound -> nbtCompound.getString(key),
|
||||
(nbtCompound, value) -> nbtCompound.setString(key, value));
|
||||
return tag(key, NBTString.class, NBTString::getValue, NBT::String);
|
||||
}
|
||||
|
||||
public static <T extends NBT> @NotNull Tag<T> NBT(@NotNull String key) {
|
||||
//noinspection unchecked
|
||||
return new Tag<>(key,
|
||||
nbt -> (T) nbt.get(key),
|
||||
((nbt, value) -> nbt.set(key, value)));
|
||||
return tag(key, NBT.class, nbt -> (T) nbt, t -> t);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -208,61 +175,26 @@ public class Tag<T> {
|
||||
* @return the created tag
|
||||
*/
|
||||
public static <T> @NotNull Tag<T> Structure(@NotNull String key, @NotNull TagSerializer<T> serializer) {
|
||||
return new Tag<>(key,
|
||||
nbtCompound -> {
|
||||
final NBTCompound compound = nbtCompound.getCompound(key);
|
||||
if (compound == null) return null;
|
||||
return serializer.read(TagReadable.fromCompound(compound));
|
||||
},
|
||||
(nbtCompound, value) -> {
|
||||
MutableNBTCompound mutableCopy = nbtCompound.get(key) instanceof NBTCompound c ?
|
||||
c.toMutableCompound() : new MutableNBTCompound();
|
||||
serializer.write(TagWritable.fromCompound(mutableCopy), value);
|
||||
nbtCompound.set(key, mutableCopy.toCompound());
|
||||
return tag(key, NBTCompound.class,
|
||||
nbt -> serializer.read(TagHandler.fromCompound(nbt)),
|
||||
(value) -> {
|
||||
TagHandler handler = TagHandler.newHandler();
|
||||
serializer.write(handler, value);
|
||||
return handler.asCompound();
|
||||
});
|
||||
}
|
||||
|
||||
public static <T> @NotNull Tag<T> View(@NotNull TagSerializer<T> serializer) {
|
||||
return new Tag<>(null,
|
||||
nbtCompound -> serializer.read(TagReadable.fromCompound(nbtCompound)),
|
||||
(nbtCompound, value) -> serializer.write(TagWritable.fromCompound(nbtCompound), value));
|
||||
return tag("", NBTCompound.class,
|
||||
nbt -> serializer.read(TagHandler.fromCompound(nbt)),
|
||||
(value) -> {
|
||||
TagHandler handler = TagHandler.newHandler();
|
||||
serializer.write(handler, value);
|
||||
return handler.asCompound();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link Tag#NBT(String)} with {@link NBT#ByteArray(byte...)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static @NotNull Tag<byte[]> ByteArray(@NotNull String key) {
|
||||
return new Tag<>(key,
|
||||
nbtCompound -> nbtCompound.getByteArray(key).copyArray(),
|
||||
(nbtCompound, value) -> nbtCompound.setByteArray(key, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link Tag#NBT(String)} with {@link NBT#IntArray(int...)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static @NotNull Tag<int[]> IntArray(@NotNull String key) {
|
||||
return new Tag<>(key,
|
||||
nbtCompound -> nbtCompound.getIntArray(key).copyArray(),
|
||||
(nbtCompound, value) -> nbtCompound.setIntArray(key, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link Tag#NBT(String)} with {@link NBT#LongArray(long...)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static @NotNull Tag<long[]> LongArray(@NotNull String key) {
|
||||
return new Tag<>(key,
|
||||
nbtCompound -> nbtCompound.getLongArray(key).copyArray(),
|
||||
(nbtCompound, value) -> nbtCompound.setLongArray(key, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #Structure(String, TagSerializer)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static <T> @NotNull Tag<T> Custom(@NotNull String key, @NotNull TagSerializer<T> serializer) {
|
||||
return Structure(key, serializer);
|
||||
public static @NotNull Tag<ItemStack> ItemStack(@NotNull String key) {
|
||||
return tag(key, NBTCompound.class, ItemStack::fromItemNBT, ItemStack::toItemNBT);
|
||||
}
|
||||
}
|
||||
|
@ -2,35 +2,34 @@ package net.minestom.server.tag;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompoundLike;
|
||||
|
||||
/**
|
||||
* Represents an element which can read and write {@link Tag tags}.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
public interface TagHandler extends TagReadable, TagWritable {
|
||||
|
||||
/**
|
||||
* Converts a nbt compound to a tag handler.
|
||||
* <p>
|
||||
* The returned tag handler is not thread-safe.
|
||||
*
|
||||
* @param compound the compound to convert
|
||||
* @return a {@link TagHandler} capable of writing and reading {@code compound}
|
||||
*/
|
||||
static @NotNull TagHandler fromCompound(@NotNull MutableNBTCompound compound) {
|
||||
return new TagHandler() {
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return tag.read(compound);
|
||||
}
|
||||
@NotNull TagReadable readableCopy();
|
||||
|
||||
@Override
|
||||
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
tag.write(compound, value);
|
||||
}
|
||||
};
|
||||
void updateContent(@NotNull NBTCompoundLike compound);
|
||||
|
||||
@NotNull NBTCompound asCompound();
|
||||
|
||||
@ApiStatus.Experimental
|
||||
static @NotNull TagHandler newHandler() {
|
||||
return new TagHandlerImpl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the content of the given {@link NBTCompoundLike} into a new {@link TagHandler}.
|
||||
*
|
||||
* @param compound the compound to read tags from
|
||||
* @return a new tag handler with the content of the given compound
|
||||
*/
|
||||
static @NotNull TagHandler fromCompound(@NotNull NBTCompoundLike compound) {
|
||||
TagHandler handler = newHandler();
|
||||
handler.updateContent(compound);
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
|
128
src/main/java/net/minestom/server/tag/TagHandlerImpl.java
Normal file
128
src/main/java/net/minestom/server/tag/TagHandlerImpl.java
Normal file
@ -0,0 +1,128 @@
|
||||
package net.minestom.server.tag;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBT;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompoundLike;
|
||||
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
|
||||
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.Arrays;
|
||||
|
||||
final class TagHandlerImpl implements TagHandler {
|
||||
private Entry<?>[] entries = new Entry[0];
|
||||
private Cache cache;
|
||||
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
VarHandle.acquireFence();
|
||||
return read(entries, tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
VarHandle.acquireFence();
|
||||
final int index = tag.index;
|
||||
Entry<?>[] entries = this.entries;
|
||||
final Entry<T> entry = value != null ? new Entry<>(tag, value) : null;
|
||||
if (index >= entries.length) {
|
||||
if (value == null)
|
||||
return; // no need to create/remove an entry
|
||||
this.entries = entries = Arrays.copyOf(entries, index + 1);
|
||||
}
|
||||
entries[index] = entry;
|
||||
this.cache = null;
|
||||
VarHandle.releaseFence();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TagReadable readableCopy() {
|
||||
return updatedCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateContent(@NotNull NBTCompoundLike compound) {
|
||||
Entry<?>[] entries = new Entry[0];
|
||||
for (var entry : compound) {
|
||||
final String key = entry.getKey();
|
||||
final NBT nbt = entry.getValue();
|
||||
final Tag<NBT> tag = Tag.NBT(key);
|
||||
final int index = tag.index;
|
||||
if (index >= entries.length) {
|
||||
entries = Arrays.copyOf(entries, index + 1);
|
||||
}
|
||||
entries[index] = new Entry<>(tag, nbt);
|
||||
}
|
||||
this.entries = entries;
|
||||
this.cache = null;
|
||||
VarHandle.releaseFence();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull NBTCompound asCompound() {
|
||||
return updatedCache().compound;
|
||||
}
|
||||
|
||||
private Cache updatedCache() {
|
||||
VarHandle.acquireFence();
|
||||
Cache cache = this.cache;
|
||||
if (cache == null) {
|
||||
Entry<?>[] entries = this.entries;
|
||||
if (entries.length > 0) {
|
||||
entries = entries.clone();
|
||||
MutableNBTCompound tmp = new MutableNBTCompound();
|
||||
for (Entry<?> entry : entries) {
|
||||
if (entry == null) continue;
|
||||
final Tag<?> tag = entry.tag;
|
||||
tag.writeUnsafe(tmp, entry.value);
|
||||
}
|
||||
cache = !tmp.isEmpty() ? new Cache(entries, tmp.toCompound()) : Cache.EMPTY;
|
||||
} else {
|
||||
cache = Cache.EMPTY;
|
||||
}
|
||||
this.cache = cache;
|
||||
VarHandle.releaseFence();
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
private static final class Entry<T> {
|
||||
final Tag<T> tag;
|
||||
final T value;
|
||||
volatile NBT nbt;
|
||||
|
||||
Entry(Tag<T> tag, T value) {
|
||||
this.tag = tag;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
private record Cache(Entry<?>[] entries, NBTCompound compound) implements TagReadable {
|
||||
static final Cache EMPTY = new Cache(new Entry[0], NBTCompound.EMPTY);
|
||||
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return read(entries, tag);
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> T read(Entry<?>[] entries, Tag<T> tag) {
|
||||
final int index = tag.index;
|
||||
final Entry<?> entry;
|
||||
if (index >= entries.length || (entry = entries[index]) == null) {
|
||||
return tag.createDefault();
|
||||
}
|
||||
final Tag entryTag = entry.tag;
|
||||
if (entryTag == tag) {
|
||||
// Tag is the same, return the value
|
||||
//noinspection unchecked
|
||||
return (T) entry.value;
|
||||
}
|
||||
// Value must be parsed from nbt if the tag is different
|
||||
NBT nbt = entry.nbt;
|
||||
if (nbt == null) entry.nbt = nbt = entryTag.convertToNbt(entry.value);
|
||||
return tag.convertToValue(nbt);
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@ package net.minestom.server.tag;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompoundLike;
|
||||
|
||||
/**
|
||||
* Represents an element which can read {@link Tag tags}.
|
||||
@ -27,21 +26,4 @@ public interface TagReadable {
|
||||
default boolean hasTag(@NotNull Tag<?> tag) {
|
||||
return getTag(tag) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a nbt compound to a tag reader.
|
||||
* <p>
|
||||
* The returned tag reader is not thread-safe.
|
||||
*
|
||||
* @param compound the compound to convert
|
||||
* @return a {@link TagReadable} capable of reading {@code compound}
|
||||
*/
|
||||
static @NotNull TagReadable fromCompound(@NotNull NBTCompoundLike compound) {
|
||||
return new TagReadable() {
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return tag.read(compound);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package net.minestom.server.tag;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
|
||||
|
||||
/**
|
||||
* Represents an element which can write {@link Tag tags}.
|
||||
@ -21,21 +20,4 @@ public interface TagWritable {
|
||||
default void removeTag(@NotNull Tag<?> tag) {
|
||||
setTag(tag, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a nbt compound to a tag writer.
|
||||
* <p>
|
||||
* The returned tag writer is not thread-safe.
|
||||
*
|
||||
* @param compound the compound to convert
|
||||
* @return a {@link TagWritable} capable of writing {@code compound}
|
||||
*/
|
||||
static @NotNull TagWritable fromCompound(@NotNull MutableNBTCompound compound) {
|
||||
return new TagWritable() {
|
||||
@Override
|
||||
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
tag.write(compound, value);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
30
src/main/java/net/minestom/server/tag/Taggable.java
Normal file
30
src/main/java/net/minestom/server/tag/Taggable.java
Normal file
@ -0,0 +1,30 @@
|
||||
package net.minestom.server.tag;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
|
||||
public interface Taggable extends TagReadable, TagWritable {
|
||||
|
||||
@NotNull TagHandler tagHandler();
|
||||
|
||||
@Override
|
||||
default <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return tagHandler().getTag(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean hasTag(@NotNull Tag<?> tag) {
|
||||
return tagHandler().hasTag(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
default <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
tagHandler().setTag(tag, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
default void removeTag(@NotNull Tag<?> tag) {
|
||||
tagHandler().removeTag(tag);
|
||||
}
|
||||
}
|
@ -2,6 +2,9 @@ package net.minestom.server.api;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class TestUtils {
|
||||
public static void waitUntilCleared(WeakReference<?> ref) {
|
||||
while (ref.get() != null) {
|
||||
@ -12,4 +15,27 @@ public class TestUtils {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertEqualsIgnoreSpace(String s1, String s2, boolean matchCase) {
|
||||
final String val1 = stripExtraSpaces(s1);
|
||||
final String val2 = stripExtraSpaces(s2);
|
||||
if (matchCase) {
|
||||
assertEquals(val1, val2);
|
||||
} else {
|
||||
assertTrue(val1.equalsIgnoreCase(val2));
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertEqualsIgnoreSpace(String s1, String s2) {
|
||||
assertEqualsIgnoreSpace(s1, s2, true);
|
||||
}
|
||||
|
||||
private static String stripExtraSpaces(String s) {
|
||||
StringBuilder formattedString = new StringBuilder();
|
||||
java.util.StringTokenizer st = new java.util.StringTokenizer(s);
|
||||
while (st.hasMoreTokens()) {
|
||||
formattedString.append(st.nextToken());
|
||||
}
|
||||
return formattedString.toString().trim();
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,8 @@ package net.minestom.server.command;
|
||||
import net.minestom.server.command.builder.Command;
|
||||
import net.minestom.server.command.builder.CommandDispatcher;
|
||||
import net.minestom.server.permission.Permission;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Set;
|
||||
@ -133,12 +131,8 @@ public class CommandConditionTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
public @NotNull TagHandler tagHandler() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,10 +4,8 @@ import net.minestom.server.command.builder.Command;
|
||||
import net.minestom.server.command.builder.CommandDispatcher;
|
||||
import net.minestom.server.command.builder.arguments.ArgumentType;
|
||||
import net.minestom.server.permission.Permission;
|
||||
import net.minestom.server.tag.Tag;
|
||||
import net.minestom.server.tag.TagHandler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Set;
|
||||
@ -73,12 +71,8 @@ public class CommandParsingTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
public @NotNull TagHandler tagHandler() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
94
src/test/java/net/minestom/server/tag/TagItemTest.java
Normal file
94
src/test/java/net/minestom/server/tag/TagItemTest.java
Normal file
@ -0,0 +1,94 @@
|
||||
package net.minestom.server.tag;
|
||||
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.item.Material;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import static net.minestom.server.api.TestUtils.waitUntilCleared;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TagItemTest {
|
||||
|
||||
@Test
|
||||
public void get() {
|
||||
var item = ItemStack.of(Material.DIAMOND);
|
||||
var tag = Tag.ItemStack("item");
|
||||
var handler = TagHandler.newHandler();
|
||||
handler.setTag(tag, item);
|
||||
|
||||
assertSame(item, handler.getTag(tag));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDifferentObject() {
|
||||
var item = ItemStack.of(Material.DIAMOND);
|
||||
var handler = TagHandler.newHandler();
|
||||
handler.setTag(Tag.ItemStack("item"), item);
|
||||
|
||||
assertEquals(item, handler.getTag(Tag.ItemStack("item")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void remove() {
|
||||
var item = ItemStack.of(Material.DIAMOND);
|
||||
var tag = Tag.ItemStack("item");
|
||||
var handler = TagHandler.newHandler();
|
||||
handler.setTag(tag, item);
|
||||
assertSame(item, handler.getTag(tag));
|
||||
|
||||
handler.setTag(tag, null);
|
||||
assertNull(handler.getTag(tag));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gc() {
|
||||
var item = ItemStack.of(Material.DIAMOND);
|
||||
var tag = Tag.ItemStack("item");
|
||||
var handler = TagHandler.newHandler();
|
||||
handler.setTag(tag, item);
|
||||
assertSame(item, handler.getTag(tag));
|
||||
handler.setTag(tag, null);
|
||||
|
||||
var ref = new WeakReference<>(item);
|
||||
//noinspection UnusedAssignment
|
||||
item = null;
|
||||
waitUntilCleared(ref);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidation() {
|
||||
var item = ItemStack.of(Material.DIAMOND);
|
||||
var item2 = ItemStack.of(Material.DIAMOND, 2);
|
||||
var handler = TagHandler.newHandler();
|
||||
|
||||
var tag = Tag.ItemStack("item");
|
||||
handler.setTag(tag, item);
|
||||
assertSame(item, handler.getTag(tag));
|
||||
handler.setTag(tag, item2);
|
||||
assertSame(item2, handler.getTag(tag));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void differentTagInvalidation() {
|
||||
var item = ItemStack.of(Material.DIAMOND);
|
||||
var item2 = ItemStack.of(Material.DIAMOND, 2);
|
||||
var handler = TagHandler.newHandler();
|
||||
|
||||
var itemTag = Tag.ItemStack("item");
|
||||
var nbtTag = Tag.NBT("item");
|
||||
// Write the item using the ItemStack tag
|
||||
{
|
||||
handler.setTag(itemTag, item);
|
||||
assertSame(item, handler.getTag(itemTag));
|
||||
assertEquals(item.toItemNBT(), handler.getTag(nbtTag));
|
||||
}
|
||||
// Override it with an NBT tag
|
||||
{
|
||||
handler.setTag(nbtTag, item2.toItemNBT());
|
||||
assertEquals(item2, handler.getTag(itemTag));
|
||||
assertEquals(item2.toItemNBT(), handler.getTag(nbtTag));
|
||||
}
|
||||
}
|
||||
}
|
35
src/test/java/net/minestom/server/tag/TagMapTest.java
Normal file
35
src/test/java/net/minestom/server/tag/TagMapTest.java
Normal file
@ -0,0 +1,35 @@
|
||||
package net.minestom.server.tag;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class TagMapTest {
|
||||
|
||||
private record Entry(int value) {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void map() {
|
||||
var handler = TagHandler.newHandler();
|
||||
var intTag = Tag.Integer("key");
|
||||
var tag = intTag.map(Entry::new, Entry::value);
|
||||
|
||||
handler.setTag(tag, new Entry(1));
|
||||
assertEquals(1, handler.getTag(intTag));
|
||||
assertEquals(new Entry(1), handler.getTag(tag));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapDefault() {
|
||||
var handler = TagHandler.newHandler();
|
||||
var intTag = Tag.Integer("key");
|
||||
var tag = intTag.map(Entry::new, Entry::value);
|
||||
|
||||
assertEquals(new Entry(1), handler.getTag(tag.defaultValue(new Entry(1))));
|
||||
|
||||
handler.setTag(tag, new Entry(2));
|
||||
assertEquals(2, handler.getTag(intTag));
|
||||
assertEquals(new Entry(2), handler.getTag(tag));
|
||||
}
|
||||
}
|
140
src/test/java/net/minestom/server/tag/TagStructureTest.java
Normal file
140
src/test/java/net/minestom/server/tag/TagStructureTest.java
Normal file
@ -0,0 +1,140 @@
|
||||
package net.minestom.server.tag;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static net.minestom.server.api.TestUtils.assertEqualsIgnoreSpace;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TagStructureTest {
|
||||
|
||||
private static final Tag<Entry> STRUCTURE_TAG = Tag.Structure("entry", new TagSerializer<>() {
|
||||
private static final Tag<String> VALUE_TAG = Tag.String("value");
|
||||
|
||||
@Override
|
||||
public @Nullable Entry read(@NotNull TagReadable reader) {
|
||||
final String value = reader.getTag(VALUE_TAG);
|
||||
return value != null ? new Entry(value) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull TagWritable writer, @Nullable Entry value) {
|
||||
if (value != null) {
|
||||
writer.setTag(VALUE_TAG, value.value);
|
||||
} else {
|
||||
writer.removeTag(VALUE_TAG);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
private static final Tag<Entry> STRUCTURE_TAG2 = Tag.Structure("entry", new TagSerializer<>() {
|
||||
private static final Tag<String> VALUE_TAG = Tag.String("value2");
|
||||
|
||||
@Override
|
||||
public @Nullable Entry read(@NotNull TagReadable reader) {
|
||||
final String value = reader.getTag(VALUE_TAG);
|
||||
return value != null ? new Entry(value) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull TagWritable writer, @Nullable Entry value) {
|
||||
if (value != null) {
|
||||
writer.setTag(VALUE_TAG, value.value);
|
||||
} else {
|
||||
writer.removeTag(VALUE_TAG);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
private record Entry(String value) {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basic() {
|
||||
var handler = TagHandler.newHandler();
|
||||
assertNull(handler.getTag(STRUCTURE_TAG));
|
||||
assertFalse(handler.hasTag(STRUCTURE_TAG));
|
||||
|
||||
var entry = new Entry("hello");
|
||||
handler.setTag(STRUCTURE_TAG, entry);
|
||||
assertTrue(handler.hasTag(STRUCTURE_TAG));
|
||||
assertEquals(entry, handler.getTag(STRUCTURE_TAG));
|
||||
|
||||
handler.removeTag(STRUCTURE_TAG);
|
||||
assertFalse(handler.hasTag(STRUCTURE_TAG));
|
||||
assertNull(handler.getTag(STRUCTURE_TAG));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void snbt() {
|
||||
var handler = TagHandler.newHandler();
|
||||
var entry = new Entry("hello");
|
||||
handler.setTag(STRUCTURE_TAG, entry);
|
||||
assertEqualsIgnoreSpace("""
|
||||
{
|
||||
"entry": {
|
||||
"value": "hello"
|
||||
}
|
||||
}
|
||||
""", handler.asCompound().toSNBT());
|
||||
|
||||
handler.removeTag(STRUCTURE_TAG);
|
||||
assertEqualsIgnoreSpace("{}", handler.asCompound().toSNBT());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overrideBasic() {
|
||||
var handler = TagHandler.newHandler();
|
||||
assertNull(handler.getTag(STRUCTURE_TAG));
|
||||
assertFalse(handler.hasTag(STRUCTURE_TAG));
|
||||
|
||||
var entry1 = new Entry("hello");
|
||||
var entry2 = new Entry("hello2");
|
||||
|
||||
// Add first entry
|
||||
{
|
||||
handler.setTag(STRUCTURE_TAG, entry1);
|
||||
assertTrue(handler.hasTag(STRUCTURE_TAG));
|
||||
assertEquals(entry1, handler.getTag(STRUCTURE_TAG));
|
||||
}
|
||||
// Add second entry
|
||||
{
|
||||
handler.setTag(STRUCTURE_TAG2, entry2);
|
||||
assertTrue(handler.hasTag(STRUCTURE_TAG2));
|
||||
assertEquals(entry2, handler.getTag(STRUCTURE_TAG2));
|
||||
// Assert first
|
||||
assertFalse(handler.hasTag(STRUCTURE_TAG));
|
||||
assertNull(handler.getTag(STRUCTURE_TAG));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overrideNbt() {
|
||||
var handler = TagHandler.newHandler();
|
||||
var entry1 = new Entry("hello");
|
||||
var entry2 = new Entry("hello2");
|
||||
// Add first entry
|
||||
{
|
||||
handler.setTag(STRUCTURE_TAG, entry1);
|
||||
assertEqualsIgnoreSpace("""
|
||||
{
|
||||
"entry": {
|
||||
"value": "hello"
|
||||
}
|
||||
}
|
||||
""", handler.asCompound().toSNBT());
|
||||
}
|
||||
// Add second entry
|
||||
{
|
||||
handler.setTag(STRUCTURE_TAG2, entry2);
|
||||
assertEqualsIgnoreSpace("""
|
||||
{
|
||||
"entry": {
|
||||
"value2": "hello2"
|
||||
}
|
||||
}
|
||||
""", handler.asCompound().toSNBT());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,44 +1,74 @@
|
||||
package net.minestom.server.tag;
|
||||
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.item.Material;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBT;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
import org.jglrxavpok.hephaistos.nbt.mutable.MutableNBTCompound;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TagTest {
|
||||
|
||||
@Test
|
||||
public void testTag() {
|
||||
var mutable = new MutableNBTCompound();
|
||||
mutable.setInt("key", 5);
|
||||
public void intGet() {
|
||||
var mutable = new MutableNBTCompound().setInt("key", 5);
|
||||
var tag = Tag.Integer("key");
|
||||
var handler = TagHandler.fromCompound(new MutableNBTCompound());
|
||||
handler.setTag(tag, 5);
|
||||
assertEquals(mutable.toCompound(), handler.getTag(Tag.NBT), "NBT is not the same");
|
||||
assertEquals(5, handler.getTag(tag));
|
||||
assertEquals(mutable.toCompound(), handler.asCompound(), "NBT is not the same");
|
||||
|
||||
// Removal
|
||||
handler.setTag(tag, null);
|
||||
assertEquals(new NBTCompound(), handler.getTag(Tag.NBT), "Tag must be removed when set to null");
|
||||
assertEquals(new NBTCompound(), handler.asCompound(), "Tag must be removed when set to null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSnbt() {
|
||||
var mutable = new MutableNBTCompound();
|
||||
mutable.setInt("key", 5);
|
||||
|
||||
var reader = TagReadable.fromCompound(mutable);
|
||||
final String snbt = reader.getTag(Tag.SNBT);
|
||||
assertEquals(snbt, mutable.toCompound().toSNBT(), "SNBT is not the same");
|
||||
public void intNull() {
|
||||
var handler = TagHandler.fromCompound(new MutableNBTCompound().set("key", NBT.Int(5)));
|
||||
// Removal
|
||||
var tag = Tag.Integer("key");
|
||||
handler.setTag(tag, null);
|
||||
assertFalse(handler.hasTag(tag));
|
||||
assertEquals(NBTCompound.EMPTY, handler.asCompound(), "Tag must be removed when set to null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefault() {
|
||||
public void intRemove() {
|
||||
var handler = TagHandler.fromCompound(new MutableNBTCompound().set("key", NBT.Int(5)));
|
||||
// Removal
|
||||
var tag = Tag.Integer("key");
|
||||
handler.removeTag(tag);
|
||||
assertFalse(handler.hasTag(tag));
|
||||
assertEquals(NBTCompound.EMPTY, handler.asCompound(), "Tag must be removed when set to null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void snbt() {
|
||||
var mutable = new MutableNBTCompound().setInt("key", 5);
|
||||
var reader = TagHandler.fromCompound(mutable);
|
||||
assertEquals(reader.asCompound().toSNBT(), mutable.toCompound().toSNBT(), "SNBT is not the same");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromNbt() {
|
||||
var mutable = new MutableNBTCompound().setInt("key", 5);
|
||||
var handler = TagHandler.fromCompound(mutable);
|
||||
assertEquals(5, handler.getTag(Tag.Integer("key")));
|
||||
assertEquals(mutable.toCompound(), handler.asCompound(), "NBT is not the same");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultValue() {
|
||||
var nullable = Tag.String("key");
|
||||
var notNull = nullable.defaultValue("Hey");
|
||||
assertNotSame(nullable, notNull);
|
||||
|
||||
var handler = TagHandler.fromCompound(new MutableNBTCompound());
|
||||
var handler = TagHandler.newHandler();
|
||||
assertFalse(handler.hasTag(nullable));
|
||||
assertTrue(handler.hasTag(notNull)); // default value is set
|
||||
assertFalse(handler.hasTag(nullable));
|
||||
@ -46,4 +76,49 @@ public class TagTest {
|
||||
assertNull(handler.getTag(nullable));
|
||||
assertEquals("Hey", handler.getTag(notNull));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidType() {
|
||||
var tag1 = Tag.Integer("key");
|
||||
var tag2 = Tag.String("key");
|
||||
|
||||
var handler = TagHandler.newHandler();
|
||||
handler.setTag(tag1, 5);
|
||||
assertEquals(5, handler.getTag(tag1));
|
||||
|
||||
assertNull(handler.getTag(tag2));
|
||||
assertEquals("hey", handler.getTag(tag2.defaultValue("hey")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void item() {
|
||||
var item = ItemStack.of(Material.DIAMOND);
|
||||
var tag = Tag.ItemStack("item");
|
||||
var handler = TagHandler.newHandler();
|
||||
handler.setTag(tag, item);
|
||||
assertEquals(item, handler.getTag(tag));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tagResizing() {
|
||||
var tag1 = Tag.Integer("tag1");
|
||||
var tag2 = Tag.Integer("tag2");
|
||||
var handler = TagHandler.newHandler();
|
||||
|
||||
handler.setTag(tag1, 5);
|
||||
handler.setTag(tag2, 1);
|
||||
|
||||
assertEquals(5, handler.getTag(tag1));
|
||||
assertEquals(1, handler.getTag(tag2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nbtResizing() {
|
||||
var handler = TagHandler.fromCompound(NBT.Compound(Map.of(
|
||||
"tag1", NBT.Int(5),
|
||||
"tag2", NBT.Int(1))));
|
||||
|
||||
assertEquals(5, handler.getTag(Tag.Integer("tag1")));
|
||||
assertEquals(1, handler.getTag(Tag.Integer("tag2")));
|
||||
}
|
||||
}
|
||||
|
66
src/test/java/net/minestom/server/tag/TagViewTest.java
Normal file
66
src/test/java/net/minestom/server/tag/TagViewTest.java
Normal file
@ -0,0 +1,66 @@
|
||||
package net.minestom.server.tag;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static net.minestom.server.api.TestUtils.assertEqualsIgnoreSpace;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
public class TagViewTest {
|
||||
|
||||
private static final Tag<Entry> VIEW_TAG = Tag.View(new TagSerializer<>() {
|
||||
private static final Tag<String> VALUE_TAG = Tag.String("value");
|
||||
|
||||
@Override
|
||||
public @Nullable Entry read(@NotNull TagReadable reader) {
|
||||
final String value = reader.getTag(VALUE_TAG);
|
||||
return value != null ? new Entry(value) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NotNull TagWritable writer, @Nullable Entry value) {
|
||||
if (value != null) {
|
||||
writer.setTag(VALUE_TAG, value.value);
|
||||
} else {
|
||||
writer.removeTag(VALUE_TAG);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
private record Entry(String value) {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basic() {
|
||||
var handler = TagHandler.newHandler();
|
||||
assertNull(handler.getTag(VIEW_TAG));
|
||||
assertFalse(handler.hasTag(VIEW_TAG));
|
||||
|
||||
var entry = new Entry("hello");
|
||||
handler.setTag(VIEW_TAG, entry);
|
||||
assertTrue(handler.hasTag(VIEW_TAG));
|
||||
assertEquals(entry, handler.getTag(VIEW_TAG));
|
||||
|
||||
handler.removeTag(VIEW_TAG);
|
||||
assertFalse(handler.hasTag(VIEW_TAG));
|
||||
assertNull(handler.getTag(VIEW_TAG));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void snbt() {
|
||||
var handler = TagHandler.newHandler();
|
||||
var entry = new Entry("hello");
|
||||
handler.setTag(VIEW_TAG, entry);
|
||||
assertEqualsIgnoreSpace("""
|
||||
{
|
||||
"value": "hello"
|
||||
}
|
||||
""", handler.asCompound().toSNBT());
|
||||
|
||||
handler.removeTag(VIEW_TAG);
|
||||
assertEqualsIgnoreSpace("{}", handler.asCompound().toSNBT());
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user