diff --git a/src/main/java/org/mvplugins/multiverse/core/config/CoreConfigNodes.java b/src/main/java/org/mvplugins/multiverse/core/config/CoreConfigNodes.java index c3e1a6b61..7032299a6 100644 --- a/src/main/java/org/mvplugins/multiverse/core/config/CoreConfigNodes.java +++ b/src/main/java/org/mvplugins/multiverse/core/config/CoreConfigNodes.java @@ -386,7 +386,7 @@ private N node(N node) { .comment("This config option defines the default language Multiverse should use.") .defaultValue(Locale.ENGLISH) .name("default-locale") - .onSetValue((oldValue, newValue) -> + .onLoadAndChange((oldValue, newValue) -> commandManager.get().getLocales().setDefaultLocale(newValue)) .build()); @@ -396,9 +396,8 @@ private N node(N node) { .comment("If the player's language does not have a translation, it will use the default language set above instead.") .defaultValue(true) .name("per-player-locale") - .onSetValue((oldValue, newValue) -> { - commandManager.get().usePerIssuerLocale(newValue); - }) + .onLoadAndChange((oldValue, newValue) -> + commandManager.get().usePerIssuerLocale(newValue)) .build()); private final ConfigHeaderNode commandHeader = node(ConfigHeaderNode.builder("command") @@ -477,7 +476,7 @@ private N node(N node) { .comment("") .comment("This config option defines the priority for the PlayerPortalEvent.") .name("event-priority-player-portal") - .onSetValue((oldValue, newValue) -> + .onLoadAndChange((oldValue, newValue) -> eventPriorityMapper.get().setPriority("mvcore-player-portal", newValue)) .build()); @@ -486,7 +485,7 @@ private N node(N node) { .name("event-priority-player-respawn") .comment("") .comment("This config option defines the priority for the PlayerRespawnEvent.") - .onSetValue((oldValue, newValue) -> + .onLoadAndChange((oldValue, newValue) -> eventPriorityMapper.get().setPriority("mvcore-player-respawn", newValue)) .build()); @@ -494,7 +493,8 @@ private N node(N node) { .defaultValue(EventPriority.NORMAL) .comment("") .comment("This config option defines the priority for the PlayerSpawnLocationEvent.") - .name("event-priority-player-spawn-location").onSetValue((oldValue, newValue) -> + .name("event-priority-player-spawn-location") + .onLoadAndChange((oldValue, newValue) -> eventPriorityMapper.get().setPriority("mvcore-player-spawn-location", newValue)) .build()); @@ -503,7 +503,7 @@ private N node(N node) { .name("event-priority-player-teleport") .comment("") .comment("This config option defines the priority for the PlayerTeleportEvent.") - .onSetValue((oldValue, newValue) -> + .onLoadAndChange((oldValue, newValue) -> eventPriorityMapper.get().setPriority("mvcore-player-teleport", newValue)) .build()); @@ -551,7 +551,7 @@ private N node(N node) { .validator(value -> (value < 0 || value > 3) ? Try.failure(new MultiverseException("Debug level must be between 0 and 3.")) : Try.success(null)) - .onSetValue((oldValue, newValue) -> { + .onLoadAndChange((oldValue, newValue) -> { if (newValue != Logging.getDebugLevel()) { Logging.setDebugLevel(newValue); pluginManager.callEvent(new MVDebugModeEvent(newValue)); @@ -565,7 +565,7 @@ private N node(N node) { .comment("This will only work if the above 'global-debug' is set to 1 or more.") .defaultValue(false) .name("debug-permissions") - .onSetValue((oldValue, newValue) -> PermissionUtils.setDebugPermissions(newValue)) + .onLoadAndChange((oldValue, newValue) -> PermissionUtils.setDebugPermissions(newValue)) .build()); final ConfigNode silentStart = node(ConfigNode.builder("misc.silent-start", Boolean.class) @@ -573,7 +573,7 @@ private N node(N node) { .comment("If true, the startup console messages will no longer show.") .defaultValue(false) .name("silent-start") - .onSetValue((oldValue, newValue) -> Logging.setShowingConfig(!newValue)) + .onLoadAndChange((oldValue, newValue) -> Logging.setShowingConfig(!newValue)) .build()); final ConfigNode showDonationMessage = node(ConfigNode.builder("misc.show-donation-message", Boolean.class) diff --git a/src/main/java/org/mvplugins/multiverse/core/config/handle/BaseConfigurationHandle.java b/src/main/java/org/mvplugins/multiverse/core/config/handle/BaseConfigurationHandle.java index 6e9d15495..5ad0a0ecd 100644 --- a/src/main/java/org/mvplugins/multiverse/core/config/handle/BaseConfigurationHandle.java +++ b/src/main/java/org/mvplugins/multiverse/core/config/handle/BaseConfigurationHandle.java @@ -3,19 +3,22 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.logging.Logger; import com.dumptruckman.minecraft.util.Logging; import io.vavr.control.Option; import io.vavr.control.Try; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.mvplugins.multiverse.core.config.migration.ConfigMigrator; import org.mvplugins.multiverse.core.config.node.ListValueNode; -import org.mvplugins.multiverse.core.config.node.Node; import org.mvplugins.multiverse.core.config.node.NodeGroup; import org.mvplugins.multiverse.core.config.node.ValueNode; @@ -84,7 +87,8 @@ protected void setUpNodes() { }); nodeValueMap.forEach((valueNode, value) -> { - valueNode.onSetValue(value, value); + valueNode.onLoad(value); + valueNode.onLoadAndChange(Bukkit.getConsoleSender(), null, value); }); } @@ -156,10 +160,29 @@ public T get(@NotNull ValueNode node) { * @return Empty try if the value was set, try containing an error otherwise. */ public Try set(@NotNull ValueNode node, T value) { + return set(Bukkit.getConsoleSender(), node, value); + } + + /** + * Sets the value of a node, if the validator is not null, it will be tested first. + * + * @param sender The sender who triggered the change. + * @param node The node to set the value of. + * @param value The value to set. + * @param The type of the node value. + * @return Empty try if the value was set, try containing an error otherwise. + * + * @since 5.4 + */ + @ApiStatus.AvailableSince("5.4") + public Try set(@NotNull CommandSender sender, @NotNull ValueNode node, T value) { return node.validate(value).map(ignore -> { T oldValue = get(node); nodeValueMap.put(node, value); - node.onSetValue(oldValue, get(node)); + if (!Objects.equals(oldValue, value)) { + node.onLoadAndChange(sender, oldValue, value); + node.onChange(sender, oldValue, value); + } return null; }); } diff --git a/src/main/java/org/mvplugins/multiverse/core/config/handle/StringPropertyHandle.java b/src/main/java/org/mvplugins/multiverse/core/config/handle/StringPropertyHandle.java index 8b2fb6aab..cc44499e8 100644 --- a/src/main/java/org/mvplugins/multiverse/core/config/handle/StringPropertyHandle.java +++ b/src/main/java/org/mvplugins/multiverse/core/config/handle/StringPropertyHandle.java @@ -226,7 +226,7 @@ public Try setPropertyString(@Nullable String name, @Nullable String value public Try setPropertyString(@NotNull CommandSender sender, @Nullable String name, @Nullable String value) { return findNode(name, ValueNode.class) .flatMap(node -> node.parseFromString(sender, value) - .flatMap(parsedValue -> handle.set(node, parsedValue))); + .flatMap(parsedValue -> handle.set(sender, node, parsedValue))); } /** diff --git a/src/main/java/org/mvplugins/multiverse/core/config/node/ConfigNode.java b/src/main/java/org/mvplugins/multiverse/core/config/node/ConfigNode.java index 30e77df34..1e56ede25 100644 --- a/src/main/java/org/mvplugins/multiverse/core/config/node/ConfigNode.java +++ b/src/main/java/org/mvplugins/multiverse/core/config/node/ConfigNode.java @@ -14,6 +14,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.mvplugins.multiverse.core.config.node.functions.NodeChangeCallback; +import org.mvplugins.multiverse.core.config.node.functions.NodeValueCallback; +import org.mvplugins.multiverse.core.config.node.functions.SenderNodeChangeCallback; import org.mvplugins.multiverse.core.config.node.functions.SenderNodeStringParser; import org.mvplugins.multiverse.core.config.node.functions.SenderNodeSuggester; import org.mvplugins.multiverse.core.config.node.serializer.DefaultSerializerProvider; @@ -53,7 +56,9 @@ public class ConfigNode extends ConfigHeaderNode implements ValueNode { protected @Nullable NodeStringParser stringParser; protected @Nullable NodeSerializer serializer; protected @Nullable Function> validator; - protected @Nullable BiConsumer onSetValue; + protected @Nullable NodeValueCallback onLoad; + protected @Nullable NodeChangeCallback onLoadAndChange; + protected @Nullable NodeChangeCallback onChange; protected ConfigNode( @NotNull String path, @@ -66,7 +71,9 @@ protected ConfigNode( @Nullable NodeStringParser stringParser, @Nullable NodeSerializer serializer, @Nullable Function> validator, - @Nullable BiConsumer onSetValue) { + @Nullable NodeValueCallback onLoad, + @Nullable NodeChangeCallback onLoadAndChange, + @Nullable NodeChangeCallback onChange) { super(path, comments); this.name = name; this.type = type; @@ -82,7 +89,9 @@ protected ConfigNode( ? serializer : DefaultSerializerProvider.getDefaultSerializer(type); this.validator = validator; - this.onSetValue = onSetValue; + this.onLoad = onLoad; + this.onLoadAndChange = onLoadAndChange; + this.onChange = onChange; } /** @@ -186,9 +195,29 @@ public Try validate(@Nullable T value) { * {@inheritDoc} */ @Override - public void onSetValue(@Nullable T oldValue, @Nullable T newValue) { - if (onSetValue != null) { - onSetValue.accept(oldValue, newValue); + public void onLoad(@Nullable T value) { + if (onLoad != null) { + onLoad.run(value); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onLoadAndChange(@NotNull CommandSender sender, @Nullable T oldValue, @Nullable T newValue) { + if (onLoadAndChange != null) { + onLoadAndChange.run(sender, oldValue, newValue); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void onChange(@NotNull CommandSender sender, @Nullable T oldValue, @Nullable T newValue) { + if (onChange != null) { + onChange.run(sender, oldValue, newValue); } } @@ -208,7 +237,9 @@ public static class Builder> extends Confi protected @Nullable NodeStringParser stringParser; protected @Nullable NodeSerializer serializer; protected @Nullable Function> validator; - protected @Nullable BiConsumer onSetValue; + protected @Nullable NodeValueCallback onLoad; + protected @Nullable NodeChangeCallback onLoadAndChange; + protected @Nullable NodeChangeCallback onChange; /** * Creates a new builder. @@ -368,15 +399,83 @@ protected Builder(@NotNull String path, @NotNull Class type) { return self(); } + /** + * Sets the action to be performed when the value is loaded. + * + * @param onLoad The action to be performed. + * @return This builder. + */ + @ApiStatus.AvailableSince("5.4") + public @NotNull B onLoad(@NotNull NodeValueCallback onLoad) { + this.onLoad = this.onLoad == null ? onLoad : this.onLoad.then(onLoad); + return self(); + } + + /** + * Sets the action to be performed when the value is loaded and changed. + * + * @param onLoadAndChange The action to be performed. + * @return This builder. + * + * @since 5.4 + */ + @ApiStatus.AvailableSince("5.4") + public @NotNull B onLoadAndChange(@NotNull NodeChangeCallback onLoadAndChange) { + this.onLoadAndChange = this.onLoadAndChange == null ? onLoadAndChange : this.onLoadAndChange.then(onLoadAndChange); + return self(); + } + + /** + * Sets the action to be performed when the value is loaded and changed. + * + * @param onLoadAndChange The action to be performed. + * @return This builder. + * + * @since 5.4 + */ + @ApiStatus.AvailableSince("5.4") + public @NotNull B onLoadAndChange(@NotNull SenderNodeChangeCallback onLoadAndChange) { + return onLoadAndChange((NodeChangeCallback) onLoadAndChange); + } + + /** + * Sets the action to be performed when the value is changed. + * + * @param onChange The action to be performed. + * @return This builder. + * + * @since 5.4 + */ + @ApiStatus.AvailableSince("5.4") + public @NotNull B onChange(@NotNull NodeChangeCallback onChange) { + this.onChange = this.onChange == null ? onChange : this.onChange.then(onChange); + return self(); + } + + /** + * Sets the action to be performed when the value is changed. + * + * @param onChange The action to be performed. + * @return This builder. + * + * @since 5.4 + */ + @ApiStatus.AvailableSince("5.4") + public @NotNull B onChange(@NotNull SenderNodeChangeCallback onChange) { + return onChange((NodeChangeCallback) onChange); + } + /** * Sets the action to be performed when the value is set. * * @param onSetValue The action to be performed. * @return This builder. + * + * @deprecated Use {@link #onLoadAndChange(NodeChangeCallback)} instead. */ + @Deprecated(since = "5.4", forRemoval = true) public @NotNull B onSetValue(@NotNull BiConsumer onSetValue) { - this.onSetValue = this.onSetValue == null ? onSetValue : this.onSetValue.andThen(onSetValue); - return self(); + return onLoadAndChange(onSetValue::accept); } /** @@ -385,7 +484,8 @@ protected Builder(@NotNull String path, @NotNull Class type) { @Override public @NotNull ConfigNode build() { return new ConfigNode<>(path, comments.toArray(new String[0]), - name, type, aliases, defaultValue, suggester, stringParser, serializer, validator, onSetValue); + name, type, aliases, defaultValue, suggester, stringParser, serializer, validator, + onLoad, onLoadAndChange, onChange); } } } diff --git a/src/main/java/org/mvplugins/multiverse/core/config/node/ListConfigNode.java b/src/main/java/org/mvplugins/multiverse/core/config/node/ListConfigNode.java index 138c2fed6..453c7a6e0 100644 --- a/src/main/java/org/mvplugins/multiverse/core/config/node/ListConfigNode.java +++ b/src/main/java/org/mvplugins/multiverse/core/config/node/ListConfigNode.java @@ -19,6 +19,8 @@ import org.mvplugins.multiverse.core.config.node.functions.DefaultStringParserProvider; import org.mvplugins.multiverse.core.config.node.functions.DefaultSuggesterProvider; +import org.mvplugins.multiverse.core.config.node.functions.NodeChangeCallback; +import org.mvplugins.multiverse.core.config.node.functions.NodeValueCallback; import org.mvplugins.multiverse.core.config.node.functions.NodeStringParser; import org.mvplugins.multiverse.core.config.node.functions.NodeSuggester; import org.mvplugins.multiverse.core.config.node.functions.SenderNodeStringParser; @@ -67,7 +69,9 @@ protected ListConfigNode( @Nullable NodeStringParser> stringParser, @Nullable NodeSerializer> serializer, @Nullable Function, Try> validator, - @Nullable BiConsumer, List> onSetValue, + @Nullable NodeValueCallback> onLoad, + @Nullable NodeChangeCallback> onLoadAndChange, + @Nullable NodeChangeCallback> onChange, @NotNull Class itemType, @Nullable NodeSuggester itemSuggester, @Nullable NodeStringParser itemStringParser, @@ -75,7 +79,7 @@ protected ListConfigNode( @Nullable Function> itemValidator, @Nullable BiConsumer onSetItemValue) { super(path, comments, name, type, aliases, defaultValueSupplier, suggester, stringParser, serializer, - validator, onSetValue); + validator, onLoad, onLoadAndChange, onChange); this.itemType = itemType; this.itemSuggester = itemSuggester != null ? itemSuggester @@ -105,8 +109,8 @@ private void setDefaults() { if (this.itemSerializer != null && this.serializer == null) { setDefaultSerialiser(); } - if (this.onSetItemValue != null && this.onSetValue == null) { - setDefaultOnSetValue(); + if (this.onSetItemValue != null && this.onLoadAndChange == null) { + setDefaultOnLoadAndChange(); } if (this.defaultValue == null) { this.defaultValue = ArrayList::new; @@ -168,8 +172,8 @@ public Object serialize(List object, Class> type) { }; } - private void setDefaultOnSetValue() { - this.onSetValue = (oldValue, newValue) -> { + private void setDefaultOnLoadAndChange() { + this.onLoadAndChange = (oldValue, newValue) -> { if (oldValue != null) { oldValue.stream() .filter(value -> !newValue.contains(value)) @@ -381,7 +385,9 @@ protected Builder(@NotNull String path, @NotNull Class itemType) { stringParser, serializer, validator, - onSetValue, + onLoad, + onLoadAndChange, + onChange, itemType, itemSuggester, itemStringParser, diff --git a/src/main/java/org/mvplugins/multiverse/core/config/node/ValueNode.java b/src/main/java/org/mvplugins/multiverse/core/config/node/ValueNode.java index aaf62fa73..8101796a1 100644 --- a/src/main/java/org/mvplugins/multiverse/core/config/node/ValueNode.java +++ b/src/main/java/org/mvplugins/multiverse/core/config/node/ValueNode.java @@ -5,11 +5,13 @@ import io.vavr.control.Option; import io.vavr.control.Try; import org.apache.logging.log4j.util.Strings; +import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.mvplugins.multiverse.core.config.handle.BaseConfigurationHandle; import org.mvplugins.multiverse.core.config.node.serializer.NodeSerializer; public interface ValueNode extends Node { @@ -108,11 +110,51 @@ public interface ValueNode extends Node { */ Try validate(@Nullable T value); + /** + * Called when the value of this node is loaded by {@link BaseConfigurationHandle#load()}. + * + * @param value The loaded value. + * + * @since 5.4 + */ + @ApiStatus.AvailableSince("5.4") + void onLoad(@Nullable T value); + + /** + * Called when the value of this node is loaded or changed. + * + * @param sender The sender who triggered the change. If triggered by loading or no target sender is specified, + * it will be the console sender. + * @param oldValue The old value, will always be null on load. + * @param newValue The new value. + * + * @since 5.4 + */ + @ApiStatus.AvailableSince("5.4") + void onLoadAndChange(@NotNull CommandSender sender, @Nullable T oldValue, @Nullable T newValue); + + /** + * Called when the value of this node is changed by {@link BaseConfigurationHandle#set(ValueNode, Object)}. + * + * @param sender The sender who changed the value, or console sender if no target sender specified. + * @param oldValue The old value. + * @param newValue The new value. + * + * @since 5.4 + */ + @ApiStatus.AvailableSince("5.4") + void onChange(@NotNull CommandSender sender, @Nullable T oldValue, @Nullable T newValue); + /** * Called when the value of this node is set. * * @param oldValue The old value. * @param newValue The new value. + * + * @deprecated Use {@link #onLoadAndChange(CommandSender, Object, Object)} instead. */ - void onSetValue(@Nullable T oldValue, @Nullable T newValue); + @Deprecated(since = "5.4", forRemoval = true) + default void onSetValue(@Nullable T oldValue, @Nullable T newValue) { + onLoadAndChange(Bukkit.getConsoleSender(), oldValue, newValue); + } } diff --git a/src/main/java/org/mvplugins/multiverse/core/config/node/functions/NodeChangeCallback.java b/src/main/java/org/mvplugins/multiverse/core/config/node/functions/NodeChangeCallback.java new file mode 100644 index 000000000..599244bc7 --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/config/node/functions/NodeChangeCallback.java @@ -0,0 +1,65 @@ +package org.mvplugins.multiverse.core.config.node.functions; + +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.ApiStatus; + +/** + * A callback interface for handling changes to configuration node values. + * + * @param The type of the node value. + * + * @since 5.4 + */ +@ApiStatus.AvailableSince("5.4") +@FunctionalInterface +public interface NodeChangeCallback { + /** + * Called when the value of a node changes. + * + * @param oldValue The old value of the node. + * @param newValue The new value of the node. + * + * @since 5.4 + */ + @ApiStatus.AvailableSince("5.4") + void run(T oldValue, T newValue); + + /** + * Called when the value of a node changes, with the sender of the change. + * + * @param sender The sender of the change. + * @param oldValue The old value of the node. + * @param newValue The new value of the node. + * + * @since 5.4 + */ + @ApiStatus.AvailableSince("5.4") + default void run(CommandSender sender, T oldValue, T newValue) { + run(oldValue, newValue); + } + + /** + * Chains another {@link NodeChangeCallback} to be executed after this one. + * + * @param after The callback to execute after this one. + * @return A new {@link NodeChangeCallback} that executes both callbacks in order. + * + * @since 5.4 + */ + @ApiStatus.AvailableSince("5.4") + default NodeChangeCallback then(NodeChangeCallback after) { + return new SenderNodeChangeCallback() { + @Override + public void run(CommandSender sender, T oldValue, T newValue) { + NodeChangeCallback.this.run(sender, oldValue, newValue); + after.run(sender, oldValue, newValue); + } + + @Override + public void run(T oldValue, T newValue) { + NodeChangeCallback.this.run(oldValue, newValue); + after.run(oldValue, newValue); + } + }; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/config/node/functions/NodeValueCallback.java b/src/main/java/org/mvplugins/multiverse/core/config/node/functions/NodeValueCallback.java new file mode 100644 index 000000000..5872e570e --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/config/node/functions/NodeValueCallback.java @@ -0,0 +1,38 @@ +package org.mvplugins.multiverse.core.config.node.functions; + +import org.jetbrains.annotations.ApiStatus; + +/** + * Handler called when an action is performed on a node such as loading. + * + * @since 5.4 + */ +@ApiStatus.AvailableSince("5.4") +@FunctionalInterface +public interface NodeValueCallback { + /** + * Called when an action is performed on a node. + * + * @param value The value of the node. + * + * @since 5.4 + */ + @ApiStatus.AvailableSince("5.4") + void run(T value); + + /** + * Chains another {@link NodeValueCallback} to be executed after this one. + * + * @param after The callback to execute after this one. + * @return A new {@link NodeValueCallback} that executes both callbacks in order. + * + * @since 5.4 + */ + @ApiStatus.AvailableSince("5.4") + default NodeValueCallback then(NodeValueCallback after) { + return (T value) -> { + this.run(value); + after.run(value); + }; + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/config/node/functions/SenderNodeChangeCallback.java b/src/main/java/org/mvplugins/multiverse/core/config/node/functions/SenderNodeChangeCallback.java new file mode 100644 index 000000000..b50a1be2e --- /dev/null +++ b/src/main/java/org/mvplugins/multiverse/core/config/node/functions/SenderNodeChangeCallback.java @@ -0,0 +1,31 @@ +package org.mvplugins.multiverse.core.config.node.functions; + +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.ApiStatus; + +/** + * A callback interface for handling changes to configuration node values with contextual information from the sender. + * + * @param The type of the node value. + * + * @since 5.4 + */ +@ApiStatus.AvailableSince("5.4") +@FunctionalInterface +public interface SenderNodeChangeCallback extends NodeChangeCallback { + /** + * {@inheritDoc} + */ + @ApiStatus.AvailableSince("5.4") + void run(CommandSender sender, T oldValue, T newValue); + + /** + * {@inheritDoc} + */ + @ApiStatus.AvailableSince("5.4") + @Override + default void run(T oldValue, T newValue) { + run(Bukkit.getConsoleSender(), oldValue, newValue); + } +} diff --git a/src/main/java/org/mvplugins/multiverse/core/config/node/functions/SenderNodeStringParser.java b/src/main/java/org/mvplugins/multiverse/core/config/node/functions/SenderNodeStringParser.java index b008dd287..65a4f35c2 100644 --- a/src/main/java/org/mvplugins/multiverse/core/config/node/functions/SenderNodeStringParser.java +++ b/src/main/java/org/mvplugins/multiverse/core/config/node/functions/SenderNodeStringParser.java @@ -11,8 +11,11 @@ * A function that parses a string into a node value object of type {@link T} with contextual information from the sender. * * @param The type of the object to parse. + * + * @since 5.1 */ @ApiStatus.AvailableSince("5.1") +@FunctionalInterface public interface SenderNodeStringParser extends NodeStringParser { /** * Parses a string into a node value object of type {@link T} with contextual information from the sender. diff --git a/src/main/java/org/mvplugins/multiverse/core/world/WorldConfigNodes.java b/src/main/java/org/mvplugins/multiverse/core/world/WorldConfigNodes.java index 410407cdc..baea0af2c 100644 --- a/src/main/java/org/mvplugins/multiverse/core/world/WorldConfigNodes.java +++ b/src/main/java/org/mvplugins/multiverse/core/world/WorldConfigNodes.java @@ -60,11 +60,14 @@ NodeGroup getNodes() { } private ConfigNode node(ConfigNode.Builder nodeBuilder) { - nodeBuilder.onSetValue((oldValue, newValue) -> { - if (Objects.equals(oldValue, newValue)) return; + nodeBuilder.onChange((oldValue, newValue) -> { if (world == null) return; MVWorldPropertyChangedEvent mvWorldPropertyChangeEvent = new MVWorldPropertyChangedEvent<>( - world, Option.of(nodeBuilder.name()).getOrElse(nodeBuilder.path()), oldValue, newValue); + world, + Option.of(nodeBuilder.name()).getOrElse(nodeBuilder.path()), + oldValue, + newValue + ); Bukkit.getPluginManager().callEvent(mvWorldPropertyChangeEvent); Logging.finer("MVWorldPropertyChangeEvent fired for world '%s' with name '%s' and value '%s'", world.getName(), nodeBuilder.path(), newValue); @@ -83,7 +86,7 @@ private ConfigNode node(ConfigNode.Builder nodeBuilder) { final ConfigNode alias = node(ConfigNode.builder("alias", String.class) .defaultValue("") - .onSetValue((oldValue, newValue) -> { + .onLoadAndChange((oldValue, newValue) -> { if (world == null) return; world.updateColourlessAlias(); })); @@ -93,14 +96,14 @@ private ConfigNode node(ConfigNode.Builder nodeBuilder) { final ConfigNode allowFlight = node(ConfigNode.builder("allow-flight", Boolean.class) .defaultValue(false) - .onSetValue((oldValue, newValue) -> { + .onLoadAndChange((oldValue, newValue) -> { if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return; enforcementHandler.handleAllFlightEnforcement(loadedWorld); })); final ConfigNode allowWeather = node(ConfigNode.builder("allow-weather", Boolean.class) .defaultValue(true) - .onSetValue((oldValue, newValue) -> { + .onLoadAndChange((oldValue, newValue) -> { if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return; loadedWorld.getBukkitWorld().peek(world -> { if (!world.isClearWeather() && !newValue) { @@ -128,7 +131,7 @@ private ConfigNode node(ConfigNode.Builder nodeBuilder) { final ConfigNode difficulty = node(ConfigNode.builder("difficulty", Difficulty.class) .defaultValue(Difficulty.NORMAL) - .onSetValue((oldValue, newValue) -> { + .onLoadAndChange((oldValue, newValue) -> { if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return; loadedWorld.getBukkitWorld().peek(bukkitWorld -> bukkitWorld.setDifficulty(newValue)); })); @@ -175,7 +178,7 @@ public Object serialize(Material object, Class type) { final ConfigNode gamemode = node(ConfigNode.builder("gamemode", GameMode.class) .defaultValue(GameMode.SURVIVAL) - .onSetValue((oldValue, newValue) -> { + .onLoadAndChange((oldValue, newValue) -> { if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return; enforcementHandler.handleAllGameModeEnforcement(loadedWorld); })); @@ -194,7 +197,7 @@ public Object serialize(Material object, Class type) { final ConfigNode keepSpawnInMemory = node(ConfigNode .builder("keep-spawn-in-memory", Boolean.class) .defaultValue(true) - .onSetValue((oldValue, newValue) -> { + .onLoadAndChange((oldValue, newValue) -> { if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return; loadedWorld.getBukkitWorld().peek(bukkitWorld -> bukkitWorld.setKeepSpawnInMemory(newValue)); })); @@ -208,7 +211,7 @@ public Object serialize(Material object, Class type) { final ConfigNode pvp = node(ConfigNode.builder("pvp", Boolean.class) .defaultValue(true) - .onSetValue((oldValue, newValue) -> { + .onLoadAndChange((oldValue, newValue) -> { if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return; loadedWorld.getBukkitWorld().peek(bukkitWorld -> bukkitWorld.setPVP(newValue)); })); @@ -237,7 +240,7 @@ public Object serialize(Material object, Class type) { final ConfigNode spawnLocation = node(ConfigNode.builder("spawn-location", SpawnLocation.class) .defaultValue(NullSpawnLocation.get()) .hidden() - .onSetValue((oldValue, newValue) -> { + .onLoadAndChange((oldValue, newValue) -> { if (!(world instanceof LoadedMultiverseWorld loadedWorld)) return; if (newValue == null || newValue instanceof NullSpawnLocation) return; loadedWorld.getBukkitWorld().peek(bukkitWorld -> { @@ -263,7 +266,7 @@ public Object serialize(EntitySpawnConfig object, Class type) return object.toSection(); } }) - .onSetValue((oldValue, newValue) -> { + .onLoadAndChange((oldValue, newValue) -> { newValue.setWorldRef(world); newValue.applyConfigToWorld(); })); diff --git a/src/main/java/org/mvplugins/multiverse/core/world/entity/SpawnCategoryConfig.java b/src/main/java/org/mvplugins/multiverse/core/world/entity/SpawnCategoryConfig.java index 2eee36ad8..afc0689a2 100644 --- a/src/main/java/org/mvplugins/multiverse/core/world/entity/SpawnCategoryConfig.java +++ b/src/main/java/org/mvplugins/multiverse/core/world/entity/SpawnCategoryConfig.java @@ -2,6 +2,7 @@ import com.dumptruckman.minecraft.util.Logging; import io.vavr.control.Try; +import org.bukkit.ChatColor; import org.bukkit.World; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Entity; @@ -161,19 +162,31 @@ private N node(N node) { final ConfigNode spawn = node(ConfigNode.builder("spawn", Boolean.class) .defaultValue(true) - .onSetValue((oldValue, newValue) -> applyConfigToWorld()) + .onLoadAndChange((oldValue, newValue) -> applyConfigToWorld()) .build()); final ConfigNode tickRate = node(ConfigNode.builder("tick-rate", Integer.class) .defaultValue(-1) .suggester(input -> List.of("-1", "10", "100", "400", "1000")) - .onSetValue((oldValue, newValue) -> applyConfigToWorld()) + .onLoadAndChange((oldValue, newValue) -> applyConfigToWorld()) + .onChange((sender, oldValue, newValue) -> { + if (!config.getApplyEntitySpawnRate()) { + sender.sendMessage(ChatColor.RED + "Warning: Changing tick rates has no effect because " + + "'apply-entity-spawn-rate' is disabled in the core config."); + } + }) .build()); final ConfigNode spawnLimit = node(ConfigNode.builder("spawn-limit", Integer.class) .defaultValue(-1) .suggester(input -> List.of("-1", "10", "100", "400", "1000")) - .onSetValue((oldValue, newValue) -> applyConfigToWorld()) + .onLoadAndChange((oldValue, newValue) -> applyConfigToWorld()) + .onChange((sender, oldValue, newValue) -> { + if (!config.getApplyEntitySpawnLimit()) { + sender.sendMessage(ChatColor.RED + "Warning: Changing spawn limits has no effect because " + + "'apply-entity-spawn-limit' is disabled in the core config."); + } + }) .build()); final ListConfigNode exceptions = node(ListConfigNode.listBuilder("exceptions", EntityType.class) @@ -181,7 +194,7 @@ private N node(N node) { .itemSuggester(input -> SpawnCategoryMapper.getEntityTypes(spawnCategory).stream() .map(EntityType::name) .toList()) - .onSetValue((oldValue, newValue) -> applyConfigToWorld()) + .onLoadAndChange((oldValue, newValue) -> applyConfigToWorld()) .build()); } } diff --git a/src/test/java/org/mvplugins/multiverse/core/world/WorldManagerTest.kt b/src/test/java/org/mvplugins/multiverse/core/world/WorldManagerTest.kt index 539426574..86b104dc3 100644 --- a/src/test/java/org/mvplugins/multiverse/core/world/WorldManagerTest.kt +++ b/src/test/java/org/mvplugins/multiverse/core/world/WorldManagerTest.kt @@ -5,6 +5,7 @@ import org.bukkit.World import org.bukkit.WorldType import org.hamcrest.MatcherAssert.assertThat import org.mockbukkit.mockbukkit.matcher.plugin.PluginManagerFiredEventClassMatcher.hasFiredEventInstance +import org.mockbukkit.mockbukkit.matcher.plugin.PluginManagerFiredEventClassMatcher.hasNotFiredEventInstance import org.mvplugins.multiverse.core.TestWithMockBukkit import org.mvplugins.multiverse.core.event.world.* import org.mvplugins.multiverse.core.world.options.CloneWorldOptions @@ -309,4 +310,18 @@ class WorldManagerTest : TestWithMockBukkit() { assertTrue(worldManager.isUnloadedWorld("WoRlD2")) assertTrue(worldManager.isWorld("wORLD2")) } + + @Test + fun `Set different value - MVWorldPropertyChangedEvent event is fired`() { + server.pluginManager.clearEvents() + world.scale = 8.0 + assertThat(server.pluginManager, hasFiredEventInstance(MVWorldPropertyChangedEvent::class.java)) + } + + @Test + fun `Set equal value - MVWorldPropertyChangedEvent event is not fired`() { + server.pluginManager.clearEvents() + world.scale = 1.0 + assertThat(server.pluginManager, hasNotFiredEventInstance(MVWorldPropertyChangedEvent::class.java)) + } }