diff --git a/core/src/main/java/com/nisovin/magicspells/Spell.java b/core/src/main/java/com/nisovin/magicspells/Spell.java index f7db15374..773b214cb 100644 --- a/core/src/main/java/com/nisovin/magicspells/Spell.java +++ b/core/src/main/java/com/nisovin/magicspells/Spell.java @@ -942,7 +942,7 @@ protected ConfigData getConfigDataBlockData(String key, BlockData def } protected ConfigData getConfigDataRegistryEntry(@NotNull String key, @NotNull RegistryKey registryKey, @Nullable T def) { - return ConfigDataUtil.getRegistryEntry(config.getMainConfig(), internalKey + key, RegistryAccess.registryAccess().getRegistry(registryKey), def); + return ConfigDataUtil.getRegistryEntry(config.getMainConfig(), internalKey + key, registryKey, def); } protected ConfigData getConfigDataRegistryEntry(@NotNull String key, @NotNull Registry registry, @Nullable T def) { diff --git a/core/src/main/java/com/nisovin/magicspells/spells/instant/DialogSpell.java b/core/src/main/java/com/nisovin/magicspells/spells/instant/DialogSpell.java new file mode 100644 index 000000000..ed95ccd3c --- /dev/null +++ b/core/src/main/java/com/nisovin/magicspells/spells/instant/DialogSpell.java @@ -0,0 +1,52 @@ +package com.nisovin.magicspells.spells.instant; + +import org.bukkit.entity.Player; + +import io.papermc.paper.dialog.Dialog; + +import com.nisovin.magicspells.util.*; +import com.nisovin.magicspells.MagicSpells; +import com.nisovin.magicspells.spells.InstantSpell; +import com.nisovin.magicspells.util.config.ConfigData; +import com.nisovin.magicspells.spells.TargetedEntitySpell; +import com.nisovin.magicspells.util.config.dialog.DialogBuilder; + +public class DialogSpell extends InstantSpell implements TargetedEntitySpell { + + private ConfigData dialog; + + public DialogSpell(MagicConfig config, String spellName) { + super(config, spellName); + } + + @Override + protected void initialize() { + super.initialize(); + + String error = "DialogSpell '" + internalName + "' reports - "; + dialog = DialogBuilder.create( + getConfigSection(""), + extra -> MagicSpells.error(error + extra), + extra -> MagicSpells.debug(" " + error + extra) + ); + } + + @Override + public CastResult cast(SpellData data) { + return castAtEntity(data.target(data.caster())); + } + + @Override + public CastResult castAtEntity(SpellData data) { + if (!(data.target() instanceof Player player)) return noTarget(data); + + Dialog dialog = this.dialog.get(data); + if (dialog == null) return new CastResult(PostCastAction.ALREADY_HANDLED, data); + + player.showDialog(dialog); + + playSpellEffects(data); + return new CastResult(PostCastAction.HANDLE_NORMALLY, data); + } + +} diff --git a/core/src/main/java/com/nisovin/magicspells/spells/targeted/CloseDialogSpell.java b/core/src/main/java/com/nisovin/magicspells/spells/targeted/CloseDialogSpell.java new file mode 100644 index 000000000..d0ad97e07 --- /dev/null +++ b/core/src/main/java/com/nisovin/magicspells/spells/targeted/CloseDialogSpell.java @@ -0,0 +1,36 @@ +package com.nisovin.magicspells.spells.targeted; + +import org.bukkit.entity.Player; + +import com.nisovin.magicspells.util.*; +import com.nisovin.magicspells.spells.TargetedSpell; +import com.nisovin.magicspells.spells.TargetedEntitySpell; + +public class CloseDialogSpell extends TargetedSpell implements TargetedEntitySpell { + + public CloseDialogSpell(MagicConfig config, String spellName) { + super(config, spellName); + } + + @Override + public CastResult cast(SpellData data) { + TargetInfo info = getTargetedPlayer(data); + if (info.noTarget()) return noTarget(info); + + return close(info.target(), info.spellData()); + } + + @Override + public CastResult castAtEntity(SpellData data) { + if (!(data.target() instanceof Player target)) return noTarget(data); + return close(target, data); + } + + private CastResult close(Player target, SpellData data) { + target.closeDialog(); + + playSpellEffects(data); + return new CastResult(PostCastAction.HANDLE_NORMALLY, data); + } + +} diff --git a/core/src/main/java/com/nisovin/magicspells/spells/targeted/CloseInventorySpell.java b/core/src/main/java/com/nisovin/magicspells/spells/targeted/CloseInventorySpell.java index b601a8b2e..30e7f19b1 100644 --- a/core/src/main/java/com/nisovin/magicspells/spells/targeted/CloseInventorySpell.java +++ b/core/src/main/java/com/nisovin/magicspells/spells/targeted/CloseInventorySpell.java @@ -21,20 +21,17 @@ public CloseInventorySpell(MagicConfig config, String spellName) { public CastResult cast(SpellData data) { TargetInfo info = getTargetedPlayer(data); if (info.noTarget()) return noTarget(info); - data = info.spellData(); - close(info.target(), data); - return new CastResult(PostCastAction.HANDLE_NORMALLY, data); + return close(info.target(), info.spellData()); } @Override public CastResult castAtEntity(SpellData data) { if (!(data.target() instanceof Player target)) return noTarget(data); - close(target, data); - return new CastResult(PostCastAction.HANDLE_NORMALLY, data); + return close(target, data); } - private void close(Player target, SpellData data) { + private CastResult close(Player target, SpellData data) { int delay = this.delay.get(data); if (delay > 0) { @@ -46,6 +43,8 @@ private void close(Player target, SpellData data) { target.closeInventory(); playSpellEffects(data); } + + return new CastResult(PostCastAction.HANDLE_NORMALLY, data); } } diff --git a/core/src/main/java/com/nisovin/magicspells/util/config/ConfigDataUtil.java b/core/src/main/java/com/nisovin/magicspells/util/config/ConfigDataUtil.java index 5b83f7409..34956a499 100644 --- a/core/src/main/java/com/nisovin/magicspells/util/config/ConfigDataUtil.java +++ b/core/src/main/java/com/nisovin/magicspells/util/config/ConfigDataUtil.java @@ -7,6 +7,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.RegistryAccess; + import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; @@ -730,6 +733,10 @@ public boolean isConstant() { } } + public static ConfigData getRegistryEntry(@NotNull ConfigurationSection config, @NotNull String path, @NotNull RegistryKey registryKey, @Nullable T def) { + return getRegistryEntry(config, path, RegistryAccess.registryAccess().getRegistry(registryKey), def); + } + public static ConfigData getRegistryEntry(@NotNull ConfigurationSection config, @NotNull String path, @NotNull Registry registry, @Nullable T def) { String value = config.getString(path); if (value == null) return data -> def; diff --git a/core/src/main/java/com/nisovin/magicspells/util/config/dialog/BuildContext.java b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/BuildContext.java new file mode 100644 index 000000000..32b0983b4 --- /dev/null +++ b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/BuildContext.java @@ -0,0 +1,80 @@ +package com.nisovin.magicspells.util.config.dialog; + +import java.util.Map; +import java.util.List; +import java.util.ArrayList; +import java.util.function.BiFunction; + +import org.jetbrains.annotations.NotNull; + +import org.apache.commons.lang3.function.TriFunction; + +import org.bukkit.configuration.ConfigurationSection; + +import com.nisovin.magicspells.util.ConfigReaderUtil; +import com.nisovin.magicspells.util.config.ConfigData; + +public abstract class BuildContext { + + protected final Debugger debugger; + + protected BuildContext(Debugger debugger) { + this.debugger = debugger; + } + + @NotNull + protected abstract NullConfigData build(); + + @NotNull + protected NullConfigData required( + TriFunction> configDataFun, + ConfigurationSection config, + String basePath, + String subPath + ) { + String error = "Missing '%s' at '%s'.".formatted(subPath, basePath); + + ConfigData configData = configDataFun.apply(config, subPath, null); + if (configData.isNull()) { + debugger.config(error); + return _ -> null; + } + + return data -> { + E object = configData.get(data); + if (object == null) debugger.cast(error); + return object; + }; + } + + protected static ConfigData> configDataList( + ConfigurationSection config, + String basePath, + String subPath, + BiFunction> configDataFun + ) { + List> list = new ArrayList<>(); + List configList = config.getList(subPath, List.of()); + for (int i = 0; i < configList.size(); i++) { + if (!(configList.get(i) instanceof Map map)) continue; + ConfigurationSection section = ConfigReaderUtil.mapToSection(map); + + String path = "%s.%s[%d]".formatted(basePath, subPath, i); + + list.add(configDataFun.apply(section, path)); + } + + return data -> { + List result = new ArrayList<>(); + + for (ConfigData element : list) { + E item = element.get(data); + if (item == null) continue; + result.add(item); + } + + return result; + }; + } + +} diff --git a/core/src/main/java/com/nisovin/magicspells/util/config/dialog/Debugger.java b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/Debugger.java new file mode 100644 index 000000000..c3693f7d7 --- /dev/null +++ b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/Debugger.java @@ -0,0 +1,23 @@ +package com.nisovin.magicspells.util.config.dialog; + +import java.util.function.Consumer; + +public final class Debugger { + + private final Consumer config; + private final Consumer cast; + + public Debugger(Consumer config, Consumer cast) { + this.config = config; + this.cast = cast; + } + + public void config(String text) { + config.accept(text); + } + + public void cast(String text) { + cast.accept(text); + } + +} diff --git a/core/src/main/java/com/nisovin/magicspells/util/config/dialog/DialogBuilder.java b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/DialogBuilder.java new file mode 100644 index 000000000..34937cb41 --- /dev/null +++ b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/DialogBuilder.java @@ -0,0 +1,71 @@ +package com.nisovin.magicspells.util.config.dialog; + +import java.util.function.Consumer; + +import org.jetbrains.annotations.NotNull; + +import net.kyori.adventure.key.Key; + +import org.bukkit.NamespacedKey; +import org.bukkit.configuration.ConfigurationSection; + +import io.papermc.paper.dialog.Dialog; +import io.papermc.paper.registry.TypedKey; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.data.dialog.DialogBase; +import io.papermc.paper.registry.data.dialog.type.DialogType; +import io.papermc.paper.registry.data.dialog.DialogRegistryEntry; + +import com.nisovin.magicspells.util.config.ConfigData; +import com.nisovin.magicspells.util.config.ConfigDataUtil; +import com.nisovin.magicspells.util.config.dialog.base.BaseBuilder; +import com.nisovin.magicspells.util.config.dialog.types.TypeBuilder; + +@SuppressWarnings("UnstableApiUsage") +public class DialogBuilder extends BuildContext { + + private final ConfigurationSection config; + + public static NullConfigData create(ConfigurationSection config, Consumer debugConfig, Consumer debugCast) { + Debugger debugger = new Debugger(debugConfig, debugCast); + return new DialogBuilder(debugger, config).build(); + } + + private DialogBuilder(Debugger debugger, ConfigurationSection config) { + super(debugger); + this.config = config; + } + + @Override + protected @NotNull NullConfigData build() { + ConfigData copyFromData = ConfigDataUtil.getNamespacedKey(config, "copy-from", null); + + ConfigData baseData = BaseBuilder.create(debugger, config); + ConfigData typeData = TypeBuilder.create(debugger, config); + + return data -> { + DialogBase base = baseData.get(data); + DialogType type = typeData.get(data); + if (base == null || type == null) return null; + + Key copyFrom = copyFromData.get(data); + + return Dialog.create(factory -> { + DialogRegistryEntry.Builder builder; + + if (copyFrom == null) builder = factory.empty(); + else { + try { + builder = factory.copyFrom(TypedKey.create(RegistryKey.DIALOG, copyFrom)); + } catch (IllegalArgumentException e) { + debugger.cast("Invalid 'copy-from': " + e.getMessage()); + return; + } + } + + builder.base(base).type(type); + }); + }; + } + +} diff --git a/core/src/main/java/com/nisovin/magicspells/util/config/dialog/NullConfigData.java b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/NullConfigData.java new file mode 100644 index 000000000..acee44581 --- /dev/null +++ b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/NullConfigData.java @@ -0,0 +1,15 @@ +package com.nisovin.magicspells.util.config.dialog; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import com.nisovin.magicspells.util.SpellData; +import com.nisovin.magicspells.util.config.ConfigData; + +public interface NullConfigData extends ConfigData { + + @Override + @Nullable + T get(@NotNull SpellData data); + +} diff --git a/core/src/main/java/com/nisovin/magicspells/util/config/dialog/action/ActionButtonBuilder.java b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/action/ActionButtonBuilder.java new file mode 100644 index 000000000..cce45a90a --- /dev/null +++ b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/action/ActionButtonBuilder.java @@ -0,0 +1,84 @@ +package com.nisovin.magicspells.util.config.dialog.action; + +import org.jetbrains.annotations.NotNull; + +import net.kyori.adventure.text.Component; + +import org.bukkit.configuration.ConfigurationSection; + +import io.papermc.paper.registry.data.dialog.ActionButton; +import io.papermc.paper.registry.data.dialog.action.DialogAction; + +import com.nisovin.magicspells.util.config.ConfigData; +import com.nisovin.magicspells.util.config.ConfigDataUtil; +import com.nisovin.magicspells.util.config.dialog.Debugger; +import com.nisovin.magicspells.util.config.dialog.BuildContext; +import com.nisovin.magicspells.util.config.dialog.NullConfigData; + +@SuppressWarnings("UnstableApiUsage") +public class ActionButtonBuilder extends BuildContext { + + private final ConfigurationSection config; + private final String basePath; + private final String subPath; + + public static NullConfigData createOptional(Debugger debugger, ConfigurationSection config, String basePath, String subPath) { + if (config == null) return _ -> null; + return new ActionButtonBuilder(debugger, config, basePath, subPath).build(); + } + + public static NullConfigData createRequired(Debugger debugger, ConfigurationSection config, String basePath, String subPath) { + if (config == null) { + debugger.config("Missing '" + subPath + "' section at '" + basePath + "'."); + return _ -> null; + } + return new ActionButtonBuilder(debugger, config, basePath, subPath).build(); + } + + private ActionButtonBuilder(Debugger debugger, ConfigurationSection config, String basePath, String subPath) { + super(debugger); + this.config = config; + this.basePath = basePath; + this.subPath = subPath; + } + + @Override + protected @NotNull NullConfigData build() { + String path = basePath + "." + subPath; + + NullConfigData labelData = required(ConfigDataUtil::getComponent, config, path, "label"); + ConfigData tooltip = ConfigDataUtil.getComponent(config, "tooltip", null); + ConfigData width = ConfigDataUtil.getInteger(config, "width", 150); + ConfigData action = dialogActionOptional(config, path, "action"); + + return data -> { + Component label = labelData.get(data); + if (label == null) return null; + + try { + return ActionButton.create(label, tooltip.get(data), width.get(data), action.get(data)); + } catch (IllegalArgumentException e) { + debugger.cast("Invalid '" + subPath + "' button defined in '" + basePath + "': " + e.getMessage()); + return null; + } + }; + } + + @NotNull + private ConfigData dialogActionOptional(ConfigurationSection config, String basePath, String subPath) { + String path = basePath + "." + subPath; + ConfigurationSection section = config.getConfigurationSection(subPath); + if (section == null) return _ -> null; + + return switch (section.getString("type")) { + case "command_template" -> CommandTemplateAction.create(debugger, section, path, "template"); + case "static_action" -> StaticAction.create(debugger, section, path, "static"); + case "custom_click" -> CustomClickAction.create(debugger, section, path, "click"); + case null, default -> { + debugger.config("Invalid 'type' defined in '" + path + "'."); + yield _ -> null; + } + }; + } + +} diff --git a/core/src/main/java/com/nisovin/magicspells/util/config/dialog/action/CommandTemplateAction.java b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/action/CommandTemplateAction.java new file mode 100644 index 000000000..57f9de2f2 --- /dev/null +++ b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/action/CommandTemplateAction.java @@ -0,0 +1,45 @@ +package com.nisovin.magicspells.util.config.dialog.action; + +import org.jetbrains.annotations.NotNull; + +import org.bukkit.configuration.ConfigurationSection; + +import io.papermc.paper.registry.data.dialog.action.DialogAction; + +import com.nisovin.magicspells.util.config.ConfigData; +import com.nisovin.magicspells.util.config.ConfigDataUtil; +import com.nisovin.magicspells.util.config.dialog.Debugger; +import com.nisovin.magicspells.util.config.dialog.BuildContext; +import com.nisovin.magicspells.util.config.dialog.NullConfigData; + +@SuppressWarnings("UnstableApiUsage") +public class CommandTemplateAction extends BuildContext { + + private final ConfigurationSection config; + private final String basePath; + private final String subPath; + + protected static NullConfigData create(Debugger debugger, ConfigurationSection config, String basePath, String subPath) { + return new CommandTemplateAction(debugger, config, basePath, subPath).build(); + } + + private CommandTemplateAction(Debugger debugger, ConfigurationSection config, String basePath, String subPath) { + super(debugger); + this.config = config; + this.basePath = basePath; + this.subPath = subPath; + } + + @Override + protected @NotNull NullConfigData build() { + ConfigData templateData = required(ConfigDataUtil::getString, config, basePath, subPath); + + return data -> { + String template = templateData.get(data); + if (template == null) return null; + + return DialogAction.commandTemplate(template); + }; + } + +} diff --git a/core/src/main/java/com/nisovin/magicspells/util/config/dialog/action/CustomClickAction.java b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/action/CustomClickAction.java new file mode 100644 index 000000000..3e7d2ff3c --- /dev/null +++ b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/action/CustomClickAction.java @@ -0,0 +1,158 @@ +package com.nisovin.magicspells.util.config.dialog.action; + +import java.util.List; + +import org.jetbrains.annotations.NotNull; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.nbt.api.BinaryTagHolder; +import net.kyori.adventure.text.event.ClickCallback; + +import org.bukkit.NamespacedKey; +import org.bukkit.entity.Player; +import org.bukkit.configuration.ConfigurationSection; + +import io.papermc.paper.dialog.DialogResponseView; +import io.papermc.paper.registry.data.dialog.action.DialogAction; + +import com.nisovin.magicspells.Subspell; +import com.nisovin.magicspells.MagicSpells; +import com.nisovin.magicspells.util.SpellData; +import com.nisovin.magicspells.variables.Variable; +import com.nisovin.magicspells.util.config.ConfigData; +import com.nisovin.magicspells.util.config.ConfigDataUtil; +import com.nisovin.magicspells.util.config.dialog.Debugger; +import com.nisovin.magicspells.util.config.dialog.BuildContext; +import com.nisovin.magicspells.util.config.dialog.NullConfigData; +import com.nisovin.magicspells.variables.variabletypes.GlobalStringVariable; +import com.nisovin.magicspells.variables.variabletypes.PlayerStringVariable; + +@SuppressWarnings("UnstableApiUsage") +public class CustomClickAction extends BuildContext { + + private static final ClickCallback.Options CLICK_OPTIONS = ClickCallback.Options.builder() + .uses(ClickCallback.UNLIMITED_USES) + .build(); + + private final ConfigurationSection config; + private final String path; + + protected static NullConfigData create(Debugger debugger, ConfigurationSection config, String basePath, String subPath) { + ConfigurationSection section = config.getConfigurationSection(subPath); + if (section == null) { + debugger.config("Missing '" + subPath + "' section at '" + basePath + "'."); + return _ -> null; + } + + String path = basePath + "." + subPath; + return new CustomClickAction(debugger, section, path).build(); + } + + private CustomClickAction(Debugger debugger, ConfigurationSection config, String path) { + super(debugger); + this.config = config; + this.path = path; + } + + @Override + protected @NotNull NullConfigData build() { + return switch (config.getString("type")) { + case "custom" -> customPayload(); + case "spell" -> customSpell(); + case null, default -> { + debugger.config("Invalid 'type' defined in '" + path + "'."); + yield _ -> null; + } + }; + } + + private NullConfigData customPayload() { + ConfigData idData = required(ConfigDataUtil::getNamespacedKey, config, path, "id"); + ConfigData additionsData = ConfigDataUtil.getString(config, "additions", null); + + return data -> { + Key id = idData.get(data); + if (id == null) return null; + + String additionsString = additionsData.get(data); + BinaryTagHolder additions = null; + if (additionsString != null) additions = BinaryTagHolder.binaryTagHolder(additionsString); + + return DialogAction.customClick(id, additions); + }; + } + + private NullConfigData customSpell() { + String spellName = config.getString("spell"); + String error = "Invalid 'spell' defined at '" + path + "': '" + spellName + "'."; + if (spellName == null) { + debugger.config(error); + return _ -> null; + } + Subspell spell = new Subspell(spellName); + if (!spell.process()) { + debugger.config(error); + return _ -> null; + } + + ConfigData> actions = configDataList(config, path, "variables", (config, path) -> { + ConfigData keyData = required(ConfigDataUtil::getString, config, path, "key"); + ConfigData variableData = required(ConfigDataUtil::getString, config, path, "variable"); + + return data -> { + String key = keyData.get(data); + String varName = variableData.get(data); + if (key == null || varName == null) return null; + + Variable variable = MagicSpells.getVariableManager().getVariable(varName); + if (variable == null) { + debugger.cast("Invalid 'variable' defined at '" + path + "'."); + return null; + } + + return new VariableAction(debugger, variable, varName, key); + }; + }); + + return data -> DialogAction.customClick((view, audience) -> { + if (!(audience instanceof Player player)) return; + + for (VariableAction action : actions.get(data)) { + action.set(view, player); + } + + spell.subcast(new SpellData(player)); + }, CLICK_OPTIONS); + } + + private record VariableAction(Debugger debugger, Variable variable, String variableName, String key) { + + private void set(DialogResponseView view, Player player) { + if (variable instanceof GlobalStringVariable || variable instanceof PlayerStringVariable) { + String text = view.getText(key); + if (text == null) { + Boolean bool = view.getBoolean(key); + if (bool == null) { + debugger.cast("String variable type '" + variableName + "' could not support data type of 'inputs' key '" + key + "'."); + return; + } + + text = bool.toString(); + } + + variable.parseAndSet(player, text); + return; + } + + Float value = view.getFloat(key); + if (value == null) { + debugger.cast("Numerical variable type '" + variableName + "' could not support data type of 'inputs' key '" + key + "'."); + return; + } + + variable.set(player, value); + } + + } + +} diff --git a/core/src/main/java/com/nisovin/magicspells/util/config/dialog/action/StaticAction.java b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/action/StaticAction.java new file mode 100644 index 000000000..fe95098bd --- /dev/null +++ b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/action/StaticAction.java @@ -0,0 +1,99 @@ +package com.nisovin.magicspells.util.config.dialog.action; + +import java.util.function.Function; + +import org.jetbrains.annotations.NotNull; + +import net.kyori.adventure.text.event.ClickEvent; + +import org.apache.commons.lang3.function.TriFunction; + +import org.bukkit.configuration.ConfigurationSection; + +import io.papermc.paper.dialog.Dialog; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.data.dialog.action.DialogAction; + +import com.nisovin.magicspells.util.config.ConfigData; +import com.nisovin.magicspells.util.config.ConfigDataUtil; +import com.nisovin.magicspells.util.config.dialog.Debugger; +import com.nisovin.magicspells.util.config.dialog.BuildContext; +import com.nisovin.magicspells.util.config.dialog.NullConfigData; + +@SuppressWarnings("UnstableApiUsage") +public class StaticAction extends BuildContext { + + private final ConfigurationSection config; + private final String path; + + protected static NullConfigData create(Debugger debugger, ConfigurationSection config, String basePath, String subPath) { + ConfigurationSection section = config.getConfigurationSection(subPath); + if (section == null) { + debugger.config("Missing '" + subPath + "' section at '" + basePath + "'."); + return _ -> null; + } + + String path = basePath + "." + subPath; + return new StaticAction(debugger, section, path).build(); + } + + private StaticAction(Debugger debugger, ConfigurationSection config, String path) { + super(debugger); + this.config = config; + this.path = path; + } + + @Override + protected @NotNull NullConfigData build() { + ConfigData eventData = switch (config.getString("type")) { + case "open_url" -> clickEventString(ClickEvent::openUrl, config, path, "url"); + case "run_command" -> clickEventString(ClickEvent::runCommand, config, path, "command"); + case "suggest_command" -> clickEventString(ClickEvent::suggestCommand, config, path, "command"); + case "copy_to_clipboard" -> clickEventString(ClickEvent::copyToClipboard, config, path, "text"); + //case "callback" -> ClickEvent#callback; // same as "custom_click" DialogAction + //case "custom" -> ClickEvent#custom; // same as "custom_click" DialogAction + case "show_dialog" -> clickEvent(this::configDataDialog, ClickEvent::showDialog, config, path, "dialog"); + case null, default -> { + debugger.config("Invalid 'type' defined in '" + path + "'."); + yield _ -> null; + } + }; + + return data -> { + ClickEvent event = eventData.get(data); + if (event == null) return null; + + return DialogAction.staticAction(event); + }; + } + + private ConfigData configDataDialog(ConfigurationSection config, String path, Dialog def) { + return ConfigDataUtil.getRegistryEntry(config, path, RegistryKey.DIALOG, def); + } + + private NullConfigData clickEventString( + Function clickEventFun, + ConfigurationSection config, + String basePath, + String subPath + ) { + return clickEvent(ConfigDataUtil::getString, clickEventFun, config, basePath, subPath); + } + + private NullConfigData clickEvent( + TriFunction> configDataFun, + Function clickEventFun, + ConfigurationSection config, + String basePath, + String subPath + ) { + ConfigData valData = required(configDataFun, config, basePath, subPath); + + return data -> { + T val = valData.get(data); + if (val == null) return null; + return clickEventFun.apply(val); + }; + } + +} diff --git a/core/src/main/java/com/nisovin/magicspells/util/config/dialog/base/BaseBuilder.java b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/base/BaseBuilder.java new file mode 100644 index 000000000..337c14e35 --- /dev/null +++ b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/base/BaseBuilder.java @@ -0,0 +1,148 @@ +package com.nisovin.magicspells.util.config.dialog.base; + +import java.util.List; + +import org.jetbrains.annotations.NotNull; + +import net.kyori.adventure.text.Component; + +import org.bukkit.configuration.ConfigurationSection; + +import com.nisovin.magicspells.util.config.ConfigData; +import com.nisovin.magicspells.util.magicitems.MagicItem; +import com.nisovin.magicspells.util.magicitems.MagicItems; +import com.nisovin.magicspells.util.config.ConfigDataUtil; +import com.nisovin.magicspells.util.config.dialog.Debugger; +import com.nisovin.magicspells.util.config.dialog.BuildContext; +import com.nisovin.magicspells.util.config.dialog.NullConfigData; + +import io.papermc.paper.registry.data.dialog.DialogBase; +import io.papermc.paper.registry.data.dialog.body.DialogBody; +import io.papermc.paper.registry.data.dialog.input.DialogInput; +import io.papermc.paper.registry.data.dialog.body.ItemDialogBody; +import io.papermc.paper.registry.data.dialog.body.PlainMessageDialogBody; +import io.papermc.paper.registry.data.dialog.DialogBase.DialogAfterAction; + +@SuppressWarnings("UnstableApiUsage") +public class BaseBuilder extends BuildContext { + + private static final String PATH = "base"; + + private final ConfigurationSection config; + + @NotNull + public static NullConfigData create(Debugger debugger, ConfigurationSection config) { + ConfigurationSection section = config.getConfigurationSection(PATH); + + if (section == null) { + debugger.config("Missing '" + PATH + "'."); + return _ -> null; + } + + return new BaseBuilder(debugger, section).build(); + } + + private BaseBuilder(Debugger debugger, ConfigurationSection config) { + super(debugger); + this.config = config; + } + + @Override + protected @NotNull NullConfigData build() { + ConfigData titleData = required(ConfigDataUtil::getComponent, config, PATH, "title"); + + ConfigData canCloseWithEscape = ConfigDataUtil.getBoolean(config, "can-close-with-escape", true); + ConfigData afterAction = ConfigDataUtil.getEnum(config, "after-action", DialogAfterAction.class, DialogAfterAction.CLOSE); + + ConfigData> body = configDataList(config, PATH, "body", (config, path) -> + switch (config.getString("type")) { + case "item" -> itemBody(config, path); + case "message" -> plainMessage(config, path); + case null, default -> { + debugger.config("Invalid 'type' defined at '" + path + "'."); + yield null; + } + } + ); + + ConfigData> inputs = configDataList(config, PATH, "inputs", (config, path) -> + switch (config.getString("type")) { + case "bool" -> BooleanInput.create(debugger, config, path); + case "number_range" -> NumberRangeInput.create(debugger, config, path); + case "single_option" -> SingleOptionInput.create(debugger, config, path); + case "text" -> TextInput.create(debugger, config, path); + case null, default -> { + debugger.config("Invalid 'type' defined at '" + path + "'."); + yield null; + } + } + ); + + return data -> { + Component title = titleData.get(data); + if (title == null) return null; + + return DialogBase.builder(title) + //.externalTitle() // Unused until you can add these dialogs to registry. + .canCloseWithEscape(canCloseWithEscape.get(data)) + .pause(false) + .afterAction(afterAction.get(data)) + .body(body.get(data)) + .inputs(inputs.get(data)) + .build(); + }; + } + + private NullConfigData plainMessage(ConfigurationSection config, String path) { + ConfigData messageData = required(ConfigDataUtil::getComponent, config, path, "message"); + ConfigData width = ConfigDataUtil.getInteger(config, "width", 200); + + return data -> { + Component message = messageData.get(data); + if (message == null) return null; + + try { + return DialogBody.plainMessage(message, width.get(data)); + } catch (IllegalArgumentException e) { + debugger.cast("Invalid 'width' defined at '" + path + "': " + e.getMessage()); + return null; + } + }; + } + + private NullConfigData itemBody(ConfigurationSection config, String path) { + MagicItem item = MagicItems.getMagicItemFromString(config.getString("item")); + if (item == null) { + debugger.config("Invalid 'item' defined at '" + path + "'."); + return null; + } + + ConfigData description = data -> { + ConfigurationSection section = config.getConfigurationSection("description"); + if (section == null) return null; + + return plainMessage(section, path + ".description").get(data); + }; + + ConfigData showDecorations = ConfigDataUtil.getBoolean(config, "show-decorations", true); + ConfigData showTooltip = ConfigDataUtil.getBoolean(config, "show-tooltip", true); + ConfigData width = ConfigDataUtil.getInteger(config, "width", 16); + ConfigData height = ConfigDataUtil.getInteger(config, "height", 16); + + return data -> { + try { + return DialogBody.item(item.getItemStack()) + .description(description.get(data)) + .showDecorations(showDecorations.get(data)) + .showTooltip(showTooltip.get(data)) + .width(width.get(data)) + .height(height.get(data)) + .build(); + } catch (IllegalArgumentException e) { + debugger.cast("Invalid '" + path + "' defined: " + e.getMessage()); + return null; + } + }; + } + +} diff --git a/core/src/main/java/com/nisovin/magicspells/util/config/dialog/base/BooleanInput.java b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/base/BooleanInput.java new file mode 100644 index 000000000..e52279aa2 --- /dev/null +++ b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/base/BooleanInput.java @@ -0,0 +1,56 @@ +package com.nisovin.magicspells.util.config.dialog.base; + +import org.jetbrains.annotations.NotNull; + +import net.kyori.adventure.text.Component; + +import org.bukkit.configuration.ConfigurationSection; + +import io.papermc.paper.registry.data.dialog.input.DialogInput; + +import com.nisovin.magicspells.util.config.ConfigData; +import com.nisovin.magicspells.util.config.ConfigDataUtil; +import com.nisovin.magicspells.util.config.dialog.Debugger; +import com.nisovin.magicspells.util.config.dialog.BuildContext; +import com.nisovin.magicspells.util.config.dialog.NullConfigData; + +@SuppressWarnings("UnstableApiUsage") +public class BooleanInput extends BuildContext { + + private final ConfigurationSection config; + private final String basePath; + + protected static NullConfigData create(Debugger debugger, ConfigurationSection config, String basePath) { + return new BooleanInput(debugger, config, basePath).build(); + } + + private BooleanInput(Debugger debugger, ConfigurationSection config, String basePath) { + super(debugger); + this.config = config; + this.basePath = basePath; + } + + @Override + protected @NotNull NullConfigData build() { + ConfigData keyData = required(ConfigDataUtil::getString, config, basePath, "key"); + ConfigData labelData = required(ConfigDataUtil::getComponent, config, basePath, "label"); + + ConfigData initial = ConfigDataUtil.getBoolean(config, "initial", false); + ConfigData onTrue = ConfigDataUtil.getString(config, "on-true", "true"); + ConfigData onFalse = ConfigDataUtil.getString(config, "on-false", "false"); + + return data -> { + String key = keyData.get(data); + Component label = labelData.get(data); + if (key == null || label == null) return null; + + try { + return DialogInput.bool(key, label, initial.get(data), onTrue.get(data), onFalse.get(data)); + } catch (IllegalArgumentException e) { + debugger.cast("Invalid '" + basePath + "': " + e.getMessage()); + return null; + } + }; + } + +} diff --git a/core/src/main/java/com/nisovin/magicspells/util/config/dialog/base/NumberRangeInput.java b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/base/NumberRangeInput.java new file mode 100644 index 000000000..21e5a4335 --- /dev/null +++ b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/base/NumberRangeInput.java @@ -0,0 +1,68 @@ +package com.nisovin.magicspells.util.config.dialog.base; + +import org.jetbrains.annotations.NotNull; + +import net.kyori.adventure.text.Component; + +import org.bukkit.configuration.ConfigurationSection; + +import io.papermc.paper.registry.data.dialog.input.DialogInput; + +import com.nisovin.magicspells.util.config.ConfigData; +import com.nisovin.magicspells.util.config.ConfigDataUtil; +import com.nisovin.magicspells.util.config.dialog.Debugger; +import com.nisovin.magicspells.util.config.dialog.BuildContext; +import com.nisovin.magicspells.util.config.dialog.NullConfigData; + +@SuppressWarnings("UnstableApiUsage") +public class NumberRangeInput extends BuildContext { + + private final ConfigurationSection config; + private final String basePath; + + protected static NullConfigData create(Debugger debugger, ConfigurationSection config, String basePath) { + return new NumberRangeInput(debugger, config, basePath).build(); + } + + private NumberRangeInput(Debugger debugger, ConfigurationSection config, String basePath) { + super(debugger); + this.config = config; + this.basePath = basePath; + } + + @Override + protected @NotNull NullConfigData build() { + ConfigData keyData = required(ConfigDataUtil::getString, config, basePath, "key"); + ConfigData labelData = required(ConfigDataUtil::getComponent, config, basePath, "label"); + + ConfigData width = ConfigDataUtil.getInteger(config, "width", 200); + ConfigData labelFormat = ConfigDataUtil.getString(config, "label-format", "options.generic_value"); + ConfigData start = ConfigDataUtil.getFloat(config, "start", 0); + ConfigData end = ConfigDataUtil.getFloat(config, "end", 0); + ConfigData initial = ConfigDataUtil.getFloat(config, "initial", 0); + ConfigData step = ConfigDataUtil.getFloat(config, "step", 1); + + return data -> { + String key = keyData.get(data); + Component label = labelData.get(data); + if (key == null || label == null) return null; + + try { + return DialogInput.numberRange( + key, + width.get(data), + label, + labelFormat.get(data), + start.get(data), + end.get(data), + initial.get(data), + step.get(data) + ); + } catch (IllegalArgumentException e) { + debugger.cast("Invalid '" + basePath + "': " + e.getMessage()); + return null; + } + }; + } + +} diff --git a/core/src/main/java/com/nisovin/magicspells/util/config/dialog/base/SingleOptionInput.java b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/base/SingleOptionInput.java new file mode 100644 index 000000000..44ee60259 --- /dev/null +++ b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/base/SingleOptionInput.java @@ -0,0 +1,77 @@ +package com.nisovin.magicspells.util.config.dialog.base; + +import java.util.List; + +import org.jetbrains.annotations.NotNull; + +import net.kyori.adventure.text.Component; + +import org.bukkit.configuration.ConfigurationSection; + +import com.nisovin.magicspells.util.config.ConfigData; +import com.nisovin.magicspells.util.config.ConfigDataUtil; +import com.nisovin.magicspells.util.config.dialog.Debugger; +import com.nisovin.magicspells.util.config.dialog.BuildContext; +import com.nisovin.magicspells.util.config.dialog.NullConfigData; + +import io.papermc.paper.registry.data.dialog.input.DialogInput; +import io.papermc.paper.registry.data.dialog.input.SingleOptionDialogInput; + +@SuppressWarnings("UnstableApiUsage") +public class SingleOptionInput extends BuildContext { + + private final ConfigurationSection config; + private final String basePath; + + protected static NullConfigData create(Debugger debugger, ConfigurationSection config, String basePath) { + return new SingleOptionInput(debugger, config, basePath).build(); + } + + private SingleOptionInput(Debugger debugger, ConfigurationSection config, String basePath) { + super(debugger); + this.config = config; + this.basePath = basePath; + } + + @Override + protected @NotNull NullConfigData build() { + ConfigData keyData = required(ConfigDataUtil::getString, config, basePath, "key"); + ConfigData labelData = required(ConfigDataUtil::getComponent, config, basePath, "label"); + + ConfigData width = ConfigDataUtil.getInteger(config, "width", 200); + ConfigData labelVisible = ConfigDataUtil.getBoolean(config, "label-visible", true); + + ConfigData> entriesData = configDataList(config, basePath, "entries", (section, basePath) -> { + ConfigData idData = required(ConfigDataUtil::getString, section, basePath, "id"); + ConfigData display = ConfigDataUtil.getComponent(section, "display", null); + ConfigData initial = ConfigDataUtil.getBoolean(section, "initial", false); + + return data -> { + String id = idData.get(data); + if (id == null) return null; + + return SingleOptionDialogInput.OptionEntry.create(id, display.get(data), initial.get(data)); + }; + }); + + return data -> { + String key = keyData.get(data); + Component label = labelData.get(data); + if (key == null || label == null) return null; + + List entries = entriesData.get(data); + if (entries.isEmpty()) { + debugger.cast("Empty 'entries' list found at '" + basePath + "'."); + return null; + } + + try { + return DialogInput.singleOption(key, width.get(data), entries, label, labelVisible.get(data)); + } catch (IllegalArgumentException e) { + debugger.cast("Invalid '" + basePath + "': " + e.getMessage()); + return null; + } + }; + } + +} diff --git a/core/src/main/java/com/nisovin/magicspells/util/config/dialog/base/TextInput.java b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/base/TextInput.java new file mode 100644 index 000000000..b5533c306 --- /dev/null +++ b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/base/TextInput.java @@ -0,0 +1,82 @@ +package com.nisovin.magicspells.util.config.dialog.base; + +import org.jetbrains.annotations.NotNull; + +import net.kyori.adventure.text.Component; + +import org.bukkit.configuration.ConfigurationSection; + +import io.papermc.paper.registry.data.dialog.input.DialogInput; +import io.papermc.paper.registry.data.dialog.input.TextDialogInput; + +import com.nisovin.magicspells.util.config.ConfigData; +import com.nisovin.magicspells.util.config.ConfigDataUtil; +import com.nisovin.magicspells.util.config.dialog.Debugger; +import com.nisovin.magicspells.util.config.dialog.BuildContext; +import com.nisovin.magicspells.util.config.dialog.NullConfigData; + +@SuppressWarnings("UnstableApiUsage") +public class TextInput extends BuildContext { + + private final ConfigurationSection config; + private final String basePath; + + protected static NullConfigData create(Debugger debugger, ConfigurationSection config, String basePath) { + return new TextInput(debugger, config, basePath).build(); + } + + private TextInput(Debugger debugger, ConfigurationSection config, String basePath) { + super(debugger); + this.config = config; + this.basePath = basePath; + } + + @Override + protected @NotNull NullConfigData build() { + ConfigData keyData = required(ConfigDataUtil::getString, config, basePath, "key"); + ConfigData labelData = required(ConfigDataUtil::getComponent, config, basePath, "label"); + + ConfigData width = ConfigDataUtil.getInteger(config, "width", 200); + ConfigData labelVisible = ConfigDataUtil.getBoolean(config, "label-visible", true); + ConfigData initial = ConfigDataUtil.getString(config, "initial", ""); + ConfigData maxLength = ConfigDataUtil.getInteger(config, "max-length", 32); + + ConfigData multilineOptions = data -> { + ConfigurationSection section = config.getConfigurationSection("multiline-options"); + if (section == null) return null; + + ConfigData maxLines = ConfigDataUtil.getInteger(section, "max-lines", _ -> null); + ConfigData height = ConfigDataUtil.getInteger(section, "height", _ -> null); + + try { + return TextDialogInput.MultilineOptions.create(maxLines.get(data), height.get(data)); + } catch (IllegalArgumentException e) { + debugger.cast("Invalid 'multiline-options' at '" + basePath + "': " + e.getMessage()); + return null; + } + }; + + return data -> { + String key = keyData.get(data); + Component label = labelData.get(data); + TextDialogInput.MultilineOptions options = multilineOptions.get(data); + if (key == null || label == null || options == null) return null; + + try { + return DialogInput.text( + key, + width.get(data), + label, + labelVisible.get(data), + initial.get(data), + maxLength.get(data), + options + ); + } catch (IllegalArgumentException e) { + debugger.cast("Invalid '" + basePath + "': " + e.getMessage()); + return null; + } + }; + } + +} diff --git a/core/src/main/java/com/nisovin/magicspells/util/config/dialog/types/ConfirmationBuilder.java b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/types/ConfirmationBuilder.java new file mode 100644 index 000000000..0c7cc7a2e --- /dev/null +++ b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/types/ConfirmationBuilder.java @@ -0,0 +1,49 @@ +package com.nisovin.magicspells.util.config.dialog.types; + +import org.jetbrains.annotations.NotNull; + +import org.bukkit.configuration.ConfigurationSection; + +import io.papermc.paper.registry.data.dialog.ActionButton; +import io.papermc.paper.registry.data.dialog.type.DialogType; + +import com.nisovin.magicspells.util.config.ConfigData; +import com.nisovin.magicspells.util.config.dialog.Debugger; +import com.nisovin.magicspells.util.config.dialog.BuildContext; +import com.nisovin.magicspells.util.config.dialog.NullConfigData; +import com.nisovin.magicspells.util.config.dialog.action.ActionButtonBuilder; + +@SuppressWarnings("UnstableApiUsage") +public class ConfirmationBuilder extends BuildContext { + + private final ConfigurationSection config; + private final String basePath; + + protected static NullConfigData create(Debugger debugger, ConfigurationSection config, String basePath) { + return new ConfirmationBuilder(debugger, config, basePath).build(); + } + + private ConfirmationBuilder(Debugger debugger, ConfigurationSection config, String basePath) { + super(debugger); + this.config = config; + this.basePath = basePath; + } + + @Override + protected @NotNull NullConfigData build() { + ConfigurationSection yesSection = config.getConfigurationSection("button-yes"); + ConfigurationSection noSection = config.getConfigurationSection("button-no"); + + ConfigData yesData = ActionButtonBuilder.createRequired(debugger, yesSection, basePath, "button-yes"); + ConfigData noData = ActionButtonBuilder.createRequired(debugger, noSection, basePath, "button-no"); + + return data -> { + ActionButton yes = yesData.get(data); + ActionButton no = noData.get(data); + if (yes == null || no == null) return null; + + return DialogType.confirmation(yes, no); + }; + } + +} diff --git a/core/src/main/java/com/nisovin/magicspells/util/config/dialog/types/DialogListBuilder.java b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/types/DialogListBuilder.java new file mode 100644 index 000000000..dbeec45fa --- /dev/null +++ b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/types/DialogListBuilder.java @@ -0,0 +1,103 @@ +package com.nisovin.magicspells.util.config.dialog.types; + +import java.util.List; +import java.util.ArrayList; + +import org.jetbrains.annotations.NotNull; + +import org.bukkit.Registry; +import org.bukkit.NamespacedKey; +import org.bukkit.configuration.ConfigurationSection; + +import io.papermc.paper.dialog.Dialog; +import io.papermc.paper.registry.TypedKey; +import io.papermc.paper.registry.tag.TagKey; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.registry.RegistryAccess; +import io.papermc.paper.registry.set.RegistrySet; +import io.papermc.paper.registry.set.RegistryKeySet; +import io.papermc.paper.registry.data.dialog.ActionButton; +import io.papermc.paper.registry.data.dialog.type.DialogType; + +import com.nisovin.magicspells.util.config.ConfigData; +import com.nisovin.magicspells.util.config.ConfigDataUtil; +import com.nisovin.magicspells.util.config.dialog.Debugger; +import com.nisovin.magicspells.util.config.dialog.BuildContext; +import com.nisovin.magicspells.util.config.dialog.NullConfigData; +import com.nisovin.magicspells.util.config.dialog.action.ActionButtonBuilder; + +@SuppressWarnings("UnstableApiUsage") +public class DialogListBuilder extends BuildContext { + + private final ConfigurationSection config; + private final String basePath; + + protected static NullConfigData create(Debugger debugger, ConfigurationSection config, String basePath) { + return new DialogListBuilder(debugger, config, basePath).build(); + } + + private DialogListBuilder(Debugger debugger, ConfigurationSection config, String basePath) { + super(debugger); + this.config = config; + this.basePath = basePath; + } + + @Override + protected @NotNull NullConfigData build() { + RegistryKeySet dialogs = getDialogKeySet(); + + ConfigurationSection exitSection = config.getConfigurationSection("exit"); + NullConfigData exit = ActionButtonBuilder.createOptional(debugger, exitSection, basePath, "exit"); + + ConfigData columns = ConfigDataUtil.getInteger(config, "columns", 2); + ConfigData buttonWidth = ConfigDataUtil.getInteger(config, "button-width", 150); + + return data -> { + try { + return DialogType.dialogList(dialogs, exit.get(data), columns.get(data), buttonWidth.get(data)); + } catch (IllegalArgumentException e) { + debugger.cast("Invalid '" + basePath + "': " + e.getMessage()); + return null; + } + }; + } + + private RegistryKeySet getDialogKeySet() { + List> keys = new ArrayList<>(); + Registry registry = RegistryAccess.registryAccess().getRegistry(RegistryKey.DIALOG); + + List strings = config.getStringList("dialogs"); + for (int i = 0; i < strings.size(); i++) { + String string = strings.get(i); + String subPath = "dialogs[" + i + "]"; + + if (!string.startsWith("#")) { + NamespacedKey key = NamespacedKey.fromString(string); + if (key == null || registry.get(key) == null) { + debugger.config("Invalid '%s' registry entry at '%s': '%s'".formatted(subPath, basePath, string)); + continue; + } + + keys.add(TypedKey.create(RegistryKey.DIALOG, key)); + continue; + } + + NamespacedKey key = NamespacedKey.fromString(string.substring(1)); + if (key == null) { + debugger.config("Invalid '%s' key at '%s': '%s'".formatted(subPath, basePath, string)); + continue; + } + + TagKey tagKey = TagKey.create(RegistryKey.DIALOG, key); + if (!registry.hasTag(tagKey)) { + debugger.config("Invalid '%s' key at '%s': '%s'".formatted(subPath, basePath, string)); + continue; + } + + keys.addAll(registry.getTag(tagKey).values()); + } + + return RegistrySet.keySet(RegistryKey.DIALOG, keys); + } + +} diff --git a/core/src/main/java/com/nisovin/magicspells/util/config/dialog/types/MultiActionBuilder.java b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/types/MultiActionBuilder.java new file mode 100644 index 000000000..ee00438fd --- /dev/null +++ b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/types/MultiActionBuilder.java @@ -0,0 +1,62 @@ +package com.nisovin.magicspells.util.config.dialog.types; + +import java.util.List; + +import org.jetbrains.annotations.NotNull; + +import org.bukkit.configuration.ConfigurationSection; + +import io.papermc.paper.registry.data.dialog.ActionButton; +import io.papermc.paper.registry.data.dialog.type.DialogType; + +import com.nisovin.magicspells.util.config.ConfigData; +import com.nisovin.magicspells.util.config.ConfigDataUtil; +import com.nisovin.magicspells.util.config.dialog.Debugger; +import com.nisovin.magicspells.util.config.dialog.BuildContext; +import com.nisovin.magicspells.util.config.dialog.NullConfigData; +import com.nisovin.magicspells.util.config.dialog.action.ActionButtonBuilder; + +@SuppressWarnings("UnstableApiUsage") +public class MultiActionBuilder extends BuildContext { + + private final ConfigurationSection config; + private final String basePath; + + protected static NullConfigData create(Debugger debugger, ConfigurationSection config, String basePath) { + return new MultiActionBuilder(debugger, config, basePath).build(); + } + + private MultiActionBuilder(Debugger debugger, ConfigurationSection config, String basePath) { + super(debugger); + this.config = config; + this.basePath = basePath; + } + + @Override + protected @NotNull NullConfigData build() { + ConfigData> actionsData = configDataList(config, basePath, "actions", (config, path) -> + ActionButtonBuilder.createOptional(debugger, config, basePath, path) + ); + + ConfigurationSection exitSection = config.getConfigurationSection("exit"); + NullConfigData exit = ActionButtonBuilder.createOptional(debugger, exitSection, basePath, "exit"); + + ConfigData columns = ConfigDataUtil.getInteger(config, "columns", 2); + + return data -> { + List actions = actionsData.get(data); + if (actions.isEmpty()) { + debugger.cast("Empty 'actions' list found at '" + basePath + "'."); + return null; + } + + try { + return DialogType.multiAction(actions, exit.get(data), columns.get(data)); + } catch (IllegalArgumentException e) { + debugger.cast("Invalid '" + basePath + "': " + e.getMessage()); + return null; + } + }; + } + +} diff --git a/core/src/main/java/com/nisovin/magicspells/util/config/dialog/types/NoticeBuilder.java b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/types/NoticeBuilder.java new file mode 100644 index 000000000..16fcec8b0 --- /dev/null +++ b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/types/NoticeBuilder.java @@ -0,0 +1,42 @@ +package com.nisovin.magicspells.util.config.dialog.types; + +import org.jetbrains.annotations.NotNull; + +import org.bukkit.configuration.ConfigurationSection; + +import io.papermc.paper.registry.data.dialog.ActionButton; +import io.papermc.paper.registry.data.dialog.type.DialogType; + +import com.nisovin.magicspells.util.config.dialog.Debugger; +import com.nisovin.magicspells.util.config.dialog.BuildContext; +import com.nisovin.magicspells.util.config.dialog.NullConfigData; +import com.nisovin.magicspells.util.config.dialog.action.ActionButtonBuilder; + +@SuppressWarnings("UnstableApiUsage") +public class NoticeBuilder extends BuildContext { + + private final ConfigurationSection config; + private final String basePath; + + protected static NullConfigData create(Debugger debugger, ConfigurationSection config, String basePath) { + return new NoticeBuilder(debugger, config, basePath).build(); + } + + private NoticeBuilder(Debugger debugger, ConfigurationSection config, String basePath) { + super(debugger); + this.config = config; + this.basePath = basePath; + } + + @Override + protected @NotNull NullConfigData build() { + ConfigurationSection buttonSection = config.getConfigurationSection("button"); + NullConfigData buttonData = ActionButtonBuilder.createOptional(debugger, buttonSection, basePath, "button"); + + return data -> { + ActionButton button = buttonData.get(data); + return button == null ? DialogType.notice() : DialogType.notice(button); + }; + } + +} diff --git a/core/src/main/java/com/nisovin/magicspells/util/config/dialog/types/ServerListTypeBuilder.java b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/types/ServerListTypeBuilder.java new file mode 100644 index 000000000..d55a3b74c --- /dev/null +++ b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/types/ServerListTypeBuilder.java @@ -0,0 +1,51 @@ +package com.nisovin.magicspells.util.config.dialog.types; + +import org.jetbrains.annotations.NotNull; + +import org.bukkit.configuration.ConfigurationSection; + +import io.papermc.paper.registry.data.dialog.ActionButton; +import io.papermc.paper.registry.data.dialog.type.DialogType; + +import com.nisovin.magicspells.util.config.ConfigData; +import com.nisovin.magicspells.util.config.ConfigDataUtil; +import com.nisovin.magicspells.util.config.dialog.Debugger; +import com.nisovin.magicspells.util.config.dialog.BuildContext; +import com.nisovin.magicspells.util.config.dialog.NullConfigData; +import com.nisovin.magicspells.util.config.dialog.action.ActionButtonBuilder; + +@SuppressWarnings("UnstableApiUsage") +public class ServerListTypeBuilder extends BuildContext { + + private final ConfigurationSection config; + private final String basePath; + + protected static NullConfigData create(Debugger debugger, ConfigurationSection config, String basePath) { + return new ServerListTypeBuilder(debugger, config, basePath).build(); + } + + private ServerListTypeBuilder(Debugger debugger, ConfigurationSection config, String basePath) { + super(debugger); + this.config = config; + this.basePath = basePath; + } + + @Override + protected @NotNull NullConfigData build() { + ConfigurationSection exitSection = config.getConfigurationSection("exit"); + NullConfigData exit = ActionButtonBuilder.createOptional(debugger, exitSection, basePath, "exit"); + + ConfigData columns = ConfigDataUtil.getInteger(config, "columns", 2); + ConfigData buttonWidth = ConfigDataUtil.getInteger(config, "button-width", 150); + + return data -> { + try { + return DialogType.serverLinks(exit.get(data), columns.get(data), buttonWidth.get(data)); + } catch (IllegalArgumentException e) { + debugger.cast("Invalid '" + basePath + "': " + e.getMessage()); + return null; + } + }; + } + +} diff --git a/core/src/main/java/com/nisovin/magicspells/util/config/dialog/types/TypeBuilder.java b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/types/TypeBuilder.java new file mode 100644 index 000000000..7c0fe7a07 --- /dev/null +++ b/core/src/main/java/com/nisovin/magicspells/util/config/dialog/types/TypeBuilder.java @@ -0,0 +1,51 @@ +package com.nisovin.magicspells.util.config.dialog.types; + +import org.jetbrains.annotations.NotNull; + +import org.bukkit.configuration.ConfigurationSection; + +import io.papermc.paper.registry.data.dialog.type.DialogType; + +import com.nisovin.magicspells.util.config.dialog.Debugger; +import com.nisovin.magicspells.util.config.dialog.BuildContext; +import com.nisovin.magicspells.util.config.dialog.NullConfigData; + +@SuppressWarnings("UnstableApiUsage") +public class TypeBuilder extends BuildContext { + + private static final String PATH = "dialog-type"; + + private final ConfigurationSection config; + + public static NullConfigData create(Debugger debugger, ConfigurationSection config) { + ConfigurationSection section = config.getConfigurationSection(PATH); + + if (section == null) { + debugger.config("Missing '" + PATH + "'."); + return _ -> null; + } + + return new TypeBuilder(debugger, section).build(); + } + + private TypeBuilder(Debugger debugger, ConfigurationSection config) { + super(debugger); + this.config = config; + } + + @Override + protected @NotNull NullConfigData build() { + return switch (config.getString("type")) { + case "confirmation" -> ConfirmationBuilder.create(debugger, config, PATH); + case "dialog_list" -> DialogListBuilder.create(debugger, config, PATH); + case "multi_action" -> MultiActionBuilder.create(debugger, config, PATH); + case "notice" -> NoticeBuilder.create(debugger, config, PATH); + case "server_links" -> ServerListTypeBuilder.create(debugger, config, PATH); + case null, default -> { + debugger.config("Invalid 'type' defined in '" + PATH + "'."); + yield _ -> null; + } + }; + } + +}