diff --git a/bukkit/src/main/java/net/william278/husksync/data/BukkitPersistentDataTagType.java b/bukkit/src/main/java/net/william278/husksync/data/BukkitPersistentDataTagType.java new file mode 100644 index 00000000..9f4d3b51 --- /dev/null +++ b/bukkit/src/main/java/net/william278/husksync/data/BukkitPersistentDataTagType.java @@ -0,0 +1,60 @@ +package net.william278.husksync.data; + +import org.bukkit.NamespacedKey; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; + +/** + * Represents the type of persistent data tag, implemented by a Bukkit PersistentDataType. + */ +public enum BukkitPersistentDataTagType { + + BYTE(PersistentDataType.BYTE), + SHORT(PersistentDataType.SHORT), + INTEGER(PersistentDataType.INTEGER), + LONG(PersistentDataType.LONG), + FLOAT(PersistentDataType.FLOAT), + DOUBLE(PersistentDataType.DOUBLE), + STRING(PersistentDataType.STRING), + BYTE_ARRAY(PersistentDataType.BYTE_ARRAY), + INTEGER_ARRAY(PersistentDataType.INTEGER_ARRAY), + LONG_ARRAY(PersistentDataType.LONG_ARRAY), + TAG_CONTAINER_ARRAY(PersistentDataType.TAG_CONTAINER_ARRAY), + TAG_CONTAINER(PersistentDataType.TAG_CONTAINER); + + public final PersistentDataType dataType; + + BukkitPersistentDataTagType(PersistentDataType persistentDataType) { + this.dataType = persistentDataType; + } + + public static Optional getDataType(@NotNull String typeName) { + for (BukkitPersistentDataTagType type : values()) { + if (type.name().equalsIgnoreCase(typeName)) { + return Optional.of(type); + } + } + return Optional.empty(); + } + + /** + * Determine the {@link BukkitPersistentDataTagType} of a tag in a {@link PersistentDataContainer}. + * + * @param container The {@link PersistentDataContainer} to check. + * @param key The {@link NamespacedKey} of the tag to check. + * @return The {@link BukkitPersistentDataTagType} of the key, or {@link Optional#empty()} if the key does not exist. + */ + public static Optional getKeyDataType(@NotNull PersistentDataContainer container, + @NotNull NamespacedKey key) { + for (BukkitPersistentDataTagType dataType : values()) { + if (container.has(key, dataType.dataType)) { + return Optional.of(dataType); + } + } + return Optional.empty(); + } + +} diff --git a/bukkit/src/main/java/net/william278/husksync/player/BukkitPlayer.java b/bukkit/src/main/java/net/william278/husksync/player/BukkitPlayer.java index aae8381a..7d56559d 100644 --- a/bukkit/src/main/java/net/william278/husksync/player/BukkitPlayer.java +++ b/bukkit/src/main/java/net/william278/husksync/player/BukkitPlayer.java @@ -7,7 +7,6 @@ import net.william278.husksync.BukkitHuskSync; import net.william278.husksync.data.*; import net.william278.husksync.editor.ItemEditorMenu; import net.william278.husksync.util.Version; -import org.apache.commons.lang.ArrayUtils; import org.bukkit.*; import org.bukkit.advancement.Advancement; import org.bukkit.advancement.AdvancementProgress; @@ -402,17 +401,20 @@ public class BukkitPlayer extends OnlineUser { if (container.isEmpty()) { return new PersistentDataContainerData(new HashMap<>()); } - final HashMap persistentDataMap = new HashMap<>(); + final HashMap persistentDataMap = new HashMap<>(); // Set persistent data keys; ignore keys that we cannot synchronise as byte arrays for (final NamespacedKey key : container.getKeys()) { - try { - persistentDataMap.put(key.toString(), ArrayUtils.toObject(container.get(key, PersistentDataType.BYTE_ARRAY))); - } catch (IllegalArgumentException | NullPointerException ignored) { - } + BukkitPersistentDataTagType.getKeyDataType(container, key).ifPresent(dataType -> { + final Object value = container.get(key, dataType.dataType); + if (value != null) { + persistentDataMap.put(key.toString(), new PersistentDataTag(dataType.name(), value)); + } + }); } return new PersistentDataContainerData(persistentDataMap); }).exceptionally(throwable -> { - BukkitHuskSync.getInstance().getLoggingAdapter().log(Level.WARNING, "Could not read " + player.getName() + "'s persistent data map, skipping!"); + BukkitHuskSync.getInstance().getLoggingAdapter().log(Level.WARNING, + "Could not read " + player.getName() + "'s persistent data map, skipping!"); throwable.printStackTrace(); return new PersistentDataContainerData(new HashMap<>()); }); @@ -423,14 +425,48 @@ public class BukkitPlayer extends OnlineUser { return CompletableFuture.runAsync(() -> { player.getPersistentDataContainer().getKeys().forEach(namespacedKey -> player.getPersistentDataContainer().remove(namespacedKey)); - persistentDataContainerData.persistentDataMap.keySet().forEach(keyString -> { + final Map dataMap = persistentDataContainerData.persistentDataMap; + dataMap.keySet().forEach(keyString -> { final NamespacedKey key = NamespacedKey.fromString(keyString); if (key != null) { - final byte[] data = ArrayUtils.toPrimitive(persistentDataContainerData - .persistentDataMap.get(keyString)); - player.getPersistentDataContainer().set(key, PersistentDataType.BYTE_ARRAY, data); + // Set a tag with the given key and value. This is crying out for a refactor. + BukkitPersistentDataTagType.getDataType(dataMap.get(keyString).type).ifPresentOrElse(dataType -> { + switch (dataType) { + case BYTE -> player.getPersistentDataContainer().set(key, + PersistentDataType.BYTE, (byte) dataMap.get(keyString).value); + case SHORT -> player.getPersistentDataContainer().set(key, + PersistentDataType.SHORT, (short) dataMap.get(keyString).value); + case INTEGER -> player.getPersistentDataContainer().set(key, + PersistentDataType.INTEGER, (int) dataMap.get(keyString).value); + case LONG -> player.getPersistentDataContainer().set(key, + PersistentDataType.LONG, (long) dataMap.get(keyString).value); + case FLOAT -> player.getPersistentDataContainer().set(key, + PersistentDataType.FLOAT, (float) dataMap.get(keyString).value); + case DOUBLE -> player.getPersistentDataContainer().set(key, + PersistentDataType.DOUBLE, (double) dataMap.get(keyString).value); + case STRING -> player.getPersistentDataContainer().set(key, + PersistentDataType.STRING, (String) dataMap.get(keyString).value); + case BYTE_ARRAY -> player.getPersistentDataContainer().set(key, + PersistentDataType.BYTE_ARRAY, (byte[]) dataMap.get(keyString).value); + case INTEGER_ARRAY -> player.getPersistentDataContainer().set(key, + PersistentDataType.INTEGER_ARRAY, (int[]) dataMap.get(keyString).value); + case LONG_ARRAY -> player.getPersistentDataContainer().set(key, + PersistentDataType.LONG_ARRAY, (long[]) dataMap.get(keyString).value); + case TAG_CONTAINER -> player.getPersistentDataContainer().set(key, + PersistentDataType.TAG_CONTAINER, (PersistentDataContainer) dataMap.get(keyString).value); + case TAG_CONTAINER_ARRAY -> player.getPersistentDataContainer().set(key, + PersistentDataType.TAG_CONTAINER_ARRAY, (PersistentDataContainer[]) dataMap.get(keyString).value); + } + }, () -> BukkitHuskSync.getInstance().getLoggingAdapter().log(Level.WARNING, + "Could not set " + player.getName() + "'s persistent data key " + keyString + + " with the invalid type, " + dataMap.get(keyString).type + ". Skipping!")); } }); + }).exceptionally(throwable -> { + BukkitHuskSync.getInstance().getLoggingAdapter().log(Level.WARNING, + "Could not write " + player.getName() + "'s persistent data map, skipping!"); + throwable.printStackTrace(); + return null; }); } diff --git a/common/src/main/java/net/william278/husksync/data/PersistentDataContainerData.java b/common/src/main/java/net/william278/husksync/data/PersistentDataContainerData.java index ae3e50cc..64760781 100644 --- a/common/src/main/java/net/william278/husksync/data/PersistentDataContainerData.java +++ b/common/src/main/java/net/william278/husksync/data/PersistentDataContainerData.java @@ -14,9 +14,9 @@ public class PersistentDataContainerData { * Map of namespaced key strings to a byte array representing the persistent data */ @SerializedName("persistent_data_map") - public Map persistentDataMap; + public Map persistentDataMap; - public PersistentDataContainerData(@NotNull final Map persistentDataMap) { + public PersistentDataContainerData(@NotNull final Map persistentDataMap) { this.persistentDataMap = persistentDataMap; } diff --git a/common/src/main/java/net/william278/husksync/data/PersistentDataTag.java b/common/src/main/java/net/william278/husksync/data/PersistentDataTag.java new file mode 100644 index 00000000..dd7d2b8b --- /dev/null +++ b/common/src/main/java/net/william278/husksync/data/PersistentDataTag.java @@ -0,0 +1,30 @@ +package net.william278.husksync.data; + +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +/** + * Represents a persistent data tag set by a plugin. + */ +public class PersistentDataTag { + + /** + * The enumerated primitive data type name value of the tag + */ + public String type; + + /** + * The value of the tag + */ + public Object value; + + public PersistentDataTag(@NotNull String type, @NotNull Object value) { + this.type = type; + this.value = value; + } + + private PersistentDataTag() { + } + +} diff --git a/common/src/main/java/net/william278/husksync/data/UserData.java b/common/src/main/java/net/william278/husksync/data/UserData.java index 8b19396b..88400847 100644 --- a/common/src/main/java/net/william278/husksync/data/UserData.java +++ b/common/src/main/java/net/william278/husksync/data/UserData.java @@ -15,7 +15,7 @@ public class UserData { *

* This value is to be incremented whenever the format changes. */ - public static final int CURRENT_FORMAT_VERSION = 1; + public static final int CURRENT_FORMAT_VERSION = 2; /** * Stores the user's status data, including health, food, etc. diff --git a/gradle.properties b/gradle.properties index ee770e04..b749668a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,5 +3,5 @@ org.gradle.jvmargs='-Dfile.encoding=UTF-8' org.gradle.daemon=true javaVersion=16 -plugin_version=2.0.1 +plugin_version=2.0.2 plugin_archive=husksync \ No newline at end of file