diff --git a/src/main/java/net/minestom/server/item/ItemMeta.java b/src/main/java/net/minestom/server/item/ItemMeta.java index 04d0023ba..2128a180a 100644 --- a/src/main/java/net/minestom/server/item/ItemMeta.java +++ b/src/main/java/net/minestom/server/item/ItemMeta.java @@ -4,6 +4,8 @@ import io.netty.buffer.ByteBuf; import net.kyori.adventure.text.Component; import net.minestom.server.instance.block.Block; import net.minestom.server.item.attribute.ItemAttribute; +import net.minestom.server.tag.Tag; +import net.minestom.server.tag.TagReadable; import net.minestom.server.utils.binary.BinaryWriter; import net.minestom.server.utils.binary.Writeable; import org.jetbrains.annotations.Contract; @@ -13,8 +15,9 @@ import org.jglrxavpok.hephaistos.nbt.NBTCompound; import java.util.*; import java.util.function.Consumer; +import java.util.function.Supplier; -public class ItemMeta implements Writeable { +public class ItemMeta implements TagReadable, Writeable { private final int damage; private final boolean unbreakable; @@ -109,18 +112,14 @@ public class ItemMeta implements Writeable { return Collections.unmodifiableSet(canPlaceOn); } - @Contract(pure = true) - public T getOrDefault(@NotNull ItemTag tag, @Nullable T defaultValue) { - var key = tag.getKey(); - if (nbt.containsKey(key)) { - return tag.read(toNBT()); - } else { - return defaultValue; - } + @Override + public @Nullable T getTag(@NotNull Tag tag) { + return tag.read(nbt); } - public @Nullable T get(@NotNull ItemTag tag) { - return tag.read(toNBT()); + @Override + public boolean hasTag(@NotNull Tag tag) { + return nbt.containsKey(tag.getKey()); } public @NotNull NBTCompound toNBT() { @@ -163,4 +162,26 @@ public class ItemMeta implements Writeable { writer.write(cachedBuffer); this.cachedBuffer.resetReaderIndex(); } + + /** + * @deprecated use {@link #getTag(Tag)} with {@link Tag#defaultValue(Supplier)} + */ + @Deprecated + @Contract(pure = true) + public T getOrDefault(@NotNull Tag tag, @Nullable T defaultValue) { + var key = tag.getKey(); + if (nbt.containsKey(key)) { + return tag.read(toNBT()); + } else { + return defaultValue; + } + } + + /** + * @deprecated use {@link #getTag(Tag)} + */ + @Deprecated + public @Nullable T get(@NotNull Tag tag) { + return getTag(tag); + } } diff --git a/src/main/java/net/minestom/server/item/ItemMetaBuilder.java b/src/main/java/net/minestom/server/item/ItemMetaBuilder.java index 58b23f92e..c0d8f9529 100644 --- a/src/main/java/net/minestom/server/item/ItemMetaBuilder.java +++ b/src/main/java/net/minestom/server/item/ItemMetaBuilder.java @@ -5,6 +5,8 @@ import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.minestom.server.adventure.AdventureSerializer; import net.minestom.server.instance.block.Block; import net.minestom.server.item.attribute.ItemAttribute; +import net.minestom.server.tag.Tag; +import net.minestom.server.tag.TagWritable; import net.minestom.server.utils.NBTUtils; import net.minestom.server.utils.Utils; import org.jetbrains.annotations.Contract; @@ -16,7 +18,7 @@ import java.util.*; import java.util.function.Consumer; import java.util.function.Supplier; -public abstract class ItemMetaBuilder { +public abstract class ItemMetaBuilder implements TagWritable { protected NBTCompound nbt = new NBTCompound(); @@ -183,12 +185,13 @@ public abstract class ItemMetaBuilder { return canDestroy(Set.of(blocks)); } - public @NotNull ItemMetaBuilder set(@NotNull ItemTag tag, @Nullable T value) { - if (value != null) { - tag.write(nbt, value); - } else { - this.nbt.removeTag(tag.getKey()); - } + @Override + public void setTag(@NotNull Tag tag, @Nullable T value) { + tag.write(nbt, value); + } + + public @NotNull ItemMetaBuilder set(@NotNull Tag tag, @Nullable T value) { + setTag(tag, value); return this; } diff --git a/src/main/java/net/minestom/server/item/ItemTag.java b/src/main/java/net/minestom/server/item/ItemTag.java index 6f0c02a65..70ad3b527 100644 --- a/src/main/java/net/minestom/server/item/ItemTag.java +++ b/src/main/java/net/minestom/server/item/ItemTag.java @@ -1,112 +1,65 @@ package net.minestom.server.item; +import net.minestom.server.tag.Tag; import org.jetbrains.annotations.NotNull; import org.jglrxavpok.hephaistos.nbt.NBT; import org.jglrxavpok.hephaistos.nbt.NBTCompound; -import org.jglrxavpok.hephaistos.nbt.NBTList; import java.util.function.BiConsumer; import java.util.function.Function; -public class ItemTag { +/** + * @deprecated use {@link Tag}. + */ +@Deprecated +public class ItemTag extends Tag { - private final String key; - private final Function readFunction; - private final BiConsumer writeConsumer; - - private ItemTag(@NotNull String key, - @NotNull Function readFunction, - @NotNull BiConsumer writeConsumer) { - this.key = key; - this.readFunction = readFunction; - this.writeConsumer = writeConsumer; + protected ItemTag(@NotNull String key, @NotNull Function readFunction, @NotNull BiConsumer writeConsumer) { + super(key, readFunction, writeConsumer); } - public @NotNull String getKey() { - return key; + public static @NotNull Tag Byte(@NotNull String key) { + return Tag.Byte(key); } - protected T read(@NotNull NBTCompound nbtCompound) { - return readFunction.apply(nbtCompound); + public static @NotNull Tag Short(@NotNull String key) { + return Tag.Short(key); } - protected void write(@NotNull NBTCompound nbtCompound, @NotNull T value) { - this.writeConsumer.accept(nbtCompound, value); + public static @NotNull Tag Integer(@NotNull String key) { + return Tag.Integer(key); } - public static @NotNull ItemTag Byte(@NotNull String key) { - return new ItemTag<>(key, - nbtCompound -> nbtCompound.getByte(key), - (nbtCompound, value) -> nbtCompound.setByte(key, value)); + public static @NotNull Tag Long(@NotNull String key) { + return Tag.Long(key); } - public static @NotNull ItemTag Short(@NotNull String key) { - return new ItemTag<>(key, - nbtCompound -> nbtCompound.getShort(key), - (nbtCompound, value) -> nbtCompound.setShort(key, value)); + public static @NotNull Tag Float(@NotNull String key) { + return Tag.Float(key); } - public static @NotNull ItemTag Integer(@NotNull String key) { - return new ItemTag<>(key, - nbtCompound -> nbtCompound.getInt(key), - (nbtCompound, integer) -> nbtCompound.setInt(key, integer)); + public static @NotNull Tag Double(@NotNull String key) { + return Tag.Double(key); } - public static @NotNull ItemTag Long(@NotNull String key) { - return new ItemTag<>(key, - nbtCompound -> nbtCompound.getLong(key), - (nbtCompound, value) -> nbtCompound.setLong(key, value)); + public static @NotNull Tag ByteArray(@NotNull String key) { + return Tag.ByteArray(key); } - public static @NotNull ItemTag Float(@NotNull String key) { - return new ItemTag<>(key, - nbtCompound -> nbtCompound.getFloat(key), - (nbtCompound, value) -> nbtCompound.setFloat(key, value)); + public static @NotNull Tag String(@NotNull String key) { + return Tag.String(key); } - public static @NotNull ItemTag Double(@NotNull String key) { - return new ItemTag<>(key, - nbtCompound -> nbtCompound.getDouble(key), - (nbtCompound, value) -> nbtCompound.setDouble(key, value)); + public static @NotNull Tag NBT(@NotNull String key) { + return Tag.NBT(key); } - public static @NotNull ItemTag ByteArray(@NotNull String key) { - return new ItemTag<>(key, - nbtCompound -> nbtCompound.getByteArray(key), - (nbtCompound, value) -> nbtCompound.setByteArray(key, value)); + public static @NotNull Tag IntArray(@NotNull String key) { + return Tag.IntArray(key); } - public static @NotNull ItemTag String(@NotNull String key) { - return new ItemTag<>(key, - nbtCompound -> nbtCompound.getString(key), - (nbtCompound, value) -> nbtCompound.setString(key, value)); - } - - public static @NotNull ItemTag NBT(@NotNull String key) { - return new ItemTag<>(key, - nbt -> { - var currentNBT = nbt.get(key); - - // Avoid a NPE when cloning a null variable. - if (currentNBT == null) { - return null; - } - - return currentNBT.deepClone(); - }, - ((nbt, value) -> nbt.set(key, value.deepClone()))); - } - - public static @NotNull ItemTag IntArray(@NotNull String key) { - return new ItemTag<>(key, - nbtCompound -> nbtCompound.getIntArray(key), - (nbtCompound, value) -> nbtCompound.setIntArray(key, value)); - } - - public static @NotNull ItemTag LongArray(@NotNull String key) { - return new ItemTag<>(key, - nbtCompound -> nbtCompound.getLongArray(key), - (nbtCompound, value) -> nbtCompound.setLongArray(key, value)); + public static @NotNull Tag LongArray(@NotNull String key) { + return Tag.LongArray(key); } } diff --git a/src/main/java/net/minestom/server/tag/Tag.java b/src/main/java/net/minestom/server/tag/Tag.java new file mode 100644 index 000000000..c60dfbf73 --- /dev/null +++ b/src/main/java/net/minestom/server/tag/Tag.java @@ -0,0 +1,147 @@ +package net.minestom.server.tag; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jglrxavpok.hephaistos.nbt.NBT; +import org.jglrxavpok.hephaistos.nbt.NBTCompound; + +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Supplier; + +/** + * Represents a key to retrieve or change a value. + *

+ * All tags are serializable. + * + * @param the tag type + */ +@ApiStatus.NonExtendable +public class Tag { + + private final String key; + private final Function readFunction; + private final BiConsumer writeConsumer; + private volatile Supplier defaultValue; + + protected Tag(@NotNull String key, + @NotNull Function readFunction, + @NotNull BiConsumer writeConsumer) { + this.key = key; + this.readFunction = readFunction; + this.writeConsumer = writeConsumer; + } + + public @NotNull String getKey() { + return key; + } + + public Tag defaultValue(@NotNull Supplier defaultValue) { + this.defaultValue = defaultValue; + return this; + } + + public Tag defaultValue(@NotNull T defaultValue) { + defaultValue(() -> defaultValue); + return this; + } + + public @Nullable T read(@NotNull NBTCompound nbtCompound) { + if (nbtCompound.containsKey(key)) { + return readFunction.apply(nbtCompound); + } else { + final var supplier = defaultValue; + return supplier != null ? supplier.get() : null; + } + } + + public void write(@NotNull NBTCompound nbtCompound, @Nullable T value) { + if (value != null) { + this.writeConsumer.accept(nbtCompound, value); + } else { + nbtCompound.removeTag(key); + } + } + + public static @NotNull Tag Byte(@NotNull String key) { + return new Tag<>(key, + nbtCompound -> nbtCompound.getByte(key), + (nbtCompound, value) -> nbtCompound.setByte(key, value)); + } + + public static @NotNull Tag Short(@NotNull String key) { + return new Tag<>(key, + nbtCompound -> nbtCompound.getShort(key), + (nbtCompound, value) -> nbtCompound.setShort(key, value)); + } + + public static @NotNull Tag Integer(@NotNull String key) { + return new Tag<>(key, + nbtCompound -> nbtCompound.getInt(key), + (nbtCompound, integer) -> nbtCompound.setInt(key, integer)); + } + + public static @NotNull Tag Long(@NotNull String key) { + return new Tag<>(key, + nbtCompound -> nbtCompound.getLong(key), + (nbtCompound, value) -> nbtCompound.setLong(key, value)); + } + + public static @NotNull Tag Float(@NotNull String key) { + return new Tag<>(key, + nbtCompound -> nbtCompound.getFloat(key), + (nbtCompound, value) -> nbtCompound.setFloat(key, value)); + } + + public static @NotNull Tag Double(@NotNull String key) { + return new Tag<>(key, + nbtCompound -> nbtCompound.getDouble(key), + (nbtCompound, value) -> nbtCompound.setDouble(key, value)); + } + + public static @NotNull Tag ByteArray(@NotNull String key) { + return new Tag<>(key, + nbtCompound -> nbtCompound.getByteArray(key), + (nbtCompound, value) -> nbtCompound.setByteArray(key, value)); + } + + public static @NotNull Tag String(@NotNull String key) { + return new Tag<>(key, + nbtCompound -> nbtCompound.getString(key), + (nbtCompound, value) -> nbtCompound.setString(key, value)); + } + + public static @NotNull Tag NBT(@NotNull String key) { + return new Tag<>(key, + nbt -> { + var currentNBT = nbt.get(key); + + // Avoid a NPE when cloning a null variable. + if (currentNBT == null) { + return null; + } + + return currentNBT.deepClone(); + }, + ((nbt, value) -> nbt.set(key, value.deepClone()))); + } + + public static @NotNull Tag IntArray(@NotNull String key) { + return new Tag<>(key, + nbtCompound -> nbtCompound.getIntArray(key), + (nbtCompound, value) -> nbtCompound.setIntArray(key, value)); + } + + public static @NotNull Tag LongArray(@NotNull String key) { + return new Tag<>(key, + nbtCompound -> nbtCompound.getLongArray(key), + (nbtCompound, value) -> nbtCompound.setLongArray(key, value)); + } + + public static @NotNull Tag Custom(@NotNull String key, @NotNull TagSerializer serializer) { + return new Tag<>(key, + nbtCompound -> serializer.read(TagReadable.fromCompound(nbtCompound)), + (nbtCompound, value) -> serializer.write(TagWritable.fromCompound(nbtCompound), value)); + } +} diff --git a/src/main/java/net/minestom/server/tag/TagHandler.java b/src/main/java/net/minestom/server/tag/TagHandler.java new file mode 100644 index 000000000..b53db14a1 --- /dev/null +++ b/src/main/java/net/minestom/server/tag/TagHandler.java @@ -0,0 +1,10 @@ +package net.minestom.server.tag; + +import com.google.common.annotations.Beta; + +/** + * Represents an element which can read and write {@link Tag tags}. + */ +@Beta +public interface TagHandler extends TagReadable, TagWritable { +} diff --git a/src/main/java/net/minestom/server/tag/TagReadable.java b/src/main/java/net/minestom/server/tag/TagReadable.java new file mode 100644 index 000000000..18deb2f02 --- /dev/null +++ b/src/main/java/net/minestom/server/tag/TagReadable.java @@ -0,0 +1,48 @@ +package net.minestom.server.tag; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jglrxavpok.hephaistos.nbt.NBTCompound; + +/** + * Represents an element which can read {@link Tag tags}. + */ +public interface TagReadable { + + /** + * Reads the specified tag. + * + * @param tag the tag to read + * @param the tag type + * @return the read tag, null if not present + */ + @Nullable T getTag(@NotNull Tag tag); + + /** + * Returns if a tag is present. + * + * @param tag the tag to check + * @return true if the tag is present, false otherwise + */ + boolean hasTag(@NotNull Tag tag); + + /** + * Converts an nbt compound to a tag reader. + * + * @param compound the compound to convert + * @return a {@link TagReadable} capable of reading {@code compound} + */ + static @NotNull TagReadable fromCompound(@NotNull NBTCompound compound) { + return new TagReadable() { + @Override + public @Nullable T getTag(@NotNull Tag tag) { + return tag.read(compound); + } + + @Override + public boolean hasTag(@NotNull Tag tag) { + return compound.containsKey(tag.getKey()); + } + }; + } +} diff --git a/src/main/java/net/minestom/server/tag/TagSerializer.java b/src/main/java/net/minestom/server/tag/TagSerializer.java new file mode 100644 index 000000000..03318bfdf --- /dev/null +++ b/src/main/java/net/minestom/server/tag/TagSerializer.java @@ -0,0 +1,28 @@ +package net.minestom.server.tag; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Interface used to create custom types compatible with {@link Tag#Custom(String, TagSerializer)}. + * + * @param the type to serialize + */ +public interface TagSerializer { + + /** + * Reads the custom tag from a {@link TagReadable}. + * + * @param reader the reader + * @return the deserialized value + */ + @Nullable T read(@NotNull TagReadable reader); + + /** + * Writes the custom tag to a {@link TagWritable}. + * + * @param writer the writer + * @param value the value to serialize + */ + void write(@NotNull TagWritable writer, @NotNull T value); +} diff --git a/src/main/java/net/minestom/server/tag/TagWritable.java b/src/main/java/net/minestom/server/tag/TagWritable.java new file mode 100644 index 000000000..fe0c2962b --- /dev/null +++ b/src/main/java/net/minestom/server/tag/TagWritable.java @@ -0,0 +1,35 @@ +package net.minestom.server.tag; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jglrxavpok.hephaistos.nbt.NBTCompound; + +/** + * Represents an element which can write {@link Tag tags}. + */ +public interface TagWritable { + + /** + * Writes the specified type. + * + * @param tag the tag to write + * @param value the tag value, null to remove + * @param the tag type + */ + void setTag(@NotNull Tag tag, @Nullable T value); + + /** + * Converts an nbt compound to a tag writer. + * + * @param compound the compound to convert + * @return a {@link TagWritable} capable of writing {@code compound} + */ + static @NotNull TagWritable fromCompound(@NotNull NBTCompound compound) { + return new TagWritable() { + @Override + public void setTag(@NotNull Tag tag, @Nullable T value) { + tag.write(compound, value); + } + }; + } +}