diff --git a/api/src/main/java/me/filoghost/chestcommands/api/ChestCommandsAPI.java b/api/src/main/java/me/filoghost/chestcommands/api/ChestCommandsAPI.java index a501312..46aa149 100644 --- a/api/src/main/java/me/filoghost/chestcommands/api/ChestCommandsAPI.java +++ b/api/src/main/java/me/filoghost/chestcommands/api/ChestCommandsAPI.java @@ -53,18 +53,24 @@ public class ChestCommandsAPI { * Menus loaded by Chest Commands from the menus folder always display placeholders, including those registered * through this method. *

- * The identifier is automatically converted to the appropriate placeholder format. For example, given the - * identifier "test", the callback would be invoked to replace the the following placeholders: + * The identifier is used to compute which placeholder formats will invoke the replacer callback. For example, given + * the identifier "test", the callback would be invoked to replace the following placeholders (case insensitive): *

+ * The plugin name is used as optional namespace, to distinguish two placeholders with the same identifier but + * registered by distinct plugins. + *

+ * This replaces any currently registered placeholder with the same plugin and identifier. * * @param plugin the plugin registering the placeholder * @param identifier the identifier of the placeholder, which can only contain letters, digits and * underscores * @param placeholderReplacer the callback that returns the displayed value + * @throws IllegalArgumentException if the identifier contains invalid characters * @see PlaceholderReplacer#getReplacement(Player, String) * @since 1 */ @@ -74,12 +80,11 @@ public class ChestCommandsAPI { BackendAPI.getImplementation().registerPlaceholder(plugin, identifier, placeholderReplacer); } - /** * Returns if a menu with a given file name exists and was loaded successfully by Chest Commands from the menus * folder. * - * @param menuFileName the file name of the menu to check + * @param menuFileName the file name of the menu to check, including the {@code .yml} file extension * @return true if the menu exists, false otherwise * @since 1 */ @@ -94,7 +99,7 @@ public class ChestCommandsAPI { * WARNING: this method opens the menu without checking the permissions of the player. * * @param player the player that will see the menu - * @param menuFileName the file name of the menu to open + * @param menuFileName the file name of the menu to open, including the {@code .yml} file extension * @return true if the menu was found and opened successfully, false otherwise * @since 1 */ diff --git a/api/src/main/java/me/filoghost/chestcommands/api/Menu.java b/api/src/main/java/me/filoghost/chestcommands/api/Menu.java index 5fb4b09..9b5e141 100644 --- a/api/src/main/java/me/filoghost/chestcommands/api/Menu.java +++ b/api/src/main/java/me/filoghost/chestcommands/api/Menu.java @@ -8,30 +8,34 @@ package me.filoghost.chestcommands.api; import me.filoghost.chestcommands.api.internal.BackendAPI; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** - * Menus are containers of {@link Icon}s that can be displayed to players as unmodifiable inventories. + * Menus are containers of {@link Icon}s that can be displayed to players as unmodifiable inventories, organized as a + * grid with a number of rows and columns. *

- * It is not recommended to implement this interface, use the provided constructor {@link Menu#create(Plugin, String, - * int)}. + * This interface should not be implemented, use the provided constructor {@link Menu#create(Plugin, String, int)}. Any + * custom implementation will not be handled by Chest Commands' event listener, which relies on internal details. New + * methods may also be added, making existing custom implementations incompatible. * * @since 1 */ +@ApiStatus.NonExtendable public interface Menu { /** * Creates a new menu. * - * @param owner the plugin creating the menu - * @param title title of the menu that will be displayed in the inventory - * @param rowCount number of rows in the menu (the number of columns is always 9, currently) + * @param plugin the plugin creating the menu + * @param title title of the menu that appears in the displayed inventory + * @param rows number of rows in the menu (the number of columns is always 9, currently) * @return the created menu * @since 1 */ - static @NotNull Menu create(@NotNull Plugin owner, @NotNull String title, int rowCount) { - return BackendAPI.getImplementation().createMenu(owner, title, rowCount); + static @NotNull Menu create(@NotNull Plugin plugin, @NotNull String title, int rows) { + return BackendAPI.getImplementation().createMenu(plugin, title, rows); } /** @@ -40,6 +44,8 @@ public interface Menu { * @param row the row position * @param column the column position * @param icon the new icon, null to remove the current one + * @throws IndexOutOfBoundsException if the row or the column is outside the limits ({@code row < 0 || row >= + * getRows() || column < 0 || column >= getColumns()}) * @since 1 */ void setIcon(int row, int column, @Nullable Icon icon); @@ -50,39 +56,17 @@ public interface Menu { * @param row the row position * @param column the column position * @return the icon at the give position, null if absent + * @throws IndexOutOfBoundsException if the row or the column is outside the limits ({@code row < 0 || row >= + * getRows() || column < 0 || column >= getColumns()}) * @since 1 */ @Nullable Icon getIcon(int row, int column); - /** - * Returns the title of the displayed inventory. - * - * @return the title - * @since 1 - */ - @NotNull String getTitle(); - - /** - * Returns the amount of rows of the displayed inventory. - * - * @return the amount of rows - * @since 1 - */ - int getRowCount(); - - /** - * Returns the amount of columns of the displayed inventory. - * - * @return the amount of columns - * @since 1 - */ - int getColumnCount(); - /** * Displays the menu to a player, creating a rendering of this menu and its icons. *

* If icons are added, removed or changed after the menu is displayed to a player, the view is not updated - * automatically and you may want to invoke {@link Menu#refreshOpenMenuViews()}. + * automatically and you may want to invoke {@link Menu#refreshOpenViews()}. * * @param player the player to which the menu will be displayed * @return the newly created view for the player @@ -97,10 +81,42 @@ public interface Menu { * This method should be called after adding, removing or changing one or more icons to update the open menu views * of players. *

- * This method invokes {@link MenuView#refresh()} on each open view created by this menu. + * This method invokes {@link MenuView#refresh()} on each currently open view created by this menu. * * @since 1 */ - void refreshOpenMenuViews(); + void refreshOpenViews(); + + /** + * Returns the amount of rows of the displayed inventory. + * + * @return the amount of rows + * @since 1 + */ + int getRows(); + + /** + * Returns the amount of columns of the displayed inventory. + * + * @return the amount of columns + * @since 1 + */ + int getColumns(); + + /** + * Returns the title of the displayed inventory. + * + * @return the title + * @since 1 + */ + @NotNull String getTitle(); + + /** + * Returns the plugin that created the menu. + * + * @return the plugin that created the menu + * @since 1 + */ + Plugin getPlugin(); } \ No newline at end of file diff --git a/api/src/main/java/me/filoghost/chestcommands/api/MenuView.java b/api/src/main/java/me/filoghost/chestcommands/api/MenuView.java index f1fb4c5..5583a15 100644 --- a/api/src/main/java/me/filoghost/chestcommands/api/MenuView.java +++ b/api/src/main/java/me/filoghost/chestcommands/api/MenuView.java @@ -29,7 +29,7 @@ public interface MenuView { * player and you want to refresh the money placeholder instantly. *

* Note that {@link ClickHandler} exposes the menu view being interacted with, so you don't need to refresh all the - * views of a menu through {@link Menu#refreshOpenMenuViews()}. + * views of a menu through {@link Menu#refreshOpenViews()}. * * @since 1 */ diff --git a/api/src/main/java/me/filoghost/chestcommands/api/PlaceholderReplacer.java b/api/src/main/java/me/filoghost/chestcommands/api/PlaceholderReplacer.java index d05c2f5..347d22b 100644 --- a/api/src/main/java/me/filoghost/chestcommands/api/PlaceholderReplacer.java +++ b/api/src/main/java/me/filoghost/chestcommands/api/PlaceholderReplacer.java @@ -27,6 +27,8 @@ public interface PlaceholderReplacer { *

* If this method returns null, the placeholder is not replaced. It is preferred to return a descriptive error * message rather than returning null. + *

+ * Warning: this method should be performance efficient, as it may be invoked quite often. * * @param player the player viewing the placeholder * @param argument the argument inside the placeholder, if present diff --git a/api/src/main/java/me/filoghost/chestcommands/api/internal/BackendAPI.java b/api/src/main/java/me/filoghost/chestcommands/api/internal/BackendAPI.java index c7ddb35..6902352 100644 --- a/api/src/main/java/me/filoghost/chestcommands/api/internal/BackendAPI.java +++ b/api/src/main/java/me/filoghost/chestcommands/api/internal/BackendAPI.java @@ -42,7 +42,7 @@ public abstract class BackendAPI { public abstract boolean openPluginMenu(@NotNull Player player, @NotNull String menuFileName); - public abstract @NotNull Menu createMenu(@NotNull Plugin owner, @NotNull String title, int rows); + public abstract @NotNull Menu createMenu(@NotNull Plugin plugin, @NotNull String title, int rows); public abstract @NotNull ConfigurableIcon createConfigurableIcon(@NotNull Material material); diff --git a/checkstyle/checkstyle.xml b/checkstyle/checkstyle.xml index bd8bc17..7375114 100644 --- a/checkstyle/checkstyle.xml +++ b/checkstyle/checkstyle.xml @@ -75,7 +75,6 @@ - diff --git a/plugin/src/main/java/me/filoghost/chestcommands/ChestCommands.java b/plugin/src/main/java/me/filoghost/chestcommands/ChestCommands.java index 6ff40f4..5d34b60 100644 --- a/plugin/src/main/java/me/filoghost/chestcommands/ChestCommands.java +++ b/plugin/src/main/java/me/filoghost/chestcommands/ChestCommands.java @@ -184,7 +184,7 @@ public class ChestCommands extends BaseJavaPlugin { } - public static Plugin getPluginInstance() { + public static Plugin getInstance() { return pluginInstance; } diff --git a/plugin/src/main/java/me/filoghost/chestcommands/DefaultBackendAPI.java b/plugin/src/main/java/me/filoghost/chestcommands/DefaultBackendAPI.java index c77bc0a..7e64a2d 100644 --- a/plugin/src/main/java/me/filoghost/chestcommands/DefaultBackendAPI.java +++ b/plugin/src/main/java/me/filoghost/chestcommands/DefaultBackendAPI.java @@ -53,8 +53,8 @@ public class DefaultBackendAPI extends BackendAPI { } @Override - public @NotNull Menu createMenu(@NotNull Plugin owner, @NotNull String title, int rows) { - return new APIMenu(owner, title, rows); + public @NotNull Menu createMenu(@NotNull Plugin plugin, @NotNull String title, int rows) { + return new APIMenu(plugin, title, rows); } @Override @@ -63,7 +63,9 @@ public class DefaultBackendAPI extends BackendAPI { } @Override - public void registerPlaceholder(@NotNull Plugin plugin, @NotNull String identifier, @NotNull PlaceholderReplacer placeholderReplacer) { + public void registerPlaceholder(@NotNull Plugin plugin, + @NotNull String identifier, + @NotNull PlaceholderReplacer placeholderReplacer) { PlaceholderManager.registerPluginPlaceholder(plugin, identifier, placeholderReplacer); } diff --git a/plugin/src/main/java/me/filoghost/chestcommands/action/OpenMenuAction.java b/plugin/src/main/java/me/filoghost/chestcommands/action/OpenMenuAction.java index 86f7c99..05fb200 100644 --- a/plugin/src/main/java/me/filoghost/chestcommands/action/OpenMenuAction.java +++ b/plugin/src/main/java/me/filoghost/chestcommands/action/OpenMenuAction.java @@ -31,7 +31,7 @@ public class OpenMenuAction implements Action { * Delay the task, since this action is executed in ClickInventoryEvent * and opening another inventory in the same moment is not a good idea. */ - Bukkit.getScheduler().runTask(ChestCommands.getPluginInstance(), () -> { + Bukkit.getScheduler().runTask(ChestCommands.getInstance(), () -> { menu.openCheckingPermission(player); }); diff --git a/plugin/src/main/java/me/filoghost/chestcommands/command/CommandHandler.java b/plugin/src/main/java/me/filoghost/chestcommands/command/CommandHandler.java index 4ac1283..e3d22c4 100644 --- a/plugin/src/main/java/me/filoghost/chestcommands/command/CommandHandler.java +++ b/plugin/src/main/java/me/filoghost/chestcommands/command/CommandHandler.java @@ -42,7 +42,7 @@ public class CommandHandler extends MultiCommandManager { @Override protected void sendNoArgsMessage(CommandSender sender, String rootCommandLabel) { sender.sendMessage(ChestCommands.CHAT_PREFIX); - sender.sendMessage(ChatColor.GREEN + "Version: " + ChatColor.GRAY + ChestCommands.getPluginInstance().getDescription().getVersion()); + sender.sendMessage(ChatColor.GREEN + "Version: " + ChatColor.GRAY + ChestCommands.getInstance().getDescription().getVersion()); sender.sendMessage(ChatColor.GREEN + "Developer: " + ChatColor.GRAY + "filoghost"); sender.sendMessage(ChatColor.GREEN + "Commands: " + ChatColor.GRAY + "/" + rootCommandLabel + " help"); } diff --git a/plugin/src/main/java/me/filoghost/chestcommands/hook/BungeeCordHook.java b/plugin/src/main/java/me/filoghost/chestcommands/hook/BungeeCordHook.java index 32d4b93..3ac3f7a 100644 --- a/plugin/src/main/java/me/filoghost/chestcommands/hook/BungeeCordHook.java +++ b/plugin/src/main/java/me/filoghost/chestcommands/hook/BungeeCordHook.java @@ -22,8 +22,8 @@ public enum BungeeCordHook implements PluginHook { @Override public void setup() { - if (!Bukkit.getMessenger().isOutgoingChannelRegistered(ChestCommands.getPluginInstance(), BUNGEE_CORD_CHANNEL)) { - Bukkit.getMessenger().registerOutgoingPluginChannel(ChestCommands.getPluginInstance(), BUNGEE_CORD_CHANNEL); + if (!Bukkit.getMessenger().isOutgoingChannelRegistered(ChestCommands.getInstance(), BUNGEE_CORD_CHANNEL)) { + Bukkit.getMessenger().registerOutgoingPluginChannel(ChestCommands.getInstance(), BUNGEE_CORD_CHANNEL); } } @@ -50,7 +50,7 @@ public enum BungeeCordHook implements PluginHook { throw new AssertionError(); } - player.sendPluginMessage(ChestCommands.getPluginInstance(), BUNGEE_CORD_CHANNEL, byteArrayOutputStream.toByteArray()); + player.sendPluginMessage(ChestCommands.getInstance(), BUNGEE_CORD_CHANNEL, byteArrayOutputStream.toByteArray()); } } diff --git a/plugin/src/main/java/me/filoghost/chestcommands/inventory/DefaultMenuView.java b/plugin/src/main/java/me/filoghost/chestcommands/inventory/DefaultMenuView.java index 6b4cade..d5f210d 100644 --- a/plugin/src/main/java/me/filoghost/chestcommands/inventory/DefaultMenuView.java +++ b/plugin/src/main/java/me/filoghost/chestcommands/inventory/DefaultMenuView.java @@ -14,9 +14,6 @@ import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -/** - * Represents a particular view of a menu. - */ public class DefaultMenuView implements MenuView { private final BaseMenu menu; @@ -26,7 +23,7 @@ public class DefaultMenuView implements MenuView { public DefaultMenuView(@NotNull BaseMenu menu, @NotNull Player viewer) { this.menu = menu; this.viewer = viewer; - this.bukkitInventory = new InventoryGrid(new MenuInventoryHolder(this), menu.getRowCount(), menu.getTitle()); + this.bukkitInventory = new InventoryGrid(new MenuInventoryHolder(this), menu.getRows(), menu.getTitle()); refresh(); } diff --git a/plugin/src/main/java/me/filoghost/chestcommands/listener/InventoryListener.java b/plugin/src/main/java/me/filoghost/chestcommands/listener/InventoryListener.java index 09e6ff5..c6235fa 100644 --- a/plugin/src/main/java/me/filoghost/chestcommands/listener/InventoryListener.java +++ b/plugin/src/main/java/me/filoghost/chestcommands/listener/InventoryListener.java @@ -8,9 +8,13 @@ package me.filoghost.chestcommands.listener; import me.filoghost.chestcommands.ChestCommands; import me.filoghost.chestcommands.api.ClickResult; import me.filoghost.chestcommands.api.Icon; +import me.filoghost.chestcommands.api.Menu; import me.filoghost.chestcommands.config.Settings; import me.filoghost.chestcommands.inventory.DefaultMenuView; +import me.filoghost.chestcommands.logging.Errors; +import me.filoghost.chestcommands.menu.InternalMenu; import me.filoghost.chestcommands.menu.MenuManager; +import me.filoghost.fcommons.logging.Log; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -75,13 +79,28 @@ public class InventoryListener implements Listener { } // Only handle the click AFTER the event has finished - Bukkit.getScheduler().runTask(ChestCommands.getPluginInstance(), () -> { - ClickResult result = icon.onClick(menuView, clicker); + Bukkit.getScheduler().runTask(ChestCommands.getInstance(), () -> { + try { + ClickResult result = icon.onClick(menuView, clicker); - if (result == ClickResult.CLOSE) { - clicker.closeInventory(); + if (result == ClickResult.CLOSE) { + clicker.closeInventory(); + } + } catch (Throwable t) { + handleIconClickException(menuView.getMenu(), t); } }); } + private void handleIconClickException(Menu menu, Throwable throwable) { + String menuDescription; + if (menu.getPlugin() == ChestCommands.getInstance()) { + menuDescription = "the menu \"" + Errors.formatPath(((InternalMenu) menu).getSourceFile()) + "\""; + } else { + menuDescription = "a menu created by the plugin \"" + menu.getPlugin().getName() + "\""; + } + + Log.severe("Encountered exception while handling a click inside " + menuDescription, throwable); + } + } diff --git a/plugin/src/main/java/me/filoghost/chestcommands/logging/Errors.java b/plugin/src/main/java/me/filoghost/chestcommands/logging/Errors.java index 246a528..5cf2c4d 100644 --- a/plugin/src/main/java/me/filoghost/chestcommands/logging/Errors.java +++ b/plugin/src/main/java/me/filoghost/chestcommands/logging/Errors.java @@ -215,7 +215,7 @@ public class Errors { } - private static String formatPath(Path path) { + public static String formatPath(Path path) { return ConfigErrors.formatPath(ChestCommands.getDataFolderPath(), path); } diff --git a/plugin/src/main/java/me/filoghost/chestcommands/menu/APIMenu.java b/plugin/src/main/java/me/filoghost/chestcommands/menu/APIMenu.java index d726acb..fa7ea05 100644 --- a/plugin/src/main/java/me/filoghost/chestcommands/menu/APIMenu.java +++ b/plugin/src/main/java/me/filoghost/chestcommands/menu/APIMenu.java @@ -11,16 +11,17 @@ import org.jetbrains.annotations.NotNull; public class APIMenu extends BaseMenu { - private final Plugin owner; - - public APIMenu(@NotNull Plugin owner, @NotNull String title, int rows) { + private final Plugin plugin; + + public APIMenu(@NotNull Plugin plugin, @NotNull String title, int rows) { super(title, rows); - Preconditions.notNull(owner, "owner"); - this.owner = owner; + Preconditions.notNull(plugin, "plugin"); + this.plugin = plugin; } - public @NotNull Plugin getOwner() { - return owner; + @Override + public Plugin getPlugin() { + return plugin; } } diff --git a/plugin/src/main/java/me/filoghost/chestcommands/menu/BaseMenu.java b/plugin/src/main/java/me/filoghost/chestcommands/menu/BaseMenu.java index 315b005..c751071 100644 --- a/plugin/src/main/java/me/filoghost/chestcommands/menu/BaseMenu.java +++ b/plugin/src/main/java/me/filoghost/chestcommands/menu/BaseMenu.java @@ -19,7 +19,7 @@ import org.jetbrains.annotations.Nullable; public abstract class BaseMenu implements Menu { - + private final String title; private final Grid icons; @@ -42,12 +42,31 @@ public abstract class BaseMenu implements Menu { } @Override - public int getRowCount() { + public @NotNull MenuView open(@NotNull Player player) { + Preconditions.notNull(player, "player"); + + DefaultMenuView menuView = new DefaultMenuView(this, player); + menuView.open(); + return menuView; + } + + @Override + public void refreshOpenViews() { + for (Player player : Bukkit.getOnlinePlayers()) { + DefaultMenuView menuView = MenuManager.getOpenMenuView(player); + if (menuView != null && menuView.getMenu() == this) { + menuView.refresh(); + } + } + } + + @Override + public int getRows() { return icons.getRows(); } - + @Override - public int getColumnCount() { + public int getColumns() { return icons.getColumns(); } @@ -60,22 +79,4 @@ public abstract class BaseMenu implements Menu { return icons; } - @Override - public @NotNull MenuView open(@NotNull Player player) { - Preconditions.notNull(player, "player"); - - DefaultMenuView menuView = new DefaultMenuView(this, player); - menuView.open(); - return menuView; - } - - @Override - public void refreshOpenMenuViews() { - for (Player player : Bukkit.getOnlinePlayers()) { - DefaultMenuView menuView = MenuManager.getOpenMenuView(player); - if (menuView != null && menuView.getMenu() == this) { - menuView.refresh(); - } - } - } } diff --git a/plugin/src/main/java/me/filoghost/chestcommands/menu/InternalMenu.java b/plugin/src/main/java/me/filoghost/chestcommands/menu/InternalMenu.java index 202c71a..014df87 100644 --- a/plugin/src/main/java/me/filoghost/chestcommands/menu/InternalMenu.java +++ b/plugin/src/main/java/me/filoghost/chestcommands/menu/InternalMenu.java @@ -6,6 +6,7 @@ package me.filoghost.chestcommands.menu; import com.google.common.collect.ImmutableList; +import me.filoghost.chestcommands.ChestCommands; import me.filoghost.chestcommands.Permissions; import me.filoghost.chestcommands.action.Action; import me.filoghost.chestcommands.api.MenuView; @@ -13,6 +14,7 @@ import me.filoghost.chestcommands.config.Lang; import me.filoghost.fcommons.collection.CollectionUtils; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.NotNull; import java.nio.file.Path; @@ -63,6 +65,11 @@ public class InternalMenu extends BaseMenu { return super.open(player); } + @Override + public Plugin getPlugin() { + return ChestCommands.getInstance(); + } + public void openCheckingPermission(Player player) { if (player.hasPermission(openPermission)) { open(player); diff --git a/plugin/src/main/java/me/filoghost/chestcommands/parsing/menu/MenuParser.java b/plugin/src/main/java/me/filoghost/chestcommands/parsing/menu/MenuParser.java index fa6a8f1..1a2f7da 100644 --- a/plugin/src/main/java/me/filoghost/chestcommands/parsing/menu/MenuParser.java +++ b/plugin/src/main/java/me/filoghost/chestcommands/parsing/menu/MenuParser.java @@ -63,16 +63,16 @@ public class MenuParser { int row = positionY.getPosition() - 1; int column = positionX.getPosition() - 1; - if (row < 0 || row >= menu.getRowCount()) { + if (row < 0 || row >= menu.getRows()) { errorCollector.add( Errors.Menu.invalidAttribute(iconSettings, AttributeType.POSITION_Y), - "it must be between 1 and " + menu.getRowCount()); + "it must be between 1 and " + menu.getRows()); return; } - if (column < 0 || column >= menu.getColumnCount()) { + if (column < 0 || column >= menu.getColumns()) { errorCollector.add( Errors.Menu.invalidAttribute(iconSettings, AttributeType.POSITION_X), - "it must be between 1 and " + menu.getColumnCount()); + "it must be between 1 and " + menu.getColumns()); return; } diff --git a/plugin/src/main/java/me/filoghost/chestcommands/placeholder/Placeholder.java b/plugin/src/main/java/me/filoghost/chestcommands/placeholder/Placeholder.java new file mode 100644 index 0000000..077b4cf --- /dev/null +++ b/plugin/src/main/java/me/filoghost/chestcommands/placeholder/Placeholder.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) filoghost and contributors + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package me.filoghost.chestcommands.placeholder; + +import me.filoghost.chestcommands.api.PlaceholderReplacer; +import org.bukkit.plugin.Plugin; + +public class Placeholder { + + private final Plugin plugin; + private final PlaceholderReplacer placeholderReplacer; + + public Placeholder(Plugin plugin, PlaceholderReplacer placeholderReplacer) { + this.plugin = plugin; + this.placeholderReplacer = placeholderReplacer; + } + + public Plugin getPlugin() { + return plugin; + } + + public PlaceholderReplacer getReplacer() { + return placeholderReplacer; + } + +} diff --git a/plugin/src/main/java/me/filoghost/chestcommands/placeholder/PlaceholderManager.java b/plugin/src/main/java/me/filoghost/chestcommands/placeholder/PlaceholderManager.java index c2cfcd2..bf09d4c 100644 --- a/plugin/src/main/java/me/filoghost/chestcommands/placeholder/PlaceholderManager.java +++ b/plugin/src/main/java/me/filoghost/chestcommands/placeholder/PlaceholderManager.java @@ -10,6 +10,7 @@ import me.filoghost.chestcommands.hook.PlaceholderAPIHook; import me.filoghost.chestcommands.placeholder.scanner.PlaceholderMatch; import me.filoghost.chestcommands.placeholder.scanner.PlaceholderScanner; import me.filoghost.fcommons.Preconditions; +import me.filoghost.fcommons.logging.Log; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.Nullable; @@ -21,14 +22,13 @@ public class PlaceholderManager { private static final List staticPlaceholders = new ArrayList<>(); private static final PlaceholderRegistry relativePlaceholderRegistry = new PlaceholderRegistry(); + private static final PlaceholderCache placeholderCache = new PlaceholderCache(); static { for (DefaultPlaceholder placeholder : DefaultPlaceholder.values()) { relativePlaceholderRegistry.registerInternalPlaceholder(placeholder.getIdentifier(), placeholder.getReplacer()); } } - private static final PlaceholderCache placeholderCache = new PlaceholderCache(); - public static boolean hasRelativePlaceholders(List list) { for (String element : list) { if (hasRelativePlaceholders(element)) { @@ -61,18 +61,24 @@ public class PlaceholderManager { } private static boolean isValidPlaceholder(PlaceholderMatch placeholderMatch) { - return relativePlaceholderRegistry.getPlaceholderReplacer(placeholderMatch) != null; + return relativePlaceholderRegistry.getPlaceholder(placeholderMatch) != null; } private static @Nullable String getReplacement(PlaceholderMatch placeholderMatch, Player player) { - PlaceholderReplacer placeholderReplacer = relativePlaceholderRegistry.getPlaceholderReplacer(placeholderMatch); + Placeholder placeholder = relativePlaceholderRegistry.getPlaceholder(placeholderMatch); - if (placeholderReplacer == null) { + if (placeholder == null) { return null; // Placeholder not found } return placeholderCache.computeIfAbsent(placeholderMatch, player, () -> { - return placeholderReplacer.getReplacement(player, placeholderMatch.getArgument()); + try { + return placeholder.getReplacer().getReplacement(player, placeholderMatch.getArgument()); + } catch (Throwable t) { + Log.severe("Encountered exception while replacing the placeholder \"" + placeholderMatch + + "\" registered by the plugin \"" + placeholder.getPlugin().getName() + "\"", t); + return "[ERROR]"; + } }); } diff --git a/plugin/src/main/java/me/filoghost/chestcommands/placeholder/PlaceholderRegistry.java b/plugin/src/main/java/me/filoghost/chestcommands/placeholder/PlaceholderRegistry.java index 3d20c61..ae3e3d7 100644 --- a/plugin/src/main/java/me/filoghost/chestcommands/placeholder/PlaceholderRegistry.java +++ b/plugin/src/main/java/me/filoghost/chestcommands/placeholder/PlaceholderRegistry.java @@ -5,47 +5,54 @@ */ package me.filoghost.chestcommands.placeholder; +import me.filoghost.chestcommands.ChestCommands; import me.filoghost.chestcommands.api.PlaceholderReplacer; import me.filoghost.chestcommands.placeholder.scanner.PlaceholderMatch; +import me.filoghost.fcommons.collection.CaseInsensitiveMap; import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.Nullable; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; public class PlaceholderRegistry { - private final Map internalPlaceholders = new HashMap<>(); - private final Map> externalPlaceholders = new HashMap<>(); + // + private final Map internalPlaceholders = new CaseInsensitiveMap<>(); + + // > + private final Map> externalPlaceholders = new CaseInsensitiveMap<>(); public void registerInternalPlaceholder(String identifier, PlaceholderReplacer replacer) { - internalPlaceholders.put(identifier, replacer); + internalPlaceholders.put(identifier, new Placeholder(ChestCommands.getInstance(), replacer)); } public void registerExternalPlaceholder(Plugin plugin, String identifier, PlaceholderReplacer placeholderReplacer) { externalPlaceholders - .computeIfAbsent(identifier, key -> new LinkedHashMap<>()) - .put(plugin.getName(), placeholderReplacer); + .computeIfAbsent(identifier, key -> new CaseInsensitiveMap<>(new LinkedHashMap<>())) + .put(plugin.getName(), new Placeholder(plugin, placeholderReplacer)); } - public @Nullable PlaceholderReplacer getPlaceholderReplacer(PlaceholderMatch placeholderMatch) { + public @Nullable Placeholder getPlaceholder(PlaceholderMatch placeholderMatch) { + String identifier = placeholderMatch.getIdentifier(); + if (placeholderMatch.getPluginNamespace() == null) { - PlaceholderReplacer internalReplacer = internalPlaceholders.get(placeholderMatch.getIdentifier()); - if (internalReplacer != null) { - return internalReplacer; + Placeholder internalPlaceholder = internalPlaceholders.get(identifier); + if (internalPlaceholder != null) { + return internalPlaceholder; } } - Map externalReplacers = externalPlaceholders.get(placeholderMatch.getIdentifier()); + Map externalPlaceholdersByPlugin = externalPlaceholders.get(identifier); // Find exact replacer if plugin name is specified if (placeholderMatch.getPluginNamespace() != null) { - return externalReplacers.get(placeholderMatch.getPluginNamespace()); + return externalPlaceholdersByPlugin.get(placeholderMatch.getPluginNamespace()); } - if (externalReplacers != null && !externalReplacers.isEmpty()) { - return externalReplacers.values().iterator().next(); + // Otherwise try find the first one registered + if (externalPlaceholdersByPlugin != null && !externalPlaceholdersByPlugin.isEmpty()) { + return externalPlaceholdersByPlugin.values().iterator().next(); } return null; diff --git a/plugin/src/main/java/me/filoghost/chestcommands/placeholder/scanner/PlaceholderMatch.java b/plugin/src/main/java/me/filoghost/chestcommands/placeholder/scanner/PlaceholderMatch.java index 29d73c0..6db89ef 100644 --- a/plugin/src/main/java/me/filoghost/chestcommands/placeholder/scanner/PlaceholderMatch.java +++ b/plugin/src/main/java/me/filoghost/chestcommands/placeholder/scanner/PlaceholderMatch.java @@ -35,9 +35,10 @@ public class PlaceholderMatch { /* * Valid formats: - * {pluginName/placeholder: argument} - * {placeholder: argument} * {placeholder} + * {placeholder: argument} + * {pluginName/identifier} + * {pluginName/identifier: argument} */ public static PlaceholderMatch parse(String placeholderContent) { String explicitPluginName = null;