refactor: gracefully handle missing data deps

feat/data-edit-commands
William 9 months ago
parent e0b81e4c76
commit 4fa7106a46
No known key found for this signature in database

@ -82,33 +82,33 @@ public class BukkitLegacyConverter extends LegacyConverter {
final JSONObject status = object.getJSONObject("status_data");
final HashMap<Identifier, Data> 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<Data.Items.Inventory> 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<Data.Items.EnderChest> 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<Data.Location> 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<Data.Advancements> 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<Data.Statistics> 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<String, Object>) serializedItemStack) : null;
}
private boolean shouldImport(@NotNull Identifier type) {
return plugin.getSettings().getSynchronization().isFeatureEnabled(type);
}
@NotNull
private Date parseDate(@NotNull String dateString) {
try {

@ -239,13 +239,11 @@ 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)
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(enabled ? "Enabled" : "Disabled")
Component.text(i.isEnabled() ? "Enabled" : "Disabled")
.append(Component.newline())
.append(Component.text("Dependencies: %s".formatted(i.getDependencies()
.isEmpty() ? "(None)" : i.getDependencies().stream()
@ -253,8 +251,7 @@ public class HuskSyncCommand extends Command implements TabProvider {
d.getKey().value(), d.isRequired() ? "Required" : "Optional"
)).collect(Collectors.joining(", ")))
).color(NamedTextColor.GRAY))
));
}).toList()
))).toList()
));
private final Function<HuskSync, Component> supplier;

@ -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<Dependency> dependencies;
@Setter
@Getter
public boolean enabled;
private Identifier(@NotNull Key key, boolean enabledByDefault, @NotNull Set<Dependency> 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
*/

@ -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<? extends Data> serializer) {
if (identifier.isCustom()) {
getPlugin().log(Level.INFO, "Registered custom data type: %s".formatted(identifier));
default void registerSerializer(@NotNull Identifier id, @NotNull Serializer<? extends Data> serializer) {
if (id.isCustom()) {
getPlugin().log(Level.INFO, "Registered custom data type: %s".formatted(id));
}
getSerializers().put(identifier, (Serializer<Data>) serializer);
id.setEnabled(id.isCustom() || getPlugin().getSettings().getSynchronization().isFeatureEnabled(id));
getSerializers().put(id, (Serializer<Data>) 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<String> 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

@ -43,7 +43,7 @@ public interface UserDataHolder extends DataHolder {
@NotNull
default Map<Identifier, Data> 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
* <p>
* 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}).
* </p>
* The {@code runAfter} callback function will be run after the snapshot has been applied.
*
@ -106,13 +107,16 @@ public interface UserDataHolder extends DataHolder {
try {
for (Map.Entry<Identifier, Data> entry : unpacked.getData().entrySet()) {
final Identifier identifier = entry.getKey();
if (plugin.getSettings().getSynchronization().isFeatureEnabled(identifier)) {
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);
plugin.runAsync(() -> runAfter.accept(false));

Loading…
Cancel
Save