From a8fdaea2755cc875c65dd12149f47ef626685bf2 Mon Sep 17 00:00:00 2001 From: BlitzOffline <52609756+BlitzOffline@users.noreply.github.com> Date: Fri, 14 Mar 2025 23:19:50 +0200 Subject: [PATCH 1/9] Commiting to not lose stuff. Should've done this a long time ago --- .../deluxemenus/action/ClickActionTask.java | 12 +- .../command/DeluxeMenusCommand.java | 4 +- .../command/subcommand/ExecuteCommand.java | 19 +- .../command/subcommand/HelpCommand.java | 13 +- .../command/subcommand/ListCommand.java | 51 +- .../command/subcommand/MetaCommand.java | 563 ++++++++++++++++ .../command/subcommand/SubCommand.java | 26 + .../deluxemenus/config/GeneralConfig.java | 7 + .../persistentmeta/PersistentMetaHandler.java | 618 ++++++++++++------ .../deluxemenus/placeholder/Expansion.java | 75 ++- .../requirement/HasMetaRequirement.java | 107 +-- .../deluxemenus/utils/DumpUtils.java | 2 +- .../deluxemenus/utils/Messages.java | 302 ++++++--- .../deluxemenus/utils/PaginationUtils.java | 44 ++ .../extendedclip/deluxemenus/utils/Pair.java | 36 + 15 files changed, 1462 insertions(+), 417 deletions(-) create mode 100644 src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java create mode 100644 src/main/java/com/extendedclip/deluxemenus/utils/PaginationUtils.java create mode 100644 src/main/java/com/extendedclip/deluxemenus/utils/Pair.java diff --git a/src/main/java/com/extendedclip/deluxemenus/action/ClickActionTask.java b/src/main/java/com/extendedclip/deluxemenus/action/ClickActionTask.java index ad2eb90e..0c4a6052 100644 --- a/src/main/java/com/extendedclip/deluxemenus/action/ClickActionTask.java +++ b/src/main/java/com/extendedclip/deluxemenus/action/ClickActionTask.java @@ -80,14 +80,10 @@ public void run() { plugin.debug(DebugLevel.HIGHEST, Level.INFO, "Meta action not supported on this server version."); break; } - try { - final boolean result = plugin.getPersistentMetaHandler().setMeta(player, executable); - if (!result) { - plugin.debug(DebugLevel.HIGHEST, Level.INFO, "Invalid meta action! Make sure you have the right syntax."); - break; - } - } catch (final NumberFormatException exception) { - plugin.debug(DebugLevel.HIGHEST, Level.INFO, "Invalid integer value for meta action!"); + final boolean success = plugin.getPersistentMetaHandler().parseAndExecuteMetaActionFromString(player, executable); + if (!success) { + plugin.debug(DebugLevel.HIGHEST, Level.INFO, "Invalid meta action! Make sure you have the right syntax."); + break; } break; diff --git a/src/main/java/com/extendedclip/deluxemenus/command/DeluxeMenusCommand.java b/src/main/java/com/extendedclip/deluxemenus/command/DeluxeMenusCommand.java index 161d4de4..16d405b2 100644 --- a/src/main/java/com/extendedclip/deluxemenus/command/DeluxeMenusCommand.java +++ b/src/main/java/com/extendedclip/deluxemenus/command/DeluxeMenusCommand.java @@ -5,10 +5,10 @@ import com.extendedclip.deluxemenus.command.subcommand.ExecuteCommand; import com.extendedclip.deluxemenus.command.subcommand.HelpCommand; import com.extendedclip.deluxemenus.command.subcommand.ListCommand; +import com.extendedclip.deluxemenus.command.subcommand.MetaCommand; import com.extendedclip.deluxemenus.command.subcommand.OpenCommand; import com.extendedclip.deluxemenus.command.subcommand.ReloadCommand; import com.extendedclip.deluxemenus.command.subcommand.SubCommand; -import com.extendedclip.deluxemenus.utils.DebugLevel; import com.extendedclip.deluxemenus.utils.Messages; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextReplacementConfig; @@ -24,7 +24,6 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.logging.Level; import java.util.stream.Collectors; import static net.kyori.adventure.text.Component.text; @@ -101,6 +100,7 @@ private void registerSubCommands() { new ExecuteCommand(plugin), new HelpCommand(plugin), new ListCommand(plugin), + new MetaCommand(plugin), new OpenCommand(plugin), new ReloadCommand(plugin) ); diff --git a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/ExecuteCommand.java b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/ExecuteCommand.java index 94e02cc7..c1ce0da1 100644 --- a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/ExecuteCommand.java +++ b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/ExecuteCommand.java @@ -133,23 +133,6 @@ public void execute(final @NotNull CommandSender sender, final @NotNull List onlinePlayerNames = Bukkit.getOnlinePlayers() - .stream() - .map(Player::getName) - .collect(Collectors.toList()); - - if (onlinePlayerNames.isEmpty()) { - return null; - } - - final String secondArgument = arguments.get(1).toLowerCase(); - - if (secondArgument.isEmpty()) { - return onlinePlayerNames; - } - - return onlinePlayerNames.stream() - .filter(playerName -> playerName.toLowerCase().startsWith(secondArgument)) - .collect(Collectors.toList()); + return getPlayerNameCompletion(arguments.get(1)); } } diff --git a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/HelpCommand.java b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/HelpCommand.java index 9b567de4..977573dd 100644 --- a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/HelpCommand.java +++ b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/HelpCommand.java @@ -23,16 +23,25 @@ public HelpCommand(final @NotNull DeluxeMenus plugin) { @Override public void execute(final @NotNull CommandSender sender, final @NotNull List arguments) { + if (sender.isOp()) { + plugin.sms(sender, Messages.HELP_OP); + return; + } + if (sender.hasPermission(ADMIN_PERMISSION)) { - plugin.sms(sender, Messages.HELP_ADMIN); + plugin.sms(sender, Messages.HELP); return; } - plugin.sms(sender, Messages.HELP); + plugin.sms(sender, Messages.NO_PERMISSION); } @Override public @Nullable List onTabComplete(final @NotNull CommandSender sender, final @NotNull List arguments) { + if (!sender.hasPermission(ADMIN_PERMISSION)) { + return null; + } + if (arguments.size() > 1) { return null; } diff --git a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/ListCommand.java b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/ListCommand.java index 28534c86..92a1a89f 100644 --- a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/ListCommand.java +++ b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/ListCommand.java @@ -3,7 +3,7 @@ import com.extendedclip.deluxemenus.DeluxeMenus; import com.extendedclip.deluxemenus.menu.Menu; import com.extendedclip.deluxemenus.utils.Messages; -import com.google.common.primitives.Ints; +import com.extendedclip.deluxemenus.utils.PaginationUtils; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.event.ClickEvent; @@ -32,7 +32,7 @@ public class ListCommand extends SubCommand { private static final String LIST_PERMISSION = "deluxemenus.list"; - public ListCommand(final @NotNull DeluxeMenus plugin) { + public ListCommand(@NotNull final DeluxeMenus plugin) { super(plugin); } @@ -42,7 +42,7 @@ public ListCommand(final @NotNull DeluxeMenus plugin) { } @Override - public void execute(final @NotNull CommandSender sender, final @NotNull List arguments) { + public void execute(@NotNull final CommandSender sender, @NotNull final List arguments) { if (!sender.hasPermission(LIST_PERMISSION)) { plugin.sms(sender, Messages.NO_PERMISSION); return; @@ -71,7 +71,7 @@ public void execute(final @NotNull CommandSender sender, final @NotNull List onTabComplete(final @NotNull CommandSender sender, final @NotNull List arguments) { + public @Nullable List onTabComplete(@NotNull final CommandSender sender, @NotNull final List arguments) { if (!sender.hasPermission(LIST_PERMISSION)) { return null; } @@ -124,7 +124,7 @@ public void execute(final @NotNull CommandSender sender, final @NotNull List menus) { + private void sendSimpleMenuList(@NotNull final CommandSender sender, @NotNull final Collection menus) { final TextComponent.Builder list = text(); list.append(text("The following " + menus.size() + " menus are loaded on the server:", NamedTextColor.GOLD).append(newline())); @@ -156,37 +156,32 @@ private void sendSimpleMenuList(final @NotNull CommandSender sender, final @NotN plugin.sms(sender, list.build()); } - private void sendPaginatedMenuList(final @NotNull CommandSender sender, final @NotNull Map> menus, - final @NotNull List configMenus, final @NotNull List args) { - final int totalMenusCount = configMenus.size() + menus.values().stream().mapToInt(List::size).sum(); - - Integer page = null; - if (totalMenusCount > plugin.getGeneralConfig().menusListPageSize() && !args.isEmpty()) { - page = Ints.tryParse(args.get(0)); - } + private void sendPaginatedMenuList(@NotNull final CommandSender sender, @NotNull final Map> menus, + @NotNull final List configMenus, @NotNull final List args) { - final int maxPages = (int) Math.ceil((double) totalMenusCount / plugin.getGeneralConfig().menusListPageSize()); - - if (page == null || page < 1) { - page = 1; - } + final int menusPerPage = plugin.getGeneralConfig().menusListPageSize(); + final int totalMenusCount = configMenus.size() + menus.values().stream().mapToInt(List::size).sum(); + final int pagesCount = PaginationUtils.getPagesCount(menusPerPage, totalMenusCount); - if (page > maxPages) { - page = maxPages; - } + final int page = PaginationUtils.parsePage( + menusPerPage, + totalMenusCount, + pagesCount, + args.isEmpty() ? null : args.get(0) + ); final Map> paginatedMenus = getPaginatedMenus( menus, configMenus.stream().collect(TreeMap::new, (map, menu) -> map.put(menu.options().name(), menu), TreeMap::putAll), page, - plugin.getGeneralConfig().menusListPageSize() + menusPerPage ); final int pageMenusCount = paginatedMenus.values().stream().mapToInt(List::size).sum(); final Map pageMenusTree = convertMenusToTree(paginatedMenus); final TextComponent.Builder list = text(); - list.append(text("Page " + page + "/" + maxPages + " - " + pageMenusCount + " menus:", NamedTextColor.GOLD).append(newline())); + list.append(text("Page " + page + "/" + pagesCount + " - " + pageMenusCount + " menus:", NamedTextColor.GOLD).append(newline())); if (sender instanceof ConsoleCommandSender) { final var menuList = createMenuListForConsole(pageMenusTree, 0); @@ -203,7 +198,7 @@ private void sendPaginatedMenuList(final @NotNull CommandSender sender, final @N list.append(menuList); - if (page > 1 || page < maxPages) { + if (page > 1 || page < pagesCount) { list.append(newline()); if (page > 1) { @@ -214,12 +209,12 @@ private void sendPaginatedMenuList(final @NotNull CommandSender sender, final @N .append(text("Executes: /dm list " + (page - 1), NamedTextColor.GRAY)) )) .clickEvent(ClickEvent.runCommand("/dm list " + (page - 1)))); - if (page < maxPages) { + if (page < pagesCount) { list.append(text(" | ", NamedTextColor.GREEN)); } } - if (page < maxPages) { + if (page < pagesCount) { list.append(text("Next >>", NamedTextColor.GOLD) .hoverEvent(HoverEvent.showText( text("Click to go to the next page", NamedTextColor.GRAY) @@ -234,7 +229,7 @@ private void sendPaginatedMenuList(final @NotNull CommandSender sender, final @N } private Map> getPaginatedMenus(final Map> menus, - final @NotNull Map configMenus, + @NotNull final Map configMenus, final int page, final int pageSize ) { @@ -367,7 +362,7 @@ private void addMenuToTreeRecursively(final Map tree, final List * If the config option to use admin commands in menus list is enabled, the admin "/dm open" command will be returned. * @return The command that can be used to open this menu. */ - public @Nullable String getMenuDisplayCommand(final @NotNull Menu menu) { + public @Nullable String getMenuDisplayCommand(@NotNull final Menu menu) { final boolean useAdminCommand = this.plugin.getGeneralConfig().useAdminCommandsInMenusList(); if (useAdminCommand) { diff --git a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java new file mode 100644 index 00000000..b98d728e --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java @@ -0,0 +1,563 @@ +package com.extendedclip.deluxemenus.command.subcommand; + +import com.extendedclip.deluxemenus.DeluxeMenus; +import com.extendedclip.deluxemenus.persistentmeta.PersistentMetaHandler; +import com.extendedclip.deluxemenus.utils.Messages; +import com.extendedclip.deluxemenus.utils.PaginationUtils; +import com.extendedclip.deluxemenus.utils.VersionHelper; +import com.google.common.primitives.Doubles; +import com.google.common.primitives.Longs; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.NamespacedKey; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.persistence.PersistentDataType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.TreeMap; +import java.util.stream.Collectors; + +import static net.kyori.adventure.text.Component.newline; +import static net.kyori.adventure.text.Component.text; + +// TODO: +// Test the command execution +// Test the tab completion +// Improve error messages (Better coloring, styling, etc.) + +public class MetaCommand extends SubCommand { + + private static final List SUB_COMMANDS = List.of("list", "show", "set", "remove", "add", "subtract", "switch"); + + public MetaCommand(@NotNull final DeluxeMenus plugin) { + super(plugin); + } + + @Override + public @NotNull String getName() { + return "meta"; + } + + @Override + public void execute(@NotNull final CommandSender sender, @NotNull final List arguments) { + if (!sender.isOp()) { + plugin.sms(sender, Messages.NO_PERMISSION); + return; + } + + if (!VersionHelper.IS_PDC_VERSION || plugin.getPersistentMetaHandler() == null) { + plugin.sms(sender, Messages.META_NOT_SUPPORTED); + return; + } + + if (arguments.size() < 2) { + plugin.sms(sender, Messages.WRONG_USAGE_META_COMMAND); + return; + } + + final Player target = Bukkit.getPlayerExact(arguments.get(0)); + + if (target == null) { + plugin.sms(sender, Messages.PLAYER_IS_NOT_ONLINE.message().replaceText(PLAYER_REPLACER_BUILDER.replacement(arguments.get(0)).build())); + return; + } + + final String actionName = arguments.get(1); + final PersistentMetaHandler.DataAction action = plugin.getPersistentMetaHandler().getActionByName(actionName); + + if (action == null) { + if (actionName.equalsIgnoreCase("list")) { + handleListMeta(sender, target, arguments.subList(2, arguments.size())); + return; + } + + if (actionName.equalsIgnoreCase("show")) { + handleShowMeta(sender, target, arguments.subList(2, arguments.size())); + return; + } + + plugin.sms(sender, Messages.WRONG_USAGE_META_COMMAND); + return; + } + + handleActionMeta(sender, target, action, arguments.subList(2, arguments.size())); + } + + @Override + public @Nullable List onTabComplete(@NotNull final CommandSender sender, @NotNull final List arguments) { + if (!sender.isOp() || !VersionHelper.IS_PDC_VERSION || plugin.getPersistentMetaHandler() == null) { + return null; + } + + if (arguments.isEmpty()) { + return List.of(getName()); + } + + if (arguments.size() > 5) { + return null; + } + + if (arguments.size() == 1) { + final String firstArgument = arguments.get(0).toLowerCase(); + if (firstArgument.isEmpty()) { + return List.of(getName()); + } + + if (getName().startsWith(firstArgument)) { + return List.of(getName()); + } + + return null; + } + + final String firstArgument = arguments.get(0).toLowerCase(); + + if (!getName().equals(firstArgument)) { + return null; + } + + if (arguments.size() == 2) { + return getPlayerNameCompletion(arguments.get(1)); + } + + if (arguments.size() == 3) { + final String thirdArgument = arguments.get(2).toLowerCase(); + if (thirdArgument.isEmpty()) { + return SUB_COMMANDS; + } + + return SUB_COMMANDS.stream() + .filter(action -> action.startsWith(thirdArgument)) + .collect(Collectors.toList()); + } + + if (arguments.size() == 4) { + final String action = arguments.get(2).toLowerCase(); + if (!action.equalsIgnoreCase("list")) { + return null; + } + + final String fourthArgument = arguments.get(3); + + if (fourthArgument.isEmpty()) { + return new ArrayList<>(plugin.getPersistentMetaHandler().getSupportedTypes()); + } + + return plugin.getPersistentMetaHandler().getSupportedTypes().stream() + .filter(type -> type.startsWith(fourthArgument.toUpperCase(Locale.ROOT))) + .collect(Collectors.toList()); + } + + if (arguments.size() == 5) { + final String action = arguments.get(2).toLowerCase(); + if (action.equalsIgnoreCase("show") || plugin.getPersistentMetaHandler().getActionByName(action) != null) { + final String fifthArgument = arguments.get(4); + + if (fifthArgument.isEmpty()) { + return new ArrayList<>(plugin.getPersistentMetaHandler().getSupportedTypes()); + } + + return plugin.getPersistentMetaHandler().getSupportedTypes().stream() + .filter(type -> type.startsWith(fifthArgument.toUpperCase(Locale.ROOT))) + .collect(Collectors.toList()); + } + + return null; + } + + return null; + } + + private void handleListMeta(@NotNull final CommandSender sender, @NotNull final Player target, + @NotNull final List arguments) { + if (arguments.isEmpty()) { + plugin.sms(sender, Messages.WRONG_USAGE_LIST_META_COMMAND); + return; + } + final String typeName = arguments.get(0).toUpperCase(Locale.ROOT); + + final PersistentDataType type = plugin.getPersistentMetaHandler().getSupportedTypeByName(typeName); + if (type == null) { + plugin.sms(sender, Messages.UNSUPPORTED_META_TYPE.message().replaceText(TYPE_REPLACER_BUILDER.replacement(typeName).build())); + return; + } + + final Map metas = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + metas.putAll(plugin.getPersistentMetaHandler().getMetaValues(target, type)); + + if (metas.isEmpty()) { + plugin.sms(sender, Messages.NO_META_VALUES.message() + .replaceText(TYPE_REPLACER_BUILDER.replacement(typeName).build()) + .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build())); + return; + } + + final int itemsPerPage = plugin.getGeneralConfig().metasListPageSize(); + final int itemsCount = metas.size(); + final int pagesCount = PaginationUtils.getPagesCount(itemsPerPage, itemsCount); + + final int page = PaginationUtils.parsePage(itemsPerPage, itemsCount, pagesCount, arguments.size() < 2 ? null : arguments.get(1)); + + final Map pageItems = new LinkedHashMap<>(); + + final int start = (page - 1) * itemsPerPage; + final int end = start + itemsPerPage; + + int index = 0; + for (final Map.Entry entry : metas.entrySet()) { + if (index >= end) { + break; + } + + if (index < start) { + index++; + continue; + } + + pageItems.put(entry.getKey(), entry.getValue()); + index++; + } + + final int pageItemsCount = pageItems.size(); + + final TextComponent.Builder list = text() + .append(text("Page " + page + "/" + pagesCount + " - " + pageItemsCount + " pairs:", NamedTextColor.GOLD)) + .append(newline()) + .append(newline()) + .append(text("Key (String) - Value (" + typeName + ")", NamedTextColor.GRAY)) + .append(newline()); + + final var pairsList = pageItems.entrySet().stream() + .map(entry -> text(entry.getKey(), NamedTextColor.DARK_AQUA) + .append(text(" - ", NamedTextColor.GRAY)) + .append(text(String.valueOf(entry.getValue()), NamedTextColor.GREEN)) + .append(newline())) + .collect(Component.toComponent()); + + list.append(newline()) + .append(pairsList) + .append(newline()) + .append(text("Use /dm meta list " + typeName + " to view more values of this type", NamedTextColor.GRAY)); + plugin.sms(sender, list.build()); + } + + private void handleShowMeta(@NotNull final CommandSender sender, @NotNull final Player target, + @NotNull final List arguments) { + + if (arguments.size() < 2) { + plugin.sms(sender, Messages.WRONG_USAGE_SHOW_META_COMMAND); + return; + } + + final String keyName = arguments.get(0); + final NamespacedKey namespacedKey = plugin.getPersistentMetaHandler().getKey(keyName); + if (namespacedKey == null) { + plugin.sms(sender, Messages.INVALID_META_KEY.message().replaceText(KEY_REPLACER_BUILDER.replacement(keyName).build())); + return; + } + + final String typeName = arguments.get(1).toUpperCase(Locale.ROOT); + final PersistentDataType type = plugin.getPersistentMetaHandler().getSupportedTypeByName(typeName); + if (type == null) { + plugin.sms(sender, Messages.UNSUPPORTED_META_TYPE); + return; + } + + final Object value = plugin.getPersistentMetaHandler().getMetaValue(target, namespacedKey, type); + + if (value == null) { + plugin.sms(sender, Messages.NO_META_VALUE.message() + .replaceText(KEY_REPLACER_BUILDER.replacement(keyName).build()) + .replaceText(TYPE_REPLACER_BUILDER.replacement(typeName).build()) + .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) + ); + return; + } + + plugin.sms(sender, Messages.META_VALUE_FOUND.message() + .replaceText(KEY_REPLACER_BUILDER.replacement(keyName).build()) + .replaceText(TYPE_REPLACER_BUILDER.replacement(typeName).build()) + .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(value)).build()) + .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) + ); + } + + private void handleActionMeta(@NotNull final CommandSender sender, @NotNull final Player target, + @NotNull final PersistentMetaHandler.DataAction action, + @NotNull final List arguments) { + if (arguments.size() < 3) { + if (action == PersistentMetaHandler.DataAction.REMOVE || action == PersistentMetaHandler.DataAction.SWITCH) { + plugin.sms(sender, Messages.WRONG_USAGE_NO_VALUE_META_COMMAND); + return; + } + + plugin.sms(sender, Messages.WRONG_USAGE_VALUE_META_COMMAND); + return; + } + + final String keyName = arguments.get(1); + final NamespacedKey namespacedKey = plugin.getPersistentMetaHandler().getKey(keyName); + if (namespacedKey == null) { + plugin.sms(sender, Messages.INVALID_META_KEY.message().replaceText(KEY_REPLACER_BUILDER.replacement(keyName).build())); + return; + } + + final String typeName = arguments.get(2).toUpperCase(Locale.ROOT); + final PersistentDataType type = plugin.getPersistentMetaHandler().getSupportedTypeByName(typeName); + if (type == null) { + plugin.sms(sender, Messages.UNSUPPORTED_META_TYPE.message().replaceText(TYPE_REPLACER_BUILDER.replacement(typeName).build())); + return; + } + + if (action == PersistentMetaHandler.DataAction.SET) { + if (arguments.size() < 4) { + plugin.sms(sender, Messages.WRONG_USAGE_META_SET_COMMAND); + return; + } + + final String value = String.join(" ", arguments); + + if (!isValidValueForTypeName(typeName, value)) { + plugin.sms(sender, Messages.WRONG_USAGE_META_SET_COMMAND); + return; + } + + handleSetMeta(sender, target, namespacedKey, type, value); + return; + } + + if (action == PersistentMetaHandler.DataAction.REMOVE) { + handleRemoveMeta(sender, target, namespacedKey, type); + return; + } + + if (action == PersistentMetaHandler.DataAction.SWITCH) { + handleSwitchMeta(sender, target, namespacedKey, type); + return; + } + + if (action == PersistentMetaHandler.DataAction.ADD) { + if (arguments.size() < 4) { + plugin.sms(sender, Messages.WRONG_USAGE_VALUE_META_COMMAND); + return; + } + + final String value = String.join(" ", arguments); + + if (!isValidValueForTypeName(typeName, value)) { + plugin.sms(sender, Messages.WRONG_USAGE_VALUE_META_COMMAND); + return; + } + + handleAddMeta(sender, target, namespacedKey, type, value); + return; + } + + if (action == PersistentMetaHandler.DataAction.SUBTRACT) { + if (arguments.size() < 4) { + plugin.sms(sender, Messages.WRONG_USAGE_VALUE_META_COMMAND); + return; + } + + final String value = String.join(" ", arguments); + + if (!isValidValueForTypeName(typeName, value)) { + plugin.sms(sender, Messages.WRONG_USAGE_VALUE_META_COMMAND); + return; + } + + handleSubtractMeta(sender, target, namespacedKey, type, value); + return; + } + + plugin.sms(sender, Messages.WRONG_USAGE_META_COMMAND); + } + + private boolean isValidValueForTypeName(@NotNull final String typeName, @Nullable final String value) { + if (value == null) { + return false; + } + + if (typeName.equalsIgnoreCase("boolean")) { + return value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false"); + } + + if (typeName.equalsIgnoreCase("string")) { + return true; + } + + if (typeName.equalsIgnoreCase("double") || typeName.equalsIgnoreCase("float")) { + return Doubles.tryParse(value) != null; + } + + if (typeName.equalsIgnoreCase("long") || typeName.equalsIgnoreCase("integer")) { + return Longs.tryParse(value) != null; + } + + return false; + } + + private void handleSetMeta(@NotNull final CommandSender sender, @NotNull final Player target, + @NotNull final NamespacedKey namespacedKey, + @NotNull final PersistentDataType type, + @NotNull final String value) { + final Object parsedValue = plugin.getPersistentMetaHandler().parseValueByType(type, value); + + if (parsedValue == null) { + plugin.sms(sender, Messages.META_VALUE_TYPE_MISMATCH); + return; + } + + final PersistentMetaHandler.OperationResult result = plugin.getPersistentMetaHandler().setMetaValue(target, namespacedKey, type, parsedValue); + switch (result) { + case SUCCESS: + plugin.sms(sender, Messages.META_VALUE_SET.message() + .replaceText(KEY_REPLACER_BUILDER.replacement(namespacedKey.toString()).build()) + .replaceText(TYPE_REPLACER_BUILDER.replacement(type.getComplexType().getSimpleName()).build()) + .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build()) + .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) + ); + return; + case NEW_VALUE_IS_DIFFERENT_TYPE: + plugin.sms(sender, Messages.META_VALUE_TYPE_MISMATCH); + return; + case EXISTENT_VALUE_IS_DIFFERENT_TYPE: + plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE); + return; + default: + plugin.sms(sender, Messages.WRONG_USAGE_META_SET_COMMAND); + } + } + + private void handleRemoveMeta(@NotNull final CommandSender sender, @NotNull final Player target, + @NotNull final NamespacedKey namespacedKey, + @NotNull final PersistentDataType type) { + final PersistentMetaHandler.OperationResult result = plugin.getPersistentMetaHandler().removeMetaValue(target, namespacedKey, type); + switch (result) { + case SUCCESS: + plugin.sms(sender, Messages.META_VALUE_REMOVED.message() + .replaceText(KEY_REPLACER_BUILDER.replacement(namespacedKey.toString()).build()) + .replaceText(TYPE_REPLACER_BUILDER.replacement(type.getComplexType().getSimpleName()).build()) + .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) + ); + return; + case EXISTENT_VALUE_IS_DIFFERENT_TYPE: + plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE); + return; + case VALUE_NOT_FOUND: + plugin.sms(sender, Messages.META_VALUE_NOT_FOUND); + return; + default: + plugin.sms(sender, Messages.WRONG_USAGE_META_REMOVE_COMMAND); + } + } + + private void handleSwitchMeta(@NotNull final CommandSender sender, @NotNull final Player target, + @NotNull final NamespacedKey namespacedKey, + @NotNull final PersistentDataType type) { + final PersistentMetaHandler.OperationResult result = plugin.getPersistentMetaHandler().switchMetaValue(target, namespacedKey, type); + + switch (result) { + case SUCCESS: + final Object newValue = plugin.getPersistentMetaHandler().getMetaValue(target, namespacedKey, type); + + plugin.sms(sender, Messages.META_VALUE_SWITCHED.message() + .replaceText(KEY_REPLACER_BUILDER.replacement(namespacedKey.toString()).build()) + .replaceText(TYPE_REPLACER_BUILDER.replacement(type.getComplexType().getSimpleName()).build()) + .replaceText(NEW_VALUE_REPLACER_BUILDER.replacement(String.valueOf(newValue)).build()) + .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) + ); + return; + case INVALID_TYPE: + plugin.sms(sender, Messages.META_SWITCH_TYPE_MISMATCH); + // TODO: Send a custom message to the sender. + return; + case EXISTENT_VALUE_IS_DIFFERENT_TYPE: + // TODO: Send a custom message to the sender. + return; + default: + plugin.sms(sender, Messages.WRONG_USAGE_META_SWITCH_COMMAND); + } + } + + private void handleAddMeta(@NotNull final CommandSender sender, @NotNull final Player target, + @NotNull final NamespacedKey namespacedKey, + @NotNull final PersistentDataType type, + @NotNull final String value) { + final Object parsedValue = plugin.getPersistentMetaHandler().parseValueByType(type, value); + + if (!(parsedValue instanceof Number)) { + plugin.sms(sender, Messages.WRONG_USAGE_VALUE_META_COMMAND); + return; + } + + final PersistentMetaHandler.OperationResult result = plugin.getPersistentMetaHandler().addMetaValue(target, namespacedKey, type, (Number) parsedValue); + + switch (result) { + case SUCCESS: + final Object newValue = plugin.getPersistentMetaHandler().getMetaValue(target, namespacedKey, type); + + plugin.sms(sender, Messages.META_VALUE_ADDED.message() + .replaceText(KEY_REPLACER_BUILDER.replacement(namespacedKey.toString()).build()) + .replaceText(TYPE_REPLACER_BUILDER.replacement(type.getComplexType().getSimpleName()).build()) + .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build()) + .replaceText(NEW_VALUE_REPLACER_BUILDER.replacement(String.valueOf(newValue)).build()) + .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) + ); + return; + case INVALID_TYPE: + // TODO: Send a custom message to the sender. + return; + case EXISTENT_VALUE_IS_DIFFERENT_TYPE: + // TODO: Send a custom message to the sender. + return; + default: + plugin.sms(sender, Messages.WRONG_USAGE_META_ADD_COMMAND); + } + } + + private void handleSubtractMeta(@NotNull final CommandSender sender, @NotNull final Player target, + @NotNull final NamespacedKey namespacedKey, + @NotNull final PersistentDataType type, + @NotNull final String value) { + final Object parsedValue = plugin.getPersistentMetaHandler().parseValueByType(type, value); + + if (!(parsedValue instanceof Number)) { + // TODO: Send a custom message to the sender. + return; + } + + final PersistentMetaHandler.OperationResult result = plugin.getPersistentMetaHandler().subtractMetaValue(target, namespacedKey, type, (Number) parsedValue); + + switch (result) { + case SUCCESS: + final Object newValue = plugin.getPersistentMetaHandler().getMetaValue(target, namespacedKey, type); + + plugin.sms(sender, Messages.META_VALUE_SUBTRACTED.message() + .replaceText(KEY_REPLACER_BUILDER.replacement(namespacedKey.toString()).build()) + .replaceText(TYPE_REPLACER_BUILDER.replacement(type.getComplexType().getSimpleName()).build()) + .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build()) + .replaceText(NEW_VALUE_REPLACER_BUILDER.replacement(String.valueOf(newValue)).build()) + .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) + ); + return; + case INVALID_TYPE: + // TODO: Send a custom message to the sender. + return; + case EXISTENT_VALUE_IS_DIFFERENT_TYPE: + // TODO: Send a custom message to the sender. + return; + default: + plugin.sms(sender, Messages.WRONG_USAGE_META_SUBTRACT_COMMAND); + } + } +} diff --git a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/SubCommand.java b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/SubCommand.java index 3e10ee20..6aa2addf 100644 --- a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/SubCommand.java +++ b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/SubCommand.java @@ -2,17 +2,24 @@ import com.extendedclip.deluxemenus.DeluxeMenus; import net.kyori.adventure.text.TextReplacementConfig; +import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.stream.Collectors; public abstract class SubCommand { protected static final TextReplacementConfig.Builder PLAYER_REPLACER_BUILDER = TextReplacementConfig.builder().matchLiteral(""); protected static final TextReplacementConfig.Builder AMOUNT_REPLACER_BUILDER = TextReplacementConfig.builder().matchLiteral(""); protected static final TextReplacementConfig.Builder MENU_REPLACER_BUILDER = TextReplacementConfig.builder().matchLiteral(""); + protected static final TextReplacementConfig.Builder KEY_REPLACER_BUILDER = TextReplacementConfig.builder().matchLiteral(""); + protected static final TextReplacementConfig.Builder VALUE_REPLACER_BUILDER = TextReplacementConfig.builder().matchLiteral(""); + protected static final TextReplacementConfig.Builder NEW_VALUE_REPLACER_BUILDER = TextReplacementConfig.builder().matchLiteral(""); + protected static final TextReplacementConfig.Builder TYPE_REPLACER_BUILDER = TextReplacementConfig.builder().matchLiteral(""); protected final DeluxeMenus plugin; @@ -25,4 +32,23 @@ public SubCommand(final @NotNull DeluxeMenus plugin) { public abstract void execute(final @NotNull CommandSender sender, final @NotNull List arguments); public abstract @Nullable List onTabComplete(final @NotNull CommandSender sender, final @NotNull List arguments); + + protected @Nullable List<@NotNull String> getPlayerNameCompletion(final @Nullable String argument) { + final List onlinePlayerNames = Bukkit.getOnlinePlayers() + .stream() + .map(Player::getName) + .collect(Collectors.toList()); + + if (onlinePlayerNames.isEmpty()) { + return null; + } + + if (argument == null || argument.isEmpty()) { + return onlinePlayerNames; + } + + return onlinePlayerNames.stream() + .filter(playerName -> playerName.toLowerCase().startsWith(argument.toLowerCase())) + .collect(Collectors.toList()); + } } diff --git a/src/main/java/com/extendedclip/deluxemenus/config/GeneralConfig.java b/src/main/java/com/extendedclip/deluxemenus/config/GeneralConfig.java index d8f25066..0421b399 100644 --- a/src/main/java/com/extendedclip/deluxemenus/config/GeneralConfig.java +++ b/src/main/java/com/extendedclip/deluxemenus/config/GeneralConfig.java @@ -11,6 +11,7 @@ public class GeneralConfig { private DebugLevel debugLevel = getDefaultDebugLevel(); private boolean useAdminCommandsInMenusList = false; private int menusListPageSize = 10; + private int metasListPageSize = 15; public GeneralConfig(final @NotNull DeluxeMenus plugin) { this.plugin = plugin; @@ -21,11 +22,13 @@ public void load() { plugin.getConfig().addDefault("debug", debugLevel.name()); plugin.getConfig().addDefault("use_admin_commands_in_menus_list", false); plugin.getConfig().addDefault("menus_list_page_size", menusListPageSize); + plugin.getConfig().addDefault("metas_list_page_size", metasListPageSize); checkForUpdates = plugin.getConfig().getBoolean("check_updates", false); debugLevel = loadDebugLevel(); useAdminCommandsInMenusList = plugin.getConfig().getBoolean("use_admin_commands_in_menus_list", false); menusListPageSize = plugin.getConfig().getInt("menus_list_page_size", 10); + metasListPageSize = plugin.getConfig().getInt("metas_list_page_size", 15); } public void reload() { @@ -49,6 +52,10 @@ public int menusListPageSize() { return menusListPageSize; } + public int metasListPageSize() { + return metasListPageSize; + } + private @NotNull DebugLevel loadDebugLevel() { String configDebugLevel = plugin.getConfig().getString("debug", "HIGHEST"); diff --git a/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java b/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java index 45768932..b5c7b9b6 100644 --- a/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java +++ b/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java @@ -1,263 +1,485 @@ package com.extendedclip.deluxemenus.persistentmeta; import com.extendedclip.deluxemenus.DeluxeMenus; -import java.util.Arrays; -import java.util.Locale; -import java.util.Map; -import java.util.function.Function; -import java.util.logging.Level; -import java.util.stream.Collectors; - import com.extendedclip.deluxemenus.utils.DebugLevel; +import com.extendedclip.deluxemenus.utils.Pair; +import com.google.common.primitives.Doubles; +import com.google.common.primitives.Longs; import org.bukkit.NamespacedKey; import org.bukkit.entity.Player; import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Arrays; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.stream.Collectors; + public class PersistentMetaHandler { - private final DeluxeMenus plugin; - - public PersistentMetaHandler(@NotNull final DeluxeMenus plugin) { - this.plugin = plugin; - } - - /** - * Get a {@link PersistentDataType} from its name. - * - * @param name The name of the type. - * @return The type, or null if type does not exist or is not supported. - */ - public @Nullable PersistentDataType getSupportedType(@NotNull final String name) { - switch (name.toUpperCase(Locale.ROOT)) { - case "DOUBLE": - return PersistentDataType.DOUBLE; - case "INTEGER": - case "LONG": - return PersistentDataType.LONG; - case "STRING": - case "BOOLEAN": - return PersistentDataType.STRING; - } - return null; - } - - /** - * Get a {@link NamespacedKey} from a string. If the key contains a namespace, it will use that, otherwise it will - * use the plugin's namespace. - * - * @param key The string to get the {@link NamespacedKey} from. - * @return The {@link NamespacedKey}. - */ - private @NotNull NamespacedKey getKey(@NotNull final String key) { - final NamespacedKey namespacedKey; - - if (key.contains(":")) { - final String[] split = key.split(":", 2); - namespacedKey = new NamespacedKey(split[0], split[1]); - } else { - namespacedKey = new NamespacedKey(plugin, key); - } + private static final Map> SUPPORTED_TYPES = Map.of( + "DOUBLE", PersistentDataType.DOUBLE, + "INTEGER", PersistentDataType.LONG, + "LONG", PersistentDataType.LONG, + "STRING", PersistentDataType.STRING, + "BOOLEAN", PersistentDataType.STRING + ); - return namespacedKey; - } - - /** - * Get a meta value from a player's {@link org.bukkit.persistence.PersistentDataContainer}. - * - * @param player The player to get the meta value from. - * @param key The key of the meta value. - * @param typeName The name of the type of the meta value. - * @param defaultValue The default value if no meta value will be found. - * @return The meta value or the default value if no meta value was found. - */ - public @Nullable String getMeta( - @NotNull final Player player, - @NotNull final String key, - @NotNull final String typeName, - @Nullable final String defaultValue - ) { - final NamespacedKey namespacedKey; - try { - namespacedKey = getKey(key); - } catch (final IllegalArgumentException e) { - plugin.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Failed to get meta value for player " + player.getName() + " with key '" + key + "' and type '" + typeName.toUpperCase(Locale.ROOT) + "'. Reason: " + e.getMessage() - ); - return defaultValue; + private final DeluxeMenus plugin; + + public PersistentMetaHandler(@NotNull final DeluxeMenus plugin) { + this.plugin = plugin; } - final PersistentDataType type = getSupportedType(typeName); - if (type == null) { - return defaultValue; + /** + * Check if a player has a meta value in their {@link org.bukkit.persistence.PersistentDataContainer}. + * + * @param player The player to check. + * @param key The key of the meta value. + * @return True if the player has the meta value, false if not. + */ + public boolean hasMetaValue( + @NotNull final Player player, + @NotNull final NamespacedKey key + ) { + return player.getPersistentDataContainer().has(key); } - final Object result; - try { - result = player.getPersistentDataContainer().get(namespacedKey, type); - } catch (final IllegalArgumentException e) { - plugin.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Failed to get meta value for player " + player.getName() + " with key '" + key + "' and type '" + typeName.toUpperCase(Locale.ROOT) + "'. Reason: Saved tag can not be converted to type: " + typeName.toUpperCase(Locale.ROOT) - ); - return defaultValue; + /** + * Check if a player has a meta value in their {@link org.bukkit.persistence.PersistentDataContainer}. + * + * @param player The player to check. + * @param key The key of the meta value. + * @param type The type of the meta value. + * @return True if the player has the meta value, false if not. + */ + public boolean hasMetaValue( + @NotNull final Player player, + @NotNull final NamespacedKey key, + @NotNull final PersistentDataType type + ) { + return player.getPersistentDataContainer().has(key, type); } - if (result == null) { - return defaultValue; + /** + * Get a meta value from a player's {@link org.bukkit.persistence.PersistentDataContainer}. + * If the meta value is not found, null is returned. + * + * @param player The player to get the meta value from. + * @param key The key of the meta value. + * @param type The type of the meta value. + * @return The meta value or null if no meta value was found. + */ + public @Nullable T getMetaValue( + @NotNull final Player player, + @NotNull final NamespacedKey key, + @NotNull final PersistentDataType type + ) { + if (!player.getPersistentDataContainer().has(key, type)) { + return null; + } + + return player.getPersistentDataContainer().get(key, type); } - return result.toString(); - } - - /** - * Set a meta value in a player's {@link org.bukkit.persistence.PersistentDataContainer}. - * - * @param player The player to set the meta value for. - * @param input The action type, key name, data type and value joined by spaces. - * @throws NumberFormatException If the value is not a number while the type is LONG or DOUBLE. - */ - public boolean setMeta(@NotNull final Player player, @NotNull final String input) throws NumberFormatException { - // [meta] set points INTEGER 0 - String[] args = input.split(" ", 4); - - if (args.length < 4) { - return false; + /** + * Get a meta value from a player's {@link org.bukkit.persistence.PersistentDataContainer}. + * If the meta value is not found, the default value is returned. + * + * @param player The player to get the meta value from. + * @param key The key of the meta value. + * @param type The type of the meta value. + * @param defaultValue The default value to return if no meta value was found. + * @return The meta value or the default value if no meta value was found. + */ + public @NotNull T getMetaValueOrDefault( + @NotNull final Player player, + @NotNull final NamespacedKey key, + @NotNull final PersistentDataType type, + @NotNull final T defaultValue + ) { + if (!player.getPersistentDataContainer().has(key, type)) { + return defaultValue; + } + + final T result = player.getPersistentDataContainer().get(key, type); + return result == null ? defaultValue : result; } - DataAction action = DataAction.getByName(args[0]); - if (action == null) { - return false; + /** + * Get a list of all meta values of the given type from a player's {@link org.bukkit.persistence.PersistentDataContainer}. + * + * @param player The player to get the meta values from. + * @param type The type of the meta values. + * @return A map of all meta values. + */ + public Map getMetaValues( + @NotNull final Player player, + @NotNull final PersistentDataType type + ) { + return player.getPersistentDataContainer().getKeys().stream() + .filter(key -> player.getPersistentDataContainer().has(key, type)) + .map(key -> Pair.of(key.toString(), player.getPersistentDataContainer().get(key, type))) + .filter(entry -> entry.getValue() != null) + .collect(Collectors.toMap(Pair::getKey, Pair::getValue)); } - final PersistentDataType type = getSupportedType(args[2]); - if (type == null) { - return false; + /** + * Set a meta value in a player's {@link org.bukkit.persistence.PersistentDataContainer}. + * If the meta value already exists, it will be overwritten. + * + * @param player The player to set the meta value for. + * @param key The key of the meta value. + * @param type The type of the meta value. + * @param value The value to set. + * @return The result of the operation. + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public @NotNull OperationResult setMetaValue( + @NotNull final Player player, + @NotNull final NamespacedKey key, + @NotNull final PersistentDataType type, + @NotNull final Object value + ) { + if (!type.getComplexType().isInstance(value)) { + return OperationResult.NEW_VALUE_IS_DIFFERENT_TYPE; + } + + if (player.getPersistentDataContainer().has(key) && !player.getPersistentDataContainer().has(key, type)) { + return OperationResult.EXISTENT_VALUE_IS_DIFFERENT_TYPE; + } + + player.getPersistentDataContainer().set(key, type, value); + return OperationResult.SUCCESS; } - final NamespacedKey namespacedKey; - try { - namespacedKey = getKey(args[1]); - } catch (final IllegalArgumentException e) { - plugin.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Failed to set meta value for player " + player.getName() + " with key '" + args[1] + "' and type '" + args[2].toUpperCase(Locale.ROOT) + "'. Reason: " + e.getMessage() - ); - return false; + /** + * Remove a meta value from a player's {@link org.bukkit.persistence.PersistentDataContainer}. + * + * @param player The player to remove the meta value from. + * @param key The key of the meta value. + * @return The result of the operation. + */ + public @NotNull OperationResult removeMetaValue( + @NotNull final Player player, + @NotNull final NamespacedKey key, + @NotNull final PersistentDataType type + ) { + if (player.getPersistentDataContainer().has(key) && !player.getPersistentDataContainer().has(key, type)) { + return OperationResult.EXISTENT_VALUE_IS_DIFFERENT_TYPE; + } + + if (!player.getPersistentDataContainer().has(key, type)) { + return OperationResult.VALUE_NOT_FOUND; + } + + player.getPersistentDataContainer().remove(key); + return OperationResult.SUCCESS; } - return setMeta(player, namespacedKey, type, action, args[3]); - } - - /** - * Set a meta value in a player's {@link org.bukkit.persistence.PersistentDataContainer}. - * - * @param player The player to set the meta value in. - * @param key The key of the meta value. - * @param type The type of the meta value. - * @param action The action to perform on the meta value. - * @param value The value to use. - * @return True if the meta value was changed, false if not. - * @throws NumberFormatException If the value is not a number while the type is LONG or DOUBLE. - */ - public boolean setMeta( - @NotNull final Player player, - @NotNull final NamespacedKey key, - @NotNull final PersistentDataType type, - @NotNull final DataAction action, - @NotNull final String value - ) throws NumberFormatException { - if (value.equalsIgnoreCase("null")) { - player.getPersistentDataContainer().remove(key); - return true; + /** + * Switch a meta value in a player's {@link org.bukkit.persistence.PersistentDataContainer}. + *

The value must be a boolean. + * If the meta value does not exist, it will be created and set to true. + * If the meta value is not a boolean, it will not be changed. + * + * @param player The player to switch the meta value for. + * @param key The key of the meta value. + * @return The result of the operation. + */ + public @NotNull OperationResult switchMetaValue( + @NotNull final Player player, + @NotNull final NamespacedKey key, + @NotNull final PersistentDataType type + ) { + if (type != PersistentDataType.STRING) { + return OperationResult.INVALID_TYPE; + } + + if (player.getPersistentDataContainer().has(key) && !player.getPersistentDataContainer().has(key, type)) { + return OperationResult.EXISTENT_VALUE_IS_DIFFERENT_TYPE; + } + + final String currentValue = player.getPersistentDataContainer().getOrDefault(key, PersistentDataType.STRING, "false"); + if (!currentValue.equalsIgnoreCase("true") && !currentValue.equalsIgnoreCase("false")) { + return OperationResult.EXISTENT_VALUE_IS_DIFFERENT_TYPE; + } + + player.getPersistentDataContainer().set(key, PersistentDataType.STRING, currentValue.equalsIgnoreCase("true") ? "false" : "true"); + return OperationResult.SUCCESS; } - switch (action) { - case SET: - if (type == PersistentDataType.STRING) { - player.getPersistentDataContainer().set(key, type, value); - return true; + /** + * Perform addition on a meta value in a player's {@link org.bukkit.persistence.PersistentDataContainer}. + *

The value must be a number. + * If the meta value does not exist, it will be created with the given value. + * If the meta value is not a number, it will not be changed. + * + * @param player The player to add the meta value for. + * @param key The key of the meta value. + * @param type The type of the meta value. + * @param value The value to add. + * @return The result of the operation. + */ + public @NotNull OperationResult addMetaValue( + @NotNull final Player player, + @NotNull final NamespacedKey key, + @NotNull final PersistentDataType type, + @NotNull final Number value + ) { + if (type != PersistentDataType.DOUBLE && type != PersistentDataType.LONG) { + return OperationResult.INVALID_TYPE; + } + + if (player.getPersistentDataContainer().has(key) && !player.getPersistentDataContainer().has(key, type)) { + return OperationResult.EXISTENT_VALUE_IS_DIFFERENT_TYPE; } + final Object currentValue = player.getPersistentDataContainer().get(key, type); + if (type == PersistentDataType.DOUBLE) { - player.getPersistentDataContainer().set(key, type, Double.parseDouble(value)); - return true; + final double newValue = (double) (currentValue == null ? 0.0 : currentValue) + value.doubleValue(); + player.getPersistentDataContainer().set(key, PersistentDataType.DOUBLE, newValue); + return OperationResult.SUCCESS; } - if (type == PersistentDataType.LONG) { - player.getPersistentDataContainer().set(key, type, Long.parseLong(value)); - return true; + final long newValue = (long) (currentValue == null ? 0 : currentValue) + value.longValue(); + player.getPersistentDataContainer().set(key, PersistentDataType.LONG, newValue); + return OperationResult.SUCCESS; + } + + /** + * Perform subtraction on a meta value in a player's {@link org.bukkit.persistence.PersistentDataContainer}. + *

The value must be a number. + * If the meta value does not exist, it will be created with the given value. + * If the meta value is not a number, it will not be changed. + * + * @param player The player to subtract the meta value for. + * @param key The key of the meta value. + * @param type The type of the meta value. + * @param value The value to subtract. + * @return The result of the operation. + */ + public @NotNull OperationResult subtractMetaValue( + @NotNull final Player player, + @NotNull final NamespacedKey key, + @NotNull final PersistentDataType type, + @NotNull final Number value + ) { + if (type != PersistentDataType.DOUBLE && type != PersistentDataType.LONG) { + return OperationResult.INVALID_TYPE; } - return false; + if (player.getPersistentDataContainer().has(key) && !player.getPersistentDataContainer().has(key, type)) { + return OperationResult.EXISTENT_VALUE_IS_DIFFERENT_TYPE; + } - case REMOVE: - player.getPersistentDataContainer().remove(key); - return true; + final Object currentValue = player.getPersistentDataContainer().get(key, type); + + if (type == PersistentDataType.DOUBLE) { + final double newValue = (double) (currentValue == null ? 0.0 : currentValue) - value.doubleValue(); + player.getPersistentDataContainer().set(key, PersistentDataType.DOUBLE, newValue); + return OperationResult.SUCCESS; + } - case SWITCH: - final boolean currentValueSwitch = Boolean.parseBoolean( - player.getPersistentDataContainer().getOrDefault(key, type, value)); + final long newValue = (long) (currentValue == null ? 0 : currentValue) - value.longValue(); + player.getPersistentDataContainer().set(key, PersistentDataType.LONG, newValue); + return OperationResult.SUCCESS; + } - player.getPersistentDataContainer().set(key, type, String.valueOf(!currentValueSwitch)); - return true; + /** + * Parse and execute a meta action from a string. + *
The format is: <action> <key> <type> [value]. + *
Example: set points INTEGER 0 + * + * @param player The player to execute the action for. + * @param input The action to execute. + * @return The result of the operation. + **/ + public @NotNull OperationResult parseAndExecuteMetaActionFromString( + @NotNull final Player player, + @NotNull final String input + ) { + // [value] + String[] args = input.split(" ", 4); + + if (args.length < 3) { + return OperationResult.INVALID_SYNTAX; + } - case ADD: - if (type == PersistentDataType.STRING) { - return false; + DataAction action = getActionByName(args[0]); + if (action == null) { + return OperationResult.INVALID_SYNTAX; } - final Object currentValueAdd = player.getPersistentDataContainer().getOrDefault(key, type, 0); + final NamespacedKey key = getKey(args[1]); + if (key == null) { + return OperationResult.INVALID_SYNTAX; + } - if (type == PersistentDataType.DOUBLE) { - final double toAdd = Double.parseDouble(currentValueAdd.toString()) + Double.parseDouble(value); - player.getPersistentDataContainer().set(key, type, toAdd); - return true; + final PersistentDataType type = getSupportedTypeByName(args[2]); + if (type == null) { + return OperationResult.INVALID_SYNTAX; } - final long toAddLong = Long.parseLong(currentValueAdd.toString()) + Long.parseLong(value); - player.getPersistentDataContainer().set(key, type, toAddLong); - return true; + final Object parsedValue = parseValueByType(type, args.length >= 4 ? args[3] : null); + + switch (action) { + case SET: + if (parsedValue == null) { + return OperationResult.NEW_VALUE_IS_DIFFERENT_TYPE; + } + + return setMetaValue(player, key, type, parsedValue); + case REMOVE: + return removeMetaValue(player, key, type); + case SWITCH: + return switchMetaValue(player, key, type); + case ADD: + if (!(parsedValue instanceof Number)) { + return OperationResult.NEW_VALUE_IS_DIFFERENT_TYPE; + } + + return addMetaValue(player, key, type, (Number) parsedValue); + case SUBTRACT: + if (!(parsedValue instanceof Number)) { + return OperationResult.NEW_VALUE_IS_DIFFERENT_TYPE; + } + + return subtractMetaValue(player, key, type, (Number) parsedValue); + } - case SUBTRACT: - if (type == PersistentDataType.STRING) { - return false; + return OperationResult.INVALID_SYNTAX; + } + + /** + * Parse a string value into an object based on the type. + * + * @param type The type to parse the value for. + * @param value The value to parse. + * @return The parsed value or null if the value is null or could not be parsed. + */ + public @Nullable Object parseValueByType( + @NotNull final PersistentDataType type, + @Nullable final String value + ) { + plugin.getLogger().info("Parsing value by type. Value: " + value + ", type: <" + type.getPrimitiveType().getTypeName() + ", " + type.getComplexType().getTypeName() + ">."); + if (value == null) { + plugin.getLogger().info("Value is null."); + return null; } - final Object currentValueSubtract = player.getPersistentDataContainer().getOrDefault(key, type, 0); + if (type == PersistentDataType.STRING) { + plugin.getLogger().info("Value is a string."); + return value; + } if (type == PersistentDataType.DOUBLE) { - final double toSub = ((double) currentValueSubtract) - Double.parseDouble(value); - player.getPersistentDataContainer().set(key, type, toSub); - return true; + final Double result = Doubles.tryParse(value); + plugin.getLogger().info("Value is a double: " + result); + return result; } - final long toSubLong = Long.parseLong(currentValueSubtract.toString()) - Long.parseLong(value); - player.getPersistentDataContainer().set(key, type, toSubLong); - return true; + if (type == PersistentDataType.LONG) { + final Long result = Longs.tryParse(value); + plugin.getLogger().info("Value is a long: " + result); + return result; + } + + plugin.getLogger().info("Unsupported type found."); + return null; } - return false; - } - public enum DataAction { - SET, REMOVE, ADD, SUBTRACT, SWITCH; + // Helper methods - final static Map BY_NAME = Arrays.stream(values()) - .collect(Collectors.toUnmodifiableMap(Enum::name, Function.identity())); + /** + * Get a list of all supported types. + * + * @return A set of all supported types. + */ + public @NotNull Set getSupportedTypes() { + return SUPPORTED_TYPES.keySet(); + } + + /** + * Helper method to parse a string into a {@link PersistentDataType}. + * + * @param name The name of the type. + * @return The type, or null if type does not exist or is not supported. + */ + public @Nullable PersistentDataType getSupportedTypeByName(@NotNull final String name) { + return SUPPORTED_TYPES.get(name.toUpperCase(Locale.ROOT)); + } /** - * Get a {@link DataAction} by its name. + * Helper method to parse a string into a {@link DataAction}. + * * @param name The name of the action type. * @return The {@link DataAction} or null if it does not exist. */ - public static @Nullable DataAction getByName(@NotNull final String name) { - return BY_NAME.get(name.toUpperCase(Locale.ROOT)); + public @Nullable DataAction getActionByName(@NotNull final String name) { + return DataAction.getByName(name); + } + + /** + * Helper method to parse a string into a {@link NamespacedKey}. + * If the key contains a namespace, it will use that, otherwise it will use the plugin's namespace. If the key is + * invalid, it will log a warning and return null. + * + * @param key The string to parse. + * @return The {@link NamespacedKey} or null if the key could not be parsed. + */ + @SuppressWarnings("UnstableApiUsage") + public @Nullable NamespacedKey getKey(@NotNull final String key) { + final NamespacedKey namespacedKey; + + try { + if (key.contains(":")) { + final String[] split = key.split(":", 2); + namespacedKey = new NamespacedKey(split[0], split[1]); + } else { + namespacedKey = new NamespacedKey(plugin, key); + } + } catch (final IllegalArgumentException e) { + plugin.debug( + DebugLevel.HIGHEST, + Level.WARNING, + "Failed to parse meta key with value: '" + key + "'. Reason: " + e.getMessage() + ); + return null; + } + + return namespacedKey; + } + + public enum DataAction { + SET, REMOVE, ADD, SUBTRACT, SWITCH; + + final static Map BY_NAME = Arrays.stream(values()) + .collect(Collectors.toUnmodifiableMap(Enum::name, Function.identity())); + + /** + * Get a {@link DataAction} by its name. + * + * @param name The name of the action type. + * @return The {@link DataAction} or null if it does not exist. + */ + public static @Nullable DataAction getByName(@NotNull final String name) { + return BY_NAME.get(name.toUpperCase(Locale.ROOT)); + } + } + + public enum OperationResult { + SUCCESS, // Operation was successful + INVALID_SYNTAX, // Used when parsing an action from a string and the syntax is invalid + INVALID_TYPE, // Used when the type is not supported + VALUE_NOT_FOUND, // Used when no value was found with the specified key and/or type + EXISTENT_VALUE_IS_DIFFERENT_TYPE, // Used when the value already exists but is a different type + NEW_VALUE_IS_DIFFERENT_TYPE // Used when the new value is of an unsupported type } - } } diff --git a/src/main/java/com/extendedclip/deluxemenus/placeholder/Expansion.java b/src/main/java/com/extendedclip/deluxemenus/placeholder/Expansion.java index a48fc7ce..ff4b9b23 100644 --- a/src/main/java/com/extendedclip/deluxemenus/placeholder/Expansion.java +++ b/src/main/java/com/extendedclip/deluxemenus/placeholder/Expansion.java @@ -7,8 +7,10 @@ import me.clip.placeholderapi.PlaceholderAPI; import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.expansion.PlaceholderExpansion; +import org.bukkit.NamespacedKey; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; +import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -52,57 +54,78 @@ public boolean persist() { } final String parsedInput = PlaceholderAPI.setBracketPlaceholders(onlinePlayer, input); + final String parsedInputLower = parsedInput.toLowerCase(); - if (input.startsWith("meta_")) { + if (parsedInputLower.startsWith("meta_")) { if (!VersionHelper.IS_PDC_VERSION) { return null; } - final boolean isHasValueRequest = parsedInput.startsWith("meta_has_value_"); + // %deluxemenus_meta_has_value__% + if (parsedInputLower.startsWith("meta_has_value_")) { + final String hasValueInput = parsedInput.substring(15); + + if (!hasValueInput.contains("_")) { + return null; + } + + String[] hasValueParts = hasValueInput.split("_", 2); + + if (hasValueParts.length < 2) { + return null; + } - final String finalInput = parsedInput.startsWith("meta_has_value_") - ? parsedInput.substring(15) - : parsedInput.substring(5); + final NamespacedKey key = plugin.getPersistentMetaHandler().getKey(hasValueParts[0]); + if (key == null) { + return "INVALID_KEY"; + } + + final PersistentDataType type = plugin.getPersistentMetaHandler().getSupportedTypeByName(hasValueParts[1]); + if (type == null) { + return "INVALID_TYPE"; + } + + final boolean hasValue = plugin.getPersistentMetaHandler().hasMetaValue(onlinePlayer, key, type); + + return getBooleanAsString(hasValue); + } - if (!finalInput.contains("_")) { + final String getValueInput = parsedInput.substring(5); + + if (!getValueInput.contains("_")) { return null; } - String[] parts = isHasValueRequest - ? finalInput.split("_", 2) - : finalInput.split("_", 3); + final String[] parts = getValueInput.split("_", 3); if (parts.length < 2) { return null; } - final String result = plugin.getPersistentMetaHandler().getMeta( - onlinePlayer, - parts[0], - parts[1], - null - ); - - // %deluxemenus_meta_has_value__% - if (isHasValueRequest) { - return result == null - ? PlaceholderAPIPlugin.booleanFalse() - : PlaceholderAPIPlugin.booleanTrue(); + final NamespacedKey key = plugin.getPersistentMetaHandler().getKey(parts[0]); + if (key == null) { + return "INVALID_KEY"; + } + final PersistentDataType type = plugin.getPersistentMetaHandler().getSupportedTypeByName(parts[1]); + if (type == null) { + return "INVALID_TYPE"; } + final Object result = plugin.getPersistentMetaHandler().getMetaValue(onlinePlayer, key, type); + // %deluxemenus_meta___[defaultValue]% if (result != null) { - return result; + return String.valueOf(result); } // return the default value return parts.length > 2 ? parts[2] : ""; } - switch (input) { + switch (parsedInputLower) { case "is_in_menu": { - return Menu.getMenuHolder(onlinePlayer).isPresent() ? PlaceholderAPIPlugin.booleanTrue() : PlaceholderAPIPlugin.booleanFalse(); + return getBooleanAsString(Menu.getMenuHolder(onlinePlayer).isPresent()); } case "opened_menu": { return Menu.getOpenMenu(onlinePlayer).map(Menu::options).map(MenuOptions::name).orElse(""); @@ -113,4 +136,8 @@ public boolean persist() { } return null; } + + private @NotNull String getBooleanAsString(final boolean value) { + return value ? PlaceholderAPIPlugin.booleanTrue() : PlaceholderAPIPlugin.booleanFalse(); + } } diff --git a/src/main/java/com/extendedclip/deluxemenus/requirement/HasMetaRequirement.java b/src/main/java/com/extendedclip/deluxemenus/requirement/HasMetaRequirement.java index c8f1108b..76c450aa 100644 --- a/src/main/java/com/extendedclip/deluxemenus/requirement/HasMetaRequirement.java +++ b/src/main/java/com/extendedclip/deluxemenus/requirement/HasMetaRequirement.java @@ -2,63 +2,76 @@ import com.extendedclip.deluxemenus.DeluxeMenus; import com.extendedclip.deluxemenus.menu.MenuHolder; +import org.bukkit.NamespacedKey; import org.bukkit.entity.Player; +import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.NotNull; public class HasMetaRequirement extends Requirement { - private final DeluxeMenus plugin; - private final String key; - private final String value; - private final String type; - private final boolean invert; + private final DeluxeMenus plugin; + private final String key; + private final String value; + private final String typeName; + private final boolean invert; - public HasMetaRequirement(@NotNull final DeluxeMenus plugin, String key, String type, String value, boolean invert) { - this.plugin = plugin; - this.key = key; - this.type = type.toUpperCase(); - this.value = value; - this.invert = invert; - } - - @Override - public boolean evaluate(MenuHolder holder) { - Player player = holder.getViewer(); - if (player == null) { - return false; - } - String parsedKey = holder.setPlaceholdersAndArguments(key); - String metaVal = plugin.getPersistentMetaHandler() - .getMeta(player, parsedKey, type, null); - if (metaVal == null) { - return invert; + public HasMetaRequirement(@NotNull final DeluxeMenus plugin, String key, String typeName, String value, boolean invert) { + this.plugin = plugin; + this.key = key; + this.typeName = typeName.toUpperCase(); + this.value = value; + this.invert = invert; } - String expected = holder.setPlaceholdersAndArguments(value); - metaVal = holder.setPlaceholdersAndArguments(metaVal); + @Override + public boolean evaluate(MenuHolder holder) { + Player player = holder.getViewer(); + if (player == null) { + return false; + } + String parsedKey = holder.setPlaceholdersAndArguments(key); + final NamespacedKey namespacedKey = plugin.getPersistentMetaHandler().getKey(parsedKey); + if (namespacedKey == null) { + return invert; + } + + final PersistentDataType type = plugin.getPersistentMetaHandler().getSupportedTypeByName(typeName); + if (type == null) { + return invert; + } - switch (type) { - case "STRING": - case "BOOLEAN": - return invert != metaVal.equalsIgnoreCase(expected); - case "INTEGER": - case "LONG": - try { - long metaNum = Long.parseLong(metaVal); - long toCheck = Long.parseLong(expected); - boolean pass = metaNum >= toCheck; - return invert != pass; - } catch (Exception ex) { + Object metaValue = plugin.getPersistentMetaHandler().getMetaValue(player, namespacedKey, type); + if (metaValue == null) { + return invert; } - case "DOUBLE": - try { - double metaNum = Double.parseDouble(metaVal); - double toCheck = Double.parseDouble(expected); - boolean pass = metaNum >= toCheck; - return invert != pass; - } catch (Exception ex) { + + final String expectedValue = holder.setPlaceholdersAndArguments(value); + final String actualValue = holder.setPlaceholdersAndArguments(String.valueOf(metaValue)); + + switch (typeName) { + case "STRING": + case "BOOLEAN": + return invert != actualValue.equalsIgnoreCase(expectedValue); + case "INTEGER": + case "LONG": + try { + long metaNum = Long.parseLong(actualValue); + long toCheck = Long.parseLong(expectedValue); + boolean pass = metaNum >= toCheck; + return invert != pass; + } catch (Exception ex) { + // In case of an exception, we will return the invert value + } + case "DOUBLE": + try { + double metaNum = Double.parseDouble(actualValue); + double toCheck = Double.parseDouble(expectedValue); + boolean pass = metaNum >= toCheck; + return invert != pass; + } catch (Exception ex) { + // In case of an exception, we will return the invert value + } } + return invert; } - return invert; - } } diff --git a/src/main/java/com/extendedclip/deluxemenus/utils/DumpUtils.java b/src/main/java/com/extendedclip/deluxemenus/utils/DumpUtils.java index 1b70d88b..64ba469c 100644 --- a/src/main/java/com/extendedclip/deluxemenus/utils/DumpUtils.java +++ b/src/main/java/com/extendedclip/deluxemenus/utils/DumpUtils.java @@ -31,7 +31,7 @@ public final class DumpUtils { private static final Gson gson = new Gson(); private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter .ofLocalizedDateTime(FormatStyle.LONG) - .withLocale(Locale.US) + .withLocale(Locale.getDefault()) .withZone(ZoneId.of("UTC")); private DumpUtils() { diff --git a/src/main/java/com/extendedclip/deluxemenus/utils/Messages.java b/src/main/java/com/extendedclip/deluxemenus/utils/Messages.java index 1e59d736..cf44f799 100644 --- a/src/main/java/com/extendedclip/deluxemenus/utils/Messages.java +++ b/src/main/java/com/extendedclip/deluxemenus/utils/Messages.java @@ -1,13 +1,15 @@ package com.extendedclip.deluxemenus.utils; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextDecoration; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + import static net.kyori.adventure.text.Component.empty; import static net.kyori.adventure.text.Component.newline; import static net.kyori.adventure.text.Component.space; @@ -16,97 +18,219 @@ public enum Messages { PLUGIN_TITLE(empty() - .append(text("Deluxe", NamedTextColor.GOLD, TextDecoration.BOLD)) - .append(text("Menus", NamedTextColor.YELLOW))), + .append(text("Deluxe", NamedTextColor.GOLD, TextDecoration.BOLD)) + .append(text("Menus", NamedTextColor.YELLOW))), PLUGIN_VERSION(PLUGIN_TITLE.message - .append(space()) - .append(text("version", NamedTextColor.WHITE)) - .append(space()) - .append(text("", NamedTextColor.YELLOW)) - .append(newline()) - .append(text("Created by", NamedTextColor.GRAY)) - .append(space()) - .append(text("", NamedTextColor.WHITE))), + .append(space()) + .append(text("version", NamedTextColor.WHITE)) + .append(space()) + .append(text("", NamedTextColor.YELLOW)) + .append(newline()) + .append(text("Created by", NamedTextColor.GRAY)) + .append(space()) + .append(text("", NamedTextColor.WHITE))), HELP(PLUGIN_TITLE.message - .append(space()) - .append(text("help", NamedTextColor.WHITE)) - .append(newline()) - .append(text(">", NamedTextColor.AQUA)) - .append(space().append(space())) - .append(text("/dm open [player]", NamedTextColor.WHITE)) - .append(newline()) - .append(text(">", NamedTextColor.AQUA)) - .append(space().append(space())) - .append(text("/dm list", NamedTextColor.WHITE))), - - HELP_ADMIN(HELP.message - .append(newline()) - .append(text(">", NamedTextColor.AQUA)) - .append(space().append(space())) - .append(text("/dm execute ", NamedTextColor.WHITE)) - .append(newline()) - .append(text(">", NamedTextColor.AQUA)) - .append(space().append(space())) - .append(text("/dm reload [menu]", NamedTextColor.WHITE))), + .append(space()) + .append(text("help", NamedTextColor.WHITE)) + .append(newline()) + .append(text(">", NamedTextColor.AQUA)) + .append(space().append(space())) + .append(text("/dm open [player]", NamedTextColor.WHITE)) + .append(newline()) + .append(text(">", NamedTextColor.AQUA)) + .append(space().append(space())) + .append(text("/dm list [page/all]", NamedTextColor.WHITE)) + .append(newline()) + .append(text(">", NamedTextColor.AQUA)) + .append(space().append(space())) + .append(text("/dm dump ", NamedTextColor.WHITE)) + .append(newline()) + .append(text(">", NamedTextColor.AQUA)) + .append(space().append(space())) + .append(text("/dm reload [menu-name]", NamedTextColor.WHITE))), + + HELP_OP(PLUGIN_TITLE.message + .append(space()) + .append(text("help", NamedTextColor.WHITE)) + .append(newline()) + .append(text(">", NamedTextColor.AQUA)) + .append(space().append(space())) + .append(text("/dm open [player]", NamedTextColor.WHITE)) + .append(newline()) + .append(text(">", NamedTextColor.AQUA)) + .append(space().append(space())) + .append(text("/dm list [page/all]", NamedTextColor.WHITE)) + .append(newline()) + .append(text(">", NamedTextColor.AQUA)) + .append(space().append(space())) + .append(text("/dm execute ", NamedTextColor.WHITE)) + .append(newline()) + .append(text(">", NamedTextColor.AQUA)) + .append(space().append(space())) + .append(text("/dm dump ", NamedTextColor.WHITE)) + .append(newline()) + .append(text(">", NamedTextColor.AQUA)) + .append(space().append(space())) + .append(text("/dm meta ", NamedTextColor.WHITE)) + .append(newline()) + .append(text(">", NamedTextColor.AQUA)) + .append(space().append(space())) + .append(text("/dm reload [menu-name]", NamedTextColor.WHITE))), NO_PERMISSION(text("You don't have permission to do that!", NamedTextColor.RED)), NO_PERMISSION_PLAYER_ARGUMENT(text("You don't have permission to use the argument -p:!", NamedTextColor.RED)), WRONG_USAGE_BASE(empty() - .append(text("Incorrect Usage!", NamedTextColor.RED)) - .append(space()) - .append(text("Use")) - .append(space())), + .append(text("Incorrect Usage!", NamedTextColor.RED)) + .append(space()) + .append(text("Use")) + .append(space())), WRONG_USAGE(WRONG_USAGE_BASE.message - .append(text("/dm help", NamedTextColor.GRAY))), + .append(text("/dm help", NamedTextColor.GRAY))), WRONG_USAGE_EXECUTE_COMMAND(WRONG_USAGE_BASE.message - .append(text("/dm execute ", NamedTextColor.GRAY))), - + .append(text("/dm execute ", NamedTextColor.GRAY))), WRONG_USAGE_DUMP_COMMAND(WRONG_USAGE_BASE.message - .append(text("/dm dump ", NamedTextColor.GRAY))), + .append(text("/dm dump ", NamedTextColor.GRAY))), + WRONG_USAGE_META_COMMAND(WRONG_USAGE_BASE.message + .append(text("/dm meta ", NamedTextColor.GRAY))), + WRONG_USAGE_VALUE_META_COMMAND(WRONG_USAGE_BASE.message + .append(text("/dm meta ", NamedTextColor.GRAY))), + WRONG_USAGE_NO_VALUE_META_COMMAND(WRONG_USAGE_BASE.message + .append(text("/dm meta ", NamedTextColor.GRAY))), + WRONG_USAGE_SHOW_META_COMMAND(WRONG_USAGE_BASE.message + .append(text("/dm meta show ", NamedTextColor.GRAY))), + WRONG_USAGE_LIST_META_COMMAND(WRONG_USAGE_BASE.message + .append(text("/dm meta list [page]", NamedTextColor.GRAY))), + UNSUPPORTED_META_TYPE(text("Unsupported meta type ! Supported values are: DOUBLE, INTEGER, LONG, STRING, BOOLEAN", NamedTextColor.RED)), + INVALID_META_KEY(text("An invalid meta key was provided: ''!", NamedTextColor.RED)), + + WRONG_USAGE_META_SET_COMMAND(WRONG_USAGE_BASE.message + .append(text("/dm meta set ", NamedTextColor.GRAY))), + WRONG_USAGE_META_REMOVE_COMMAND(WRONG_USAGE_BASE.message + .append(text("/dm meta remove ", NamedTextColor.GRAY))), + WRONG_USAGE_META_ADD_COMMAND(WRONG_USAGE_BASE.message + .append(text("/dm meta add ", NamedTextColor.GRAY))), + WRONG_USAGE_META_SUBTRACT_COMMAND(WRONG_USAGE_BASE.message + .append(text("/dm meta subtract ", NamedTextColor.GRAY))), + WRONG_USAGE_META_SWITCH_COMMAND(WRONG_USAGE_BASE.message + .append(text("/dm meta switch ", NamedTextColor.GRAY))), + WRONG_USAGE_META_LIST_COMMAND(WRONG_USAGE_BASE.message + .append(text("/dm meta list [page]", NamedTextColor.GRAY))), + WRONG_USAGE_META_SHOW_COMMAND(WRONG_USAGE_BASE.message + .append(text("/dm meta show ", NamedTextColor.GRAY))), + + META_SWITCH_TYPE_MISMATCH(text("", NamedTextColor.RED)), + META_VALUE_TYPE_MISMATCH(text("Given value does not match the given type!", NamedTextColor.RED)), + META_EXISTENT_VALUE_WRONG_TYPE(text("Given key stores a value with a different type!", NamedTextColor.RED)), + META_VALUE_NOT_FOUND(text("Could not find a meta value with given key and type for the given player!", NamedTextColor.RED)), + + WRONG_USAGE_OPEN_COMMAND(WRONG_USAGE_BASE.message - .append(text("/dm open [player]", NamedTextColor.GRAY))), + .append(text("/dm open [player]", NamedTextColor.GRAY))), PLAYER_IS_NOT_ONLINE(empty() - .append(text("Player:", NamedTextColor.RED)) - .append(space()) - .append(text("", NamedTextColor.WHITE)) - .append(space()) - .append(text("is not online!", NamedTextColor.RED)) + .append(text("Player:", NamedTextColor.RED)) + .append(space()) + .append(text("", NamedTextColor.WHITE)) + .append(space()) + .append(text("is not online!", NamedTextColor.RED)) ), PLAYER_IS_EXEMPT( text("", NamedTextColor.WHITE) - .append(space()) - .append(text("is exempt from placeholder target arguments.", NamedTextColor.GRAY))), + .append(space()) + .append(text("is exempt from placeholder target arguments.", NamedTextColor.GRAY))), + + META_NOT_SUPPORTED(text("Meta is not supported on this server version!", NamedTextColor.RED)), + NO_META_VALUE(text("Could not find a meta value with key ", NamedTextColor.RED) + .append(text("", NamedTextColor.GOLD)) + .append(text(" and type ", NamedTextColor.RED)) + .append(text("", NamedTextColor.GOLD)) + .append(text(" for ", NamedTextColor.RED)) + .append(text("", NamedTextColor.GOLD))), + NO_META_VALUES(text("Could not find any meta values with type ", NamedTextColor.RED) + .append(text("", NamedTextColor.GOLD)) + .append(text(" for ", NamedTextColor.RED)) + .append(text("", NamedTextColor.GOLD))), + META_VALUE_FOUND(text("Meta value with key ", NamedTextColor.GRAY) + .append(text("", NamedTextColor.GREEN)) + .append(text(" and type ", NamedTextColor.GRAY)) + .append(text("", NamedTextColor.GREEN)) + .append(text(" for ", NamedTextColor.GRAY)) + .append(text("", NamedTextColor.GREEN)) + .append(text(": ", NamedTextColor.GRAY)) + .append(text("", NamedTextColor.GREEN))), + META_VALUE_SET(text("Meta value with key ", NamedTextColor.GRAY) + .append(text("", NamedTextColor.GREEN)) + .append(text(" and type ", NamedTextColor.GRAY)) + .append(text("", NamedTextColor.GREEN)) + .append(text(" for ", NamedTextColor.GRAY)) + .append(text("", NamedTextColor.GREEN)) + .append(text(" set to: ", NamedTextColor.GRAY)) + .append(text("", NamedTextColor.GREEN))), + META_VALUE_REMOVED(text("Meta value with key ", NamedTextColor.GRAY) + .append(text("", NamedTextColor.GREEN)) + .append(text(" and type ", NamedTextColor.GRAY)) + .append(text("", NamedTextColor.GREEN)) + .append(text(" for ", NamedTextColor.GRAY)) + .append(text("", NamedTextColor.GREEN)) + .append(text(" removed.", NamedTextColor.GRAY))), + META_VALUE_ADDED(text("Added ", NamedTextColor.GRAY) + .append(text("", NamedTextColor.GREEN)) + .append(text(" to the meta value with key ", NamedTextColor.GRAY)) + .append(text("", NamedTextColor.GREEN)) + .append(text(" and type ", NamedTextColor.GRAY)) + .append(text("", NamedTextColor.GREEN)) + .append(text(" for ", NamedTextColor.GRAY)) + .append(text("", NamedTextColor.GREEN)) + .append(text(". New value: ", NamedTextColor.GRAY)) + .append(text("", NamedTextColor.GREEN))), + META_VALUE_SUBTRACTED(text("Subtracted ", NamedTextColor.GRAY) + .append(text("", NamedTextColor.GREEN)) + .append(text(" from the meta value with key ", NamedTextColor.GRAY)) + .append(text("", NamedTextColor.GREEN)) + .append(text(" and type ", NamedTextColor.GRAY)) + .append(text("", NamedTextColor.GREEN)) + .append(text(" for ", NamedTextColor.GRAY)) + .append(text("", NamedTextColor.GREEN)) + .append(text(". New value: ", NamedTextColor.GRAY)) + .append(text("", NamedTextColor.GREEN))), + META_VALUE_SWITCHED(text("Meta value with key ", NamedTextColor.GRAY) + .append(text("", NamedTextColor.GREEN)) + .append(text(" and type ", NamedTextColor.GRAY)) + .append(text("", NamedTextColor.GREEN)) + .append(text(" for ", NamedTextColor.GRAY)) + .append(text("", NamedTextColor.GREEN)) + .append(text(" switched to: ", NamedTextColor.GRAY)) + .append(text("", NamedTextColor.GREEN))), MUST_SPECIFY_PLAYER(text("You must specify a player to open a menu for!", NamedTextColor.RED)), WRONG_ACTION_TYPE(text("Action type specified does not exist!", NamedTextColor.RED)), CHANCE_FAIL(text("The chance for this action determined the action should not execute!", NamedTextColor.RED)), ACTION_TO_BE_EXECUTED(text("Action set to be executed in", NamedTextColor.GREEN) - .append(space()) - .append(text("")) - .append(space()) - .append(text("ticks."))), + .append(space()) + .append(text("")) + .append(space()) + .append(text("ticks."))), ACTION_EXECUTED_FOR(text("Action executed for player:", NamedTextColor.GREEN) - .append(space()) - .append(text(""))), + .append(space()) + .append(text(""))), RELOAD_FAIL(text("Errors detected in config.yml. Failed to reload.", NamedTextColor.RED)), RELOAD_SUCCESS(PLUGIN_TITLE.message - .append(space()) - .append(text("successfully reloaded!", NamedTextColor.GREEN))), + .append(space()) + .append(text("successfully reloaded!", NamedTextColor.GREEN))), INVALID_MENU(text("Could not find menu:", NamedTextColor.RED) - .append(space()) - .append(text("

", NamedTextColor.GOLD)) - .append(text(".", NamedTextColor.RED))), + .append(space()) + .append(text("", NamedTextColor.GOLD)) + .append(text(".", NamedTextColor.RED))), MENU_RELOADED(text("", NamedTextColor.GOLD) - .append(space()) - .append(text("menu successfully reloaded!", NamedTextColor.GREEN))), + .append(space()) + .append(text("menu successfully reloaded!", NamedTextColor.GREEN))), MENU_NOT_RELOADED(text("", NamedTextColor.GOLD) - .append(space()) - .append(text("menu could not be reloaded!", NamedTextColor.RED))), + .append(space()) + .append(text("menu could not be reloaded!", NamedTextColor.RED))), MENU_LOADED(text(" menu loaded...", NamedTextColor.YELLOW)), MENUS_LOADED(text(" menus loaded...", NamedTextColor.YELLOW)), @@ -115,41 +239,41 @@ public enum Messages { DUMP_SUCCESS(text("Dump created successfully! Find it at: ", NamedTextColor.GREEN)), UPDATE_AVAILABLE(text("An update for", NamedTextColor.GREEN) - .append(space()) - .append(Messages.PLUGIN_TITLE.message()) - .append(space()) - .append(text("is available. Version", NamedTextColor.GREEN)) - .append(space()) - .append(text("", NamedTextColor.WHITE)) - .append(text(", You are running", NamedTextColor.GREEN)) - .append(space()) - .append(text("", NamedTextColor.WHITE)) - .append(newline()) - .append(text("Download the latest version at:", NamedTextColor.GREEN)) - .append(space()) - .append(text("https://www.spigotmc.org/resources/deluxemenus.11734/", NamedTextColor.WHITE)) + .append(space()) + .append(Messages.PLUGIN_TITLE.message()) + .append(space()) + .append(text("is available. Version", NamedTextColor.GREEN)) + .append(space()) + .append(text("", NamedTextColor.WHITE)) + .append(text(", You are running", NamedTextColor.GREEN)) + .append(space()) + .append(text("", NamedTextColor.WHITE)) + .append(newline()) + .append(text("Download the latest version at:", NamedTextColor.GREEN)) + .append(space()) + .append(text("https://www.spigotmc.org/resources/deluxemenus.11734/", NamedTextColor.WHITE)) ); - Messages(final @NotNull Component message) { - this.message = message; - } - - private final Component message; - - public @NotNull Component message() { - return message; - } - private static final Map BY_NAME = new HashMap<>(); static { for (Messages message : values()) { - BY_NAME.put(message.name().toLowerCase(Locale.getDefault()), message); + BY_NAME.put(message.name().toLowerCase(Locale.ROOT), message); } } + private final Component message; + + Messages(final @NotNull Component message) { + this.message = message; + } + public static @Nullable Messages getMessage(@NotNull final String name) { - return BY_NAME.getOrDefault(name.toLowerCase(Locale.getDefault()), null); + return BY_NAME.getOrDefault(name.toLowerCase(Locale.ROOT), null); + } + + public @NotNull Component message() { + return message; } } diff --git a/src/main/java/com/extendedclip/deluxemenus/utils/PaginationUtils.java b/src/main/java/com/extendedclip/deluxemenus/utils/PaginationUtils.java new file mode 100644 index 00000000..4741907c --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/utils/PaginationUtils.java @@ -0,0 +1,44 @@ +package com.extendedclip.deluxemenus.utils; + +import com.google.common.primitives.Ints; +import org.jetbrains.annotations.Nullable; + +public class PaginationUtils { + private PaginationUtils() { + throw new AssertionError("Util classes should not be initialized"); + } + + /** + * Loose parsing of a page number. If the provided argument is not a number or is less than 1, 1 is returned. + * If the provided page number is greater than the maximum number of pages, the maximum number of pages is returned. + * @param itemsPerPage The number of items per page + * @param itemsCount The total number of items + * @param pages The maximum number of pages + * @param argument The argument to parse as a page number + * @return The parsed page number + */ + public static int parsePage(final int itemsPerPage, final int itemsCount, @Nullable final Integer pages, + @Nullable final String argument) { + if (itemsCount <= itemsPerPage || argument == null) { + return 1; + } + + final Integer page = Ints.tryParse(argument); + + if (page == null || page < 1) { + return 1; + } + + final int maxPages = pages != null ? pages : getPagesCount(itemsPerPage, itemsCount); + + if (page > maxPages) { + return maxPages; + } + + return page; + } + + public static int getPagesCount(final int itemsPerPage, final int itemsCount) { + return (int) Math.ceil((double) itemsCount / itemsPerPage); + } +} diff --git a/src/main/java/com/extendedclip/deluxemenus/utils/Pair.java b/src/main/java/com/extendedclip/deluxemenus/utils/Pair.java new file mode 100644 index 00000000..3ba367cc --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/utils/Pair.java @@ -0,0 +1,36 @@ +package com.extendedclip.deluxemenus.utils; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class Pair { + + private final K key; + private final V value; + + public static Pair of(@NotNull final K key, @Nullable final V value) { + return new Pair<>(key, value); + } + + public static Pair of(@NotNull final K key) { + return new Pair<>(key, null); + } + + public Pair(@NotNull final K key, @Nullable final V value) { + this.key = key; + this.value = value; + } + + public @NotNull K getKey() { + return key; + } + + public @Nullable V getValue() { + return value; + } + + @Override + public String toString() { + return "Pair{" + "key=" + key + ", value=" + key + '}'; + } +} From 32316ab6c3f021031388e662f6f42224cf3434de Mon Sep 17 00:00:00 2001 From: BlitzOffline <52609756+BlitzOffline@users.noreply.github.com> Date: Sat, 15 Mar 2025 00:40:19 +0200 Subject: [PATCH 2/9] fixed some stuff, not tested yet --- .../deluxemenus/action/ClickActionTask.java | 23 +- .../command/subcommand/MetaCommand.java | 325 ++++++++---------- .../persistentmeta/PersistentMetaHandler.java | 29 +- .../deluxemenus/placeholder/Expansion.java | 5 +- .../requirement/HasMetaRequirement.java | 3 +- .../deluxemenus/utils/Messages.java | 166 +++++---- 6 files changed, 270 insertions(+), 281 deletions(-) diff --git a/src/main/java/com/extendedclip/deluxemenus/action/ClickActionTask.java b/src/main/java/com/extendedclip/deluxemenus/action/ClickActionTask.java index 0c4a6052..971caa8c 100644 --- a/src/main/java/com/extendedclip/deluxemenus/action/ClickActionTask.java +++ b/src/main/java/com/extendedclip/deluxemenus/action/ClickActionTask.java @@ -3,6 +3,7 @@ import com.extendedclip.deluxemenus.DeluxeMenus; import com.extendedclip.deluxemenus.menu.Menu; import com.extendedclip.deluxemenus.menu.MenuHolder; +import com.extendedclip.deluxemenus.persistentmeta.PersistentMetaHandler; import com.extendedclip.deluxemenus.utils.AdventureUtils; import com.extendedclip.deluxemenus.utils.DebugLevel; import com.extendedclip.deluxemenus.utils.ExpUtils; @@ -80,10 +81,24 @@ public void run() { plugin.debug(DebugLevel.HIGHEST, Level.INFO, "Meta action not supported on this server version."); break; } - final boolean success = plugin.getPersistentMetaHandler().parseAndExecuteMetaActionFromString(player, executable); - if (!success) { - plugin.debug(DebugLevel.HIGHEST, Level.INFO, "Invalid meta action! Make sure you have the right syntax."); - break; + final PersistentMetaHandler.OperationResult result = plugin.getPersistentMetaHandler().parseAndExecuteMetaActionFromString(player, executable); + switch (result) { + case INVALID_SYNTAX: + plugin.debug(DebugLevel.HIGHEST, Level.INFO, "Invalid meta action! Make sure you have the right syntax."); + break; + case NEW_VALUE_IS_DIFFERENT_TYPE: + plugin.debug(DebugLevel.HIGHEST, Level.INFO, "Invalid meta action! New value is a different type than the old value!"); + break; + case INVALID_TYPE: + plugin.debug(DebugLevel.HIGHEST, Level.INFO, "Invalid meta action! The specified type is not supported for the specified action!"); + break; + case EXISTENT_VALUE_IS_DIFFERENT_TYPE: + plugin.debug(DebugLevel.HIGHEST, Level.INFO, "Invalid meta action! Existent value is a different type than the new value!"); + break; + case VALUE_NOT_FOUND: + case SUCCESS: + default: + break; } break; diff --git a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java index b98d728e..3bfcaf28 100644 --- a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java +++ b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java @@ -5,8 +5,6 @@ import com.extendedclip.deluxemenus.utils.Messages; import com.extendedclip.deluxemenus.utils.PaginationUtils; import com.extendedclip.deluxemenus.utils.VersionHelper; -import com.google.common.primitives.Doubles; -import com.google.common.primitives.Longs; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.format.NamedTextColor; @@ -19,6 +17,7 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; @@ -31,7 +30,7 @@ // TODO: // Test the command execution -// Test the tab completion +// Test the tab completion // Improve error messages (Better coloring, styling, etc.) public class MetaCommand extends SubCommand { @@ -60,7 +59,7 @@ public void execute(@NotNull final CommandSender sender, @NotNull final List context = new HashMap<>(); + context.put(ContextKeys.KEY_NAME, keyName); + + if (action == PersistentMetaHandler.DataAction.SWITCH) { + handleSwitchMeta(sender, target, namespacedKey, context); + return; + } + + if (arguments.size() < 4) { + sendWrongUsageMessage(sender, action); + return; + } + + final String typeName = arguments.get(3).toUpperCase(Locale.ROOT); + final PersistentDataType type = PersistentMetaHandler.getSupportedTypeByName(typeName); + if (type == null) { + plugin.sms(sender, Messages.META_TYPE_UNSUPPORTED.message().replaceText(TYPE_REPLACER_BUILDER.replacement(typeName).build())); + return; + } + + context.put(ContextKeys.KEY_NAME, keyName); + + if (action == PersistentMetaHandler.DataAction.REMOVE) { + handleRemoveMeta(sender, target, namespacedKey, type, context); + return; + } + + if (arguments.size() < 5) { + sendWrongUsageMessage(sender, action); + return; + } + + if (action == PersistentMetaHandler.DataAction.SET) { + handleSetMeta(sender, target, namespacedKey, type, String.join(" ", arguments.subList(4, arguments.size())), context); + return; + } + + if (action == PersistentMetaHandler.DataAction.ADD) { + handleAddMeta(sender, target, namespacedKey, type, String.join(" ", arguments.subList(4, arguments.size())), context); return; } - handleActionMeta(sender, target, action, arguments.subList(2, arguments.size())); + if (action == PersistentMetaHandler.DataAction.SUBTRACT) { + handleSubtractMeta(sender, target, namespacedKey, type, String.join(" ", arguments.subList(4, arguments.size())), context); + return; + } + + sendWrongUsageMessage(sender, action); } @Override @@ -149,29 +207,31 @@ public void execute(@NotNull final CommandSender sender, @NotNull final List(plugin.getPersistentMetaHandler().getSupportedTypes()); + return new ArrayList<>(PersistentMetaHandler.getSupportedTypes()); } - return plugin.getPersistentMetaHandler().getSupportedTypes().stream() + return PersistentMetaHandler.getSupportedTypes().stream() .filter(type -> type.startsWith(fourthArgument.toUpperCase(Locale.ROOT))) .collect(Collectors.toList()); } if (arguments.size() == 5) { - final String action = arguments.get(2).toLowerCase(); - if (action.equalsIgnoreCase("show") || plugin.getPersistentMetaHandler().getActionByName(action) != null) { - final String fifthArgument = arguments.get(4); + final String actionName = arguments.get(2).toLowerCase(); + final PersistentMetaHandler.DataAction action = PersistentMetaHandler.getActionByName(actionName); - if (fifthArgument.isEmpty()) { - return new ArrayList<>(plugin.getPersistentMetaHandler().getSupportedTypes()); - } + if (actionName.equalsIgnoreCase("list") || action == PersistentMetaHandler.DataAction.SWITCH) { + return null; + } + + final String fifthArgument = arguments.get(4); - return plugin.getPersistentMetaHandler().getSupportedTypes().stream() - .filter(type -> type.startsWith(fifthArgument.toUpperCase(Locale.ROOT))) - .collect(Collectors.toList()); + if (fifthArgument.isEmpty()) { + return new ArrayList<>(PersistentMetaHandler.getSupportedTypes()); } - return null; + return PersistentMetaHandler.getSupportedTypes().stream() + .filter(type -> type.startsWith(fifthArgument.toUpperCase(Locale.ROOT))) + .collect(Collectors.toList()); } return null; @@ -180,14 +240,14 @@ public void execute(@NotNull final CommandSender sender, @NotNull final List arguments) { if (arguments.isEmpty()) { - plugin.sms(sender, Messages.WRONG_USAGE_LIST_META_COMMAND); + plugin.sms(sender, Messages.WRONG_USAGE_META_LIST_COMMAND); return; } - final String typeName = arguments.get(0).toUpperCase(Locale.ROOT); - final PersistentDataType type = plugin.getPersistentMetaHandler().getSupportedTypeByName(typeName); + final String typeName = arguments.get(0).toUpperCase(Locale.ROOT); + final PersistentDataType type = PersistentMetaHandler.getSupportedTypeByName(typeName); if (type == null) { - plugin.sms(sender, Messages.UNSUPPORTED_META_TYPE.message().replaceText(TYPE_REPLACER_BUILDER.replacement(typeName).build())); + plugin.sms(sender, Messages.META_TYPE_UNSUPPORTED.message().replaceText(TYPE_REPLACER_BUILDER.replacement(typeName).build())); return; } @@ -254,21 +314,21 @@ private void handleShowMeta(@NotNull final CommandSender sender, @NotNull final @NotNull final List arguments) { if (arguments.size() < 2) { - plugin.sms(sender, Messages.WRONG_USAGE_SHOW_META_COMMAND); + plugin.sms(sender, Messages.WRONG_USAGE_META_SHOW_COMMAND); return; } final String keyName = arguments.get(0); final NamespacedKey namespacedKey = plugin.getPersistentMetaHandler().getKey(keyName); if (namespacedKey == null) { - plugin.sms(sender, Messages.INVALID_META_KEY.message().replaceText(KEY_REPLACER_BUILDER.replacement(keyName).build())); + plugin.sms(sender, Messages.META_KEY_INVALID.message().replaceText(KEY_REPLACER_BUILDER.replacement(keyName).build())); return; } final String typeName = arguments.get(1).toUpperCase(Locale.ROOT); - final PersistentDataType type = plugin.getPersistentMetaHandler().getSupportedTypeByName(typeName); + final PersistentDataType type = PersistentMetaHandler.getSupportedTypeByName(typeName); if (type == null) { - plugin.sms(sender, Messages.UNSUPPORTED_META_TYPE); + plugin.sms(sender, Messages.META_TYPE_UNSUPPORTED.message().replaceText(TYPE_REPLACER_BUILDER.replacement(typeName).build())); return; } @@ -291,127 +351,12 @@ private void handleShowMeta(@NotNull final CommandSender sender, @NotNull final ); } - private void handleActionMeta(@NotNull final CommandSender sender, @NotNull final Player target, - @NotNull final PersistentMetaHandler.DataAction action, - @NotNull final List arguments) { - if (arguments.size() < 3) { - if (action == PersistentMetaHandler.DataAction.REMOVE || action == PersistentMetaHandler.DataAction.SWITCH) { - plugin.sms(sender, Messages.WRONG_USAGE_NO_VALUE_META_COMMAND); - return; - } - - plugin.sms(sender, Messages.WRONG_USAGE_VALUE_META_COMMAND); - return; - } - - final String keyName = arguments.get(1); - final NamespacedKey namespacedKey = plugin.getPersistentMetaHandler().getKey(keyName); - if (namespacedKey == null) { - plugin.sms(sender, Messages.INVALID_META_KEY.message().replaceText(KEY_REPLACER_BUILDER.replacement(keyName).build())); - return; - } - - final String typeName = arguments.get(2).toUpperCase(Locale.ROOT); - final PersistentDataType type = plugin.getPersistentMetaHandler().getSupportedTypeByName(typeName); - if (type == null) { - plugin.sms(sender, Messages.UNSUPPORTED_META_TYPE.message().replaceText(TYPE_REPLACER_BUILDER.replacement(typeName).build())); - return; - } - - if (action == PersistentMetaHandler.DataAction.SET) { - if (arguments.size() < 4) { - plugin.sms(sender, Messages.WRONG_USAGE_META_SET_COMMAND); - return; - } - - final String value = String.join(" ", arguments); - - if (!isValidValueForTypeName(typeName, value)) { - plugin.sms(sender, Messages.WRONG_USAGE_META_SET_COMMAND); - return; - } - - handleSetMeta(sender, target, namespacedKey, type, value); - return; - } - - if (action == PersistentMetaHandler.DataAction.REMOVE) { - handleRemoveMeta(sender, target, namespacedKey, type); - return; - } - - if (action == PersistentMetaHandler.DataAction.SWITCH) { - handleSwitchMeta(sender, target, namespacedKey, type); - return; - } - - if (action == PersistentMetaHandler.DataAction.ADD) { - if (arguments.size() < 4) { - plugin.sms(sender, Messages.WRONG_USAGE_VALUE_META_COMMAND); - return; - } - - final String value = String.join(" ", arguments); - - if (!isValidValueForTypeName(typeName, value)) { - plugin.sms(sender, Messages.WRONG_USAGE_VALUE_META_COMMAND); - return; - } - - handleAddMeta(sender, target, namespacedKey, type, value); - return; - } - - if (action == PersistentMetaHandler.DataAction.SUBTRACT) { - if (arguments.size() < 4) { - plugin.sms(sender, Messages.WRONG_USAGE_VALUE_META_COMMAND); - return; - } - - final String value = String.join(" ", arguments); - - if (!isValidValueForTypeName(typeName, value)) { - plugin.sms(sender, Messages.WRONG_USAGE_VALUE_META_COMMAND); - return; - } - - handleSubtractMeta(sender, target, namespacedKey, type, value); - return; - } - - plugin.sms(sender, Messages.WRONG_USAGE_META_COMMAND); - } - - private boolean isValidValueForTypeName(@NotNull final String typeName, @Nullable final String value) { - if (value == null) { - return false; - } - - if (typeName.equalsIgnoreCase("boolean")) { - return value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false"); - } - - if (typeName.equalsIgnoreCase("string")) { - return true; - } - - if (typeName.equalsIgnoreCase("double") || typeName.equalsIgnoreCase("float")) { - return Doubles.tryParse(value) != null; - } - - if (typeName.equalsIgnoreCase("long") || typeName.equalsIgnoreCase("integer")) { - return Longs.tryParse(value) != null; - } - - return false; - } - private void handleSetMeta(@NotNull final CommandSender sender, @NotNull final Player target, @NotNull final NamespacedKey namespacedKey, @NotNull final PersistentDataType type, - @NotNull final String value) { + @NotNull final String value, + @NotNull final Map context) { final Object parsedValue = plugin.getPersistentMetaHandler().parseValueByType(type, value); - if (parsedValue == null) { plugin.sms(sender, Messages.META_VALUE_TYPE_MISMATCH); return; @@ -421,8 +366,8 @@ private void handleSetMeta(@NotNull final CommandSender sender, @NotNull final P switch (result) { case SUCCESS: plugin.sms(sender, Messages.META_VALUE_SET.message() - .replaceText(KEY_REPLACER_BUILDER.replacement(namespacedKey.toString()).build()) - .replaceText(TYPE_REPLACER_BUILDER.replacement(type.getComplexType().getSimpleName()).build()) + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build()) + .replaceText(TYPE_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build()) .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build()) .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) ); @@ -440,13 +385,14 @@ private void handleSetMeta(@NotNull final CommandSender sender, @NotNull final P private void handleRemoveMeta(@NotNull final CommandSender sender, @NotNull final Player target, @NotNull final NamespacedKey namespacedKey, - @NotNull final PersistentDataType type) { + @NotNull final PersistentDataType type, + @NotNull final Map context) { final PersistentMetaHandler.OperationResult result = plugin.getPersistentMetaHandler().removeMetaValue(target, namespacedKey, type); switch (result) { case SUCCESS: plugin.sms(sender, Messages.META_VALUE_REMOVED.message() - .replaceText(KEY_REPLACER_BUILDER.replacement(namespacedKey.toString()).build()) - .replaceText(TYPE_REPLACER_BUILDER.replacement(type.getComplexType().getSimpleName()).build()) + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build()) + .replaceText(TYPE_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build()) .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) ); return; @@ -454,7 +400,11 @@ private void handleRemoveMeta(@NotNull final CommandSender sender, @NotNull fina plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE); return; case VALUE_NOT_FOUND: - plugin.sms(sender, Messages.META_VALUE_NOT_FOUND); + plugin.sms(sender, Messages.NO_META_VALUE.message() + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build()) + .replaceText(TYPE_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build()) + .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) + ); return; default: plugin.sms(sender, Messages.WRONG_USAGE_META_REMOVE_COMMAND); @@ -462,27 +412,21 @@ private void handleRemoveMeta(@NotNull final CommandSender sender, @NotNull fina } private void handleSwitchMeta(@NotNull final CommandSender sender, @NotNull final Player target, - @NotNull final NamespacedKey namespacedKey, - @NotNull final PersistentDataType type) { - final PersistentMetaHandler.OperationResult result = plugin.getPersistentMetaHandler().switchMetaValue(target, namespacedKey, type); + @NotNull final NamespacedKey namespacedKey, @NotNull final Map context) { + final PersistentMetaHandler.OperationResult result = plugin.getPersistentMetaHandler().switchMetaValue(target, namespacedKey); switch (result) { case SUCCESS: - final Object newValue = plugin.getPersistentMetaHandler().getMetaValue(target, namespacedKey, type); + final Object newValue = plugin.getPersistentMetaHandler().getMetaValue(target, namespacedKey, PersistentDataType.STRING); plugin.sms(sender, Messages.META_VALUE_SWITCHED.message() - .replaceText(KEY_REPLACER_BUILDER.replacement(namespacedKey.toString()).build()) - .replaceText(TYPE_REPLACER_BUILDER.replacement(type.getComplexType().getSimpleName()).build()) + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build()) .replaceText(NEW_VALUE_REPLACER_BUILDER.replacement(String.valueOf(newValue)).build()) .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) ); return; - case INVALID_TYPE: - plugin.sms(sender, Messages.META_SWITCH_TYPE_MISMATCH); - // TODO: Send a custom message to the sender. - return; case EXISTENT_VALUE_IS_DIFFERENT_TYPE: - // TODO: Send a custom message to the sender. + plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE); return; default: plugin.sms(sender, Messages.WRONG_USAGE_META_SWITCH_COMMAND); @@ -492,11 +436,12 @@ private void handleSwitchMeta(@NotNull final CommandSender sender, @NotNull fina private void handleAddMeta(@NotNull final CommandSender sender, @NotNull final Player target, @NotNull final NamespacedKey namespacedKey, @NotNull final PersistentDataType type, - @NotNull final String value) { + @NotNull final String value, + @NotNull final Map context) { final Object parsedValue = plugin.getPersistentMetaHandler().parseValueByType(type, value); if (!(parsedValue instanceof Number)) { - plugin.sms(sender, Messages.WRONG_USAGE_VALUE_META_COMMAND); + plugin.sms(sender, Messages.META_ADD_TYPE_MISMATCH); return; } @@ -507,18 +452,18 @@ private void handleAddMeta(@NotNull final CommandSender sender, @NotNull final P final Object newValue = plugin.getPersistentMetaHandler().getMetaValue(target, namespacedKey, type); plugin.sms(sender, Messages.META_VALUE_ADDED.message() - .replaceText(KEY_REPLACER_BUILDER.replacement(namespacedKey.toString()).build()) - .replaceText(TYPE_REPLACER_BUILDER.replacement(type.getComplexType().getSimpleName()).build()) + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build()) + .replaceText(TYPE_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build()) .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build()) .replaceText(NEW_VALUE_REPLACER_BUILDER.replacement(String.valueOf(newValue)).build()) .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) ); return; case INVALID_TYPE: - // TODO: Send a custom message to the sender. + plugin.sms(sender, Messages.META_ADD_TYPE_MISMATCH); return; case EXISTENT_VALUE_IS_DIFFERENT_TYPE: - // TODO: Send a custom message to the sender. + plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE); return; default: plugin.sms(sender, Messages.WRONG_USAGE_META_ADD_COMMAND); @@ -528,11 +473,12 @@ private void handleAddMeta(@NotNull final CommandSender sender, @NotNull final P private void handleSubtractMeta(@NotNull final CommandSender sender, @NotNull final Player target, @NotNull final NamespacedKey namespacedKey, @NotNull final PersistentDataType type, - @NotNull final String value) { + @NotNull final String value, + @NotNull final Map context) { final Object parsedValue = plugin.getPersistentMetaHandler().parseValueByType(type, value); if (!(parsedValue instanceof Number)) { - // TODO: Send a custom message to the sender. + plugin.sms(sender, Messages.META_SUBTRACT_TYPE_MISMATCH); return; } @@ -543,21 +489,54 @@ private void handleSubtractMeta(@NotNull final CommandSender sender, @NotNull fi final Object newValue = plugin.getPersistentMetaHandler().getMetaValue(target, namespacedKey, type); plugin.sms(sender, Messages.META_VALUE_SUBTRACTED.message() - .replaceText(KEY_REPLACER_BUILDER.replacement(namespacedKey.toString()).build()) - .replaceText(TYPE_REPLACER_BUILDER.replacement(type.getComplexType().getSimpleName()).build()) + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build()) + .replaceText(TYPE_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build()) .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build()) .replaceText(NEW_VALUE_REPLACER_BUILDER.replacement(String.valueOf(newValue)).build()) .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) ); return; case INVALID_TYPE: - // TODO: Send a custom message to the sender. + plugin.sms(sender, Messages.META_SUBTRACT_TYPE_MISMATCH); return; case EXISTENT_VALUE_IS_DIFFERENT_TYPE: - // TODO: Send a custom message to the sender. + plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE); return; default: plugin.sms(sender, Messages.WRONG_USAGE_META_SUBTRACT_COMMAND); } } + + private void sendWrongUsageMessage(@NotNull final CommandSender sender, @Nullable final PersistentMetaHandler.DataAction action) { + if (action == null) { + plugin.sms(sender, Messages.WRONG_USAGE_META_COMMAND); + return; + } + + switch (action) { + case SET: + plugin.sms(sender, Messages.WRONG_USAGE_META_SET_COMMAND); + return; + case REMOVE: + plugin.sms(sender, Messages.WRONG_USAGE_META_REMOVE_COMMAND); + return; + case ADD: + plugin.sms(sender, Messages.WRONG_USAGE_META_ADD_COMMAND); + return; + case SUBTRACT: + plugin.sms(sender, Messages.WRONG_USAGE_META_SUBTRACT_COMMAND); + return; + case SWITCH: + plugin.sms(sender, Messages.WRONG_USAGE_META_SWITCH_COMMAND); + return; + default: + plugin.sms(sender, Messages.WRONG_USAGE_META_COMMAND); + } + } + + public enum ContextKeys { + KEY_NAME, + TYPE_NAME, + VALUE, + } } diff --git a/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java b/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java index b5c7b9b6..39db6d2b 100644 --- a/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java +++ b/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java @@ -193,14 +193,9 @@ public Map getMetaValues( */ public @NotNull OperationResult switchMetaValue( @NotNull final Player player, - @NotNull final NamespacedKey key, - @NotNull final PersistentDataType type + @NotNull final NamespacedKey key ) { - if (type != PersistentDataType.STRING) { - return OperationResult.INVALID_TYPE; - } - - if (player.getPersistentDataContainer().has(key) && !player.getPersistentDataContainer().has(key, type)) { + if (player.getPersistentDataContainer().has(key) && !player.getPersistentDataContainer().has(key, PersistentDataType.STRING)) { return OperationResult.EXISTENT_VALUE_IS_DIFFERENT_TYPE; } @@ -304,10 +299,10 @@ public Map getMetaValues( @NotNull final Player player, @NotNull final String input ) { - // [value] + // [type] [value] - type is optional for switch action since it only toggles a boolean String[] args = input.split(" ", 4); - if (args.length < 3) { + if (args.length < 2) { return OperationResult.INVALID_SYNTAX; } @@ -321,6 +316,14 @@ public Map getMetaValues( return OperationResult.INVALID_SYNTAX; } + if (action == DataAction.SWITCH) { + return switchMetaValue(player, key); + } + + if (args.length < 3) { + return OperationResult.INVALID_SYNTAX; + } + final PersistentDataType type = getSupportedTypeByName(args[2]); if (type == null) { return OperationResult.INVALID_SYNTAX; @@ -337,8 +340,6 @@ public Map getMetaValues( return setMetaValue(player, key, type, parsedValue); case REMOVE: return removeMetaValue(player, key, type); - case SWITCH: - return switchMetaValue(player, key, type); case ADD: if (!(parsedValue instanceof Number)) { return OperationResult.NEW_VALUE_IS_DIFFERENT_TYPE; @@ -402,7 +403,7 @@ public Map getMetaValues( * * @return A set of all supported types. */ - public @NotNull Set getSupportedTypes() { + public static @NotNull Set getSupportedTypes() { return SUPPORTED_TYPES.keySet(); } @@ -412,7 +413,7 @@ public Map getMetaValues( * @param name The name of the type. * @return The type, or null if type does not exist or is not supported. */ - public @Nullable PersistentDataType getSupportedTypeByName(@NotNull final String name) { + public static @Nullable PersistentDataType getSupportedTypeByName(@NotNull final String name) { return SUPPORTED_TYPES.get(name.toUpperCase(Locale.ROOT)); } @@ -422,7 +423,7 @@ public Map getMetaValues( * @param name The name of the action type. * @return The {@link DataAction} or null if it does not exist. */ - public @Nullable DataAction getActionByName(@NotNull final String name) { + public static @Nullable DataAction getActionByName(@NotNull final String name) { return DataAction.getByName(name); } diff --git a/src/main/java/com/extendedclip/deluxemenus/placeholder/Expansion.java b/src/main/java/com/extendedclip/deluxemenus/placeholder/Expansion.java index ff4b9b23..c7849b46 100644 --- a/src/main/java/com/extendedclip/deluxemenus/placeholder/Expansion.java +++ b/src/main/java/com/extendedclip/deluxemenus/placeholder/Expansion.java @@ -3,6 +3,7 @@ import com.extendedclip.deluxemenus.DeluxeMenus; import com.extendedclip.deluxemenus.menu.Menu; import com.extendedclip.deluxemenus.menu.options.MenuOptions; +import com.extendedclip.deluxemenus.persistentmeta.PersistentMetaHandler; import com.extendedclip.deluxemenus.utils.VersionHelper; import me.clip.placeholderapi.PlaceholderAPI; import me.clip.placeholderapi.PlaceholderAPIPlugin; @@ -80,7 +81,7 @@ public boolean persist() { return "INVALID_KEY"; } - final PersistentDataType type = plugin.getPersistentMetaHandler().getSupportedTypeByName(hasValueParts[1]); + final PersistentDataType type = PersistentMetaHandler.getSupportedTypeByName(hasValueParts[1]); if (type == null) { return "INVALID_TYPE"; } @@ -107,7 +108,7 @@ public boolean persist() { return "INVALID_KEY"; } - final PersistentDataType type = plugin.getPersistentMetaHandler().getSupportedTypeByName(parts[1]); + final PersistentDataType type = PersistentMetaHandler.getSupportedTypeByName(parts[1]); if (type == null) { return "INVALID_TYPE"; } diff --git a/src/main/java/com/extendedclip/deluxemenus/requirement/HasMetaRequirement.java b/src/main/java/com/extendedclip/deluxemenus/requirement/HasMetaRequirement.java index 76c450aa..0c350282 100644 --- a/src/main/java/com/extendedclip/deluxemenus/requirement/HasMetaRequirement.java +++ b/src/main/java/com/extendedclip/deluxemenus/requirement/HasMetaRequirement.java @@ -2,6 +2,7 @@ import com.extendedclip.deluxemenus.DeluxeMenus; import com.extendedclip.deluxemenus.menu.MenuHolder; +import com.extendedclip.deluxemenus.persistentmeta.PersistentMetaHandler; import org.bukkit.NamespacedKey; import org.bukkit.entity.Player; import org.bukkit.persistence.PersistentDataType; @@ -35,7 +36,7 @@ public boolean evaluate(MenuHolder holder) { return invert; } - final PersistentDataType type = plugin.getPersistentMetaHandler().getSupportedTypeByName(typeName); + final PersistentDataType type = PersistentMetaHandler.getSupportedTypeByName(typeName); if (type == null) { return invert; } diff --git a/src/main/java/com/extendedclip/deluxemenus/utils/Messages.java b/src/main/java/com/extendedclip/deluxemenus/utils/Messages.java index cf44f799..d3e60d64 100644 --- a/src/main/java/com/extendedclip/deluxemenus/utils/Messages.java +++ b/src/main/java/com/extendedclip/deluxemenus/utils/Messages.java @@ -1,5 +1,6 @@ package com.extendedclip.deluxemenus.utils; +import com.extendedclip.deluxemenus.persistentmeta.PersistentMetaHandler; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextDecoration; @@ -92,39 +93,6 @@ public enum Messages { .append(text("/dm execute ", NamedTextColor.GRAY))), WRONG_USAGE_DUMP_COMMAND(WRONG_USAGE_BASE.message .append(text("/dm dump ", NamedTextColor.GRAY))), - WRONG_USAGE_META_COMMAND(WRONG_USAGE_BASE.message - .append(text("/dm meta ", NamedTextColor.GRAY))), - WRONG_USAGE_VALUE_META_COMMAND(WRONG_USAGE_BASE.message - .append(text("/dm meta ", NamedTextColor.GRAY))), - WRONG_USAGE_NO_VALUE_META_COMMAND(WRONG_USAGE_BASE.message - .append(text("/dm meta ", NamedTextColor.GRAY))), - WRONG_USAGE_SHOW_META_COMMAND(WRONG_USAGE_BASE.message - .append(text("/dm meta show ", NamedTextColor.GRAY))), - WRONG_USAGE_LIST_META_COMMAND(WRONG_USAGE_BASE.message - .append(text("/dm meta list [page]", NamedTextColor.GRAY))), - UNSUPPORTED_META_TYPE(text("Unsupported meta type ! Supported values are: DOUBLE, INTEGER, LONG, STRING, BOOLEAN", NamedTextColor.RED)), - INVALID_META_KEY(text("An invalid meta key was provided: ''!", NamedTextColor.RED)), - - WRONG_USAGE_META_SET_COMMAND(WRONG_USAGE_BASE.message - .append(text("/dm meta set ", NamedTextColor.GRAY))), - WRONG_USAGE_META_REMOVE_COMMAND(WRONG_USAGE_BASE.message - .append(text("/dm meta remove ", NamedTextColor.GRAY))), - WRONG_USAGE_META_ADD_COMMAND(WRONG_USAGE_BASE.message - .append(text("/dm meta add ", NamedTextColor.GRAY))), - WRONG_USAGE_META_SUBTRACT_COMMAND(WRONG_USAGE_BASE.message - .append(text("/dm meta subtract ", NamedTextColor.GRAY))), - WRONG_USAGE_META_SWITCH_COMMAND(WRONG_USAGE_BASE.message - .append(text("/dm meta switch ", NamedTextColor.GRAY))), - WRONG_USAGE_META_LIST_COMMAND(WRONG_USAGE_BASE.message - .append(text("/dm meta list [page]", NamedTextColor.GRAY))), - WRONG_USAGE_META_SHOW_COMMAND(WRONG_USAGE_BASE.message - .append(text("/dm meta show ", NamedTextColor.GRAY))), - - META_SWITCH_TYPE_MISMATCH(text("", NamedTextColor.RED)), - META_VALUE_TYPE_MISMATCH(text("Given value does not match the given type!", NamedTextColor.RED)), - META_EXISTENT_VALUE_WRONG_TYPE(text("Given key stores a value with a different type!", NamedTextColor.RED)), - META_VALUE_NOT_FOUND(text("Could not find a meta value with given key and type for the given player!", NamedTextColor.RED)), - WRONG_USAGE_OPEN_COMMAND(WRONG_USAGE_BASE.message .append(text("/dm open [player]", NamedTextColor.GRAY))), @@ -140,6 +108,83 @@ public enum Messages { .append(space()) .append(text("is exempt from placeholder target arguments.", NamedTextColor.GRAY))), + MUST_SPECIFY_PLAYER(text("You must specify a player to open a menu for!", NamedTextColor.RED)), + WRONG_ACTION_TYPE(text("Action type specified does not exist!", NamedTextColor.RED)), + CHANCE_FAIL(text("The chance for this action determined the action should not execute!", NamedTextColor.RED)), + + ACTION_TO_BE_EXECUTED(text("Action set to be executed in", NamedTextColor.GREEN) + .append(space()) + .append(text("")) + .append(space()) + .append(text("ticks."))), + ACTION_EXECUTED_FOR(text("Action executed for player:", NamedTextColor.GREEN) + .append(space()) + .append(text(""))), + + RELOAD_FAIL(text("Errors detected in config.yml. Failed to reload.", NamedTextColor.RED)), + RELOAD_SUCCESS(PLUGIN_TITLE.message + .append(space()) + .append(text("successfully reloaded!", NamedTextColor.GREEN))), + + INVALID_MENU(text("Could not find menu:", NamedTextColor.RED) + .append(space()) + .append(text("", NamedTextColor.GOLD)) + .append(text(".", NamedTextColor.RED))), + MENU_RELOADED(text("", NamedTextColor.GOLD) + .append(space()) + .append(text("menu successfully reloaded!", NamedTextColor.GREEN))), + MENU_NOT_RELOADED(text("", NamedTextColor.GOLD) + .append(space()) + .append(text("menu could not be reloaded!", NamedTextColor.RED))), + MENU_LOADED(text(" menu loaded...", NamedTextColor.YELLOW)), + MENUS_LOADED(text(" menus loaded...", NamedTextColor.YELLOW)), + + DUMP_FAILED(text("Failed to create and post dump!", NamedTextColor.RED)), + + DUMP_SUCCESS(text("Dump created successfully! Find it at: ", NamedTextColor.GREEN)), + + UPDATE_AVAILABLE(text("An update for", NamedTextColor.GREEN) + .append(space()) + .append(Messages.PLUGIN_TITLE.message()) + .append(space()) + .append(text("is available. Version", NamedTextColor.GREEN)) + .append(space()) + .append(text("", NamedTextColor.WHITE)) + .append(text(", You are running", NamedTextColor.GREEN)) + .append(space()) + .append(text("", NamedTextColor.WHITE)) + .append(newline()) + .append(text("Download the latest version at:", NamedTextColor.GREEN)) + .append(space()) + .append(text("https://www.spigotmc.org/resources/deluxemenus.11734/", NamedTextColor.WHITE)) + + ), + + // Meta related messages + WRONG_USAGE_META_COMMAND(WRONG_USAGE_BASE.message + .append(text("/dm meta ", NamedTextColor.GRAY))), + + WRONG_USAGE_META_LIST_COMMAND(WRONG_USAGE_BASE.message + .append(text("/dm meta list [page]", NamedTextColor.GRAY))), + WRONG_USAGE_META_SWITCH_COMMAND(WRONG_USAGE_BASE.message + .append(text("/dm meta switch ", NamedTextColor.GRAY))), + WRONG_USAGE_META_SHOW_COMMAND(WRONG_USAGE_BASE.message + .append(text("/dm meta show ", NamedTextColor.GRAY))), + WRONG_USAGE_META_REMOVE_COMMAND(WRONG_USAGE_BASE.message + .append(text("/dm meta remove ", NamedTextColor.GRAY))), + WRONG_USAGE_META_SET_COMMAND(WRONG_USAGE_BASE.message + .append(text("/dm meta set ", NamedTextColor.GRAY))), + WRONG_USAGE_META_ADD_COMMAND(WRONG_USAGE_BASE.message + .append(text("/dm meta add ", NamedTextColor.GRAY))), + WRONG_USAGE_META_SUBTRACT_COMMAND(WRONG_USAGE_BASE.message + .append(text("/dm meta subtract ", NamedTextColor.GRAY))), + + META_TYPE_UNSUPPORTED(text("Unsupported meta type ! Supported values are: " + String.join(", ", PersistentMetaHandler.getSupportedTypes()), NamedTextColor.RED)), + META_KEY_INVALID(text("An invalid meta key was provided: ''!", NamedTextColor.RED)), + META_ADD_TYPE_MISMATCH(text("Only NUMBERS can be added!", NamedTextColor.RED)), + META_SUBTRACT_TYPE_MISMATCH(text("Only NUMBERS can be subtracted!", NamedTextColor.RED)), + META_VALUE_TYPE_MISMATCH(text("Given value does not match the given type!", NamedTextColor.RED)), + META_EXISTENT_VALUE_WRONG_TYPE(text("Given key stores a value with a different type!", NamedTextColor.RED)), META_NOT_SUPPORTED(text("Meta is not supported on this server version!", NamedTextColor.RED)), NO_META_VALUE(text("Could not find a meta value with key ", NamedTextColor.RED) .append(text("", NamedTextColor.GOLD)) @@ -196,64 +241,11 @@ public enum Messages { .append(text("", NamedTextColor.GREEN))), META_VALUE_SWITCHED(text("Meta value with key ", NamedTextColor.GRAY) .append(text("", NamedTextColor.GREEN)) - .append(text(" and type ", NamedTextColor.GRAY)) - .append(text("", NamedTextColor.GREEN)) .append(text(" for ", NamedTextColor.GRAY)) .append(text("", NamedTextColor.GREEN)) .append(text(" switched to: ", NamedTextColor.GRAY)) - .append(text("", NamedTextColor.GREEN))), - - MUST_SPECIFY_PLAYER(text("You must specify a player to open a menu for!", NamedTextColor.RED)), - WRONG_ACTION_TYPE(text("Action type specified does not exist!", NamedTextColor.RED)), - CHANCE_FAIL(text("The chance for this action determined the action should not execute!", NamedTextColor.RED)), - - ACTION_TO_BE_EXECUTED(text("Action set to be executed in", NamedTextColor.GREEN) - .append(space()) - .append(text("")) - .append(space()) - .append(text("ticks."))), - ACTION_EXECUTED_FOR(text("Action executed for player:", NamedTextColor.GREEN) - .append(space()) - .append(text(""))), - - RELOAD_FAIL(text("Errors detected in config.yml. Failed to reload.", NamedTextColor.RED)), - RELOAD_SUCCESS(PLUGIN_TITLE.message - .append(space()) - .append(text("successfully reloaded!", NamedTextColor.GREEN))), - - INVALID_MENU(text("Could not find menu:", NamedTextColor.RED) - .append(space()) - .append(text("", NamedTextColor.GOLD)) - .append(text(".", NamedTextColor.RED))), - MENU_RELOADED(text("", NamedTextColor.GOLD) - .append(space()) - .append(text("menu successfully reloaded!", NamedTextColor.GREEN))), - MENU_NOT_RELOADED(text("", NamedTextColor.GOLD) - .append(space()) - .append(text("menu could not be reloaded!", NamedTextColor.RED))), - MENU_LOADED(text(" menu loaded...", NamedTextColor.YELLOW)), - MENUS_LOADED(text(" menus loaded...", NamedTextColor.YELLOW)), - - DUMP_FAILED(text("Failed to create and post dump!", NamedTextColor.RED)), - - DUMP_SUCCESS(text("Dump created successfully! Find it at: ", NamedTextColor.GREEN)), - - UPDATE_AVAILABLE(text("An update for", NamedTextColor.GREEN) - .append(space()) - .append(Messages.PLUGIN_TITLE.message()) - .append(space()) - .append(text("is available. Version", NamedTextColor.GREEN)) - .append(space()) - .append(text("", NamedTextColor.WHITE)) - .append(text(", You are running", NamedTextColor.GREEN)) - .append(space()) - .append(text("", NamedTextColor.WHITE)) - .append(newline()) - .append(text("Download the latest version at:", NamedTextColor.GREEN)) - .append(space()) - .append(text("https://www.spigotmc.org/resources/deluxemenus.11734/", NamedTextColor.WHITE)) + .append(text("", NamedTextColor.GREEN))); - ); private static final Map BY_NAME = new HashMap<>(); From 9e689dfb154dad55e0f65635fb0b02878b160cb4 Mon Sep 17 00:00:00 2001 From: BlitzOffline <52609756+BlitzOffline@users.noreply.github.com> Date: Sun, 23 Mar 2025 16:18:45 +0200 Subject: [PATCH 3/9] General improvements and fixes for Meta. - Improved some error messages, - Added a bridge DataType between strings and supported PDTs - Improved the meta_ placeholders - Made the type optional for meta_has_value placeholder - PMH#hasMeta now only checks the supported types if no type is specified. --- .../command/subcommand/MetaCommand.java | 53 +++--- .../persistentmeta/DataAction.java | 27 +++ .../deluxemenus/persistentmeta/DataType.java | 92 +++++++++ .../persistentmeta/PersistentMetaHandler.java | 176 ++++++------------ .../deluxemenus/placeholder/Expansion.java | 132 +++++++------ .../requirement/HasMetaRequirement.java | 61 +++--- .../deluxemenus/utils/Messages.java | 11 +- 7 files changed, 315 insertions(+), 237 deletions(-) create mode 100644 src/main/java/com/extendedclip/deluxemenus/persistentmeta/DataAction.java create mode 100644 src/main/java/com/extendedclip/deluxemenus/persistentmeta/DataType.java diff --git a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java index 3bfcaf28..f73ffd4c 100644 --- a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java +++ b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java @@ -1,6 +1,8 @@ package com.extendedclip.deluxemenus.command.subcommand; import com.extendedclip.deluxemenus.DeluxeMenus; +import com.extendedclip.deluxemenus.persistentmeta.DataAction; +import com.extendedclip.deluxemenus.persistentmeta.DataType; import com.extendedclip.deluxemenus.persistentmeta.PersistentMetaHandler; import com.extendedclip.deluxemenus.utils.Messages; import com.extendedclip.deluxemenus.utils.PaginationUtils; @@ -12,7 +14,6 @@ import org.bukkit.NamespacedKey; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -28,11 +29,7 @@ import static net.kyori.adventure.text.Component.newline; import static net.kyori.adventure.text.Component.text; -// TODO: -// Test the command execution -// Test the tab completion -// Improve error messages (Better coloring, styling, etc.) - +// TODO: Add placeholders support? public class MetaCommand extends SubCommand { private static final List SUB_COMMANDS = List.of("list", "show", "set", "remove", "add", "subtract", "switch"); @@ -71,7 +68,7 @@ public void execute(@NotNull final CommandSender sender, @NotNull final List context = new HashMap<>(); context.put(ContextKeys.KEY_NAME, keyName); - if (action == PersistentMetaHandler.DataAction.SWITCH) { + if (action == DataAction.SWITCH) { handleSwitchMeta(sender, target, namespacedKey, context); return; } @@ -114,15 +111,15 @@ public void execute(@NotNull final CommandSender sender, @NotNull final List type = PersistentMetaHandler.getSupportedTypeByName(typeName); + final DataType type = DataType.getSupportedTypeByName(typeName); if (type == null) { plugin.sms(sender, Messages.META_TYPE_UNSUPPORTED.message().replaceText(TYPE_REPLACER_BUILDER.replacement(typeName).build())); return; } - context.put(ContextKeys.KEY_NAME, keyName); + context.put(ContextKeys.TYPE_NAME, typeName); - if (action == PersistentMetaHandler.DataAction.REMOVE) { + if (action == DataAction.REMOVE) { handleRemoveMeta(sender, target, namespacedKey, type, context); return; } @@ -132,17 +129,17 @@ public void execute(@NotNull final CommandSender sender, @NotNull final List(PersistentMetaHandler.getSupportedTypes()); + return new ArrayList<>(DataType.getSupportedTypeNames()); } - return PersistentMetaHandler.getSupportedTypes().stream() + return DataType.getSupportedTypeNames().stream() .filter(type -> type.startsWith(fourthArgument.toUpperCase(Locale.ROOT))) .collect(Collectors.toList()); } if (arguments.size() == 5) { final String actionName = arguments.get(2).toLowerCase(); - final PersistentMetaHandler.DataAction action = PersistentMetaHandler.getActionByName(actionName); + final DataAction action = DataAction.getActionByName(actionName); - if (actionName.equalsIgnoreCase("list") || action == PersistentMetaHandler.DataAction.SWITCH) { + if (actionName.equalsIgnoreCase("list") || action == DataAction.SWITCH) { return null; } final String fifthArgument = arguments.get(4); if (fifthArgument.isEmpty()) { - return new ArrayList<>(PersistentMetaHandler.getSupportedTypes()); + return new ArrayList<>(DataType.getSupportedTypeNames()); } - return PersistentMetaHandler.getSupportedTypes().stream() + return DataType.getSupportedTypeNames().stream() .filter(type -> type.startsWith(fifthArgument.toUpperCase(Locale.ROOT))) .collect(Collectors.toList()); } @@ -245,7 +242,7 @@ private void handleListMeta(@NotNull final CommandSender sender, @NotNull final } final String typeName = arguments.get(0).toUpperCase(Locale.ROOT); - final PersistentDataType type = PersistentMetaHandler.getSupportedTypeByName(typeName); + final DataType type = DataType.getSupportedTypeByName(typeName); if (type == null) { plugin.sms(sender, Messages.META_TYPE_UNSUPPORTED.message().replaceText(TYPE_REPLACER_BUILDER.replacement(typeName).build())); return; @@ -326,7 +323,7 @@ private void handleShowMeta(@NotNull final CommandSender sender, @NotNull final } final String typeName = arguments.get(1).toUpperCase(Locale.ROOT); - final PersistentDataType type = PersistentMetaHandler.getSupportedTypeByName(typeName); + final DataType type = DataType.getSupportedTypeByName(typeName); if (type == null) { plugin.sms(sender, Messages.META_TYPE_UNSUPPORTED.message().replaceText(TYPE_REPLACER_BUILDER.replacement(typeName).build())); return; @@ -353,7 +350,7 @@ private void handleShowMeta(@NotNull final CommandSender sender, @NotNull final private void handleSetMeta(@NotNull final CommandSender sender, @NotNull final Player target, @NotNull final NamespacedKey namespacedKey, - @NotNull final PersistentDataType type, + @NotNull final DataType type, @NotNull final String value, @NotNull final Map context) { final Object parsedValue = plugin.getPersistentMetaHandler().parseValueByType(type, value); @@ -385,7 +382,7 @@ private void handleSetMeta(@NotNull final CommandSender sender, @NotNull final P private void handleRemoveMeta(@NotNull final CommandSender sender, @NotNull final Player target, @NotNull final NamespacedKey namespacedKey, - @NotNull final PersistentDataType type, + @NotNull final DataType type, @NotNull final Map context) { final PersistentMetaHandler.OperationResult result = plugin.getPersistentMetaHandler().removeMetaValue(target, namespacedKey, type); switch (result) { @@ -417,7 +414,7 @@ private void handleSwitchMeta(@NotNull final CommandSender sender, @NotNull fina switch (result) { case SUCCESS: - final Object newValue = plugin.getPersistentMetaHandler().getMetaValue(target, namespacedKey, PersistentDataType.STRING); + final Object newValue = plugin.getPersistentMetaHandler().getMetaValue(target, namespacedKey, DataType.BOOLEAN); plugin.sms(sender, Messages.META_VALUE_SWITCHED.message() .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build()) @@ -435,7 +432,7 @@ private void handleSwitchMeta(@NotNull final CommandSender sender, @NotNull fina private void handleAddMeta(@NotNull final CommandSender sender, @NotNull final Player target, @NotNull final NamespacedKey namespacedKey, - @NotNull final PersistentDataType type, + @NotNull final DataType type, @NotNull final String value, @NotNull final Map context) { final Object parsedValue = plugin.getPersistentMetaHandler().parseValueByType(type, value); @@ -472,7 +469,7 @@ private void handleAddMeta(@NotNull final CommandSender sender, @NotNull final P private void handleSubtractMeta(@NotNull final CommandSender sender, @NotNull final Player target, @NotNull final NamespacedKey namespacedKey, - @NotNull final PersistentDataType type, + @NotNull final DataType type, @NotNull final String value, @NotNull final Map context) { final Object parsedValue = plugin.getPersistentMetaHandler().parseValueByType(type, value); @@ -507,7 +504,7 @@ private void handleSubtractMeta(@NotNull final CommandSender sender, @NotNull fi } } - private void sendWrongUsageMessage(@NotNull final CommandSender sender, @Nullable final PersistentMetaHandler.DataAction action) { + private void sendWrongUsageMessage(@NotNull final CommandSender sender, @Nullable final DataAction action) { if (action == null) { plugin.sms(sender, Messages.WRONG_USAGE_META_COMMAND); return; diff --git a/src/main/java/com/extendedclip/deluxemenus/persistentmeta/DataAction.java b/src/main/java/com/extendedclip/deluxemenus/persistentmeta/DataAction.java new file mode 100644 index 00000000..b887acb9 --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/persistentmeta/DataAction.java @@ -0,0 +1,27 @@ +package com.extendedclip.deluxemenus.persistentmeta; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Locale; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +public enum DataAction { + SET, REMOVE, ADD, SUBTRACT, SWITCH; + + final static Map BY_NAME = Arrays.stream(values()) + .collect(Collectors.toUnmodifiableMap(Enum::name, Function.identity())); + + /** + * Get a {@link DataAction} by its name. + * + * @param name The name of the action type. + * @return The {@link DataAction} or null if it does not exist. + */ + public static @Nullable DataAction getActionByName(@NotNull final String name) { + return BY_NAME.get(name.toUpperCase(Locale.ROOT)); + } +} diff --git a/src/main/java/com/extendedclip/deluxemenus/persistentmeta/DataType.java b/src/main/java/com/extendedclip/deluxemenus/persistentmeta/DataType.java new file mode 100644 index 00000000..8977608e --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/persistentmeta/DataType.java @@ -0,0 +1,92 @@ +package com.extendedclip.deluxemenus.persistentmeta; + + +import org.bukkit.persistence.PersistentDataType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class DataType { + public static final DataType DOUBLE = create("DOUBLE", PersistentDataType.DOUBLE, value -> true); + public static final DataType INTEGER = new DataType<>("INTEGER", PersistentDataType.LONG, value -> true); + public static final DataType LONG = new DataType<>("LONG", PersistentDataType.LONG, value -> true); + public static final DataType STRING = new DataType<>("STRING", PersistentDataType.STRING, value -> true); + public static final DataType BOOLEAN = new DataType<>("BOOLEAN", PersistentDataType.STRING, value -> "true".equalsIgnoreCase((String) value) || "false".equalsIgnoreCase((String) value)); + + private static final List> SUPPORTED_TYPES = List.of(DOUBLE, INTEGER, LONG, STRING, BOOLEAN); + + private static DataType create( + @NotNull final String name, + @NotNull final PersistentDataType pdType, + @NotNull final Function<@NotNull Object, @NotNull Boolean> checker + ) { + return new DataType<>(name, pdType, checker); + } + + public final String name; + public final PersistentDataType pdType; + public final Function checker; + + private DataType( + @NotNull final String name, + @NotNull final PersistentDataType pdType, + @NotNull final Function<@NotNull Object, @NotNull Boolean> checker + ) { + this.name = name; + this.pdType = pdType; + this.checker = checker; + } + + public @NotNull String getName() { + return name; + } + + public @NotNull PersistentDataType getPDType() { + return pdType; + } + + public @NotNull Class getComplexType() { + return pdType.getComplexType(); + } + + public @NotNull Class

getPrimitiveType() { + return pdType.getPrimitiveType(); + } + + public boolean isSupported(@NotNull final Object value) { + return this.getComplexType().isInstance(value) && checker.apply(value); + } + + /** + * Helper method to parse a string into a {@link DataType}. + * + * @param name The name of the type. + * @return The type, or null if type does not exist or is not supported. + */ + public static @Nullable DataType getSupportedTypeByName(@NotNull final String name) { + return SUPPORTED_TYPES.stream().filter(type -> type.name.equalsIgnoreCase(name)).findFirst().orElse(null); + } + + /** + * Get a list of all supported type names. + * + * @return A set of all supported type names. + */ + public static @NotNull Set getSupportedTypeNames() { + return SUPPORTED_TYPES.stream().map(DataType::getName).collect(Collectors.toSet()); + } + + /** + * Get a list of all supported types. + * + * @return A list of all supported types. + */ + public static @NotNull List> getSupportedTypes() { + return SUPPORTED_TYPES; + } + +} diff --git a/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java b/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java index 39db6d2b..d3e6c192 100644 --- a/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java +++ b/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java @@ -7,28 +7,16 @@ import com.google.common.primitives.Longs; import org.bukkit.NamespacedKey; import org.bukkit.entity.Player; -import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Arrays; -import java.util.Locale; import java.util.Map; -import java.util.Set; -import java.util.function.Function; import java.util.logging.Level; import java.util.stream.Collectors; +@SuppressWarnings({"unchecked", "rawtypes"}) public class PersistentMetaHandler { - private static final Map> SUPPORTED_TYPES = Map.of( - "DOUBLE", PersistentDataType.DOUBLE, - "INTEGER", PersistentDataType.LONG, - "LONG", PersistentDataType.LONG, - "STRING", PersistentDataType.STRING, - "BOOLEAN", PersistentDataType.STRING - ); - private final DeluxeMenus plugin; public PersistentMetaHandler(@NotNull final DeluxeMenus plugin) { @@ -37,6 +25,7 @@ public PersistentMetaHandler(@NotNull final DeluxeMenus plugin) { /** * Check if a player has a meta value in their {@link org.bukkit.persistence.PersistentDataContainer}. + * It will check all supported types. See {@link DataType#getSupportedTypes()}. * * @param player The player to check. * @param key The key of the meta value. @@ -46,7 +35,11 @@ public boolean hasMetaValue( @NotNull final Player player, @NotNull final NamespacedKey key ) { - return player.getPersistentDataContainer().has(key); + return DataType.getSupportedTypes() + .stream() + .map(DataType::getPDType) + .distinct() + .anyMatch(type -> player.getPersistentDataContainer().has(key, type)); } /** @@ -60,9 +53,9 @@ public boolean hasMetaValue( public boolean hasMetaValue( @NotNull final Player player, @NotNull final NamespacedKey key, - @NotNull final PersistentDataType type + @NotNull final DataType type ) { - return player.getPersistentDataContainer().has(key, type); + return player.getPersistentDataContainer().has(key, type.getPDType()); } /** @@ -77,13 +70,13 @@ public boolean hasMetaValue( public @Nullable T getMetaValue( @NotNull final Player player, @NotNull final NamespacedKey key, - @NotNull final PersistentDataType type + @NotNull final DataType type ) { - if (!player.getPersistentDataContainer().has(key, type)) { + if (!player.getPersistentDataContainer().has(key, type.getPDType())) { return null; } - return player.getPersistentDataContainer().get(key, type); + return player.getPersistentDataContainer().get(key, type.getPDType()); } /** @@ -99,14 +92,14 @@ public boolean hasMetaValue( public @NotNull T getMetaValueOrDefault( @NotNull final Player player, @NotNull final NamespacedKey key, - @NotNull final PersistentDataType type, + @NotNull final DataType type, @NotNull final T defaultValue ) { - if (!player.getPersistentDataContainer().has(key, type)) { + if (!player.getPersistentDataContainer().has(key, type.getPDType())) { return defaultValue; } - final T result = player.getPersistentDataContainer().get(key, type); + final T result = player.getPersistentDataContainer().get(key, type.getPDType()); return result == null ? defaultValue : result; } @@ -119,12 +112,13 @@ public boolean hasMetaValue( */ public Map getMetaValues( @NotNull final Player player, - @NotNull final PersistentDataType type + @NotNull final DataType type ) { return player.getPersistentDataContainer().getKeys().stream() - .filter(key -> player.getPersistentDataContainer().has(key, type)) - .map(key -> Pair.of(key.toString(), player.getPersistentDataContainer().get(key, type))) + .filter(key -> player.getPersistentDataContainer().has(key, type.getPDType())) + .map(key -> Pair.of(key.toString(), player.getPersistentDataContainer().get(key, type.getPDType()))) .filter(entry -> entry.getValue() != null) + .filter(entry -> type.isSupported(entry.getValue())) .collect(Collectors.toMap(Pair::getKey, Pair::getValue)); } @@ -138,22 +132,21 @@ public Map getMetaValues( * @param value The value to set. * @return The result of the operation. */ - @SuppressWarnings({"rawtypes", "unchecked"}) public @NotNull OperationResult setMetaValue( @NotNull final Player player, @NotNull final NamespacedKey key, - @NotNull final PersistentDataType type, + @NotNull final DataType type, @NotNull final Object value ) { - if (!type.getComplexType().isInstance(value)) { + if (!type.isSupported(value)) { return OperationResult.NEW_VALUE_IS_DIFFERENT_TYPE; } - if (player.getPersistentDataContainer().has(key) && !player.getPersistentDataContainer().has(key, type)) { + if (player.getPersistentDataContainer().has(key) && !player.getPersistentDataContainer().has(key, type.getPDType())) { return OperationResult.EXISTENT_VALUE_IS_DIFFERENT_TYPE; } - player.getPersistentDataContainer().set(key, type, value); + player.getPersistentDataContainer().set(key, type.getPDType(), value); return OperationResult.SUCCESS; } @@ -167,13 +160,13 @@ public Map getMetaValues( public @NotNull OperationResult removeMetaValue( @NotNull final Player player, @NotNull final NamespacedKey key, - @NotNull final PersistentDataType type + @NotNull final DataType type ) { - if (player.getPersistentDataContainer().has(key) && !player.getPersistentDataContainer().has(key, type)) { + if (player.getPersistentDataContainer().has(key) && !player.getPersistentDataContainer().has(key, type.getPDType())) { return OperationResult.EXISTENT_VALUE_IS_DIFFERENT_TYPE; } - if (!player.getPersistentDataContainer().has(key, type)) { + if (!player.getPersistentDataContainer().has(key, type.getPDType())) { return OperationResult.VALUE_NOT_FOUND; } @@ -195,16 +188,16 @@ public Map getMetaValues( @NotNull final Player player, @NotNull final NamespacedKey key ) { - if (player.getPersistentDataContainer().has(key) && !player.getPersistentDataContainer().has(key, PersistentDataType.STRING)) { + if (player.getPersistentDataContainer().has(key) && !player.getPersistentDataContainer().has(key, DataType.BOOLEAN.getPDType())) { return OperationResult.EXISTENT_VALUE_IS_DIFFERENT_TYPE; } - final String currentValue = player.getPersistentDataContainer().getOrDefault(key, PersistentDataType.STRING, "false"); - if (!currentValue.equalsIgnoreCase("true") && !currentValue.equalsIgnoreCase("false")) { + final String currentValue = player.getPersistentDataContainer().getOrDefault(key, DataType.BOOLEAN.getPDType(), "false"); + if (!DataType.BOOLEAN.isSupported(currentValue)) { return OperationResult.EXISTENT_VALUE_IS_DIFFERENT_TYPE; } - player.getPersistentDataContainer().set(key, PersistentDataType.STRING, currentValue.equalsIgnoreCase("true") ? "false" : "true"); + player.getPersistentDataContainer().set(key, DataType.BOOLEAN.getPDType(), currentValue.equalsIgnoreCase("true") ? "false" : "true"); return OperationResult.SUCCESS; } @@ -223,27 +216,27 @@ public Map getMetaValues( public @NotNull OperationResult addMetaValue( @NotNull final Player player, @NotNull final NamespacedKey key, - @NotNull final PersistentDataType type, + @NotNull final DataType type, @NotNull final Number value ) { - if (type != PersistentDataType.DOUBLE && type != PersistentDataType.LONG) { + if (type != DataType.DOUBLE && type != DataType.LONG && type != DataType.INTEGER) { return OperationResult.INVALID_TYPE; } - if (player.getPersistentDataContainer().has(key) && !player.getPersistentDataContainer().has(key, type)) { + if (player.getPersistentDataContainer().has(key) && !player.getPersistentDataContainer().has(key, type.getPDType())) { return OperationResult.EXISTENT_VALUE_IS_DIFFERENT_TYPE; } - final Object currentValue = player.getPersistentDataContainer().get(key, type); + final Object currentValue = player.getPersistentDataContainer().get(key, type.getPDType()); - if (type == PersistentDataType.DOUBLE) { + if (type == DataType.DOUBLE) { final double newValue = (double) (currentValue == null ? 0.0 : currentValue) + value.doubleValue(); - player.getPersistentDataContainer().set(key, PersistentDataType.DOUBLE, newValue); + player.getPersistentDataContainer().set(key, type.getPDType(), newValue); return OperationResult.SUCCESS; } final long newValue = (long) (currentValue == null ? 0 : currentValue) + value.longValue(); - player.getPersistentDataContainer().set(key, PersistentDataType.LONG, newValue); + player.getPersistentDataContainer().set(key, type.getPDType(), newValue); return OperationResult.SUCCESS; } @@ -262,27 +255,27 @@ public Map getMetaValues( public @NotNull OperationResult subtractMetaValue( @NotNull final Player player, @NotNull final NamespacedKey key, - @NotNull final PersistentDataType type, + @NotNull final DataType type, @NotNull final Number value ) { - if (type != PersistentDataType.DOUBLE && type != PersistentDataType.LONG) { + if (type != DataType.DOUBLE && type != DataType.LONG && type != DataType.INTEGER) { return OperationResult.INVALID_TYPE; } - if (player.getPersistentDataContainer().has(key) && !player.getPersistentDataContainer().has(key, type)) { + if (player.getPersistentDataContainer().has(key) && !player.getPersistentDataContainer().has(key, type.getPDType())) { return OperationResult.EXISTENT_VALUE_IS_DIFFERENT_TYPE; } - final Object currentValue = player.getPersistentDataContainer().get(key, type); + final Object currentValue = player.getPersistentDataContainer().get(key, type.getPDType()); - if (type == PersistentDataType.DOUBLE) { + if (type == DataType.DOUBLE) { final double newValue = (double) (currentValue == null ? 0.0 : currentValue) - value.doubleValue(); - player.getPersistentDataContainer().set(key, PersistentDataType.DOUBLE, newValue); + player.getPersistentDataContainer().set(key, type.getPDType(), newValue); return OperationResult.SUCCESS; } final long newValue = (long) (currentValue == null ? 0 : currentValue) - value.longValue(); - player.getPersistentDataContainer().set(key, PersistentDataType.LONG, newValue); + player.getPersistentDataContainer().set(key, type.getPDType(), newValue); return OperationResult.SUCCESS; } @@ -306,7 +299,7 @@ public Map getMetaValues( return OperationResult.INVALID_SYNTAX; } - DataAction action = getActionByName(args[0]); + DataAction action = DataAction.getActionByName(args[0]); if (action == null) { return OperationResult.INVALID_SYNTAX; } @@ -324,7 +317,7 @@ public Map getMetaValues( return OperationResult.INVALID_SYNTAX; } - final PersistentDataType type = getSupportedTypeByName(args[2]); + final DataType type = DataType.getSupportedTypeByName(args[2]); if (type == null) { return OperationResult.INVALID_SYNTAX; } @@ -365,66 +358,34 @@ public Map getMetaValues( * @return The parsed value or null if the value is null or could not be parsed. */ public @Nullable Object parseValueByType( - @NotNull final PersistentDataType type, + @NotNull final DataType type, @Nullable final String value ) { - plugin.getLogger().info("Parsing value by type. Value: " + value + ", type: <" + type.getPrimitiveType().getTypeName() + ", " + type.getComplexType().getTypeName() + ">."); if (value == null) { - plugin.getLogger().info("Value is null."); return null; } - if (type == PersistentDataType.STRING) { - plugin.getLogger().info("Value is a string."); + if (type == DataType.BOOLEAN) { + if (!value.equalsIgnoreCase("true") && !value.equalsIgnoreCase("false")) { + return null; + } + return value; } - if (type == PersistentDataType.DOUBLE) { - final Double result = Doubles.tryParse(value); - plugin.getLogger().info("Value is a double: " + result); - return result; + if (type == DataType.STRING) { + return value; } - if (type == PersistentDataType.LONG) { - final Long result = Longs.tryParse(value); - plugin.getLogger().info("Value is a long: " + result); - return result; + if (type == DataType.DOUBLE) { + return Doubles.tryParse(value); } - plugin.getLogger().info("Unsupported type found."); - return null; - } - - - // Helper methods - - /** - * Get a list of all supported types. - * - * @return A set of all supported types. - */ - public static @NotNull Set getSupportedTypes() { - return SUPPORTED_TYPES.keySet(); - } - - /** - * Helper method to parse a string into a {@link PersistentDataType}. - * - * @param name The name of the type. - * @return The type, or null if type does not exist or is not supported. - */ - public static @Nullable PersistentDataType getSupportedTypeByName(@NotNull final String name) { - return SUPPORTED_TYPES.get(name.toUpperCase(Locale.ROOT)); - } + if (type == DataType.LONG || type == DataType.INTEGER) { + return Longs.tryParse(value); + } - /** - * Helper method to parse a string into a {@link DataAction}. - * - * @param name The name of the action type. - * @return The {@link DataAction} or null if it does not exist. - */ - public static @Nullable DataAction getActionByName(@NotNull final String name) { - return DataAction.getByName(name); + return null; } /** @@ -458,23 +419,6 @@ public Map getMetaValues( return namespacedKey; } - public enum DataAction { - SET, REMOVE, ADD, SUBTRACT, SWITCH; - - final static Map BY_NAME = Arrays.stream(values()) - .collect(Collectors.toUnmodifiableMap(Enum::name, Function.identity())); - - /** - * Get a {@link DataAction} by its name. - * - * @param name The name of the action type. - * @return The {@link DataAction} or null if it does not exist. - */ - public static @Nullable DataAction getByName(@NotNull final String name) { - return BY_NAME.get(name.toUpperCase(Locale.ROOT)); - } - } - public enum OperationResult { SUCCESS, // Operation was successful INVALID_SYNTAX, // Used when parsing an action from a string and the syntax is invalid diff --git a/src/main/java/com/extendedclip/deluxemenus/placeholder/Expansion.java b/src/main/java/com/extendedclip/deluxemenus/placeholder/Expansion.java index c7849b46..6a1a6ea0 100644 --- a/src/main/java/com/extendedclip/deluxemenus/placeholder/Expansion.java +++ b/src/main/java/com/extendedclip/deluxemenus/placeholder/Expansion.java @@ -3,7 +3,7 @@ import com.extendedclip.deluxemenus.DeluxeMenus; import com.extendedclip.deluxemenus.menu.Menu; import com.extendedclip.deluxemenus.menu.options.MenuOptions; -import com.extendedclip.deluxemenus.persistentmeta.PersistentMetaHandler; +import com.extendedclip.deluxemenus.persistentmeta.DataType; import com.extendedclip.deluxemenus.utils.VersionHelper; import me.clip.placeholderapi.PlaceholderAPI; import me.clip.placeholderapi.PlaceholderAPIPlugin; @@ -11,10 +11,11 @@ import org.bukkit.NamespacedKey; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; -import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.List; + public class Expansion extends PlaceholderExpansion { private final DeluxeMenus plugin; @@ -43,6 +44,17 @@ public boolean persist() { return plugin.getDescription().getVersion(); } + @Override + public @NotNull List getPlaceholders() { + return List.of( + "%deluxemenus_is_in_menu%", + "%deluxemenus_opened_menu%", + "%deluxemenus_last_menu%", + "%deluxemenus_meta_has_value__[type]%", + "%deluxemenus_meta___[default-value]%" + ); + } + @Override public @Nullable String onRequest(final OfflinePlayer offlinePlayer, @NotNull final String input) { if (offlinePlayer == null || !offlinePlayer.isOnline()) { @@ -57,85 +69,83 @@ public boolean persist() { final String parsedInput = PlaceholderAPI.setBracketPlaceholders(onlinePlayer, input); final String parsedInputLower = parsedInput.toLowerCase(); - if (parsedInputLower.startsWith("meta_")) { - if (!VersionHelper.IS_PDC_VERSION) { - return null; + switch (parsedInputLower) { + case "is_in_menu": { + return getBooleanAsString(Menu.getMenuHolder(onlinePlayer).isPresent()); } - - // %deluxemenus_meta_has_value__% - if (parsedInputLower.startsWith("meta_has_value_")) { - final String hasValueInput = parsedInput.substring(15); - - if (!hasValueInput.contains("_")) { - return null; - } - - String[] hasValueParts = hasValueInput.split("_", 2); - - if (hasValueParts.length < 2) { - return null; - } - - final NamespacedKey key = plugin.getPersistentMetaHandler().getKey(hasValueParts[0]); - if (key == null) { - return "INVALID_KEY"; - } - - final PersistentDataType type = PersistentMetaHandler.getSupportedTypeByName(hasValueParts[1]); - if (type == null) { - return "INVALID_TYPE"; - } - - final boolean hasValue = plugin.getPersistentMetaHandler().hasMetaValue(onlinePlayer, key, type); - - return getBooleanAsString(hasValue); + case "opened_menu": { + return Menu.getOpenMenu(onlinePlayer).map(Menu::options).map(MenuOptions::name).orElse(""); + } + case "last_menu": { + return Menu.getLastMenu(onlinePlayer).map(Menu::options).map(MenuOptions::name).orElse(""); } + } - final String getValueInput = parsedInput.substring(5); + if (!parsedInputLower.startsWith("meta_")) { + return null; + } - if (!getValueInput.contains("_")) { - return null; - } + if (!VersionHelper.IS_PDC_VERSION || plugin.getPersistentMetaHandler() == null) { + return null; + } - final String[] parts = getValueInput.split("_", 3); + // %deluxemenus_meta_has_value__[type]% + if (parsedInputLower.startsWith("meta_has_value_")) { + final String hasValueInput = parsedInput.substring(15); + final String[] hasValueParts = hasValueInput.split("_", 2); - if (parts.length < 2) { + if (hasValueParts.length < 1 || hasValueParts.length > 2) { return null; } - final NamespacedKey key = plugin.getPersistentMetaHandler().getKey(parts[0]); + final NamespacedKey key = plugin.getPersistentMetaHandler().getKey(hasValueParts[0]); if (key == null) { - return "INVALID_KEY"; + return getBooleanAsString(false); + } + + if (hasValueParts.length == 1) { + return getBooleanAsString(plugin.getPersistentMetaHandler().hasMetaValue(onlinePlayer, key)); } - final PersistentDataType type = PersistentMetaHandler.getSupportedTypeByName(parts[1]); + final DataType type = DataType.getSupportedTypeByName(hasValueParts[1]); if (type == null) { - return "INVALID_TYPE"; + return getBooleanAsString(false); } - final Object result = plugin.getPersistentMetaHandler().getMetaValue(onlinePlayer, key, type); + return getBooleanAsString(plugin.getPersistentMetaHandler().hasMetaValue(onlinePlayer, key, type)); + } - // %deluxemenus_meta___[defaultValue]% - if (result != null) { - return String.valueOf(result); - } + // %deluxemenus_meta___[default-value]% + final String getValueInput = parsedInput.substring(5); - // return the default value - return parts.length > 2 ? parts[2] : ""; + if (!getValueInput.contains("_")) { + return null; } - switch (parsedInputLower) { - case "is_in_menu": { - return getBooleanAsString(Menu.getMenuHolder(onlinePlayer).isPresent()); - } - case "opened_menu": { - return Menu.getOpenMenu(onlinePlayer).map(Menu::options).map(MenuOptions::name).orElse(""); - } - case "last_menu": { - return Menu.getLastMenu(onlinePlayer).map(Menu::options).map(MenuOptions::name).orElse(""); - } + final String[] parts = getValueInput.split("_", 3); + + if (parts.length < 2) { + return null; + } + + final NamespacedKey key = plugin.getPersistentMetaHandler().getKey(parts[0]); + if (key == null) { + return getBooleanAsString(false); } - return null; + + final DataType type = DataType.getSupportedTypeByName(parts[1]); + if (type == null) { + return getBooleanAsString(false); + } + + final Object result = plugin.getPersistentMetaHandler().getMetaValue(onlinePlayer, key, type); + + if (result != null) { + return String.valueOf(result); + } + + // return the default value + return parts.length > 2 ? parts[2] : ""; } private @NotNull String getBooleanAsString(final boolean value) { diff --git a/src/main/java/com/extendedclip/deluxemenus/requirement/HasMetaRequirement.java b/src/main/java/com/extendedclip/deluxemenus/requirement/HasMetaRequirement.java index 0c350282..3730bd6e 100644 --- a/src/main/java/com/extendedclip/deluxemenus/requirement/HasMetaRequirement.java +++ b/src/main/java/com/extendedclip/deluxemenus/requirement/HasMetaRequirement.java @@ -2,10 +2,9 @@ import com.extendedclip.deluxemenus.DeluxeMenus; import com.extendedclip.deluxemenus.menu.MenuHolder; -import com.extendedclip.deluxemenus.persistentmeta.PersistentMetaHandler; +import com.extendedclip.deluxemenus.persistentmeta.DataType; import org.bukkit.NamespacedKey; import org.bukkit.entity.Player; -import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.NotNull; public class HasMetaRequirement extends Requirement { @@ -26,53 +25,57 @@ public HasMetaRequirement(@NotNull final DeluxeMenus plugin, String key, String @Override public boolean evaluate(MenuHolder holder) { - Player player = holder.getViewer(); + final Player player = holder.getViewer(); if (player == null) { return false; } - String parsedKey = holder.setPlaceholdersAndArguments(key); + + final String parsedKey = holder.setPlaceholdersAndArguments(key); final NamespacedKey namespacedKey = plugin.getPersistentMetaHandler().getKey(parsedKey); if (namespacedKey == null) { return invert; } - final PersistentDataType type = PersistentMetaHandler.getSupportedTypeByName(typeName); + final DataType type = DataType.getSupportedTypeByName(typeName); if (type == null) { return invert; } - Object metaValue = plugin.getPersistentMetaHandler().getMetaValue(player, namespacedKey, type); + final Object metaValue = plugin.getPersistentMetaHandler().getMetaValue(player, namespacedKey, type); if (metaValue == null) { return invert; } final String expectedValue = holder.setPlaceholdersAndArguments(value); + // TODO: Determine if there is a good use for parsing placeholders in the stored/existent value. final String actualValue = holder.setPlaceholdersAndArguments(String.valueOf(metaValue)); - switch (typeName) { - case "STRING": - case "BOOLEAN": - return invert != actualValue.equalsIgnoreCase(expectedValue); - case "INTEGER": - case "LONG": - try { - long metaNum = Long.parseLong(actualValue); - long toCheck = Long.parseLong(expectedValue); - boolean pass = metaNum >= toCheck; - return invert != pass; - } catch (Exception ex) { - // In case of an exception, we will return the invert value - } - case "DOUBLE": - try { - double metaNum = Double.parseDouble(actualValue); - double toCheck = Double.parseDouble(expectedValue); - boolean pass = metaNum >= toCheck; - return invert != pass; - } catch (Exception ex) { - // In case of an exception, we will return the invert value - } + if (type.equals(DataType.STRING) || type.equals(DataType.BOOLEAN)) { + return invert != actualValue.equalsIgnoreCase(expectedValue); + } + + if (type.equals(DataType.LONG) || type.equals(DataType.INTEGER)) { + try { + long metaNum = Long.parseLong(actualValue); + long toCheck = Long.parseLong(expectedValue); + boolean pass = metaNum >= toCheck; + return invert != pass; + } catch (Exception ex) { + return invert; + } } + + if (type.equals(DataType.DOUBLE)) { + try { + double metaNum = Double.parseDouble(actualValue); + double toCheck = Double.parseDouble(expectedValue); + boolean pass = metaNum >= toCheck; + return invert != pass; + } catch (Exception ex) { + return invert; + } + } + return invert; } } diff --git a/src/main/java/com/extendedclip/deluxemenus/utils/Messages.java b/src/main/java/com/extendedclip/deluxemenus/utils/Messages.java index d3e60d64..13698d13 100644 --- a/src/main/java/com/extendedclip/deluxemenus/utils/Messages.java +++ b/src/main/java/com/extendedclip/deluxemenus/utils/Messages.java @@ -1,6 +1,6 @@ package com.extendedclip.deluxemenus.utils; -import com.extendedclip.deluxemenus.persistentmeta.PersistentMetaHandler; +import com.extendedclip.deluxemenus.persistentmeta.DataType; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextDecoration; @@ -179,8 +179,13 @@ public enum Messages { WRONG_USAGE_META_SUBTRACT_COMMAND(WRONG_USAGE_BASE.message .append(text("/dm meta subtract ", NamedTextColor.GRAY))), - META_TYPE_UNSUPPORTED(text("Unsupported meta type ! Supported values are: " + String.join(", ", PersistentMetaHandler.getSupportedTypes()), NamedTextColor.RED)), - META_KEY_INVALID(text("An invalid meta key was provided: ''!", NamedTextColor.RED)), + META_TYPE_UNSUPPORTED(text("Unsupported meta type ", NamedTextColor.RED) + .append(text("", NamedTextColor.GOLD)) + .append(text("! Supported values are: ", NamedTextColor.RED)) + .append(text(String.join(", ", DataType.getSupportedTypeNames()), NamedTextColor.GOLD))), + META_KEY_INVALID(text("An invalid meta key was provided: '", NamedTextColor.RED) + .append(text("", NamedTextColor.GOLD)) + .append(text("'!", NamedTextColor.RED))), META_ADD_TYPE_MISMATCH(text("Only NUMBERS can be added!", NamedTextColor.RED)), META_SUBTRACT_TYPE_MISMATCH(text("Only NUMBERS can be subtracted!", NamedTextColor.RED)), META_VALUE_TYPE_MISMATCH(text("Given value does not match the given type!", NamedTextColor.RED)), From 6ea7d44cf40f7f4b6c8a9189a612cbd0148fba84 Mon Sep 17 00:00:00 2001 From: BlitzOffline <52609756+BlitzOffline@users.noreply.github.com> Date: Sat, 5 Apr 2025 02:56:49 +0300 Subject: [PATCH 4/9] check if stored meta value is supported after reading it. --- .../persistentmeta/PersistentMetaHandler.java | 11 ++++++++++- .../deluxemenus/requirement/HasMetaRequirement.java | 3 ++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java b/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java index d3e6c192..b268bf17 100644 --- a/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java +++ b/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java @@ -76,7 +76,16 @@ public boolean hasMetaValue( return null; } - return player.getPersistentDataContainer().get(key, type.getPDType()); + final T value = player.getPersistentDataContainer().get(key, type.getPDType()); + if (value == null) { + return null; + } + + if (!type.isSupported(value)) { + return null; + } + + return value; } /** diff --git a/src/main/java/com/extendedclip/deluxemenus/requirement/HasMetaRequirement.java b/src/main/java/com/extendedclip/deluxemenus/requirement/HasMetaRequirement.java index 3730bd6e..383d6171 100644 --- a/src/main/java/com/extendedclip/deluxemenus/requirement/HasMetaRequirement.java +++ b/src/main/java/com/extendedclip/deluxemenus/requirement/HasMetaRequirement.java @@ -47,7 +47,8 @@ public boolean evaluate(MenuHolder holder) { } final String expectedValue = holder.setPlaceholdersAndArguments(value); - // TODO: Determine if there is a good use for parsing placeholders in the stored/existent value. + // TODO: Is there any reason to parse placeholders in the stored value when reading them? + // Placeholders are parsed before value are stored. This means there will (or should) be no placeholders when reading. final String actualValue = holder.setPlaceholdersAndArguments(String.valueOf(metaValue)); if (type.equals(DataType.STRING) || type.equals(DataType.BOOLEAN)) { From 27596dc50efaf006a466a4ae49fb6dc86ce6835d Mon Sep 17 00:00:00 2001 From: BlitzOffline <52609756+BlitzOffline@users.noreply.github.com> Date: Sat, 5 Apr 2025 03:17:14 +0300 Subject: [PATCH 5/9] added placeholder support to the /dm meta command --- .../command/subcommand/MetaCommand.java | 15 +++++++++------ .../deluxemenus/persistentmeta/DataType.java | 2 +- .../persistentmeta/PersistentMetaHandler.java | 6 +----- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java index f73ffd4c..0f27fe0c 100644 --- a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java +++ b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java @@ -6,6 +6,7 @@ import com.extendedclip.deluxemenus.persistentmeta.PersistentMetaHandler; import com.extendedclip.deluxemenus.utils.Messages; import com.extendedclip.deluxemenus.utils.PaginationUtils; +import com.extendedclip.deluxemenus.utils.StringUtils; import com.extendedclip.deluxemenus.utils.VersionHelper; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; @@ -67,7 +68,7 @@ public void execute(@NotNull final CommandSender sender, @NotNull final List type = DataType.getSupportedTypeByName(typeName); if (type == null) { plugin.sms(sender, Messages.META_TYPE_UNSUPPORTED.message().replaceText(TYPE_REPLACER_BUILDER.replacement(typeName).build())); @@ -129,18 +130,20 @@ public void execute(@NotNull final CommandSender sender, @NotNull final List Date: Sat, 5 Apr 2025 03:17:26 +0300 Subject: [PATCH 6/9] removed todo message --- .../extendedclip/deluxemenus/command/subcommand/MetaCommand.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java index 0f27fe0c..a204472f 100644 --- a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java +++ b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java @@ -30,7 +30,6 @@ import static net.kyori.adventure.text.Component.newline; import static net.kyori.adventure.text.Component.text; -// TODO: Add placeholders support? public class MetaCommand extends SubCommand { private static final List SUB_COMMANDS = List.of("list", "show", "set", "remove", "add", "subtract", "switch"); From 9fc5c1aba9bf01322d5ab3899eac3158ec4f1a83 Mon Sep 17 00:00:00 2001 From: BlitzOffline <52609756+BlitzOffline@users.noreply.github.com> Date: Sat, 5 Apr 2025 04:28:52 +0300 Subject: [PATCH 7/9] fixed hasMetaValue not checking the type properly. --- .../persistentmeta/PersistentMetaHandler.java | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java b/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java index d99b9e63..f812e773 100644 --- a/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java +++ b/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java @@ -37,9 +37,8 @@ public boolean hasMetaValue( ) { return DataType.getSupportedTypes() .stream() - .map(DataType::getPDType) .distinct() - .anyMatch(type -> player.getPersistentDataContainer().has(key, type)); + .anyMatch(type -> hasMetaValue(player, key, type)); } /** @@ -55,7 +54,7 @@ public boolean hasMetaValue( @NotNull final NamespacedKey key, @NotNull final DataType type ) { - return player.getPersistentDataContainer().has(key, type.getPDType()); + return player.getPersistentDataContainer().has(key, type.getPDType()) && type.isSupported(player.getPersistentDataContainer().get(key, type.getPDType())); } /** @@ -160,6 +159,7 @@ public Map getMetaValues( * * @param player The player to remove the meta value from. * @param key The key of the meta value. + * @param type The type of the meta value. * @return The result of the operation. */ public @NotNull OperationResult removeMetaValue( @@ -179,6 +179,26 @@ public Map getMetaValues( return OperationResult.SUCCESS; } + /** + * Remove a meta value from a player's {@link org.bukkit.persistence.PersistentDataContainer}. + * + * @param player The player to remove the meta value from. + * @param key The key of the meta value. + * @return The result of the operation. + */ + public @NotNull OperationResult removeMetaValue( + @NotNull final Player player, + @NotNull final NamespacedKey key + ) { + if (!player.getPersistentDataContainer().has(key)) { + return OperationResult.VALUE_NOT_FOUND; + } + + player.getPersistentDataContainer().remove(key); + return OperationResult.SUCCESS; + } + + /** * Switch a meta value in a player's {@link org.bukkit.persistence.PersistentDataContainer}. *

The value must be a boolean. From 9a833874a7fcfa1553bb205835a76b463ee4c80d Mon Sep 17 00:00:00 2001 From: BlitzOffline <52609756+BlitzOffline@users.noreply.github.com> Date: Sat, 5 Apr 2025 04:54:02 +0300 Subject: [PATCH 8/9] improved meta command error messages and removed unnecessary method --- .../command/subcommand/MetaCommand.java | 35 +++++++++++++------ .../deluxemenus/persistentmeta/DataType.java | 16 +++------ .../deluxemenus/utils/Messages.java | 20 ++++++++--- 3 files changed, 43 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java index a204472f..0b49373a 100644 --- a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java +++ b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java @@ -357,7 +357,9 @@ private void handleSetMeta(@NotNull final CommandSender sender, @NotNull final P @NotNull final Map context) { final Object parsedValue = plugin.getPersistentMetaHandler().parseValueByType(type, value); if (parsedValue == null) { - plugin.sms(sender, Messages.META_VALUE_TYPE_MISMATCH); + plugin.sms(sender, Messages.META_VALUE_TYPE_MISMATCH.message() + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build()) + .replaceText(TYPE_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build())); return; } @@ -372,10 +374,13 @@ private void handleSetMeta(@NotNull final CommandSender sender, @NotNull final P ); return; case NEW_VALUE_IS_DIFFERENT_TYPE: - plugin.sms(sender, Messages.META_VALUE_TYPE_MISMATCH); + plugin.sms(sender, Messages.META_VALUE_TYPE_MISMATCH.message() + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build()) + .replaceText(TYPE_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build())); return; case EXISTENT_VALUE_IS_DIFFERENT_TYPE: - plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE); + plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE.message() + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build())); return; default: plugin.sms(sender, Messages.WRONG_USAGE_META_SET_COMMAND); @@ -396,7 +401,8 @@ private void handleRemoveMeta(@NotNull final CommandSender sender, @NotNull fina ); return; case EXISTENT_VALUE_IS_DIFFERENT_TYPE: - plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE); + plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE.message() + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build())); return; case VALUE_NOT_FOUND: plugin.sms(sender, Messages.NO_META_VALUE.message() @@ -425,7 +431,8 @@ private void handleSwitchMeta(@NotNull final CommandSender sender, @NotNull fina ); return; case EXISTENT_VALUE_IS_DIFFERENT_TYPE: - plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE); + plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE.message() + .replaceText(KEY_REPLACER_BUILDER.replacement(DataType.BOOLEAN.getName()).build())); return; default: plugin.sms(sender, Messages.WRONG_USAGE_META_SWITCH_COMMAND); @@ -440,7 +447,8 @@ private void handleAddMeta(@NotNull final CommandSender sender, @NotNull final P final Object parsedValue = plugin.getPersistentMetaHandler().parseValueByType(type, value); if (!(parsedValue instanceof Number)) { - plugin.sms(sender, Messages.META_ADD_TYPE_MISMATCH); + plugin.sms(sender, Messages.META_ADD_TYPE_MISMATCH.message() + .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build())); return; } @@ -459,10 +467,12 @@ private void handleAddMeta(@NotNull final CommandSender sender, @NotNull final P ); return; case INVALID_TYPE: - plugin.sms(sender, Messages.META_ADD_TYPE_MISMATCH); + plugin.sms(sender, Messages.META_ADD_TYPE_MISMATCH.message() + .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build())); return; case EXISTENT_VALUE_IS_DIFFERENT_TYPE: - plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE); + plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE.message() + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build())); return; default: plugin.sms(sender, Messages.WRONG_USAGE_META_ADD_COMMAND); @@ -477,7 +487,8 @@ private void handleSubtractMeta(@NotNull final CommandSender sender, @NotNull fi final Object parsedValue = plugin.getPersistentMetaHandler().parseValueByType(type, value); if (!(parsedValue instanceof Number)) { - plugin.sms(sender, Messages.META_SUBTRACT_TYPE_MISMATCH); + plugin.sms(sender, Messages.META_SUBTRACT_TYPE_MISMATCH.message() + .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build())); return; } @@ -496,10 +507,12 @@ private void handleSubtractMeta(@NotNull final CommandSender sender, @NotNull fi ); return; case INVALID_TYPE: - plugin.sms(sender, Messages.META_SUBTRACT_TYPE_MISMATCH); + plugin.sms(sender, Messages.META_SUBTRACT_TYPE_MISMATCH.message() + .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build())); return; case EXISTENT_VALUE_IS_DIFFERENT_TYPE: - plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE); + plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE.message() + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build())); return; default: plugin.sms(sender, Messages.WRONG_USAGE_META_SUBTRACT_COMMAND); diff --git a/src/main/java/com/extendedclip/deluxemenus/persistentmeta/DataType.java b/src/main/java/com/extendedclip/deluxemenus/persistentmeta/DataType.java index 417261ce..2d729517 100644 --- a/src/main/java/com/extendedclip/deluxemenus/persistentmeta/DataType.java +++ b/src/main/java/com/extendedclip/deluxemenus/persistentmeta/DataType.java @@ -11,7 +11,7 @@ import java.util.stream.Collectors; public class DataType { - public static final DataType DOUBLE = create("DOUBLE", PersistentDataType.DOUBLE, value -> true); + public static final DataType DOUBLE = new DataType<>("DOUBLE", PersistentDataType.DOUBLE, value -> true); public static final DataType INTEGER = new DataType<>("INTEGER", PersistentDataType.LONG, value -> true); public static final DataType LONG = new DataType<>("LONG", PersistentDataType.LONG, value -> true); public static final DataType STRING = new DataType<>("STRING", PersistentDataType.STRING, value -> true); @@ -19,17 +19,9 @@ public class DataType { private static final List> SUPPORTED_TYPES = List.of(DOUBLE, INTEGER, LONG, STRING, BOOLEAN); - private static DataType create( - @NotNull final String name, - @NotNull final PersistentDataType pdType, - @NotNull final Function<@NotNull Object, @NotNull Boolean> checker - ) { - return new DataType<>(name, pdType, checker); - } - - public final String name; - public final PersistentDataType pdType; - public final Function checker; + private final String name; + private final PersistentDataType pdType; + private final Function checker; private DataType( @NotNull final String name, diff --git a/src/main/java/com/extendedclip/deluxemenus/utils/Messages.java b/src/main/java/com/extendedclip/deluxemenus/utils/Messages.java index 13698d13..274423bb 100644 --- a/src/main/java/com/extendedclip/deluxemenus/utils/Messages.java +++ b/src/main/java/com/extendedclip/deluxemenus/utils/Messages.java @@ -179,6 +179,7 @@ public enum Messages { WRONG_USAGE_META_SUBTRACT_COMMAND(WRONG_USAGE_BASE.message .append(text("/dm meta subtract ", NamedTextColor.GRAY))), + META_NOT_SUPPORTED(text("Meta is not supported on this server version!", NamedTextColor.RED)), META_TYPE_UNSUPPORTED(text("Unsupported meta type ", NamedTextColor.RED) .append(text("", NamedTextColor.GOLD)) .append(text("! Supported values are: ", NamedTextColor.RED)) @@ -186,11 +187,20 @@ public enum Messages { META_KEY_INVALID(text("An invalid meta key was provided: '", NamedTextColor.RED) .append(text("", NamedTextColor.GOLD)) .append(text("'!", NamedTextColor.RED))), - META_ADD_TYPE_MISMATCH(text("Only NUMBERS can be added!", NamedTextColor.RED)), - META_SUBTRACT_TYPE_MISMATCH(text("Only NUMBERS can be subtracted!", NamedTextColor.RED)), - META_VALUE_TYPE_MISMATCH(text("Given value does not match the given type!", NamedTextColor.RED)), - META_EXISTENT_VALUE_WRONG_TYPE(text("Given key stores a value with a different type!", NamedTextColor.RED)), - META_NOT_SUPPORTED(text("Meta is not supported on this server version!", NamedTextColor.RED)), + META_ADD_TYPE_MISMATCH(text("Only NUMBERS can be added, and '", NamedTextColor.RED) + .append(text("", NamedTextColor.GOLD)) + .append(text("' is not a number!", NamedTextColor.RED))), + META_SUBTRACT_TYPE_MISMATCH(text("Only NUMBERS can be subtracted, and '", NamedTextColor.RED) + .append(text("", NamedTextColor.GOLD)) + .append(text("' is not a number!", NamedTextColor.RED))), + META_VALUE_TYPE_MISMATCH(text("Given value '", NamedTextColor.RED) + .append(text("", NamedTextColor.GOLD)) + .append(text("' does not match the given type '", NamedTextColor.RED)) + .append(text("", NamedTextColor.GOLD)) + .append(text("'!", NamedTextColor.RED))), + META_EXISTENT_VALUE_WRONG_TYPE(text("Given key '", NamedTextColor.RED) + .append(text("", NamedTextColor.GOLD)) + .append(text("' stores a value with a different type!", NamedTextColor.RED))), NO_META_VALUE(text("Could not find a meta value with key ", NamedTextColor.RED) .append(text("", NamedTextColor.GOLD)) .append(text(" and type ", NamedTextColor.RED)) From c77bad427397c3b25e204f788fbe15d513629659 Mon Sep 17 00:00:00 2001 From: BlitzOffline <52609756+BlitzOffline@users.noreply.github.com> Date: Sat, 5 Apr 2025 05:28:13 +0300 Subject: [PATCH 9/9] fixed small issues with messages and meta value checks --- .../command/subcommand/MetaCommand.java | 18 ++++++++-------- .../persistentmeta/PersistentMetaHandler.java | 21 +++++++++++-------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java index 0b49373a..c046d442 100644 --- a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java +++ b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java @@ -358,7 +358,7 @@ private void handleSetMeta(@NotNull final CommandSender sender, @NotNull final P final Object parsedValue = plugin.getPersistentMetaHandler().parseValueByType(type, value); if (parsedValue == null) { plugin.sms(sender, Messages.META_VALUE_TYPE_MISMATCH.message() - .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build()) + .replaceText(VALUE_REPLACER_BUILDER.replacement(value).build()) .replaceText(TYPE_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build())); return; } @@ -375,12 +375,12 @@ private void handleSetMeta(@NotNull final CommandSender sender, @NotNull final P return; case NEW_VALUE_IS_DIFFERENT_TYPE: plugin.sms(sender, Messages.META_VALUE_TYPE_MISMATCH.message() - .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build()) + .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build()) .replaceText(TYPE_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build())); return; case EXISTENT_VALUE_IS_DIFFERENT_TYPE: plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE.message() - .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build())); + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build())); return; default: plugin.sms(sender, Messages.WRONG_USAGE_META_SET_COMMAND); @@ -402,7 +402,7 @@ private void handleRemoveMeta(@NotNull final CommandSender sender, @NotNull fina return; case EXISTENT_VALUE_IS_DIFFERENT_TYPE: plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE.message() - .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build())); + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build())); return; case VALUE_NOT_FOUND: plugin.sms(sender, Messages.NO_META_VALUE.message() @@ -432,7 +432,7 @@ private void handleSwitchMeta(@NotNull final CommandSender sender, @NotNull fina return; case EXISTENT_VALUE_IS_DIFFERENT_TYPE: plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE.message() - .replaceText(KEY_REPLACER_BUILDER.replacement(DataType.BOOLEAN.getName()).build())); + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build())); return; default: plugin.sms(sender, Messages.WRONG_USAGE_META_SWITCH_COMMAND); @@ -448,7 +448,7 @@ private void handleAddMeta(@NotNull final CommandSender sender, @NotNull final P if (!(parsedValue instanceof Number)) { plugin.sms(sender, Messages.META_ADD_TYPE_MISMATCH.message() - .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build())); + .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(value)).build())); return; } @@ -472,7 +472,7 @@ private void handleAddMeta(@NotNull final CommandSender sender, @NotNull final P return; case EXISTENT_VALUE_IS_DIFFERENT_TYPE: plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE.message() - .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build())); + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build())); return; default: plugin.sms(sender, Messages.WRONG_USAGE_META_ADD_COMMAND); @@ -488,7 +488,7 @@ private void handleSubtractMeta(@NotNull final CommandSender sender, @NotNull fi if (!(parsedValue instanceof Number)) { plugin.sms(sender, Messages.META_SUBTRACT_TYPE_MISMATCH.message() - .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build())); + .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(value)).build())); return; } @@ -512,7 +512,7 @@ private void handleSubtractMeta(@NotNull final CommandSender sender, @NotNull fi return; case EXISTENT_VALUE_IS_DIFFERENT_TYPE: plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE.message() - .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build())); + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build())); return; default: plugin.sms(sender, Messages.WRONG_USAGE_META_SUBTRACT_COMMAND); diff --git a/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java b/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java index f812e773..799a63f7 100644 --- a/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java +++ b/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java @@ -99,12 +99,11 @@ public boolean hasMetaValue( @NotNull final DataType type, @NotNull final T defaultValue ) { - if (!player.getPersistentDataContainer().has(key, type.getPDType())) { - return defaultValue; + final T value = getMetaValue(player, key, type); + if (value != null) { + return value; } - - final T result = player.getPersistentDataContainer().get(key, type.getPDType()); - return result == null ? defaultValue : result; + return defaultValue; } /** @@ -146,7 +145,8 @@ public Map getMetaValues( return OperationResult.NEW_VALUE_IS_DIFFERENT_TYPE; } - if (player.getPersistentDataContainer().has(key) && !player.getPersistentDataContainer().has(key, type.getPDType())) { + if (player.getPersistentDataContainer().has(key) && + (!player.getPersistentDataContainer().has(key, type.getPDType()) || !type.isSupported(player.getPersistentDataContainer().get(key, type.getPDType())))) { return OperationResult.EXISTENT_VALUE_IS_DIFFERENT_TYPE; } @@ -167,7 +167,8 @@ public Map getMetaValues( @NotNull final NamespacedKey key, @NotNull final DataType type ) { - if (player.getPersistentDataContainer().has(key) && !player.getPersistentDataContainer().has(key, type.getPDType())) { + if (player.getPersistentDataContainer().has(key) && + (!player.getPersistentDataContainer().has(key, type.getPDType()) || !type.isSupported(player.getPersistentDataContainer().get(key, type.getPDType())))) { return OperationResult.EXISTENT_VALUE_IS_DIFFERENT_TYPE; } @@ -248,7 +249,8 @@ public Map getMetaValues( return OperationResult.INVALID_TYPE; } - if (player.getPersistentDataContainer().has(key) && !player.getPersistentDataContainer().has(key, type.getPDType())) { + if (player.getPersistentDataContainer().has(key) && + (!player.getPersistentDataContainer().has(key, type.getPDType()) || !type.isSupported(player.getPersistentDataContainer().get(key, type.getPDType())))) { return OperationResult.EXISTENT_VALUE_IS_DIFFERENT_TYPE; } @@ -287,7 +289,8 @@ public Map getMetaValues( return OperationResult.INVALID_TYPE; } - if (player.getPersistentDataContainer().has(key) && !player.getPersistentDataContainer().has(key, type.getPDType())) { + if (player.getPersistentDataContainer().has(key) && + (!player.getPersistentDataContainer().has(key, type.getPDType()) || !type.isSupported(player.getPersistentDataContainer().get(key, type.getPDType())))) { return OperationResult.EXISTENT_VALUE_IS_DIFFERENT_TYPE; }