Better defaults and completions for ListConfigNode

This commit is contained in:
Ben Woo 2023-09-14 19:07:39 +08:00
parent 362d871ab8
commit ee1bf8216e
No known key found for this signature in database
GPG Key ID: FB2A3645536E12C8
6 changed files with 119 additions and 48 deletions

View File

@ -5,6 +5,7 @@ import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission; import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Description; import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Flags; import co.aikar.commands.annotation.Flags;
import co.aikar.commands.annotation.Single;
import co.aikar.commands.annotation.Subcommand; import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax; import co.aikar.commands.annotation.Syntax;
import com.dumptruckman.minecraft.util.Logging; import com.dumptruckman.minecraft.util.Logging;
@ -55,6 +56,7 @@ class ModifyCommand extends MultiverseCommand {
String propertyName, String propertyName,
@Optional @Optional
@Single
@Syntax("[value]") @Syntax("[value]")
@Description("") @Description("")
String propertyValue) { String propertyValue) {

View File

@ -42,11 +42,11 @@ public class ConfigNode<T> extends ConfigHeaderNode implements ValueNode<T> {
protected final @Nullable String name; protected final @Nullable String name;
protected final @NotNull Class<T> type; protected final @NotNull Class<T> type;
protected final @Nullable Supplier<T> defaultValueSupplier; protected final @Nullable Supplier<T> defaultValueSupplier;
protected final @Nullable NodeSuggester suggester; protected @Nullable NodeSuggester suggester;
protected final @Nullable NodeStringParser<T> stringParser; protected @Nullable NodeStringParser<T> stringParser;
protected final @Nullable NodeSerializer<T> serializer; protected @Nullable NodeSerializer<T> serializer;
protected final @Nullable Function<T, Try<Void>> validator; protected @Nullable Function<T, Try<Void>> validator;
protected final @Nullable BiConsumer<T, T> onSetValue; protected @Nullable BiConsumer<T, T> onSetValue;
protected ConfigNode( protected ConfigNode(
@NotNull String path, @NotNull String path,

View File

@ -1,13 +1,17 @@
package org.mvplugins.multiverse.core.configuration.node; package org.mvplugins.multiverse.core.configuration.node;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import com.dumptruckman.minecraft.util.Logging;
import io.vavr.Value;
import io.vavr.control.Try; import io.vavr.control.Try;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -70,6 +74,91 @@ public class ListConfigNode<I> extends ConfigNode<List<I>> implements ListValueN
this.itemSerializer = itemSerializer; this.itemSerializer = itemSerializer;
this.itemValidator = itemValidator; this.itemValidator = itemValidator;
this.onSetItemValue = onSetItemValue; this.onSetItemValue = onSetItemValue;
if (this.itemSuggester != null && this.suggester == null) {
setDefaultSuggester();
}
if (this.itemStringParser != null && this.stringParser == null) {
Logging.fine("Setting default string parser for list node " + path);
setDefaultStringParser();
}
if (this.itemValidator != null && this.validator == null) {
setDefaultValidator();
}
if (this.itemSerializer != null && this.serializer == null) {
setDefaultSerialiser();
}
if (this.onSetItemValue != null && this.onSetValue == null) {
setDefaultOnSetValue();
}
}
private void setDefaultSuggester() {
this.suggester = input -> {
int lastIndexOf = input.lastIndexOf(',');
if (lastIndexOf == -1) {
return itemSuggester.suggest(input);
}
String lastInput = input.substring(lastIndexOf + 1);
String inputBeforeLast = input.substring(0, lastIndexOf + 1);
Set<String> inputs = Set.of(inputBeforeLast.split(","));
return itemSuggester.suggest(lastInput).stream()
.filter(item -> !inputs.contains(item))
.map(item -> inputBeforeLast + item)
.toList();
};
}
private void setDefaultStringParser() {
this.stringParser = (input, type) -> {
if (input == null) {
return Try.failure(new IllegalArgumentException("Input cannot be null"));
}
return Try.sequence(Arrays.stream(input.split(","))
.map(inputItem -> itemStringParser.parse(inputItem, itemType))
.toList()).map(Value::toJavaList);
};
}
private void setDefaultValidator() {
this.validator = value -> {
if (value != null) {
return Try.sequence(value.stream().map(itemValidator).toList()).map(v -> null);
}
return Try.success(null);
};
}
private void setDefaultSerialiser() {
this.serializer = new NodeSerializer<>() {
@Override
public List<I> deserialize(Object object, Class<List<I>> type) {
if (object instanceof List list) {
return list.stream().map(item -> itemSerializer.deserialize(item, itemType)).toList();
}
return new ArrayList<>();
}
@Override
public Object serialize(List<I> object, Class<List<I>> type) {
if (object != null) {
return object.stream().map(item -> itemSerializer.serialize(item, itemType)).toList();
}
return new ArrayList<>();
}
};
}
private void setDefaultOnSetValue() {
this.onSetValue = (oldValue, newValue) -> {
if (oldValue != null) {
oldValue.stream()
.filter(value -> !newValue.contains(value))
.forEach(item -> onSetItemValue.accept(item, null));
}
newValue.forEach(item -> onSetItemValue.accept(null, item));
};
} }
/** /**
@ -80,6 +169,9 @@ public class ListConfigNode<I> extends ConfigNode<List<I>> implements ListValueN
return itemType; return itemType;
} }
/**
* {@inheritDoc}
*/
@Override @Override
public @NotNull Collection<String> suggestItem(@Nullable String input) { public @NotNull Collection<String> suggestItem(@Nullable String input) {
if (itemSuggester != null) { if (itemSuggester != null) {
@ -88,6 +180,9 @@ public class ListConfigNode<I> extends ConfigNode<List<I>> implements ListValueN
return Collections.emptyList(); return Collections.emptyList();
} }
/**
* {@inheritDoc}
*/
@Override @Override
public @NotNull Try<I> parseItemFromString(@Nullable String input) { public @NotNull Try<I> parseItemFromString(@Nullable String input) {
if (itemStringParser != null) { if (itemStringParser != null) {
@ -176,21 +271,9 @@ public class ListConfigNode<I> extends ConfigNode<List<I>> implements ListValueN
*/ */
public @NotNull B itemValidator(@NotNull Function<I, Try<Void>> itemValidator) { public @NotNull B itemValidator(@NotNull Function<I, Try<Void>> itemValidator) {
this.itemValidator = itemValidator; this.itemValidator = itemValidator;
if (validator == null) {
setDefaultValidator();
}
return self(); return self();
} }
private void setDefaultValidator() {
this.validator = value -> {
if (value != null) {
return Try.sequence(value.stream().map(itemValidator).toList()).map(v -> null);
}
return Try.success(null);
};
}
/** /**
* Sets the onSetValue for the node. * Sets the onSetValue for the node.
* *
@ -199,23 +282,9 @@ public class ListConfigNode<I> extends ConfigNode<List<I>> implements ListValueN
*/ */
public @NotNull B onSetItemValue(@Nullable BiConsumer<I, I> onSetItemValue) { public @NotNull B onSetItemValue(@Nullable BiConsumer<I, I> onSetItemValue) {
this.onSetItemValue = onSetItemValue; this.onSetItemValue = onSetItemValue;
if (onSetValue == null) {
setDefaultOnSetValue();
}
return self(); return self();
} }
private void setDefaultOnSetValue() {
this.onSetValue = (oldValue, newValue) -> {
if (oldValue != null) {
oldValue.stream()
.filter(value -> !newValue.contains(value))
.forEach(item -> onSetItemValue.accept(item, null));
}
newValue.forEach(item -> onSetItemValue.accept(null, item));
};
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

View File

@ -14,6 +14,7 @@ import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.mvplugins.multiverse.core.MultiverseCore;
import org.mvplugins.multiverse.core.configuration.handle.ConfigModifyType; import org.mvplugins.multiverse.core.configuration.handle.ConfigModifyType;
import org.mvplugins.multiverse.core.configuration.handle.ConfigurationSectionHandle; import org.mvplugins.multiverse.core.configuration.handle.ConfigurationSectionHandle;
import org.mvplugins.multiverse.core.configuration.migration.BooleanMigratorAction; import org.mvplugins.multiverse.core.configuration.migration.BooleanMigratorAction;
@ -24,7 +25,6 @@ import org.mvplugins.multiverse.core.configuration.migration.MoveMigratorAction;
import org.mvplugins.multiverse.core.configuration.migration.NullStringMigratorAction; import org.mvplugins.multiverse.core.configuration.migration.NullStringMigratorAction;
import org.mvplugins.multiverse.core.configuration.migration.VersionMigrator; import org.mvplugins.multiverse.core.configuration.migration.VersionMigrator;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.helpers.EnforcementHandler;
/** /**
* Represents a world configuration. * Represents a world configuration.
@ -38,9 +38,9 @@ public final class WorldConfig {
WorldConfig( WorldConfig(
@NotNull String worldName, @NotNull String worldName,
@NotNull ConfigurationSection configSection, @NotNull ConfigurationSection configSection,
@NotNull EnforcementHandler enforcementHandler) { @NotNull MultiverseCore multiverseCore) {
this.worldName = worldName; this.worldName = worldName;
this.configNodes = new WorldConfigNodes(enforcementHandler); this.configNodes = new WorldConfigNodes(multiverseCore);
this.configHandle = ConfigurationSectionHandle.builder(configSection) this.configHandle = ConfigurationSectionHandle.builder(configSection)
.logger(Logging.getLogger()) .logger(Logging.getLogger())
.nodes(configNodes.getNodes()) .nodes(configNodes.getNodes())

View File

@ -1,8 +1,5 @@
package org.mvplugins.multiverse.core.world.config; package org.mvplugins.multiverse.core.world.config;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.Difficulty; import org.bukkit.Difficulty;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.Location; import org.bukkit.Location;
@ -10,12 +7,14 @@ import org.bukkit.Material;
import org.bukkit.World; import org.bukkit.World;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.mvplugins.multiverse.core.MultiverseCore;
import org.mvplugins.multiverse.core.configuration.node.ConfigNode; import org.mvplugins.multiverse.core.configuration.node.ConfigNode;
import org.mvplugins.multiverse.core.configuration.node.ListConfigNode; import org.mvplugins.multiverse.core.configuration.node.ListConfigNode;
import org.mvplugins.multiverse.core.configuration.node.Node; import org.mvplugins.multiverse.core.configuration.node.Node;
import org.mvplugins.multiverse.core.configuration.node.NodeGroup; import org.mvplugins.multiverse.core.configuration.node.NodeGroup;
import org.mvplugins.multiverse.core.world.config.AllowedPortalType;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld; import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.MultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;
import org.mvplugins.multiverse.core.world.helpers.EnforcementHandler; import org.mvplugins.multiverse.core.world.helpers.EnforcementHandler;
/** /**
@ -26,10 +25,12 @@ public class WorldConfigNodes {
private final NodeGroup nodes = new NodeGroup(); private final NodeGroup nodes = new NodeGroup();
private EnforcementHandler enforcementHandler; private EnforcementHandler enforcementHandler;
private WorldManager worldManager;
private LoadedMultiverseWorld world = null; private LoadedMultiverseWorld world = null;
WorldConfigNodes(@NotNull EnforcementHandler enforcementHandler) { WorldConfigNodes(@NotNull MultiverseCore multiverseCore) {
this.enforcementHandler = enforcementHandler; this.enforcementHandler = multiverseCore.getService(EnforcementHandler.class);
this.worldManager = multiverseCore.getService(WorldManager.class);
} }
LoadedMultiverseWorld getWorld() { LoadedMultiverseWorld getWorld() {
@ -245,6 +246,7 @@ public class WorldConfigNodes {
final ListConfigNode<String> WORLD_BLACKLIST = node(ListConfigNode final ListConfigNode<String> WORLD_BLACKLIST = node(ListConfigNode
.listBuilder("world-blacklist", String.class) .listBuilder("world-blacklist", String.class)
.itemSuggester(input -> worldManager.getWorlds().stream().map(MultiverseWorld::getName).toList())
.build()); .build());
final ConfigNode<Double> VERSION = node(ConfigNode.builder("version", Double.class) final ConfigNode<Double> VERSION = node(ConfigNode.builder("version", Double.class)

View File

@ -33,16 +33,14 @@ public final class WorldsConfigManager {
private final Map<String, WorldConfig> worldConfigMap; private final Map<String, WorldConfig> worldConfigMap;
private final File worldConfigFile; private final File worldConfigFile;
private final MultiverseCore multiverseCore;
private YamlConfiguration worldsConfig; private YamlConfiguration worldsConfig;
private final EnforcementHandler enforcementHandler;
@Inject @Inject
WorldsConfigManager(@NotNull MultiverseCore core, @NotNull EnforcementHandler enforcementHandler) { WorldsConfigManager(@NotNull MultiverseCore multiverseCore) {
worldConfigMap = new HashMap<>(); worldConfigMap = new HashMap<>();
worldConfigFile = core.getDataFolder().toPath().resolve(CONFIG_FILENAME).toFile(); worldConfigFile = multiverseCore.getDataFolder().toPath().resolve(CONFIG_FILENAME).toFile();
this.multiverseCore = multiverseCore;
this.enforcementHandler = enforcementHandler;
} }
/** /**
@ -128,7 +126,7 @@ public final class WorldsConfigManager {
WorldConfig newWorldConfig = new WorldConfig( WorldConfig newWorldConfig = new WorldConfig(
worldName, worldName,
getWorldConfigSection(worldName), getWorldConfigSection(worldName),
enforcementHandler); multiverseCore);
worldConfigMap.put(worldName, newWorldConfig); worldConfigMap.put(worldName, newWorldConfig);
newWorldsAdded.add(newWorldConfig); newWorldsAdded.add(newWorldConfig);
}); });
@ -183,7 +181,7 @@ public final class WorldsConfigManager {
if (worldConfigMap.containsKey(worldName)) { if (worldConfigMap.containsKey(worldName)) {
throw new IllegalArgumentException("WorldConfig for world " + worldName + " already exists."); throw new IllegalArgumentException("WorldConfig for world " + worldName + " already exists.");
} }
WorldConfig worldConfig = new WorldConfig(worldName, getWorldConfigSection(worldName), enforcementHandler); WorldConfig worldConfig = new WorldConfig(worldName, getWorldConfigSection(worldName), multiverseCore);
worldConfigMap.put(worldName, worldConfig); worldConfigMap.put(worldName, worldConfig);
return worldConfig; return worldConfig;
} }