diff --git a/bukkit/src/main/java/net/william278/husksync/util/BukkitLegacyConverter.java b/bukkit/src/main/java/net/william278/husksync/util/BukkitLegacyConverter.java index 237bd856..633a344a 100644 --- a/bukkit/src/main/java/net/william278/husksync/util/BukkitLegacyConverter.java +++ b/bukkit/src/main/java/net/william278/husksync/util/BukkitLegacyConverter.java @@ -82,33 +82,33 @@ public class BukkitLegacyConverter extends LegacyConverter { final JSONObject status = object.getJSONObject("status_data"); final HashMap containers = Maps.newHashMap(); - if (shouldImport(Identifier.HEALTH)) { + if (Identifier.HEALTH.isEnabled()) { containers.put(Identifier.HEALTH, BukkitData.Health.from( status.getDouble("health"), status.getDouble("health_scale"), false )); } - if (shouldImport(Identifier.HUNGER)) { + if (Identifier.HUNGER.isEnabled()) { containers.put(Identifier.HUNGER, BukkitData.Hunger.from( status.getInt("hunger"), status.getFloat("saturation"), status.getFloat("saturation_exhaustion") )); } - if (shouldImport(Identifier.EXPERIENCE)) { + if (Identifier.EXPERIENCE.isEnabled()) { containers.put(Identifier.EXPERIENCE, BukkitData.Experience.from( status.getInt("total_experience"), status.getInt("experience_level"), status.getFloat("experience_progress") )); } - if (shouldImport(Identifier.GAME_MODE)) { + if (Identifier.GAME_MODE.isEnabled()) { containers.put(Identifier.GAME_MODE, BukkitData.GameMode.from( status.getString("game_mode") )); } - if (shouldImport(Identifier.FLIGHT_STATUS)) { + if (Identifier.FLIGHT_STATUS.isEnabled()) { containers.put(Identifier.FLIGHT_STATUS, BukkitData.FlightStatus.from( status.getBoolean("is_flying"), status.getBoolean("is_flying") @@ -119,7 +119,7 @@ public class BukkitLegacyConverter extends LegacyConverter { @NotNull private Optional readInventory(@NotNull JSONObject object) { - if (!object.has("inventory") || !shouldImport(Identifier.INVENTORY)) { + if (!object.has("inventory") || !Identifier.INVENTORY.isEnabled()) { return Optional.empty(); } @@ -131,7 +131,7 @@ public class BukkitLegacyConverter extends LegacyConverter { @NotNull private Optional readEnderChest(@NotNull JSONObject object) { - if (!object.has("ender_chest") || !shouldImport(Identifier.ENDER_CHEST)) { + if (!object.has("ender_chest") || !Identifier.ENDER_CHEST.isEnabled()) { return Optional.empty(); } @@ -143,7 +143,7 @@ public class BukkitLegacyConverter extends LegacyConverter { @NotNull private Optional readLocation(@NotNull JSONObject object) { - if (!object.has("location") || !shouldImport(Identifier.LOCATION)) { + if (!object.has("location") || !Identifier.LOCATION.isEnabled()) { return Optional.empty(); } @@ -164,7 +164,7 @@ public class BukkitLegacyConverter extends LegacyConverter { @NotNull private Optional readAdvancements(@NotNull JSONObject object) { - if (!object.has("advancements") || !shouldImport(Identifier.ADVANCEMENTS)) { + if (!object.has("advancements") || !Identifier.ADVANCEMENTS.isEnabled()) { return Optional.empty(); } @@ -187,7 +187,7 @@ public class BukkitLegacyConverter extends LegacyConverter { @NotNull private Optional readStatistics(@NotNull JSONObject object) { - if (!object.has("statistics") || !shouldImport(Identifier.STATISTICS)) { + if (!object.has("statistics") || !Identifier.STATISTICS.isEnabled()) { return Optional.empty(); } @@ -281,11 +281,6 @@ public class BukkitLegacyConverter extends LegacyConverter { return serializedItemStack != null ? ItemStack.deserialize((Map) serializedItemStack) : null; } - - private boolean shouldImport(@NotNull Identifier type) { - return plugin.getSettings().getSynchronization().isFeatureEnabled(type); - } - @NotNull private Date parseDate(@NotNull String dateString) { try { diff --git a/common/src/main/java/net/william278/husksync/command/HuskSyncCommand.java b/common/src/main/java/net/william278/husksync/command/HuskSyncCommand.java index 82f19dbc..f7f96679 100644 --- a/common/src/main/java/net/william278/husksync/command/HuskSyncCommand.java +++ b/common/src/main/java/net/william278/husksync/command/HuskSyncCommand.java @@ -239,22 +239,19 @@ public class HuskSyncCommand extends Command implements TabProvider { )), DATA_TYPES(plugin -> Component.join( JoinConfiguration.commas(true), - plugin.getRegisteredDataTypes().stream().map(i -> { - boolean enabled = plugin.getSettings().getSynchronization().isFeatureEnabled(i); - return Component.textOfChildren(Component.text(i.toString()) - .appendSpace().append(Component.text(enabled ? '✔' : '❌'))) - .color(enabled ? NamedTextColor.GREEN : NamedTextColor.RED) - .hoverEvent(HoverEvent.showText( - Component.text(enabled ? "Enabled" : "Disabled") - .append(Component.newline()) - .append(Component.text("Dependencies: %s".formatted(i.getDependencies() - .isEmpty() ? "(None)" : i.getDependencies().stream() - .map(d -> "%s (%s)".formatted( - d.getKey().value(), d.isRequired() ? "Required" : "Optional" - )).collect(Collectors.joining(", "))) - ).color(NamedTextColor.GRAY)) - )); - }).toList() + plugin.getRegisteredDataTypes().stream().map(i -> Component.textOfChildren(Component.text(i.toString()) + .appendSpace().append(Component.text(i.isEnabled() ? '✔' : '❌'))) + .color(i.isEnabled() ? NamedTextColor.GREEN : NamedTextColor.RED) + .hoverEvent(HoverEvent.showText( + Component.text(i.isEnabled() ? "Enabled" : "Disabled") + .append(Component.newline()) + .append(Component.text("Dependencies: %s".formatted(i.getDependencies() + .isEmpty() ? "(None)" : i.getDependencies().stream() + .map(d -> "%s (%s)".formatted( + d.getKey().value(), d.isRequired() ? "Required" : "Optional" + )).collect(Collectors.joining(", "))) + ).color(NamedTextColor.GRAY)) + ))).toList() )); private final Function supplier; diff --git a/common/src/main/java/net/william278/husksync/data/Identifier.java b/common/src/main/java/net/william278/husksync/data/Identifier.java index 6147fbda..03b5aea2 100644 --- a/common/src/main/java/net/william278/husksync/data/Identifier.java +++ b/common/src/main/java/net/william278/husksync/data/Identifier.java @@ -19,10 +19,7 @@ package net.william278.husksync.data; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; import net.kyori.adventure.key.InvalidKeyException; import net.kyori.adventure.key.Key; import org.intellij.lang.annotations.Subst; @@ -73,10 +70,14 @@ public class Identifier { private final boolean enabledByDefault; @Getter private final Set dependencies; + @Setter + @Getter + public boolean enabled; private Identifier(@NotNull Key key, boolean enabledByDefault, @NotNull Set dependencies) { this.key = key; this.enabledByDefault = enabledByDefault; + this.enabled = enabledByDefault; this.dependencies = dependencies; } @@ -269,7 +270,7 @@ public class Identifier { } /** - * Represents a data dependency of an identifier + * Represents a data dependency of an identifier, used to determine the order in which data is applied to users * * @since 3.5.4 */ diff --git a/common/src/main/java/net/william278/husksync/data/SerializerRegistry.java b/common/src/main/java/net/william278/husksync/data/SerializerRegistry.java index 0ce03e55..4ce4a812 100644 --- a/common/src/main/java/net/william278/husksync/data/SerializerRegistry.java +++ b/common/src/main/java/net/william278/husksync/data/SerializerRegistry.java @@ -44,17 +44,17 @@ public interface SerializerRegistry { /** * Register a data serializer for the given {@link Identifier} * - * @param identifier the {@link Identifier} + * @param id the {@link Identifier} * @param serializer the {@link Serializer} * @since 3.0 */ @SuppressWarnings("unchecked") - default void registerSerializer(@NotNull Identifier identifier, - @NotNull Serializer serializer) { - if (identifier.isCustom()) { - getPlugin().log(Level.INFO, "Registered custom data type: %s".formatted(identifier)); + default void registerSerializer(@NotNull Identifier id, @NotNull Serializer serializer) { + if (id.isCustom()) { + getPlugin().log(Level.INFO, "Registered custom data type: %s".formatted(id)); } - getSerializers().put(identifier, (Serializer) serializer); + id.setEnabled(id.isCustom() || getPlugin().getSettings().getSynchronization().isFeatureEnabled(id)); + getSerializers().put(id, (Serializer) serializer); } /** @@ -66,17 +66,16 @@ public interface SerializerRegistry { * @since 3.5.4 */ default void validateDependencies() throws IllegalStateException { - getSerializers().keySet().stream().filter(this::isDataTypeEnabled) + getSerializers().keySet().stream().filter(Identifier::isEnabled) .forEach(identifier -> { final List unmet = identifier.getDependencies().stream() .filter(Identifier.Dependency::isRequired) .filter(dep -> !isDataTypeAvailable(dep.getKey().asString())) .map(dep -> dep.getKey().asString()).toList(); if (!unmet.isEmpty()) { - throw new IllegalStateException( - "\"%s\" data requires the following disabled data types to facilitate syncing: %s" - .formatted(identifier, String.join(", ", unmet)) - ); + identifier.setEnabled(false); + getPlugin().log(Level.WARNING, "Disabled %s syncing as the following types need to be on: %s" + .formatted(identifier, String.join(", ", unmet))); } }); } @@ -148,12 +147,7 @@ public interface SerializerRegistry { // Returns if a data type is available and enabled in the config private boolean isDataTypeAvailable(@NotNull String key) { - return getIdentifier(key).map(this::isDataTypeEnabled).orElse(false); - } - - // Returns if a data type is enabled in the config - private boolean isDataTypeEnabled(@NotNull Identifier identifier) { - return getPlugin().getSettings().getSynchronization().isFeatureEnabled(identifier); + return getIdentifier(key).map(Identifier::isEnabled).orElse(false); } @NotNull diff --git a/common/src/main/java/net/william278/husksync/data/UserDataHolder.java b/common/src/main/java/net/william278/husksync/data/UserDataHolder.java index d173cc2b..5f1cb9ff 100644 --- a/common/src/main/java/net/william278/husksync/data/UserDataHolder.java +++ b/common/src/main/java/net/william278/husksync/data/UserDataHolder.java @@ -43,7 +43,7 @@ public interface UserDataHolder extends DataHolder { @NotNull default Map getData() { return getPlugin().getRegisteredDataTypes().stream() - .filter(type -> type.isCustom() || getPlugin().getSettings().getSynchronization().isFeatureEnabled(type)) + .filter(Identifier::isEnabled) .map(id -> Map.entry(id, getData(id))) .filter(data -> data.getValue().isPresent()) .collect(HashMap::new, (map, data) -> map.put(data.getKey(), data.getValue().get()), HashMap::putAll); @@ -79,7 +79,8 @@ public interface UserDataHolder extends DataHolder { * Deserialize and apply a data snapshot to this data owner *

* This method will deserialize the data on the current thread, then synchronously apply it on - * the main server thread. + * the main server thread. The order data will be applied is determined based on the dependencies of + * each data type (see {@link Identifier.Dependency}). *

* The {@code runAfter} callback function will be run after the snapshot has been applied. * @@ -106,12 +107,15 @@ public interface UserDataHolder extends DataHolder { try { for (Map.Entry entry : unpacked.getData().entrySet()) { final Identifier identifier = entry.getKey(); - if (plugin.getSettings().getSynchronization().isFeatureEnabled(identifier)) { - if (identifier.isCustom()) { - getCustomDataStore().put(identifier, entry.getValue()); - } - entry.getValue().apply(this, plugin); + if (!identifier.isEnabled()) { + continue; } + + // Apply the identified data + if (identifier.isCustom()) { + getCustomDataStore().put(identifier, entry.getValue()); + } + entry.getValue().apply(this, plugin); } } catch (Throwable e) { plugin.log(Level.SEVERE, String.format("Failed to apply data snapshot to %s", getUsername()), e);