feat: Fully implement working migration and validation

This commit is contained in:
Ben Woo 2023-03-26 00:31:36 +08:00
parent 358404b407
commit cd260b0f5e
No known key found for this signature in database
GPG Key ID: FB2A3645536E12C8
10 changed files with 152 additions and 33 deletions

View File

@ -5,8 +5,9 @@ import java.io.IOException;
import java.nio.file.Path;
import java.util.logging.Logger;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.configuration.migration.ConfigMigrator;
import com.onarandombox.MultiverseCore.configuration.node.EnchancedValueNode;
import com.onarandombox.MultiverseCore.configuration.node.EnhancedValueNode;
import com.onarandombox.MultiverseCore.configuration.node.NodeGroup;
import io.github.townyadvanced.commentedconfiguration.CommentedConfiguration;
import io.github.townyadvanced.commentedconfiguration.setting.CommentedNode;
@ -78,7 +79,7 @@ public class ConfigHandle {
return false;
}
migrateConfig();
addDefaultNodes();
parseAllNodes();
return true;
}
@ -112,22 +113,26 @@ public class ConfigHandle {
/**
* Adds default node values to the configuration if they are not already present.
*/
protected void addDefaultNodes() {
if (nodes.isEmpty()) {
return;
}
CommentedConfiguration tempConfig = new CommentedConfiguration(configPath, logger);
protected void parseAllNodes() {
CommentedConfiguration oldConfig = config;
this.config = new CommentedConfiguration(configPath, logger);
for (CommentedNode node : nodes) {
Logging.config("Parsing node: %s", node.getPath());
if (node.getComments().length > 0) {
tempConfig.addComment(node.getPath(), node.getComments());
config.addComment(node.getPath(), node.getComments());
}
if (node instanceof ValueNode) {
tempConfig.set(node.getPath(), get((ValueNode) node));
if (node instanceof TypedValueNode typedNode) {
if (!set(typedNode, oldConfig.getObject(node.getPath(), typedNode.getType(), typedNode.getDefaultValue()))) {
Logging.warning("Invalid value for node: %s, resetting to default...", node.getPath());
setDefault(typedNode);
}
} else if (node instanceof ValueNode valueNode) {
if (!set(valueNode, oldConfig.get(node.getPath(), valueNode.getDefaultValue()))) {
Logging.warning("Invalid value for node: %s, resetting to default...", node.getPath());
setDefault(valueNode);
}
}
}
this.config = tempConfig;
}
/**
@ -210,8 +215,8 @@ public class ConfigHandle {
* @param value The value to set.
*/
public boolean set(@NotNull ValueNode node, Object value) {
if (node instanceof TypedValueNode) {
return set(node, value);
if (node instanceof TypedValueNode typedValueNode) {
return set(typedValueNode, value);
}
config.set(node.getPath(), value);
return true;
@ -225,8 +230,8 @@ public class ConfigHandle {
* @param <T> The type of the node value.
*/
public <T> boolean set(@NotNull TypedValueNode<T> node, T value) {
if (node instanceof EnchancedValueNode) {
return set((EnchancedValueNode<T>) node, value);
if (node instanceof EnhancedValueNode<T> enhancedValueNode) {
return set(enhancedValueNode, value);
}
config.set(node.getPath(), value);
return true;
@ -240,7 +245,10 @@ public class ConfigHandle {
* @return True if the value was set, false otherwise.
* @param <T> The type of the node value.
*/
public <T> boolean set(@NotNull EnchancedValueNode<T> node, T value) {
public <T> boolean set(@NotNull EnhancedValueNode<T> node, T value) {
if (!node.isValid(value)) {
return false;
}
T oldValue = get(node);
config.set(node.getPath(), value);
node.onSetValue(oldValue, get(node));
@ -251,9 +259,8 @@ public class ConfigHandle {
* Sets the default value of a node.
*
* @param node The node to set the default value of.
* @param <T> The type of the node value.
*/
public <T> void setDefault(@NotNull TypedValueNode<T> node) {
public void setDefault(@NotNull ValueNode node) {
config.set(node.getPath(), node.getDefaultValue());
}

View File

@ -7,7 +7,9 @@ import java.nio.file.Path;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVConfig;
import com.onarandombox.MultiverseCore.configuration.migration.BooleanMigratorAction;
import com.onarandombox.MultiverseCore.configuration.migration.ConfigMigrator;
import com.onarandombox.MultiverseCore.configuration.migration.IntegerMigratorAction;
import com.onarandombox.MultiverseCore.configuration.migration.InvertBoolMigratorAction;
import com.onarandombox.MultiverseCore.configuration.migration.MoveMigratorAction;
import com.onarandombox.MultiverseCore.configuration.migration.VersionMigrator;
@ -43,19 +45,29 @@ public class DefaultMVConfig implements MVConfig {
.migrator(ConfigMigrator.builder(MVConfigNodes.VERSION)
.addVersionMigrator(VersionMigrator.builder(5.0)
.addAction(MoveMigratorAction.of("multiverse-configuration.enforceaccess", "world.enforce-access"))
.addAction(BooleanMigratorAction.of("world.enforce-access"))
.addAction(MoveMigratorAction.of("multiverse-configuration.prefixchat", "messaging.enable-chat-prefix"))
.addAction(BooleanMigratorAction.of("messaging.enable-chat-prefix"))
.addAction(MoveMigratorAction.of("multiverse-configuration.prefixchatformat", "messaging.chat-prefix-format"))
.addAction(MoveMigratorAction.of("multiverse-configuration.teleportintercept", "world.teleport-intercept"))
.addAction(BooleanMigratorAction.of("world.teleport-intercept"))
.addAction(MoveMigratorAction.of("multiverse-configuration.firstspawnoverride", "spawn.first-spawn-override"))
.addAction(BooleanMigratorAction.of("spawn.first-spawn-override"))
//.addAction(MoveMigratorAction.of("multiverse-configuration.displaypermerrors", ""))
.addAction(MoveMigratorAction.of("multiverse-configuration.globaldebug", "misc.global-debug"))
.addAction(IntegerMigratorAction.of("misc.global-debug"))
.addAction(MoveMigratorAction.of("multiverse-configuration.silentstart", "misc.silent-start"))
.addAction(BooleanMigratorAction.of("misc.silent-start"))
.addAction(MoveMigratorAction.of("multiverse-configuration.firstspawnworld", "spawn.first-spawn-location"))
.addAction(MoveMigratorAction.of("multiverse-configuration.defaultportalsearch", "portal.use-custom-portal-search"))
.addAction(BooleanMigratorAction.of("portal.use-custom-portal-search"))
.addAction(InvertBoolMigratorAction.of("portal.use-custom-portal-search"))
.addAction(MoveMigratorAction.of("multiverse-configuration.portalsearchradius", "portal.custom-portal-search-radius"))
.addAction(IntegerMigratorAction.of("portal.custom-portal-search-radius"))
.addAction(MoveMigratorAction.of("multiverse-configuration.autopurge", "world.auto-purge-entities"))
.addAction(BooleanMigratorAction.of("world.auto-purge-entities"))
.addAction(MoveMigratorAction.of("multiverse-configuration.idonotwanttodonate", "misc.show-donation-message"))
.addAction(BooleanMigratorAction.of("misc.show-donation-message"))
.addAction(InvertBoolMigratorAction.of("misc.show-donation-message"))
.build())
.build())

View File

@ -119,6 +119,7 @@ public class MVConfigNodes {
.comment("This only applies if use-custom-portal-search is set to true.")
.defaultValue(128)
.name("custom-portal-search-radius")
.validator(value -> value >= 0)
.build());
private static final MVCommentedNode MESSAGING_HEADER = node(MVCommentedNode.builder("messaging")
@ -164,6 +165,7 @@ public class MVConfigNodes {
.comment(" 3 = finest")
.defaultValue(0)
.name("global-debug")
.validator(value -> value >= 0 && value <= 3)
.onSetValue((oldValue, newValue) -> Logging.setDebugLevel(newValue))
.build());

View File

@ -0,0 +1,28 @@
package com.onarandombox.MultiverseCore.configuration.migration;
import co.aikar.commands.ACFUtil;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.configuration.ConfigHandle;
/**
* Single migrator action that converts a string value to a boolean.
*/
public class BooleanMigratorAction implements MigratorAction {
public static BooleanMigratorAction of(String path) {
return new BooleanMigratorAction(path);
}
private final String path;
protected BooleanMigratorAction(String path) {
this.path = path;
}
@Override
public void migrate(ConfigHandle settings) {
settings.getConfig().set(path, ACFUtil.isTruthy(settings.getConfig().getString(path, "")));
Logging.info("Converted %s to boolean %s", path, settings.getConfig().getBoolean(path));
}
}

View File

@ -40,7 +40,7 @@ public class ConfigMigrator {
double versionNumber = settings.get(versionNode);
for (VersionMigrator versionMigrator : versionMigrators) {
if (versionNumber < versionMigrator.getVersion()) {
Logging.config("Migrating config from version %s to %s...", versionNumber, versionMigrator.getVersion());
Logging.info("Migrating config from version %s to %s...", versionNumber, versionMigrator.getVersion());
versionMigrator.migrate(settings);
}
}

View File

@ -0,0 +1,28 @@
package com.onarandombox.MultiverseCore.configuration.migration;
import co.aikar.commands.ACFUtil;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.configuration.ConfigHandle;
import org.bukkit.util.NumberConversions;
/**
* Single migrator action that converts a string value to an integer.
*/
public class IntegerMigratorAction implements MigratorAction {
public static IntegerMigratorAction of(String path) {
return new IntegerMigratorAction(path);
}
private final String path;
public IntegerMigratorAction(String path) {
this.path = path;
}
@Override
public void migrate(ConfigHandle settings) {
settings.getConfig().set(path, ACFUtil.parseInt(settings.getConfig().getString(path)));
Logging.info("Converted %s to integer %s", path, settings.getConfig().getInt(path));
}
}

View File

@ -1,5 +1,6 @@
package com.onarandombox.MultiverseCore.configuration.migration;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.configuration.ConfigHandle;
/**
@ -28,6 +29,8 @@ public class InvertBoolMigratorAction implements MigratorAction {
*/
@Override
public void migrate(ConfigHandle settings) {
settings.getConfig().set(path, !settings.getConfig().getBoolean(path));
boolean boolValue = !settings.getConfig().getBoolean(path);
settings.getConfig().set(path, boolValue);
Logging.info("Inverted %s to boolean %s", path, boolValue);
}
}

View File

@ -5,11 +5,11 @@ import java.util.Optional;
import io.github.townyadvanced.commentedconfiguration.setting.TypedValueNode;
/**
* A {@link TypedValueNode} that has a name.
* A {@link TypedValueNode} that has a name, validation, and action to be performed when the value is set.
*
* @param <T> The type of the node's value.
*/
public interface EnchancedValueNode<T> extends TypedValueNode<T> {
public interface EnhancedValueNode<T> extends TypedValueNode<T> {
/**
* Gets the name of this node. Used for identifying the node from user input.
*
@ -17,5 +17,13 @@ public interface EnchancedValueNode<T> extends TypedValueNode<T> {
*/
Optional<String> getName();
boolean isValid(T value);
/**
* Called when the value of this node is set.
*
* @param oldValue The old value.
* @param newValue The new value.
*/
void onSetValue(T oldValue, T newValue);
}

View File

@ -2,15 +2,16 @@ package com.onarandombox.MultiverseCore.configuration.node;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Function;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Implementation of {@link EnchancedValueNode}.
* Implementation of {@link EnhancedValueNode}.
* @param <T> The type of the value.
*/
public class MVValueNode<T> extends MVCommentedNode implements EnchancedValueNode<T> {
public class MVValueNode<T> extends MVCommentedNode implements EnhancedValueNode<T> {
/**
* Creates a new builder for a {@link MVValueNode}.
@ -27,13 +28,15 @@ public class MVValueNode<T> extends MVCommentedNode implements EnchancedValueNod
protected final Class<T> type;
protected final T defaultValue;
protected final String name;
protected final Function<T, Boolean> validator;
protected final BiConsumer<T, T> onSetValue;
protected MVValueNode(String path, String[] comments, Class<T> type, T defaultValue, String name, BiConsumer<T, T> onSetValue) {
protected MVValueNode(String path, String[] comments, Class<T> type, T defaultValue, String name, Function<T, Boolean> validator, BiConsumer<T, T> onSetValue) {
super(path, comments);
this.type = type;
this.defaultValue = defaultValue;
this.name = name;
this.validator = validator;
this.onSetValue = onSetValue;
}
@ -61,9 +64,25 @@ public class MVValueNode<T> extends MVCommentedNode implements EnchancedValueNod
return Optional.ofNullable(name);
}
/**
* {@inheritDoc}
*/
@Override
public boolean isValid(T value) {
if (validator != null) {
return validator.apply(value);
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public void onSetValue(T oldValue, T newValue) {
onSetValue.accept(oldValue, newValue);
if (onSetValue != null) {
onSetValue.accept(oldValue, newValue);
}
}
/**
@ -77,6 +96,7 @@ public class MVValueNode<T> extends MVCommentedNode implements EnchancedValueNod
protected final Class<T> type;
protected T defaultValue;
protected String name;
protected Function<T, Boolean> validator;
protected BiConsumer<T, T> onSetValue;
/**
@ -113,6 +133,17 @@ public class MVValueNode<T> extends MVCommentedNode implements EnchancedValueNod
return (B) this;
}
public B validator(@Nullable Function<T, Boolean> validator) {
this.validator = validator;
return (B) this;
}
/**
* Sets the action to be performed when the value is set.
*
* @param onSetValue The action to be performed.
* @return This builder.
*/
public B onSetValue(@Nullable BiConsumer<T, T> onSetValue) {
this.onSetValue = onSetValue;
return (B) this;
@ -123,7 +154,7 @@ public class MVValueNode<T> extends MVCommentedNode implements EnchancedValueNod
*/
@Override
public MVValueNode<T> build() {
return new MVValueNode<>(path, comments.toArray(new String[0]), type, defaultValue, name, onSetValue);
return new MVValueNode<>(path, comments.toArray(new String[0]), type, defaultValue, name, validator, onSetValue);
}
}
}

View File

@ -29,14 +29,14 @@ public class NodeGroup implements Collection<CommentedNode> {
}
private void addNodeIndex(CommentedNode node) {
if (node instanceof EnchancedValueNode) {
((EnchancedValueNode<?>) node).getName().ifPresent(name -> nodesMap.put(name, node));
if (node instanceof EnhancedValueNode) {
((EnhancedValueNode<?>) node).getName().ifPresent(name -> nodesMap.put(name, node));
}
}
private void removeNodeIndex(CommentedNode node) {
if (node instanceof EnchancedValueNode) {
((EnchancedValueNode<?>) node).getName().ifPresent(nodesMap::remove);
if (node instanceof EnhancedValueNode) {
((EnhancedValueNode<?>) node).getName().ifPresent(nodesMap::remove);
}
}