From a0652f08fccda5ba1317edf960a9affb6d8b105d Mon Sep 17 00:00:00 2001 From: Jake Date: Thu, 2 Jan 2025 15:30:09 +0100 Subject: [PATCH 1/7] Basic PDC implementation --- .../lusk/api/enums/PersistentTagType.java | 46 ++++++++++ .../ExprPersistentDataContainer.java | 34 +++++++ .../expressions/ExprPersistentTag.java | 92 +++++++++++++++++++ .../types/PersistenceClassInfos.java | 58 ++++++++++++ .../jakegblp/lusk/utils/PersistenceUtils.java | 54 +++++++++++ 5 files changed, 284 insertions(+) create mode 100644 src/main/java/it/jakegblp/lusk/api/enums/PersistentTagType.java create mode 100644 src/main/java/it/jakegblp/lusk/elements/minecraft/persistence/expressions/ExprPersistentDataContainer.java create mode 100644 src/main/java/it/jakegblp/lusk/elements/minecraft/persistence/expressions/ExprPersistentTag.java create mode 100644 src/main/java/it/jakegblp/lusk/elements/minecraft/persistence/types/PersistenceClassInfos.java create mode 100644 src/main/java/it/jakegblp/lusk/utils/PersistenceUtils.java 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..c625e243 --- /dev/null +++ b/src/main/java/it/jakegblp/lusk/api/enums/PersistentTagType.java @@ -0,0 +1,46 @@ +package it.jakegblp.lusk.api.enums; + +import org.apache.logging.log4j.util.TriConsumer; +import org.bukkit.NamespacedKey; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; + +import java.util.function.BiFunction; + +public enum PersistentTagType { + BYTE(PersistentDataType.BYTE, (container, key) -> container.get(key,PersistentDataType.BYTE), (container, key, o) -> container.set(key,PersistentDataType.BYTE, o)), + SHORT(PersistentDataType.SHORT, (container, key) -> container.get(key,PersistentDataType.SHORT), (container, key, o) -> container.set(key,PersistentDataType.SHORT, o)), + INTEGER(PersistentDataType.INTEGER, (container, key) -> container.get(key,PersistentDataType.INTEGER), (container, key, o) -> container.set(key,PersistentDataType.INTEGER, o)), + LONG(PersistentDataType.LONG, (container, key) -> container.get(key,PersistentDataType.LONG), (container, key, o) -> container.set(key,PersistentDataType.LONG, o)), + FLOAT(PersistentDataType.FLOAT, (container, key) -> container.get(key,PersistentDataType.FLOAT), (container, key, o) -> container.set(key,PersistentDataType.FLOAT, o)), + DOUBLE(PersistentDataType.DOUBLE, (container, key) -> container.get(key,PersistentDataType.DOUBLE), (container, key, o) -> container.set(key,PersistentDataType.DOUBLE, o)), + BOOLEAN(PersistentDataType.BOOLEAN, (container, key) -> container.get(key,PersistentDataType.BOOLEAN), (container, key, o) -> container.set(key,PersistentDataType.BOOLEAN, o)), + STRING(PersistentDataType.STRING, (container, key) -> container.get(key,PersistentDataType.STRING), (container, key, o) -> container.set(key,PersistentDataType.STRING, o)), + BYTE_ARRAY(PersistentDataType.BYTE_ARRAY, (container, key) -> container.get(key,PersistentDataType.BYTE_ARRAY), (container, key, o) -> container.set(key,PersistentDataType.BYTE_ARRAY, o)), + INTEGER_ARRAY(PersistentDataType.INTEGER_ARRAY, (container, key) -> container.get(key,PersistentDataType.INTEGER_ARRAY), (container, key, o) -> container.set(key,PersistentDataType.INTEGER_ARRAY, o)), + LONG_ARRAY(PersistentDataType.LONG_ARRAY, (container, key) -> container.get(key,PersistentDataType.LONG_ARRAY), (container, key, o) -> container.set(key,PersistentDataType.LONG_ARRAY, o)), + TAG_CONTAINER(PersistentDataType.TAG_CONTAINER, (container, key) -> container.get(key,PersistentDataType.TAG_CONTAINER), (container, key, o) -> container.set(key,PersistentDataType.TAG_CONTAINER, o)); + + private final PersistentDataType type; + private final BiFunction get; + private final TriConsumer set; + + @SuppressWarnings("unchecked") + PersistentTagType(PersistentDataType type, BiFunction get, TriConsumer set) { + this.type = type; + this.get = (BiFunction) get; + this.set = (TriConsumer) set; + } + + public PersistentDataType getType() { + return type; + } + + public Object get(PersistentDataContainer container, NamespacedKey key) { + return get.apply(container, key); + } + + public void set(PersistentDataContainer container, NamespacedKey key, Object o) { + set.accept(container, key, o); + } +} 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..36c9bd76 --- /dev/null +++ b/src/main/java/it/jakegblp/lusk/elements/minecraft/persistence/expressions/ExprPersistentDataContainer.java @@ -0,0 +1,34 @@ +package it.jakegblp.lusk.elements.minecraft.persistence.expressions; + +import ch.njol.skript.doc.Description; +import ch.njol.skript.doc.Name; +import ch.njol.skript.expressions.base.SimplePropertyExpression; +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. + """) +public class ExprPersistentDataContainer extends SimplePropertyExpression { + + static { + register(ExprPersistentDataContainer.class, PersistentDataContainer.class, "persistent data [container]", "persistentdataholders"); + } + + @Override + public PersistentDataContainer convert(PersistentDataHolder from) { + return from.getPersistentDataContainer(); + } + + @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..8ace3e5e --- /dev/null +++ b/src/main/java/it/jakegblp/lusk/elements/minecraft/persistence/expressions/ExprPersistentTag.java @@ -0,0 +1,92 @@ +package it.jakegblp.lusk.elements.minecraft.persistence.expressions; + +import ch.njol.skript.classes.Changer; +import ch.njol.skript.expressions.base.PropertyExpression; +import ch.njol.skript.lang.Expression; +import ch.njol.skript.lang.SkriptParser; +import ch.njol.skript.util.LiteralUtils; +import ch.njol.util.Kleenean; +import it.jakegblp.lusk.Lusk; +import it.jakegblp.lusk.api.enums.PersistentTagType; +import org.bukkit.NamespacedKey; +import org.bukkit.event.Event; +import org.bukkit.persistence.PersistentDataContainer; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; + +public class ExprPersistentTag extends PropertyExpression { + + static { + register(ExprPersistentTag.class, Object.class, "%persistenttagtype% %string%", "persistentdatacontainers"); + // todo: fix issues with number classes, add nested tags, improve toString + } + + private Expression tagTypeExpression; + private Expression stringExpression; + + @Override + @SuppressWarnings("unchecked") + public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { + if (LiteralUtils.hasUnparsedLiteral(expressions[0])) { + setExpr(LiteralUtils.defendExpression(expressions[0])); + return LiteralUtils.canInitSafely(getExpr()); + } + if (matchedPattern == 0) { + tagTypeExpression = (Expression) expressions[0]; + stringExpression = (Expression) expressions[1]; + setExpr((Expression) expressions[2]); + } else { + setExpr((Expression) expressions[0]); + tagTypeExpression = (Expression) expressions[1]; + stringExpression = (Expression) expressions[2]; + } + return true; + } + + + @Override + public Class getReturnType() { + return Object.class; + } + + @Override + protected Object[] get(Event event, PersistentDataContainer[] 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).map(container -> tagType.get(container,new NamespacedKey(Lusk.getInstance(),string))).toArray(); + } + + @Override + public @Nullable Class[] acceptChange(Changer.ChangeMode mode) { + return switch (mode) { + case SET, DELETE -> new Class[]{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; + if (mode == Changer.ChangeMode.DELETE) { + for (PersistentDataContainer persistentDataContainer : getExpr().getArray(event)) { + persistentDataContainer.remove(new NamespacedKey(Lusk.getInstance(),string)); + } + } else { + PersistentTagType tagType = tagTypeExpression.getSingle(event); + if (tagType == null) return; + Object o = delta[0]; + for (PersistentDataContainer persistentDataContainer : getExpr().getArray(event)) { + tagType.set(persistentDataContainer, new NamespacedKey(Lusk.getInstance(),string), o); + } + } + } + + @Override + public String toString(@Nullable Event event, boolean debug) { + return getExpr().toString(event, debug) + "'s persistent " + tagTypeExpression.toString(event, debug) + " tag " + 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/PersistenceUtils.java b/src/main/java/it/jakegblp/lusk/utils/PersistenceUtils.java new file mode 100644 index 00000000..15404dde --- /dev/null +++ b/src/main/java/it/jakegblp/lusk/utils/PersistenceUtils.java @@ -0,0 +1,54 @@ +package it.jakegblp.lusk.utils; + +import org.bukkit.NamespacedKey; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Set; + +public class PersistenceUtils { + public static final PersistentDataType[] DATA_TYPES = { + PersistentDataType.BYTE, + PersistentDataType.SHORT, + PersistentDataType.INTEGER, + PersistentDataType.LONG, + PersistentDataType.FLOAT, + PersistentDataType.DOUBLE, + PersistentDataType.TAG_CONTAINER + }; + + @Nullable + public static PersistentDataType getPersistentDataType(PersistentDataContainer container, NamespacedKey key) { + return Arrays.stream(DATA_TYPES).filter(persistentDataType -> container.has(key, persistentDataType)).findFirst().orElse(null); + } + + @Nullable + public static Object getValue(PersistentDataContainer container, NamespacedKey key) { + PersistentDataType dataType = getPersistentDataType(container, key); + if (dataType == null) return null; + return container.get(key, dataType); + } + + 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(", "); + PersistentDataType dataType = getPersistentDataType(container, key); + if (dataType == null) continue; + Object value = container.get(key, dataType); + builder.append(dataType).append(": "); + if (value instanceof PersistentDataContainer innerContainer) { + builder.append(asString(innerContainer)); + } else { + builder.append(value); + } + i++; + } + return builder.append("}").toString(); + } +} From 81b10c66cccc940e07b5a2e17b77970ae947431e Mon Sep 17 00:00:00 2001 From: Jake Date: Thu, 2 Jan 2025 15:31:19 +0100 Subject: [PATCH 2/7] Added one more task to the todo --- .../minecraft/persistence/expressions/ExprPersistentTag.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 8ace3e5e..ac85cdcb 100644 --- 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 @@ -19,7 +19,7 @@ public class ExprPersistentTag extends PropertyExpression tagTypeExpression; From f6e0678b34083a0a2cd254c6a0b338d1c4ca76a4 Mon Sep 17 00:00:00 2001 From: Jake Date: Fri, 3 Jan 2025 01:34:13 +0100 Subject: [PATCH 3/7] Improved pdc implementation --- .../lusk/api/enums/PersistentTagType.java | 7 ++ .../expressions/ExprPersistentTag.java | 74 ++++++++++++++----- .../jakegblp/lusk/utils/PersistenceUtils.java | 52 ++++++------- src/main/resources/lang/default.lang | 3 + 4 files changed, 93 insertions(+), 43 deletions(-) diff --git a/src/main/java/it/jakegblp/lusk/api/enums/PersistentTagType.java b/src/main/java/it/jakegblp/lusk/api/enums/PersistentTagType.java index c625e243..49a226fc 100644 --- a/src/main/java/it/jakegblp/lusk/api/enums/PersistentTagType.java +++ b/src/main/java/it/jakegblp/lusk/api/enums/PersistentTagType.java @@ -4,7 +4,9 @@ import org.bukkit.NamespacedKey; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; +import org.jetbrains.annotations.Nullable; +import java.util.Arrays; import java.util.function.BiFunction; public enum PersistentTagType { @@ -43,4 +45,9 @@ public Object get(PersistentDataContainer container, NamespacedKey key) { public void set(PersistentDataContainer container, NamespacedKey key, Object o) { set.accept(container, key, o); } + + @Nullable + public static PersistentTagType fromDataType(PersistentDataType type) { + return Arrays.stream(values()).filter(persistentTagType -> persistentTagType.type == type).findFirst().orElse(null); + } } 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 index ac85cdcb..5abeaddb 100644 --- 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 @@ -3,41 +3,49 @@ import ch.njol.skript.classes.Changer; 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.skript.util.LiteralUtils; import ch.njol.util.Kleenean; import it.jakegblp.lusk.Lusk; import it.jakegblp.lusk.api.enums.PersistentTagType; import org.bukkit.NamespacedKey; 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; -public class ExprPersistentTag extends PropertyExpression { +import static it.jakegblp.lusk.utils.LuskUtils.consoleLog; + +public class ExprPersistentTag extends PropertyExpression { static { - register(ExprPersistentTag.class, Object.class, "%persistenttagtype% %string%", "persistentdatacontainers"); - // todo: fix issues with number classes, add nested tags, improve toString, support PDH ^^^ + register(ExprPersistentTag.class, Object.class, "%persistenttagtype% %string%", "persistentdatacontainers/persistentdataholders"); + // todo: add nested tags, improve toString } + private Literal tagTypeLiteral; private Expression tagTypeExpression; private Expression stringExpression; @Override @SuppressWarnings("unchecked") public boolean init(Expression[] expressions, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { - if (LiteralUtils.hasUnparsedLiteral(expressions[0])) { - setExpr(LiteralUtils.defendExpression(expressions[0])); - return LiteralUtils.canInitSafely(getExpr()); - } if (matchedPattern == 0) { tagTypeExpression = (Expression) expressions[0]; stringExpression = (Expression) expressions[1]; - setExpr((Expression) expressions[2]); + if (expressions[2] instanceof Literal) { + tagTypeLiteral = (Literal) expressions[2]; + } + setExpr(expressions[2]); } else { - setExpr((Expression) expressions[0]); + if (expressions[0] instanceof Literal) { + tagTypeLiteral = (Literal) expressions[0]; + } + setExpr(expressions[0]); tagTypeExpression = (Expression) expressions[1]; stringExpression = (Expression) expressions[2]; } @@ -47,22 +55,33 @@ public boolean init(Expression[] expressions, int matchedPattern, Kleenean is @Override public Class getReturnType() { - return Object.class; + return tagTypeLiteral != null ? tagTypeLiteral.getSingle().getType().getComplexType() : Object.class; } @Override - protected Object[] get(Event event, PersistentDataContainer[] source) { + public boolean isSingle() { + return super.isSingle() || (tagTypeLiteral != null && tagTypeLiteral.getSingle().getType().getComplexType().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).map(container -> tagType.get(container,new NamespacedKey(Lusk.getInstance(),string))).toArray(); + 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 Stream.of(); + 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[]{Object.class}; + case SET, DELETE -> new Class[]{tagTypeLiteral != null ? tagTypeLiteral.getSingle().getType().getComplexType() : Object.class}; default -> null; }; } @@ -71,16 +90,35 @@ protected Object[] get(Event event, PersistentDataContainer[] source) { 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 -> { + consoleLog("1: {0}",o instanceof PersistentDataContainer); + consoleLog("2: {0}",o instanceof PersistentDataHolder); + 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 persistentDataContainer : getExpr().getArray(event)) { + for (PersistentDataContainer persistentDataContainer : containers) { persistentDataContainer.remove(new NamespacedKey(Lusk.getInstance(),string)); } } else { PersistentTagType tagType = tagTypeExpression.getSingle(event); if (tagType == null) return; - Object o = delta[0]; - for (PersistentDataContainer persistentDataContainer : getExpr().getArray(event)) { - tagType.set(persistentDataContainer, new NamespacedKey(Lusk.getInstance(),string), o); + Object object = delta[0]; + if (object instanceof Number number) { + object = switch (tagType) { + case BYTE -> number.byteValue(); + case SHORT -> number.shortValue(); + case INTEGER -> number.intValue(); + case LONG -> number.longValue(); + case FLOAT -> number.floatValue(); + case DOUBLE -> number.doubleValue(); + default -> object; + }; + } + for (PersistentDataContainer persistentDataContainer : containers) { + tagType.set(persistentDataContainer, new NamespacedKey(Lusk.getInstance(),string), object); } } } diff --git a/src/main/java/it/jakegblp/lusk/utils/PersistenceUtils.java b/src/main/java/it/jakegblp/lusk/utils/PersistenceUtils.java index 15404dde..d60c7b7e 100644 --- a/src/main/java/it/jakegblp/lusk/utils/PersistenceUtils.java +++ b/src/main/java/it/jakegblp/lusk/utils/PersistenceUtils.java @@ -1,34 +1,27 @@ package it.jakegblp.lusk.utils; +import it.jakegblp.lusk.api.enums.PersistentTagType; import org.bukkit.NamespacedKey; import org.bukkit.persistence.PersistentDataContainer; -import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.Nullable; -import java.util.Arrays; import java.util.Set; public class PersistenceUtils { - public static final PersistentDataType[] DATA_TYPES = { - PersistentDataType.BYTE, - PersistentDataType.SHORT, - PersistentDataType.INTEGER, - PersistentDataType.LONG, - PersistentDataType.FLOAT, - PersistentDataType.DOUBLE, - PersistentDataType.TAG_CONTAINER - }; - @Nullable - public static PersistentDataType getPersistentDataType(PersistentDataContainer container, NamespacedKey key) { - return Arrays.stream(DATA_TYPES).filter(persistentDataType -> container.has(key, persistentDataType)).findFirst().orElse(null); + public static PersistentTagType getPersistentDataType(PersistentDataContainer container, NamespacedKey key) { + for (PersistentTagType value : PersistentTagType.values()) { + if (container.has(key, value.getType())) + return value; + } + return null; } @Nullable public static Object getValue(PersistentDataContainer container, NamespacedKey key) { - PersistentDataType dataType = getPersistentDataType(container, key); - if (dataType == null) return null; - return container.get(key, dataType); + PersistentTagType tag = getPersistentDataType(container, key); + if (tag == null) return null; + return container.get(key, tag.getType()); } public static String asString(PersistentDataContainer container) { @@ -38,14 +31,23 @@ public static String asString(PersistentDataContainer container) { int i = 0; for (NamespacedKey key : keys) { if (i > 0) builder.append(", "); - PersistentDataType dataType = getPersistentDataType(container, key); - if (dataType == null) continue; - Object value = container.get(key, dataType); - builder.append(dataType).append(": "); - if (value instanceof PersistentDataContainer innerContainer) { - builder.append(asString(innerContainer)); - } else { - builder.append(value); + PersistentTagType tagType = getPersistentDataType(container, key); + if (tagType == null) continue; + 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("-").append(value).append("-"); + case INTEGER_ARRAY -> builder.append("-").append(value).append("-"); + case LONG_ARRAY -> builder.append("-").append(value).append("-"); + case TAG_CONTAINER -> builder.append(asString((PersistentDataContainer) value)); } i++; } diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang index b258f319..c01fc129 100644 --- a/src/main/resources/lang/default.lang +++ b/src/main/resources/lang/default.lang @@ -36,6 +36,9 @@ types: timespanperiod: time span period¦s @a ignitecause: ignition cause¦s @an entitysnapshot: entity snapshot¦s @an + persistentdataholder: persistent data holder¦s @a + persistentdatacontainer: persistent data container¦s @a + persistenttagtype: persistent tag type¦s @a # -- Rotations -- rotations: From 4e3184a590e9081a126620a9443c9ad56e421c80 Mon Sep 17 00:00:00 2001 From: Jake Date: Fri, 3 Jan 2025 20:12:25 +0100 Subject: [PATCH 4/7] Trying new stuff --- .../java/it/jakegblp/lusk/api/PDCApi.java | 84 +++++++++++++++++++ .../java/it/jakegblp/lusk/api/PDCWrapper.java | 59 +++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 src/main/java/it/jakegblp/lusk/api/PDCApi.java create mode 100644 src/main/java/it/jakegblp/lusk/api/PDCWrapper.java 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..1b9b8369 --- /dev/null +++ b/src/main/java/it/jakegblp/lusk/api/PDCApi.java @@ -0,0 +1,84 @@ +package it.jakegblp.lusk.api; + +import it.jakegblp.lusk.api.enums.PersistentTagType; +import org.bukkit.NamespacedKey; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; +import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.NullMarked; + +import java.util.Arrays; +import java.util.function.Function; + +public class PDCApi { + + @NullMarked + public static void deleteTag(NamespacedKey key, PersistentDataContainer container) { + container.remove(key); + } + + public static void setTag(PersistentTagType tagType, NamespacedKey key, PersistentDataContainer container, 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 -> null; + } + } +} diff --git a/src/main/java/it/jakegblp/lusk/api/PDCWrapper.java b/src/main/java/it/jakegblp/lusk/api/PDCWrapper.java new file mode 100644 index 00000000..0e46e15f --- /dev/null +++ b/src/main/java/it/jakegblp/lusk/api/PDCWrapper.java @@ -0,0 +1,59 @@ +package it.jakegblp.lusk.api; + +import org.apache.logging.log4j.util.TriConsumer; +import org.bukkit.NamespacedKey; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.function.BiFunction; + +public class PDCWrapper { + + public enum PersistentTagType { + BYTE(PersistentDataType.BYTE, (container, key) -> container.get(key,PersistentDataType.BYTE), (container, key, o) -> container.set(key,PersistentDataType.BYTE, o)), + SHORT(PersistentDataType.SHORT, (container, key) -> container.get(key,PersistentDataType.SHORT), (container, key, o) -> container.set(key,PersistentDataType.SHORT, o)), + INTEGER(PersistentDataType.INTEGER, (container, key) -> container.get(key,PersistentDataType.INTEGER), (container, key, o) -> container.set(key,PersistentDataType.INTEGER, o)), + LONG(PersistentDataType.LONG, (container, key) -> container.get(key,PersistentDataType.LONG), (container, key, o) -> container.set(key,PersistentDataType.LONG, o)), + FLOAT(PersistentDataType.FLOAT, (container, key) -> container.get(key,PersistentDataType.FLOAT), (container, key, o) -> container.set(key,PersistentDataType.FLOAT, o)), + DOUBLE(PersistentDataType.DOUBLE, (container, key) -> container.get(key,PersistentDataType.DOUBLE), (container, key, o) -> container.set(key,PersistentDataType.DOUBLE, o)), + BOOLEAN(PersistentDataType.BOOLEAN, (container, key) -> container.get(key,PersistentDataType.BOOLEAN), (container, key, o) -> container.set(key,PersistentDataType.BOOLEAN, o)), + STRING(PersistentDataType.STRING, (container, key) -> container.get(key,PersistentDataType.STRING), (container, key, o) -> container.set(key,PersistentDataType.STRING, o)), + BYTE_ARRAY(PersistentDataType.BYTE_ARRAY, (container, key) -> container.get(key,PersistentDataType.BYTE_ARRAY), (container, key, o) -> container.set(key,PersistentDataType.BYTE_ARRAY, o)), + INTEGER_ARRAY(PersistentDataType.INTEGER_ARRAY, (container, key) -> container.get(key,PersistentDataType.INTEGER_ARRAY), (container, key, o) -> container.set(key,PersistentDataType.INTEGER_ARRAY, o)), + LONG_ARRAY(PersistentDataType.LONG_ARRAY, (container, key) -> container.get(key,PersistentDataType.LONG_ARRAY), (container, key, o) -> container.set(key,PersistentDataType.LONG_ARRAY, o)), + TAG_CONTAINER(PersistentDataType.TAG_CONTAINER, (container, key) -> container.get(key,PersistentDataType.TAG_CONTAINER), (container, key, o) -> container.set(key,PersistentDataType.TAG_CONTAINER, o)); + + private final PersistentDataType type; + private final BiFunction get; + private final TriConsumer set; + + @SuppressWarnings("unchecked") + PersistentTagType(PersistentDataType type, BiFunction get, TriConsumer set) { + this.type = type; + this.get = (BiFunction) get; + this.set = (TriConsumer) set; + } + + public PersistentDataType getType() { + return type; + } + + public Object get(PersistentDataContainer container, NamespacedKey key) { + return get.apply(container, key); + } + + public void set(PersistentDataContainer container, NamespacedKey key, Object o) { + set.accept(container, key, o); + } + + @Nullable + public static PersistentTagType fromDataType(PersistentDataType type) { + return Arrays.stream(values()).filter(persistentTagType -> persistentTagType.type == type).findFirst().orElse(null); + } + } + + PDCWrapper + +} From 2afc18ccbf9bae2071bb002edfcf77afb0e14149 Mon Sep 17 00:00:00 2001 From: Jake Date: Sat, 4 Jan 2025 08:46:57 +0100 Subject: [PATCH 5/7] Added some small things to the pdc methods, moving everything to PDCApi. --- src/main/java/it/jakegblp/lusk/api/PDCApi.java | 10 +++++++--- .../it/jakegblp/lusk/api/enums/PersistentTagType.java | 9 ++++++++- .../persistence/expressions/ExprPersistentTag.java | 6 +++--- .../java/it/jakegblp/lusk/utils/PersistenceUtils.java | 4 ++-- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/main/java/it/jakegblp/lusk/api/PDCApi.java b/src/main/java/it/jakegblp/lusk/api/PDCApi.java index 1b9b8369..1f254b8f 100644 --- a/src/main/java/it/jakegblp/lusk/api/PDCApi.java +++ b/src/main/java/it/jakegblp/lusk/api/PDCApi.java @@ -4,11 +4,9 @@ import org.bukkit.NamespacedKey; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; -import org.jetbrains.annotations.NotNull; import org.jspecify.annotations.NullMarked; import java.util.Arrays; -import java.util.function.Function; public class PDCApi { @@ -78,7 +76,13 @@ public static void setTag(PersistentTagType tagType, NamespacedKey key, Persiste container.set(key, PersistentDataType.LONG_ARRAY, Arrays.stream(numbers).mapToLong(Number::longValue).toArray()); } } - case TAG_CONTAINER -> null; + case TAG_CONTAINER -> { + if (object instanceof PersistentDataContainer c) { + container.set(key, PersistentDataType.TAG_CONTAINER, c); + } + } } } + + public static PersistentTagType getNestedContainer(NamespacedKey key, PersistentDataContainer container) {} } diff --git a/src/main/java/it/jakegblp/lusk/api/enums/PersistentTagType.java b/src/main/java/it/jakegblp/lusk/api/enums/PersistentTagType.java index 49a226fc..334b51bd 100644 --- a/src/main/java/it/jakegblp/lusk/api/enums/PersistentTagType.java +++ b/src/main/java/it/jakegblp/lusk/api/enums/PersistentTagType.java @@ -34,10 +34,17 @@ PersistentTagType(PersistentDataType type, BiFunction) set; } - public PersistentDataType getType() { + 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; + } + public Object get(PersistentDataContainer container, NamespacedKey key) { return get.apply(container, key); } 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 index 5abeaddb..5bd315a5 100644 --- 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 @@ -55,12 +55,12 @@ public boolean init(Expression[] expressions, int matchedPattern, Kleenean is @Override public Class getReturnType() { - return tagTypeLiteral != null ? tagTypeLiteral.getSingle().getType().getComplexType() : Object.class; + return tagTypeLiteral != null ? tagTypeLiteral.getSingle().getDataType().getComplexType() : Object.class; } @Override public boolean isSingle() { - return super.isSingle() || (tagTypeLiteral != null && tagTypeLiteral.getSingle().getType().getComplexType().isArray()); + return super.isSingle() || (tagTypeLiteral != null && tagTypeLiteral.getSingle().getDataType().getComplexType().isArray()); } @Override @@ -81,7 +81,7 @@ protected Object[] get(Event event, Object[] source) { @Override public @Nullable Class[] acceptChange(Changer.ChangeMode mode) { return switch (mode) { - case SET, DELETE -> new Class[]{tagTypeLiteral != null ? tagTypeLiteral.getSingle().getType().getComplexType() : Object.class}; + case SET, DELETE -> new Class[]{tagTypeLiteral != null ? tagTypeLiteral.getSingle().getDataType().getComplexType() : Object.class}; default -> null; }; } diff --git a/src/main/java/it/jakegblp/lusk/utils/PersistenceUtils.java b/src/main/java/it/jakegblp/lusk/utils/PersistenceUtils.java index d60c7b7e..2aaa41e9 100644 --- a/src/main/java/it/jakegblp/lusk/utils/PersistenceUtils.java +++ b/src/main/java/it/jakegblp/lusk/utils/PersistenceUtils.java @@ -11,7 +11,7 @@ public class PersistenceUtils { @Nullable public static PersistentTagType getPersistentDataType(PersistentDataContainer container, NamespacedKey key) { for (PersistentTagType value : PersistentTagType.values()) { - if (container.has(key, value.getType())) + if (container.has(key, value.getDataType())) return value; } return null; @@ -21,7 +21,7 @@ public static PersistentTagType getPersistentDataType(PersistentDataContainer co public static Object getValue(PersistentDataContainer container, NamespacedKey key) { PersistentTagType tag = getPersistentDataType(container, key); if (tag == null) return null; - return container.get(key, tag.getType()); + return container.get(key, tag.getDataType()); } public static String asString(PersistentDataContainer container) { From 93ea5cb2f0145f5fb7d91bf7f95bdb0a58718c75 Mon Sep 17 00:00:00 2001 From: Jake Date: Sat, 11 Jan 2025 17:27:27 +0100 Subject: [PATCH 6/7] Added functional get, set and clear for all persistent tag types and nested ones; added set, reset, and delete for persistent data of x; added max namespaced key length for paper and spigot respectively; updated build.gradle version; added lang entries; added util method for namespaced keys and NamespacedKeyClassInfos. --- build.gradle | 2 +- .../java/it/jakegblp/lusk/api/PDCApi.java | 94 ++++++++++++++++++- .../lusk/api/enums/PersistentTagType.java | 62 +++++------- .../types/NamespacedKeyClassInfos.java | 80 ++++++++++++++++ .../ExprPersistentDataContainer.java | 43 ++++++++- .../expressions/ExprPersistentTag.java | 55 ++++------- .../it/jakegblp/lusk/utils/Constants.java | 2 + .../it/jakegblp/lusk/utils/LuskUtils.java | 14 +++ .../jakegblp/lusk/utils/PersistenceUtils.java | 12 ++- src/main/resources/lang/default.lang | 1 + 10 files changed, 282 insertions(+), 83 deletions(-) create mode 100644 src/main/java/it/jakegblp/lusk/elements/minecraft/namespacedkey/types/NamespacedKeyClassInfos.java diff --git a/build.gradle b/build.gradle index 398e1a50..4af8758a 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ compileJava { options.encoding = "UTF-8" } -version = "1.3.1" +version = "1.4" def latestJava = 21 def oldestJava = 16 diff --git a/src/main/java/it/jakegblp/lusk/api/PDCApi.java b/src/main/java/it/jakegblp/lusk/api/PDCApi.java index 1f254b8f..c61db227 100644 --- a/src/main/java/it/jakegblp/lusk/api/PDCApi.java +++ b/src/main/java/it/jakegblp/lusk/api/PDCApi.java @@ -1,21 +1,47 @@ 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.Arrays; +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(NamespacedKey key, PersistentDataContainer container) { + 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); } - public static void setTag(PersistentTagType tagType, NamespacedKey key, PersistentDataContainer container, Object[] objects) { + @NullMarked + public static void setTag(PersistentDataContainer container, PersistentTagType tagType, NamespacedKey key, Object[] objects) { Object object = objects[0]; switch (tagType) { case BOOLEAN -> { @@ -84,5 +110,65 @@ public static void setTag(PersistentTagType tagType, NamespacedKey key, Persiste } } - public static PersistentTagType getNestedContainer(NamespacedKey key, PersistentDataContainer container) {} + @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); + } + + public static boolean hasTag(@NotNull String tag, @NotNull 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 index 334b51bd..a489410f 100644 --- a/src/main/java/it/jakegblp/lusk/api/enums/PersistentTagType.java +++ b/src/main/java/it/jakegblp/lusk/api/enums/PersistentTagType.java @@ -1,37 +1,38 @@ package it.jakegblp.lusk.api.enums; -import org.apache.logging.log4j.util.TriConsumer; import org.bukkit.NamespacedKey; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; -import org.jetbrains.annotations.Nullable; - -import java.util.Arrays; -import java.util.function.BiFunction; public enum PersistentTagType { - BYTE(PersistentDataType.BYTE, (container, key) -> container.get(key,PersistentDataType.BYTE), (container, key, o) -> container.set(key,PersistentDataType.BYTE, o)), - SHORT(PersistentDataType.SHORT, (container, key) -> container.get(key,PersistentDataType.SHORT), (container, key, o) -> container.set(key,PersistentDataType.SHORT, o)), - INTEGER(PersistentDataType.INTEGER, (container, key) -> container.get(key,PersistentDataType.INTEGER), (container, key, o) -> container.set(key,PersistentDataType.INTEGER, o)), - LONG(PersistentDataType.LONG, (container, key) -> container.get(key,PersistentDataType.LONG), (container, key, o) -> container.set(key,PersistentDataType.LONG, o)), - FLOAT(PersistentDataType.FLOAT, (container, key) -> container.get(key,PersistentDataType.FLOAT), (container, key, o) -> container.set(key,PersistentDataType.FLOAT, o)), - DOUBLE(PersistentDataType.DOUBLE, (container, key) -> container.get(key,PersistentDataType.DOUBLE), (container, key, o) -> container.set(key,PersistentDataType.DOUBLE, o)), - BOOLEAN(PersistentDataType.BOOLEAN, (container, key) -> container.get(key,PersistentDataType.BOOLEAN), (container, key, o) -> container.set(key,PersistentDataType.BOOLEAN, o)), - STRING(PersistentDataType.STRING, (container, key) -> container.get(key,PersistentDataType.STRING), (container, key, o) -> container.set(key,PersistentDataType.STRING, o)), - BYTE_ARRAY(PersistentDataType.BYTE_ARRAY, (container, key) -> container.get(key,PersistentDataType.BYTE_ARRAY), (container, key, o) -> container.set(key,PersistentDataType.BYTE_ARRAY, o)), - INTEGER_ARRAY(PersistentDataType.INTEGER_ARRAY, (container, key) -> container.get(key,PersistentDataType.INTEGER_ARRAY), (container, key, o) -> container.set(key,PersistentDataType.INTEGER_ARRAY, o)), - LONG_ARRAY(PersistentDataType.LONG_ARRAY, (container, key) -> container.get(key,PersistentDataType.LONG_ARRAY), (container, key, o) -> container.set(key,PersistentDataType.LONG_ARRAY, o)), - TAG_CONTAINER(PersistentDataType.TAG_CONTAINER, (container, key) -> container.get(key,PersistentDataType.TAG_CONTAINER), (container, key, o) -> container.set(key,PersistentDataType.TAG_CONTAINER, o)); - + 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; - private final BiFunction get; - private final TriConsumer set; - @SuppressWarnings("unchecked") - PersistentTagType(PersistentDataType type, BiFunction get, TriConsumer set) { + PersistentTagType(PersistentDataType type) { this.type = type; - this.get = (BiFunction) get; - this.set = (TriConsumer) set; + this.complexClass = type.getComplexType(); + } + + PersistentTagType(PersistentDataType type, Class complexClass) { + this.type = type; + this.complexClass = complexClass; + } + + public Class getComplexClass() { + return complexClass; } public PersistentDataType getDataType() { @@ -44,17 +45,4 @@ public static PersistentTagType getByTag(NamespacedKey key, PersistentDataContai } return null; } - - public Object get(PersistentDataContainer container, NamespacedKey key) { - return get.apply(container, key); - } - - public void set(PersistentDataContainer container, NamespacedKey key, Object o) { - set.accept(container, key, o); - } - - @Nullable - public static PersistentTagType fromDataType(PersistentDataType type) { - return Arrays.stream(values()).filter(persistentTagType -> persistentTagType.type == type).findFirst().orElse(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 index 36c9bd76..8e57fd22 100644 --- 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 @@ -1,8 +1,11 @@ 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.expressions.base.SimplePropertyExpression; +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; @@ -11,7 +14,9 @@ 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. """) -public class ExprPersistentDataContainer extends SimplePropertyExpression { +@Examples("send persistent data of player") +@Since("1.4") +public class ExprPersistentDataContainer extends SimplerPropertyExpression { static { register(ExprPersistentDataContainer.class, PersistentDataContainer.class, "persistent data [container]", "persistentdataholders"); @@ -22,6 +27,40 @@ 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"; 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 index 5bd315a5..c94eb3d4 100644 --- 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 @@ -1,14 +1,15 @@ 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.Lusk; +import it.jakegblp.lusk.api.PDCApi; import it.jakegblp.lusk.api.enums.PersistentTagType; -import org.bukkit.NamespacedKey; import org.bukkit.event.Event; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataHolder; @@ -18,16 +19,18 @@ import java.util.Objects; import java.util.stream.Stream; -import static it.jakegblp.lusk.utils.LuskUtils.consoleLog; +@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: add nested tags, improve toString + // todo: improve toString, add number class char suffix for arrays, add nbt readable and pretty print } - private Literal tagTypeLiteral; private Expression tagTypeExpression; private Expression stringExpression; @@ -37,14 +40,8 @@ public boolean init(Expression[] expressions, int matchedPattern, Kleenean is if (matchedPattern == 0) { tagTypeExpression = (Expression) expressions[0]; stringExpression = (Expression) expressions[1]; - if (expressions[2] instanceof Literal) { - tagTypeLiteral = (Literal) expressions[2]; - } setExpr(expressions[2]); } else { - if (expressions[0] instanceof Literal) { - tagTypeLiteral = (Literal) expressions[0]; - } setExpr(expressions[0]); tagTypeExpression = (Expression) expressions[1]; stringExpression = (Expression) expressions[2]; @@ -55,12 +52,12 @@ public boolean init(Expression[] expressions, int matchedPattern, Kleenean is @Override public Class getReturnType() { - return tagTypeLiteral != null ? tagTypeLiteral.getSingle().getDataType().getComplexType() : Object.class; + return tagTypeExpression instanceof Literal literal ? literal.getSingle().getComplexClass() : Object.class; } @Override public boolean isSingle() { - return super.isSingle() || (tagTypeLiteral != null && tagTypeLiteral.getSingle().getDataType().getComplexType().isArray()); + return super.isSingle() || tagTypeExpression instanceof Literal literal && literal.getSingle().getComplexClass().isArray(); } @Override @@ -73,15 +70,17 @@ protected Object[] get(Event event, Object[] source) { PersistentDataContainer container; if (object instanceof PersistentDataContainer c) container = c; else if (object instanceof PersistentDataHolder holder) container = holder.getPersistentDataContainer(); - else return Stream.of(); - return Stream.of(tagType.get(container, new NamespacedKey(Lusk.getInstance(), string))); + 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[]{tagTypeLiteral != null ? tagTypeLiteral.getSingle().getDataType().getComplexType() : Object.class}; + case SET, DELETE -> new Class[]{tagTypeExpression instanceof Literal literal ? literal.getSingle().getComplexClass() : Object.class}; default -> null; }; } @@ -91,40 +90,26 @@ public void change(Event event, @Nullable Object[] delta, Changer.ChangeMode mod String string = stringExpression.getSingle(event); if (string == null) return; PersistentDataContainer[] containers = getExpr().stream(event).map(o -> { - consoleLog("1: {0}",o instanceof PersistentDataContainer); - consoleLog("2: {0}",o instanceof PersistentDataHolder); 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 persistentDataContainer : containers) { - persistentDataContainer.remove(new NamespacedKey(Lusk.getInstance(),string)); + for (PersistentDataContainer container : containers) { + PDCApi.deleteTag(container, string); } } else { PersistentTagType tagType = tagTypeExpression.getSingle(event); if (tagType == null) return; - Object object = delta[0]; - if (object instanceof Number number) { - object = switch (tagType) { - case BYTE -> number.byteValue(); - case SHORT -> number.shortValue(); - case INTEGER -> number.intValue(); - case LONG -> number.longValue(); - case FLOAT -> number.floatValue(); - case DOUBLE -> number.doubleValue(); - default -> object; - }; - } - for (PersistentDataContainer persistentDataContainer : containers) { - tagType.set(persistentDataContainer, new NamespacedKey(Lusk.getInstance(),string), object); + 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 persistent " + tagTypeExpression.toString(event, debug) + " tag " + stringExpression.toString(event, debug); + return getExpr().toString(event, debug) + "'s " + tagTypeExpression.toString(event, debug) + " " + stringExpression.toString(event, debug); } } diff --git a/src/main/java/it/jakegblp/lusk/utils/Constants.java b/src/main/java/it/jakegblp/lusk/utils/Constants.java index ca1f06ee..5b3be45e 100644 --- a/src/main/java/it/jakegblp/lusk/utils/Constants.java +++ b/src/main/java/it/jakegblp/lusk/utils/Constants.java @@ -33,6 +33,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 Semver diff --git a/src/main/java/it/jakegblp/lusk/utils/LuskUtils.java b/src/main/java/it/jakegblp/lusk/utils/LuskUtils.java index 594c2501..57bfd43c 100644 --- a/src/main/java/it/jakegblp/lusk/utils/LuskUtils.java +++ b/src/main/java/it/jakegblp/lusk/utils/LuskUtils.java @@ -5,8 +5,10 @@ import com.vdurmont.semver4j.Semver; import net.md_5.bungee.api.ChatColor; import org.bukkit.Bukkit; +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; @@ -107,4 +109,16 @@ public static Kleenean getKleenean(boolean a, boolean b) { else return Kleenean.UNKNOWN; } + @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 index 2aaa41e9..fae5f938 100644 --- a/src/main/java/it/jakegblp/lusk/utils/PersistenceUtils.java +++ b/src/main/java/it/jakegblp/lusk/utils/PersistenceUtils.java @@ -1,10 +1,12 @@ 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 { @@ -33,7 +35,9 @@ public static String asString(PersistentDataContainer container) { if (i > 0) builder.append(", "); PersistentTagType tagType = getPersistentDataType(container, key); if (tagType == null) continue; - Object value = tagType.get(container,key); + 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'); @@ -44,9 +48,9 @@ public static String asString(PersistentDataContainer container) { case DOUBLE -> builder.append(value); case BOOLEAN -> builder.append(value); case STRING -> builder.append('"').append(value).append('"'); - case BYTE_ARRAY -> builder.append("-").append(value).append("-"); - case INTEGER_ARRAY -> builder.append("-").append(value).append("-"); - case LONG_ARRAY -> 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++; diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang index c01fc129..f8b80e39 100644 --- a/src/main/resources/lang/default.lang +++ b/src/main/resources/lang/default.lang @@ -39,6 +39,7 @@ types: persistentdataholder: persistent data holder¦s @a persistentdatacontainer: persistent data container¦s @a persistenttagtype: persistent tag type¦s @a + namespacedkey: namespaced key¦s @a # -- Rotations -- rotations: From cebd5d6d4102ac9be83f3fd1471be0ed95d8a6e8 Mon Sep 17 00:00:00 2001 From: Jake Date: Sun, 12 Jan 2025 02:15:02 +0100 Subject: [PATCH 7/7] Removed unused experimental class, improved annotations in PDCApi --- .../java/it/jakegblp/lusk/api/PDCApi.java | 3 +- .../java/it/jakegblp/lusk/api/PDCWrapper.java | 59 ------------------- 2 files changed, 2 insertions(+), 60 deletions(-) delete mode 100644 src/main/java/it/jakegblp/lusk/api/PDCWrapper.java diff --git a/src/main/java/it/jakegblp/lusk/api/PDCApi.java b/src/main/java/it/jakegblp/lusk/api/PDCApi.java index c61db227..19083a55 100644 --- a/src/main/java/it/jakegblp/lusk/api/PDCApi.java +++ b/src/main/java/it/jakegblp/lusk/api/PDCApi.java @@ -167,7 +167,8 @@ public static Nested getNestedContainer(PersistentDataContainer container, Strin return new Nested(container, key); } - public static boolean hasTag(@NotNull String tag, @NotNull PersistentDataContainer container) { + @NullMarked + public static boolean hasTag(String tag, PersistentDataContainer container) { return getNestedContainer(container, tag) != null; } diff --git a/src/main/java/it/jakegblp/lusk/api/PDCWrapper.java b/src/main/java/it/jakegblp/lusk/api/PDCWrapper.java deleted file mode 100644 index 0e46e15f..00000000 --- a/src/main/java/it/jakegblp/lusk/api/PDCWrapper.java +++ /dev/null @@ -1,59 +0,0 @@ -package it.jakegblp.lusk.api; - -import org.apache.logging.log4j.util.TriConsumer; -import org.bukkit.NamespacedKey; -import org.bukkit.persistence.PersistentDataContainer; -import org.bukkit.persistence.PersistentDataType; -import org.jetbrains.annotations.Nullable; - -import java.util.Arrays; -import java.util.function.BiFunction; - -public class PDCWrapper { - - public enum PersistentTagType { - BYTE(PersistentDataType.BYTE, (container, key) -> container.get(key,PersistentDataType.BYTE), (container, key, o) -> container.set(key,PersistentDataType.BYTE, o)), - SHORT(PersistentDataType.SHORT, (container, key) -> container.get(key,PersistentDataType.SHORT), (container, key, o) -> container.set(key,PersistentDataType.SHORT, o)), - INTEGER(PersistentDataType.INTEGER, (container, key) -> container.get(key,PersistentDataType.INTEGER), (container, key, o) -> container.set(key,PersistentDataType.INTEGER, o)), - LONG(PersistentDataType.LONG, (container, key) -> container.get(key,PersistentDataType.LONG), (container, key, o) -> container.set(key,PersistentDataType.LONG, o)), - FLOAT(PersistentDataType.FLOAT, (container, key) -> container.get(key,PersistentDataType.FLOAT), (container, key, o) -> container.set(key,PersistentDataType.FLOAT, o)), - DOUBLE(PersistentDataType.DOUBLE, (container, key) -> container.get(key,PersistentDataType.DOUBLE), (container, key, o) -> container.set(key,PersistentDataType.DOUBLE, o)), - BOOLEAN(PersistentDataType.BOOLEAN, (container, key) -> container.get(key,PersistentDataType.BOOLEAN), (container, key, o) -> container.set(key,PersistentDataType.BOOLEAN, o)), - STRING(PersistentDataType.STRING, (container, key) -> container.get(key,PersistentDataType.STRING), (container, key, o) -> container.set(key,PersistentDataType.STRING, o)), - BYTE_ARRAY(PersistentDataType.BYTE_ARRAY, (container, key) -> container.get(key,PersistentDataType.BYTE_ARRAY), (container, key, o) -> container.set(key,PersistentDataType.BYTE_ARRAY, o)), - INTEGER_ARRAY(PersistentDataType.INTEGER_ARRAY, (container, key) -> container.get(key,PersistentDataType.INTEGER_ARRAY), (container, key, o) -> container.set(key,PersistentDataType.INTEGER_ARRAY, o)), - LONG_ARRAY(PersistentDataType.LONG_ARRAY, (container, key) -> container.get(key,PersistentDataType.LONG_ARRAY), (container, key, o) -> container.set(key,PersistentDataType.LONG_ARRAY, o)), - TAG_CONTAINER(PersistentDataType.TAG_CONTAINER, (container, key) -> container.get(key,PersistentDataType.TAG_CONTAINER), (container, key, o) -> container.set(key,PersistentDataType.TAG_CONTAINER, o)); - - private final PersistentDataType type; - private final BiFunction get; - private final TriConsumer set; - - @SuppressWarnings("unchecked") - PersistentTagType(PersistentDataType type, BiFunction get, TriConsumer set) { - this.type = type; - this.get = (BiFunction) get; - this.set = (TriConsumer) set; - } - - public PersistentDataType getType() { - return type; - } - - public Object get(PersistentDataContainer container, NamespacedKey key) { - return get.apply(container, key); - } - - public void set(PersistentDataContainer container, NamespacedKey key, Object o) { - set.accept(container, key, o); - } - - @Nullable - public static PersistentTagType fromDataType(PersistentDataType type) { - return Arrays.stream(values()).filter(persistentTagType -> persistentTagType.type == type).findFirst().orElse(null); - } - } - - PDCWrapper - -}