Skip to content

Commit

Permalink
Merge branch 'refs/heads/dev/pdc' into dev/nms
Browse files Browse the repository at this point in the history
# Conflicts:
#	build.gradle
#	src/main/java/it/jakegblp/lusk/utils/LuskUtils.java
#	src/main/resources/lang/default.lang
  • Loading branch information
JakeGBLP committed Feb 12, 2025
2 parents 25a315c + cebd5d6 commit 793739b
Show file tree
Hide file tree
Showing 10 changed files with 629 additions and 0 deletions.
175 changes: 175 additions & 0 deletions src/main/java/it/jakegblp/lusk/api/PDCApi.java
Original file line number Diff line number Diff line change
@@ -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<String> parts = new ArrayList<>(List.of(path.split(";")));

TreeMap<NamespacedKey, PersistentDataContainer> 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<String> 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;
}

}
48 changes: 48 additions & 0 deletions src/main/java/it/jakegblp/lusk/api/enums/PersistentTagType.java
Original file line number Diff line number Diff line change
@@ -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;

<P,C> PersistentTagType(PersistentDataType<P,C> type) {
this.type = type;
this.complexClass = type.getComplexType();
}

<P,C> PersistentTagType(PersistentDataType<P,C> 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;
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}));
}
}
}
Original file line number Diff line number Diff line change
@@ -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<PersistentDataHolder, PersistentDataContainer> {

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<? extends PersistentDataContainer> getReturnType() {
return PersistentDataContainer.class;
}
}
Loading

0 comments on commit 793739b

Please sign in to comment.