diff --git a/src/main/java/it/jakegblp/lusk/api/PDCApi.java b/src/main/java/it/jakegblp/lusk/api/PDCApi.java new file mode 100644 index 00000000..19083a55 --- /dev/null +++ b/src/main/java/it/jakegblp/lusk/api/PDCApi.java @@ -0,0 +1,175 @@ +package it.jakegblp.lusk.api; + +import it.jakegblp.lusk.api.enums.PersistentTagType; +import it.jakegblp.lusk.utils.LuskUtils; +import org.bukkit.NamespacedKey; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.NullMarked; + +import java.util.*; + +public class PDCApi { + + public record Nested(@NotNull PersistentDataContainer container, @NotNull NamespacedKey key) {} + + @NullMarked + @Nullable + public static Object getTag(PersistentDataContainer container, PersistentTagType tagType, String path) { + Nested nested = getNestedContainer(container, path); + if (nested == null) return null; + return getTag(nested.container, tagType, nested.key); + } + + @NullMarked + @Nullable + public static Object getTag(PersistentDataContainer container, PersistentTagType tagType, NamespacedKey key) { + return container.get(key, tagType.getDataType()); + } + + @NullMarked + public static void deleteTag(PersistentDataContainer container, String path) { + setTag(container, null, path, null); + + } + + @NullMarked + public static void deleteTag(PersistentDataContainer container, NamespacedKey key) { + container.remove(key); + } + + @NullMarked + public static void setTag(PersistentDataContainer container, PersistentTagType tagType, NamespacedKey key, Object[] objects) { + Object object = objects[0]; + switch (tagType) { + case BOOLEAN -> { + if (object instanceof Boolean bool) { + container.set(key, PersistentDataType.BOOLEAN, bool); + } + } + case BYTE -> { + if (object instanceof Number number) { + container.set(key, PersistentDataType.BYTE, number.byteValue()); + } + } + case SHORT -> { + if (object instanceof Number number) { + container.set(key, PersistentDataType.SHORT, number.shortValue()); + } + } + case INTEGER -> { + if (object instanceof Number number) { + container.set(key, PersistentDataType.INTEGER, number.intValue()); + } + } + case LONG -> { + if (object instanceof Number number) { + container.set(key, PersistentDataType.LONG, number.longValue()); + } + } + case FLOAT -> { + if (object instanceof Number number) { + container.set(key, PersistentDataType.FLOAT, number.floatValue()); + } + } + case DOUBLE -> { + if (object instanceof Number number) { + container.set(key, PersistentDataType.DOUBLE, number.doubleValue()); + } + } + case STRING -> { + if (object instanceof String s) { + container.set(key, PersistentDataType.STRING, s); + } + } + case BYTE_ARRAY -> { + if (objects instanceof Number[] numbers) { + byte[] bytes = new byte[numbers.length]; + for (int i = 0; i < numbers.length; i++) + bytes[i] = numbers[i].byteValue(); + container.set(key, PersistentDataType.BYTE_ARRAY, bytes); + } + } + case INTEGER_ARRAY -> { + if (objects instanceof Number[] numbers) { + container.set(key, PersistentDataType.INTEGER_ARRAY, Arrays.stream(numbers).mapToInt(Number::intValue).toArray()); + } + } + case LONG_ARRAY -> { + if (objects instanceof Number[] numbers) { + container.set(key, PersistentDataType.LONG_ARRAY, Arrays.stream(numbers).mapToLong(Number::longValue).toArray()); + } + } + case TAG_CONTAINER -> { + if (object instanceof PersistentDataContainer c) { + container.set(key, PersistentDataType.TAG_CONTAINER, c); + } + } + } + } + + @NullMarked + public static void setTag(PersistentDataContainer container, @Nullable PersistentTagType tagType, String path, Object @Nullable [] objects) { + List parts = new ArrayList<>(List.of(path.split(";"))); + + TreeMap subContainers = new TreeMap<>(); + PersistentDataContainer currentContainer = container; + + for (int i = 0; i < parts.size(); i++) { + String part = parts.get(i); + NamespacedKey key = LuskUtils.getNamespacedKey(part); + if (key == null) return; + + if (i < parts.size() - 1) { + PersistentDataContainer nextContainer = currentContainer.getOrDefault( + key, + PersistentDataType.TAG_CONTAINER, + currentContainer.getAdapterContext().newPersistentDataContainer() + ); + subContainers.putIfAbsent(key, nextContainer); + currentContainer = nextContainer; + } else if (objects == null || tagType == null) { + deleteTag(currentContainer, key); + } else { + setTag(currentContainer, tagType, key, objects); + } + } + + subContainers.descendingMap().forEach((key, nested) -> { + PersistentDataContainer parent = subContainers.lowerEntry(key) == null ? container + : subContainers.lowerEntry(key).getValue(); + + parent.set(key, PersistentDataType.TAG_CONTAINER, nested); + }); + } + + @Nullable + @NullMarked + public static Nested getNestedContainer(PersistentDataContainer container, String path) { + NamespacedKey key; + if (path.contains(";")) { + List parts = new ArrayList<>(List.of(path.split(";"))); + String lastPart = parts.removeLast(); + PersistentDataContainer subContainer = container; + for (String part : parts) { + NamespacedKey subKey = LuskUtils.getNamespacedKey(part); + if (subKey == null) return null; + if (subContainer.has(subKey, PersistentDataType.TAG_CONTAINER)) { + subContainer = container.get(subKey, PersistentDataType.TAG_CONTAINER); + assert subContainer != null; + } else return null; + } + key = LuskUtils.getNamespacedKey(lastPart); + } else key = LuskUtils.getNamespacedKey(path); + if (key == null) return null; + return new Nested(container, key); + } + + @NullMarked + public static boolean hasTag(String tag, PersistentDataContainer container) { + return getNestedContainer(container, tag) != null; + } + +} diff --git a/src/main/java/it/jakegblp/lusk/api/enums/PersistentTagType.java b/src/main/java/it/jakegblp/lusk/api/enums/PersistentTagType.java new file mode 100644 index 00000000..a489410f --- /dev/null +++ b/src/main/java/it/jakegblp/lusk/api/enums/PersistentTagType.java @@ -0,0 +1,48 @@ +package it.jakegblp.lusk.api.enums; + +import org.bukkit.NamespacedKey; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; + +public enum PersistentTagType { + BYTE(PersistentDataType.BYTE), + SHORT(PersistentDataType.SHORT), + INTEGER(PersistentDataType.INTEGER), + LONG(PersistentDataType.LONG), + FLOAT(PersistentDataType.FLOAT), + DOUBLE(PersistentDataType.DOUBLE), + BOOLEAN(PersistentDataType.BOOLEAN), + STRING(PersistentDataType.STRING), + BYTE_ARRAY(PersistentDataType.BYTE_ARRAY, Byte[].class), + INTEGER_ARRAY(PersistentDataType.INTEGER_ARRAY, Integer[].class), + LONG_ARRAY(PersistentDataType.LONG_ARRAY, Long[].class), + TAG_CONTAINER(PersistentDataType.TAG_CONTAINER); + + private final Class complexClass; + private final PersistentDataType type; + + PersistentTagType(PersistentDataType type) { + this.type = type; + this.complexClass = type.getComplexType(); + } + + PersistentTagType(PersistentDataType type, Class complexClass) { + this.type = type; + this.complexClass = complexClass; + } + + public Class getComplexClass() { + return complexClass; + } + + public PersistentDataType getDataType() { + return type; + } + + public static PersistentTagType getByTag(NamespacedKey key, PersistentDataContainer container) { + for (PersistentTagType value : values()) { + if (container.has(key, value.getDataType())) return value; + } + return null; + } +} diff --git a/src/main/java/it/jakegblp/lusk/elements/minecraft/namespacedkey/types/NamespacedKeyClassInfos.java b/src/main/java/it/jakegblp/lusk/elements/minecraft/namespacedkey/types/NamespacedKeyClassInfos.java new file mode 100644 index 00000000..2ac447c3 --- /dev/null +++ b/src/main/java/it/jakegblp/lusk/elements/minecraft/namespacedkey/types/NamespacedKeyClassInfos.java @@ -0,0 +1,80 @@ +package it.jakegblp.lusk.elements.minecraft.namespacedkey.types; + +import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.classes.Parser; +import ch.njol.skript.classes.Serializer; +import ch.njol.skript.lang.ParseContext; +import ch.njol.skript.registrations.Classes; +import ch.njol.yggdrasil.Fields; +import it.jakegblp.lusk.utils.LuskUtils; +import org.bukkit.NamespacedKey; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.StreamCorruptedException; + +public class NamespacedKeyClassInfos { + static { + if (Classes.getExactClassInfo(NamespacedKey.class) == null) { + Classes.registerClass(new ClassInfo<>(NamespacedKey.class, "namespacedkey") + .user("namespaced ?keys?") + .name("Namespaced Key") + .description(""" + A String based key which consists of a namespace and a key; used to declare and specify game objects in Minecraft without without potential ambiguity or conflicts. + + Namespaces may only contain lowercase alphanumeric characters, periods, underscores, and hyphens. + Keys may only contain lowercase alphanumeric characters, periods, underscores, hyphens, and forward slashes. + + More Info: [**Resource Location**](https://minecraft.wiki/w/Resource_location) + """) + .since("1.4") + .parser(new Parser<>() { + @Override + public @Nullable NamespacedKey parse(String s, ParseContext context) { + return LuskUtils.getNamespacedKey(s); + } + + @Override + public String toString(NamespacedKey o, int flags) { + return o.toString(); + } + + @Override + public String toVariableNameString(NamespacedKey o) { + return o.toString(); + } + }) + .serializer(new Serializer<>() { + @Override + public @NotNull Fields serialize(NamespacedKey namespacedKey) { + Fields fields = new Fields(); + fields.putObject("key", namespacedKey.toString()); + return fields; + } + + @Override + public void deserialize(NamespacedKey o, Fields f) { + } + + @Override + protected NamespacedKey deserialize(Fields fields) throws StreamCorruptedException { + String key = fields.getObject("key", String.class); + if (key == null) { + throw new StreamCorruptedException("NamespacedKey string is null"); + } + return NamespacedKey.fromString(key); + } + + @Override + public boolean mustSyncDeserialization() { + return true; + } + + @Override + protected boolean canBeInstantiated() { + return false; + } + })); + } + } +} \ No newline at end of file diff --git a/src/main/java/it/jakegblp/lusk/elements/minecraft/persistence/expressions/ExprPersistentDataContainer.java b/src/main/java/it/jakegblp/lusk/elements/minecraft/persistence/expressions/ExprPersistentDataContainer.java new file mode 100644 index 00000000..8e57fd22 --- /dev/null +++ b/src/main/java/it/jakegblp/lusk/elements/minecraft/persistence/expressions/ExprPersistentDataContainer.java @@ -0,0 +1,73 @@ +package it.jakegblp.lusk.elements.minecraft.persistence.expressions; + +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Examples; +import ch.njol.skript.doc.Name; +import ch.njol.skript.doc.Since; +import it.jakegblp.lusk.api.skript.SimplerPropertyExpression; +import org.bukkit.NamespacedKey; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataHolder; + +@Name("Persistence - Persistent Data of X") +@Description(""" + Gets the custom tag container capable of storing tags on the provided holders. + Note: the tags stored on this container are all stored under their own custom namespace therefore modifying default tags using this PersistentDataHolder is impossible. + """) +@Examples("send persistent data of player") +@Since("1.4") +public class ExprPersistentDataContainer extends SimplerPropertyExpression { + + static { + register(ExprPersistentDataContainer.class, PersistentDataContainer.class, "persistent data [container]", "persistentdataholders"); + } + + @Override + public PersistentDataContainer convert(PersistentDataHolder from) { + return from.getPersistentDataContainer(); + } + + @Override + public boolean allowSet() { + return true; + } + + @Override + public boolean allowReset() { + return true; + } + + @Override + public boolean allowDelete() { + return true; + } + + @Override + public void set(PersistentDataHolder from, PersistentDataContainer to) { + delete(from); + to.copyTo(from.getPersistentDataContainer(), false); + } + + @Override + public void delete(PersistentDataHolder from) { + PersistentDataContainer container = from.getPersistentDataContainer(); + for (NamespacedKey key : container.getKeys()) { + container.remove(key); + } + } + + @Override + public void reset(PersistentDataHolder from) { + delete(from); + } + + @Override + protected String getPropertyName() { + return "persistent data"; + } + + @Override + public Class getReturnType() { + return PersistentDataContainer.class; + } +} diff --git a/src/main/java/it/jakegblp/lusk/elements/minecraft/persistence/expressions/ExprPersistentTag.java b/src/main/java/it/jakegblp/lusk/elements/minecraft/persistence/expressions/ExprPersistentTag.java new file mode 100644 index 00000000..c94eb3d4 --- /dev/null +++ b/src/main/java/it/jakegblp/lusk/elements/minecraft/persistence/expressions/ExprPersistentTag.java @@ -0,0 +1,115 @@ +package it.jakegblp.lusk.elements.minecraft.persistence.expressions; + +import ch.njol.skript.classes.Changer; +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Name; +import ch.njol.skript.expressions.base.PropertyExpression; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.Literal; +import ch.njol.skript.lang.SkriptParser; +import ch.njol.util.Kleenean; +import it.jakegblp.lusk.api.PDCApi; +import it.jakegblp.lusk.api.enums.PersistentTagType; +import org.bukkit.event.Event; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataHolder; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Objects; +import java.util.stream.Stream; + +@Name("Persistence - Persistent Tag of X") +@Description(""" +Get, Set and Delete the value of the specified namespaced key and type, supports getting nested keys using semicolons as delimiters. + +""")// todo: finish docs +public class ExprPersistentTag extends PropertyExpression { + + static { + register(ExprPersistentTag.class, Object.class, "%persistenttagtype% %string%", "persistentdatacontainers/persistentdataholders"); + // todo: improve toString, add number class char suffix for arrays, add nbt readable and pretty print + } + + private Expression tagTypeExpression; + private Expression stringExpression; + + @Override + @SuppressWarnings("unchecked") + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { + if (matchedPattern == 0) { + tagTypeExpression = (Expression) expressions[0]; + stringExpression = (Expression) expressions[1]; + setExpr(expressions[2]); + } else { + setExpr(expressions[0]); + tagTypeExpression = (Expression) expressions[1]; + stringExpression = (Expression) expressions[2]; + } + return true; + } + + + @Override + public Class getReturnType() { + return tagTypeExpression instanceof Literal literal ? literal.getSingle().getComplexClass() : Object.class; + } + + @Override + public boolean isSingle() { + return super.isSingle() || tagTypeExpression instanceof Literal literal && literal.getSingle().getComplexClass().isArray(); + } + + @Override + protected Object[] get(Event event, Object[] source) { + String string = stringExpression.getSingle(event); + if (string == null) return new Object[0]; + PersistentTagType tagType = tagTypeExpression.getSingle(event); + if (tagType == null) return new Object[0]; + return Arrays.stream(source).flatMap(object -> { + PersistentDataContainer container; + if (object instanceof PersistentDataContainer c) container = c; + else if (object instanceof PersistentDataHolder holder) container = holder.getPersistentDataContainer(); + else return null; + //Object value = PDCApi.getTag(container, tagType, string); + return Stream.ofNullable(PDCApi.getTag(container, tagType, string)); + //return Stream.of(tagType.get(container, new NamespacedKey(Lusk.getInstance(), string))); + }).filter(Objects::nonNull).toArray(); + } + + @Override + public @Nullable Class[] acceptChange(Changer.ChangeMode mode) { + return switch (mode) { + case SET, DELETE -> new Class[]{tagTypeExpression instanceof Literal literal ? literal.getSingle().getComplexClass() : Object.class}; + default -> null; + }; + } + + @Override + public void change(Event event, @Nullable Object[] delta, Changer.ChangeMode mode) { + String string = stringExpression.getSingle(event); + if (string == null) return; + PersistentDataContainer[] containers = getExpr().stream(event).map(o -> { + if (o instanceof PersistentDataContainer container) return container; + else if (o instanceof PersistentDataHolder holder) return holder.getPersistentDataContainer(); + return null; + }).filter(Objects::nonNull).toArray(PersistentDataContainer[]::new); + + if (mode == Changer.ChangeMode.DELETE) { + for (PersistentDataContainer container : containers) { + PDCApi.deleteTag(container, string); + } + } else { + PersistentTagType tagType = tagTypeExpression.getSingle(event); + if (tagType == null) return; + for (PersistentDataContainer container : containers) { + PDCApi.setTag(container, tagType, string, delta); + } + } + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return getExpr().toString(event, debug) + "'s " + tagTypeExpression.toString(event, debug) + " " + stringExpression.toString(event, debug); + } +} diff --git a/src/main/java/it/jakegblp/lusk/elements/minecraft/persistence/types/PersistenceClassInfos.java b/src/main/java/it/jakegblp/lusk/elements/minecraft/persistence/types/PersistenceClassInfos.java new file mode 100644 index 00000000..e95be22b --- /dev/null +++ b/src/main/java/it/jakegblp/lusk/elements/minecraft/persistence/types/PersistenceClassInfos.java @@ -0,0 +1,58 @@ +package it.jakegblp.lusk.elements.minecraft.persistence.types; + +import ch.njol.skript.classes.ClassInfo; +import ch.njol.skript.classes.Parser; +import ch.njol.skript.lang.ParseContext; +import ch.njol.skript.registrations.Classes; +import it.jakegblp.lusk.api.enums.PersistentTagType; +import it.jakegblp.lusk.api.skript.EnumWrapper; +import it.jakegblp.lusk.utils.PersistenceUtils; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataHolder; +import org.jetbrains.annotations.Nullable; + +public class PersistenceClassInfos { + static { + EnumWrapper PERSISTENT_TAG_TYPE_ENUM = new EnumWrapper<>(PersistentTagType.class, "persistent", "tag"); + Classes.registerClass(PERSISTENT_TAG_TYPE_ENUM.getClassInfo("persistenttagtype") + .user("persistent ?tag ?types?") + .name("Persistence - Tag Type") + .description("All the Persistent Tag Type.") // add example + .since("1.4")); + if (Classes.getExactClassInfo(PersistentDataHolder.class) == null) { + Classes.registerClass(new ClassInfo<>(PersistentDataHolder.class, "persistentdataholder") + .user("(persistent ?)?data ?holders?") + .name("Persistent Data Holder") + .description("A Persistent Data Holder.") // add example + .since("1.4")); + } + if (Classes.getExactClassInfo(PersistentDataContainer.class) == null) { + Classes.registerClass(new ClassInfo<>(PersistentDataContainer.class, "persistentdatacontainer") + .user("((persistent ?)?data ?container|persistent ?data)s?") + .name("Persistent Data Container") + .description("A Persistent Data Container.") // add example + .since("1.4") + .parser(new Parser<>() { + @Override + public @Nullable PersistentDataContainer parse(String s, ParseContext context) { + return null; + } + + @Override + public boolean canParse(ParseContext context) { + return false; + } + + @Override + public String toString(PersistentDataContainer o, int flags) { + return PersistenceUtils.asString(o); + } + + @Override + public String toVariableNameString(PersistentDataContainer o) { + return "PersistentDataContainer [ " + PersistenceUtils.asString(o) + " ]"; + } + })); + } + } +} diff --git a/src/main/java/it/jakegblp/lusk/utils/Constants.java b/src/main/java/it/jakegblp/lusk/utils/Constants.java index fb707ef8..94b65d9a 100644 --- a/src/main/java/it/jakegblp/lusk/utils/Constants.java +++ b/src/main/java/it/jakegblp/lusk/utils/Constants.java @@ -35,6 +35,8 @@ public class Constants { public static final double EPSILON = 1e-7; + public static final short MAX_NAMESPACED_KEY_LENGTH = isPaper() ? Short.MAX_VALUE : 255; + public static final String[] LUSK_COLORS = new String[]{"&7", "&9"}; public static final Pattern diff --git a/src/main/java/it/jakegblp/lusk/utils/LuskUtils.java b/src/main/java/it/jakegblp/lusk/utils/LuskUtils.java index e2f4fa2c..8bd202ba 100644 --- a/src/main/java/it/jakegblp/lusk/utils/LuskUtils.java +++ b/src/main/java/it/jakegblp/lusk/utils/LuskUtils.java @@ -7,8 +7,10 @@ import net.md_5.bungee.api.ChatColor; import org.bukkit.Bukkit; import org.bukkit.Color; +import org.bukkit.NamespacedKey; import org.bukkit.command.CommandSender; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.text.MessageFormat; import java.util.List; @@ -125,4 +127,16 @@ public static ColorRGB getColorAsRGB(@NotNull Color color) { return new ColorRGB(color.getRed(), color.getGreen(), color.getBlue()); } + @Nullable + public static NamespacedKey getNamespacedKey(@NotNull String key) { + if (key.isEmpty()) return null; + if (!key.contains(":")) key = "minecraft:" + key; + if (key.length() > MAX_NAMESPACED_KEY_LENGTH) { + warning("Namespacedkey {0} with length {1} exceeds the max length of {2}!", key, key.length(), MAX_NAMESPACED_KEY_LENGTH); + return null; + } + key = key.toLowerCase(); + if (key.contains(" ")) key = key.replace(" ", "_"); + return NamespacedKey.fromString(key); + } } diff --git a/src/main/java/it/jakegblp/lusk/utils/PersistenceUtils.java b/src/main/java/it/jakegblp/lusk/utils/PersistenceUtils.java new file mode 100644 index 00000000..fae5f938 --- /dev/null +++ b/src/main/java/it/jakegblp/lusk/utils/PersistenceUtils.java @@ -0,0 +1,60 @@ +package it.jakegblp.lusk.utils; + +import it.jakegblp.lusk.api.PDCApi; +import it.jakegblp.lusk.api.enums.PersistentTagType; +import org.bukkit.NamespacedKey; +import org.bukkit.persistence.PersistentDataContainer; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Set; + +public class PersistenceUtils { + @Nullable + public static PersistentTagType getPersistentDataType(PersistentDataContainer container, NamespacedKey key) { + for (PersistentTagType value : PersistentTagType.values()) { + if (container.has(key, value.getDataType())) + return value; + } + return null; + } + + @Nullable + public static Object getValue(PersistentDataContainer container, NamespacedKey key) { + PersistentTagType tag = getPersistentDataType(container, key); + if (tag == null) return null; + return container.get(key, tag.getDataType()); + } + + public static String asString(PersistentDataContainer container) { + if (container.isEmpty()) return "{}"; + StringBuilder builder = new StringBuilder("{"); + Set keys = container.getKeys(); + int i = 0; + for (NamespacedKey key : keys) { + if (i > 0) builder.append(", "); + PersistentTagType tagType = getPersistentDataType(container, key); + if (tagType == null) continue; + Object value = PDCApi.getTag(container, tagType, key); + assert value != null; + //Object value = tagType.get(container,key); + builder.append('"').append(key.asString()).append("\": "); + switch (tagType) { + case BYTE -> builder.append(value).append('b'); + case SHORT -> builder.append(value).append('s'); + case INTEGER -> builder.append(value); + case LONG -> builder.append(value).append('l'); + case FLOAT -> builder.append(value).append('f') ; + case DOUBLE -> builder.append(value); + case BOOLEAN -> builder.append(value); + case STRING -> builder.append('"').append(value).append('"'); + case BYTE_ARRAY -> builder.append(Arrays.toString((byte[]) value)); + case INTEGER_ARRAY -> builder.append(Arrays.toString((int[]) value)); + case LONG_ARRAY -> builder.append(Arrays.toString((long[]) value)); + case TAG_CONTAINER -> builder.append(asString((PersistentDataContainer) value)); + } + i++; + } + return builder.append("}").toString(); + } +} diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang index b955cf00..7a3a951c 100644 --- a/src/main/resources/lang/default.lang +++ b/src/main/resources/lang/default.lang @@ -37,6 +37,10 @@ types: ignitecause: ignition cause¦s @an entitysnapshot: entity snapshot¦s @an blockaction: block action¦s @a + persistentdataholder: persistent data holder¦s @a + persistentdatacontainer: persistent data container¦s @a + persistenttagtype: persistent tag type¦s @a + namespacedkey: namespaced key¦s @a # - Packet-related - entityanimation: entity animation¦s @an