diff --git a/build.gradle.kts b/build.gradle.kts index ad7045e2..aac3f127 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,7 @@ plugins { java - id("com.github.johnrengelman.shadow") version("8.1.1") - id("com.github.ben-manes.versions") version "0.48.0" + id("com.gradleup.shadow") version("8.3.5") + id("com.github.ben-manes.versions") version("0.51.0") } // Change to true when releasing @@ -18,6 +18,7 @@ repositories { maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") maven("https://repo.glaremasters.me/repository/public/") maven("https://nexus.phoenixdevt.fr/repository/maven-public/") + maven("https://repo.oraxen.com/releases") maven("https://jitpack.io") } @@ -30,6 +31,7 @@ dependencies { compileOnly(libs.headdb) compileOnly(libs.itemsadder) compileOnly(libs.oraxen) + compileOnly(libs.mythiclib) compileOnly(libs.mmoitems) compileOnly(libs.score) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cb90ac7f..d2c462c6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,19 +1,20 @@ [versions] # Compile only -spigot = "1.21-R0.1-SNAPSHOT" +spigot = "1.21.4-R0.1-SNAPSHOT" vault = "1.7.1" authlib = "1.5.25" -headdb = "1.3.1" -itemsadder = "3.2.5" +headdb = "1.3.2" +itemsadder = "3.6.3-beta-14" oraxen = "1.159.0" -mmoitems = "6.9.4-SNAPSHOT" +mythiclib = "1.7.1-SNAPSHOT" +mmoitems = "6.10-SNAPSHOT" papi = "2.11.6" -score = "4.23.10.8" +score = "4.24.3.5" # Implementation -nashorn = "15.4" -adventure-platform = "4.3.3" -adventure-minimessage = "4.17.0" +nashorn = "15.6" +adventure-platform = "4.3.4" +adventure-minimessage = "4.18.0" minelib = "1.2.1" [libraries] @@ -24,6 +25,7 @@ authlib = { module = "com.mojang:authlib", version.ref = "authlib" } headdb = { module = "com.arcaniax:HeadDatabase-API", version.ref = "headdb" } itemsadder = { module = "com.github.LoneDev6:api-itemsadder", version.ref = "itemsadder" } oraxen = { module = "com.github.oraxen:oraxen", version.ref = "oraxen" } +mythiclib = { module = "io.lumine:MythicLib-dist", version.ref = "mythiclib"} mmoitems = { module = "net.Indyuce:MMOItems-API", version.ref = "mmoitems" } papi = { module = "me.clip:placeholderapi", version.ref = "papi" } score = { module = "com.github.Ssomar-Developement:SCore", version.ref = "score" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7f93135c..e6441136 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a4413138..cea7a793 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 0adc8e1a..b740cf13 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -145,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -153,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -202,11 +202,11 @@ fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/gradlew.bat b/gradlew.bat index 6689b85b..7101f8e4 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -43,11 +43,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail diff --git a/src/main/java/com/extendedclip/deluxemenus/DeluxeMenus.java b/src/main/java/com/extendedclip/deluxemenus/DeluxeMenus.java index 6b3f77ee..43bfdadb 100644 --- a/src/main/java/com/extendedclip/deluxemenus/DeluxeMenus.java +++ b/src/main/java/com/extendedclip/deluxemenus/DeluxeMenus.java @@ -1,14 +1,15 @@ package com.extendedclip.deluxemenus; import com.extendedclip.deluxemenus.cache.SimpleCache; -import com.extendedclip.deluxemenus.commands.DeluxeMenusCommands; +import com.extendedclip.deluxemenus.command.DeluxeMenusCommand; import com.extendedclip.deluxemenus.config.DeluxeMenusConfig; +import com.extendedclip.deluxemenus.config.GeneralConfig; import com.extendedclip.deluxemenus.dupe.DupeFixer; import com.extendedclip.deluxemenus.dupe.MenuItemMarker; import com.extendedclip.deluxemenus.hooks.*; import com.extendedclip.deluxemenus.listener.PlayerListener; -import com.extendedclip.deluxemenus.menu.options.HeadType; import com.extendedclip.deluxemenus.menu.Menu; +import com.extendedclip.deluxemenus.menu.options.HeadType; import com.extendedclip.deluxemenus.metrics.Metrics; import com.extendedclip.deluxemenus.nbt.NbtProvider; import com.extendedclip.deluxemenus.persistentmeta.PersistentMetaHandler; @@ -20,324 +21,291 @@ import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteStreams; import io.github.projectunified.minelib.scheduler.canceller.TaskCanceller; -import me.clip.placeholderapi.PlaceholderAPIPlugin; import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; +import java.util.*; import java.util.function.Function; import java.util.logging.Level; import java.util.stream.Collectors; public class DeluxeMenus extends JavaPlugin { - public final static Map MATERIALS - = Arrays.stream(Material.values()).collect(Collectors.toUnmodifiableMap(Enum::name, Function.identity())); - private static DeluxeMenus instance; - private static DebugLevel debugLevel = DebugLevel.LOWEST; - private DeluxeMenusConfig menuConfig; - private Map itemHooks; - private VaultHook vaultHook; - private boolean checkUpdates; - private ItemStack head; - private PersistentMetaHandler persistentMetaHandler; - private MenuItemMarker menuItemMarker; - private DupeFixer dupeFixer; - private BukkitAudiences adventure; - - @Override - public void onLoad() { - instance = this; - - this.persistentMetaHandler = new PersistentMetaHandler(); - - if (NbtProvider.isAvailable()) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.INFO, - "NMS hook has been setup successfully!" - ); - return; - } - - debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Could not setup a NMS hook for your server version!" - ); - } - - @SuppressWarnings("deprecation") - @Override - public void onEnable() { - if (!hookPlaceholderAPI()) { - debug( - DebugLevel.HIGHEST, - Level.SEVERE, - "Could not hook into PlaceholderAPI!", - "DeluxeMenus will now disable!" - ); - Bukkit.getPluginManager().disablePlugin(this); - return; - } else { - debug( - DebugLevel.HIGHEST, - Level.INFO, - "Successfully hooked into PlaceholderAPI!" - ); - } + public static final Map MATERIALS = Arrays.stream(Material.values()).collect(Collectors.toUnmodifiableMap(Enum::name, Function.identity())); - menuItemMarker = new MenuItemMarker(this); - dupeFixer = new DupeFixer(this, menuItemMarker); + private static final DebugLevel STACKTRACE_PRINT_LEVEL = DebugLevel.MEDIUM; - this.adventure = BukkitAudiences.create(this); + private static DeluxeMenus instance; + private final GeneralConfig generalConfig = new GeneralConfig(this); + private PersistentMetaHandler persistentMetaHandler; + private MenuItemMarker menuItemMarker; + private BukkitAudiences audiences; + private VaultHook vaultHook; + private ItemStack head; + private Map itemHooks; + private DeluxeMenusConfig menuConfig; - setupItemHooks(); + public static DeluxeMenus getInstance() { + return instance; + } - if (Bukkit.getPluginManager().isPluginEnabled("Vault")) { - vaultHook = new VaultHook(); + @Override + public void onLoad() { + instance = this; + if (NbtProvider.isAvailable()) { + this.debug(DebugLevel.HIGHEST, Level.INFO, "NMS hook has been setup successfully!"); + return; + } - if (vaultHook.hooked()) { - debug( - DebugLevel.HIGHEST, - Level.INFO, - "Successfully hooked into Vault!" - ); - } + this.debug(DebugLevel.HIGHEST, Level.WARNING, "Could not setup a NMS hook for your server version!"); } - if (!VersionHelper.IS_ITEM_LEGACY) { - head = new ItemStack(Material.PLAYER_HEAD, 1); - } else { - head = new ItemStack(Material.valueOf("SKULL_ITEM"), 1, (short) 3); - } + @Override + public void onEnable() { + this.generalConfig.load(); - menuConfig = new DeluxeMenusConfig(this); - if (menuConfig.loadDefConfig()) { - debugLevel(menuConfig.debugLevel()); - checkUpdates = getConfig().getBoolean("check_updates"); - debug( - DebugLevel.HIGHEST, - Level.INFO, - menuConfig.loadGUIMenus() + " GUI menus loaded!" - ); - } else { - debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Failed to load from config.yml. Use /dm reload after fixing your errors." - ); - } + if (!hookIntoPlaceholderAPI()) { + Bukkit.getPluginManager().disablePlugin(this); + return; + } - new PlayerListener(this); - new DeluxeMenusCommands(this); - Bukkit.getMessenger().registerOutgoingPluginChannel(this, "BungeeCord"); - - if (checkUpdates) { - UpdateChecker updateChecker = new UpdateChecker(this); - - if (updateChecker.updateAvailable()) { - debug( - DebugLevel.HIGHEST, - Level.INFO, - "An update for DeluxeMenus (DeluxeMenus v" + updateChecker.getLatestVersion() + ")", - "is available at https://www.spigotmc.org/resources/deluxemenus.11734/" - ); - } else { - debug( - DebugLevel.HIGHEST, - Level.INFO, - "You are running the latest version of DeluxeMenus!" - ); - } - } + this.persistentMetaHandler = new PersistentMetaHandler(this); + this.menuItemMarker = new MenuItemMarker(this); + new DupeFixer(this, this.menuItemMarker).register(); - startMetrics(); + this.audiences = BukkitAudiences.create(this); - new Expansion(this).register(); - } + hookIntoVault(); + setUpItemHooks(); - @Override - public void onDisable() { - Bukkit.getMessenger().unregisterOutgoingPluginChannel(this, "BungeeCord"); + this.menuConfig = new DeluxeMenusConfig(this); + if (this.menuConfig.loadDefConfig()) { + debug(DebugLevel.HIGHEST, Level.INFO, menuConfig.loadGUIMenus() + " GUI menus loaded!"); + } else { + debug(DebugLevel.HIGHEST, Level.WARNING, "Failed to load from config.yml. Use /dm reload after fixing your errors."); + } - TaskCanceller.get(this).cancelAll(); + new PlayerListener(this).register(); + if (!new DeluxeMenusCommand(this).register()) { + debug(DebugLevel.HIGHEST, Level.SEVERE, "Could not register the DeluxeMenus command!"); + } + new Expansion(this).register(); - if (this.adventure != null) { - this.adventure.close(); - this.adventure = null; + setUpBungeeCordMessaging(); + setUpUpdateChecker(); + setUpMetrics(); } - Menu.unloadForShutdown(); + @Override + public void onDisable() { + Bukkit.getMessenger().unregisterOutgoingPluginChannel(this, "BungeeCord"); - itemHooks.clear(); + TaskCanceller.get(this).cancelAll(); - instance = null; - } + if (this.audiences != null) { + this.audiences.close(); + this.audiences = null; + } - private void setupItemHooks() { - itemHooks = new HashMap<>(); + Menu.unloadForShutdown(this); - if (PlaceholderAPIPlugin.getServerVersion().isSpigot()) { - itemHooks.put(HeadType.NAMED.getHookName(), new NamedHeadHook(this)); - itemHooks.put(HeadType.BASE64.getHookName(), new BaseHeadHook()); - itemHooks.put(HeadType.TEXTURE.getHookName(), new TextureHeadHook()); - } + itemHooks.clear(); - if (Bukkit.getPluginManager().isPluginEnabled("HeadDatabase")) { - try { - Class.forName("me.arcaniax.hdb.api.HeadDatabaseAPI"); - itemHooks.put(HeadType.HDB.getHookName(), new HeadDatabaseHook()); - } catch (ClassNotFoundException ignored) {} + HandlerList.unregisterAll(this); } - if (Bukkit.getPluginManager().isPluginEnabled("ItemsAdder")) { - itemHooks.put("itemsadder", new ItemsAdderHook()); + public Optional getItemHook(String id) { + return Optional.ofNullable(itemHooks.get(id)); } - if (Bukkit.getPluginManager().isPluginEnabled("Oraxen")) { - itemHooks.put("oraxen", new OraxenHook()); + public Map getItemHooks() { + return itemHooks; } - if (Bukkit.getPluginManager().isPluginEnabled("MMOItems")) { - itemHooks.put("mmoitems", new MMOItemsHook()); + public ItemStack getHead() { + return head != null ? head : new ItemStack(Material.DIRT, 1); } - if (Bukkit.getPluginManager().isPluginEnabled("ExecutableItems")) { - itemHooks.put("executableitems", new ExecutableItemsHook()); + public boolean shouldPrintStackTrace() { + return generalConfig.debugLevel().getPriority() <= STACKTRACE_PRINT_LEVEL.getPriority(); } - if (Bukkit.getPluginManager().isPluginEnabled("ExecutableBlocks")) { - itemHooks.put("executableblocks", new ExecutableBlocksHook()); + public void printStacktrace(final String message, final Throwable throwable) { + if (!shouldPrintStackTrace()) return; + + this.getLogger().log(Level.SEVERE, message, throwable); } - } + public void connect(Player p, String server) { + ByteArrayDataOutput out = ByteStreams.newDataOutput(); - public Optional getItemHook(String id) { - return Optional.ofNullable(itemHooks.get(id)); - } + try { + out.writeUTF("Connect"); + out.writeUTF(server); + } catch (Exception e) { + debug(DebugLevel.HIGHEST, Level.SEVERE, "There was a problem attempting to send " + p.getName() + " to server " + server + "!"); - public Map getItemHooks() { - return itemHooks; - } + printStacktrace("There was a problem attempting to send " + p.getName() + " to server " + server + "!", e); + } - public ItemStack getHead() { - return head != null ? head : new ItemStack(Material.DIRT, 1); - } + p.sendPluginMessage(this, "BungeeCord", out.toByteArray()); + } - public static DeluxeMenus getInstance() { - return instance; - } + public void sms(CommandSender s, Component msg) { + audiences().sender(s).sendMessage(msg); + } - public static DebugLevel debugLevel() { - return debugLevel; - } + public void sms(CommandSender s, Messages msg) { + audiences().sender(s).sendMessage(msg.message()); + } - public static void debugLevel(final DebugLevel level) { - debugLevel = level; - } + public void debug(@NotNull final DebugLevel messageDebugLevel, @NotNull final Level level, @NotNull final String... messages) { + this.debug(generalConfig.debugLevel(), messageDebugLevel, level, messages); + } - public static DebugLevel printStacktraceLevel() { - return DebugLevel.MEDIUM; - } + public void debug(@NotNull final DebugLevel generalDebugLevel, @NotNull final DebugLevel messageDebugLevel, @NotNull final Level level, @NotNull final String... messages) { + if (generalDebugLevel.getPriority() > messageDebugLevel.getPriority()) return; - public static boolean shouldPrintStackTrace() { - return debugLevel().getPriority() <= printStacktraceLevel().getPriority(); - } + this.getLogger().log(level, String.join(System.lineSeparator(), messages)); + } - public static void printStacktrace(final String message, final Throwable throwable) { - if (!shouldPrintStackTrace()) return; + public MenuItemMarker getMenuItemMarker() { + return menuItemMarker; + } - getInstance().getLogger().log(Level.SEVERE, message, throwable); - } + public DeluxeMenusConfig getConfiguration() { + return menuConfig; + } - private void startMetrics() { - Metrics metrics = new Metrics(this, 445); - metrics.addCustomChart(new Metrics.SingleLineChart("menus", Menu::getLoadedMenuSize)); - } + public VaultHook getVault() { + return vaultHook; + } - public void connect(Player p, String server) { - ByteArrayDataOutput out = ByteStreams.newDataOutput(); + public PersistentMetaHandler getPersistentMetaHandler() { + return persistentMetaHandler; + } - try { - out.writeUTF("Connect"); - out.writeUTF(server); - } catch (Exception e) { - debug( - DebugLevel.HIGHEST, - Level.SEVERE, - "There was a problem attempting to send " + p.getName() + " to server " + server + "!" - ); + public BukkitAudiences audiences() { + if (this.audiences == null) { + throw new IllegalStateException("Tried to access Adventure when the plugin was disabled!"); + } + return this.audiences; + } - printStacktrace( - "There was a problem attempting to send " + p.getName() + " to server " + server + "!", - e - ); + public void clearCaches() { + itemHooks.values().stream().filter(Objects::nonNull).filter(hook -> hook instanceof SimpleCache).map(hook -> (SimpleCache) hook).forEach(SimpleCache::clearCache); } - p.sendPluginMessage(this, "BungeeCord", out.toByteArray()); - } + public void reload() { + this.generalConfig.reload(); + } - public void sms(CommandSender s, Component msg) { - adventure().sender(s).sendMessage(msg); - } + public GeneralConfig getGeneralConfig() { + return generalConfig; + } - public void sms(CommandSender s, Messages msg) { - adventure().sender(s).sendMessage(msg.message()); - } + private boolean hookIntoPlaceholderAPI() { + final boolean canHook = Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null; + if (!canHook) { + this.debug(DebugLevel.HIGHEST, Level.SEVERE, "Could not hook into PlaceholderAPI!", "DeluxeMenus will now disable!"); + return false; + } - public static void debug(@NotNull final DebugLevel debugLevel, @NotNull final Level level, @NotNull final String... messages) { - if (debugLevel().getPriority() > debugLevel.getPriority()) return; + this.debug(DebugLevel.HIGHEST, Level.INFO, "Successfully hooked into PlaceholderAPI!"); + return true; + } - getInstance().getLogger().log( - level, - String.join(System.lineSeparator(), messages) - ); - } + private void hookIntoVault() { + if (!Bukkit.getPluginManager().isPluginEnabled("Vault")) { + return; + } + this.vaultHook = new VaultHook(); - private boolean hookPlaceholderAPI() { - return Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null; - } + if (this.vaultHook.hooked()) { + this.debug(DebugLevel.HIGHEST, Level.INFO, "Successfully hooked into Vault!"); + return; + } - public MenuItemMarker getMenuItemMarker() { - return menuItemMarker; - } + this.debug(DebugLevel.HIGHEST, Level.WARNING, "Could not hook into Vault!", + "DeluxeMenus will continue to work but some features (such as the 'has money' requirement) may not be available."); + } + + @SuppressWarnings("deprecation") + private void setUpItemHooks() { + if (!VersionHelper.IS_ITEM_LEGACY) { + this.head = new ItemStack(Material.PLAYER_HEAD, 1); + } else { + this.head = new ItemStack(Material.valueOf("SKULL_ITEM"), 1, (short) 3); + } + + this.itemHooks = new HashMap<>(); + + final NamedHeadHook namedHeadHook = new NamedHeadHook(this); + namedHeadHook.register(); + this.itemHooks.put(HeadType.NAMED.getHookName(), namedHeadHook); + this.itemHooks.put(HeadType.BASE64.getHookName(), new BaseHeadHook(this)); + this.itemHooks.put(HeadType.TEXTURE.getHookName(), new TextureHeadHook(this)); + + if (Bukkit.getPluginManager().isPluginEnabled("HeadDatabase")) { + try { + Class.forName("me.arcaniax.hdb.api.HeadDatabaseAPI"); + this.itemHooks.put(HeadType.HDB.getHookName(), new HeadDatabaseHook(this)); + } catch (ClassNotFoundException ignored) { + // We are looking for this specific class because we've had issues with other plugins being named HeadDatabase + // in the past + } + } + + if (Bukkit.getPluginManager().isPluginEnabled("ItemsAdder")) { + this.itemHooks.put("itemsadder", new ItemsAdderHook()); + } + + if (Bukkit.getPluginManager().isPluginEnabled("Oraxen")) { + this.itemHooks.put("oraxen", new OraxenHook()); + } + + if (Bukkit.getPluginManager().isPluginEnabled("MMOItems")) { + this.itemHooks.put("mmoitems", new MMOItemsHook(this)); + } + + if (Bukkit.getPluginManager().isPluginEnabled("ExecutableItems")) { + this.itemHooks.put("executableitems", new ExecutableItemsHook()); + } + + if (Bukkit.getPluginManager().isPluginEnabled("ExecutableBlocks")) { + this.itemHooks.put("executableblocks", new ExecutableBlocksHook()); + } + } - public DeluxeMenusConfig getConfiguration() { - return menuConfig; - } + private void setUpBungeeCordMessaging() { + Bukkit.getMessenger().registerOutgoingPluginChannel(this, "BungeeCord"); + } - public VaultHook getVault() { - return vaultHook; - } + private void setUpUpdateChecker() { + if (!this.generalConfig.checkForUpdates()) { + return; + } - public PersistentMetaHandler getPersistentMetaHandler() { - return persistentMetaHandler; - } + final UpdateChecker updateChecker = new UpdateChecker(this); + updateChecker.register(); + + if (updateChecker.updateAvailable()) { + this.debug(DebugLevel.HIGHEST, Level.INFO, "An update for DeluxeMenus (DeluxeMenus v" + updateChecker.getLatestVersion() + ")", "is available at https://www.spigotmc.org/resources/deluxemenus.11734/"); + return; + } + + this.debug(DebugLevel.HIGHEST, Level.INFO, "You are running the latest version of DeluxeMenus!"); + } - public BukkitAudiences adventure() { - if (this.adventure == null) { - throw new IllegalStateException("Tried to access Adventure when the plugin was disabled!"); + private void setUpMetrics() { + final Metrics metrics = new Metrics(this, 445); } - return this.adventure; - } - - public void clearCaches() { - itemHooks.values().stream() - .filter(Objects::nonNull) - .filter(hook -> hook instanceof SimpleCache) - .map(hook -> (SimpleCache) hook) - .forEach(SimpleCache::clearCache); - } } diff --git a/src/main/java/com/extendedclip/deluxemenus/action/ClickActionTask.java b/src/main/java/com/extendedclip/deluxemenus/action/ClickActionTask.java index 8202c2f7..87e68a42 100644 --- a/src/main/java/com/extendedclip/deluxemenus/action/ClickActionTask.java +++ b/src/main/java/com/extendedclip/deluxemenus/action/ClickActionTask.java @@ -3,11 +3,8 @@ import com.extendedclip.deluxemenus.DeluxeMenus; import com.extendedclip.deluxemenus.menu.Menu; import com.extendedclip.deluxemenus.menu.MenuHolder; -import com.extendedclip.deluxemenus.utils.AdventureUtils; -import com.extendedclip.deluxemenus.utils.DebugLevel; -import com.extendedclip.deluxemenus.utils.ExpUtils; -import com.extendedclip.deluxemenus.utils.StringUtils; -import com.extendedclip.deluxemenus.utils.VersionHelper; +import com.extendedclip.deluxemenus.utils.*; +import io.github.projectunified.minelib.scheduler.global.GlobalScheduler; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Bukkit; import org.bukkit.Sound; @@ -15,12 +12,7 @@ import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.jetbrains.annotations.NotNull; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; +import java.util.*; import java.util.logging.Level; public class ClickActionTask implements Runnable { @@ -74,18 +66,18 @@ public void run() { switch (actionType) { case META: - if (!VersionHelper.IS_PDC_VERSION || DeluxeMenus.getInstance().getPersistentMetaHandler() == null) { - DeluxeMenus.debug(DebugLevel.HIGHEST, Level.INFO, "Meta action not supported on this server version."); + if (!VersionHelper.IS_PDC_VERSION || plugin.getPersistentMetaHandler() == null) { + plugin.debug(DebugLevel.HIGHEST, Level.INFO, "Meta action not supported on this server version."); break; } try { - final boolean result = DeluxeMenus.getInstance().getPersistentMetaHandler().setMeta(player, executable); + final boolean result = plugin.getPersistentMetaHandler().setMeta(player, executable); if (!result) { - DeluxeMenus.debug(DebugLevel.HIGHEST, Level.INFO, "Invalid meta action! Make sure you have the right syntax."); + plugin.debug(DebugLevel.HIGHEST, Level.INFO, "Invalid meta action! Make sure you have the right syntax."); break; } } catch (final NumberFormatException exception) { - DeluxeMenus.debug(DebugLevel.HIGHEST, Level.INFO, "Invalid integer value for meta action!"); + plugin.debug(DebugLevel.HIGHEST, Level.INFO, "Invalid integer value for meta action!"); } break; @@ -110,11 +102,11 @@ public void run() { break; case MINI_MESSAGE: - plugin.adventure().player(player).sendMessage(MiniMessage.miniMessage().deserialize(executable)); + plugin.audiences().player(player).sendMessage(MiniMessage.miniMessage().deserialize(executable)); break; case MINI_BROADCAST: - plugin.adventure().all().sendMessage(MiniMessage.miniMessage().deserialize(executable)); + plugin.audiences().all().sendMessage(MiniMessage.miniMessage().deserialize(executable)); break; case MESSAGE: @@ -126,7 +118,7 @@ public void run() { break; case CLOSE: - Menu.closeMenu(player, true, true); + Menu.closeMenu(plugin, player, true, true); break; case OPEN_GUI_MENU: @@ -135,7 +127,7 @@ public void run() { final String[] executableParts = temporaryExecutable.split(" ", 2); if (executableParts.length == 0) { - DeluxeMenus.debug(DebugLevel.HIGHEST, Level.WARNING, "Could not find and open menu " + executable); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Could not find and open menu " + executable); break; } @@ -144,7 +136,7 @@ public void run() { final Optional optionalMenuToOpen = Menu.getMenuByName(menuName); if (optionalMenuToOpen.isEmpty()) { - DeluxeMenus.debug(DebugLevel.HIGHEST, Level.WARNING, "Could not find and open menu " + executable); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Could not find and open menu " + executable); break; } @@ -159,7 +151,7 @@ public void run() { if (menuArgumentNames.isEmpty()) { if (passedArgumentValues != null && passedArgumentValues.length > 0) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Arguments were given for menu " + menuName + " in action [openguimenu] or [openmenu], but the menu does not support arguments!" @@ -187,7 +179,7 @@ public void run() { } if (passedArgumentValues.length < menuArgumentNames.size()) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Not enough arguments given for menu " + menuName + " when opening using the [openguimenu] or [openmenu] action!" @@ -207,7 +199,7 @@ public void run() { if (passedArgumentValues.length <= index) { // This should never be the case! - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Not enough arguments given for menu " + menuName + " when opening using the [openguimenu] or [openmenu] action!" @@ -234,21 +226,21 @@ public void run() { break; case CONNECT: - DeluxeMenus.getInstance().connect(player, executable); + plugin.connect(player, executable); break; case JSON_MESSAGE: - AdventureUtils.sendJson(player, executable); + AdventureUtils.sendJson(plugin, player, executable); break; case JSON_BROADCAST: case BROADCAST_JSON: - plugin.adventure().all().sendMessage(AdventureUtils.fromJson(executable)); + plugin.audiences().all().sendMessage(AdventureUtils.fromJson(executable)); break; case REFRESH: if (holder.isEmpty()) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.MEDIUM, Level.WARNING, player.getName() + " does not have menu open! Nothing to refresh!" @@ -260,15 +252,15 @@ public void run() { break; case TAKE_MONEY: - if (DeluxeMenus.getInstance().getVault() == null || !DeluxeMenus.getInstance().getVault().hooked()) { - DeluxeMenus.debug(DebugLevel.HIGHEST, Level.WARNING, "Vault not hooked! Cannot take money!"); + if (plugin.getVault() == null || !plugin.getVault().hooked()) { + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Vault not hooked! Cannot take money!"); break; } try { - DeluxeMenus.getInstance().getVault().takeMoney(player, Double.parseDouble(executable)); + plugin.getVault().takeMoney(player, Double.parseDouble(executable)); } catch (final NumberFormatException exception) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Amount for take money action: " + executable + ", is not a valid number!" @@ -277,15 +269,15 @@ public void run() { break; case GIVE_MONEY: - if (DeluxeMenus.getInstance().getVault() == null || !DeluxeMenus.getInstance().getVault().hooked()) { - DeluxeMenus.debug(DebugLevel.HIGHEST, Level.WARNING, "Vault not hooked! Cannot give money!"); + if (plugin.getVault() == null || !plugin.getVault().hooked()) { + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Vault not hooked! Cannot give money!"); break; } try { - DeluxeMenus.getInstance().getVault().giveMoney(player, Double.parseDouble(executable)); + plugin.getVault().giveMoney(player, Double.parseDouble(executable)); } catch (final NumberFormatException exception) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Amount for give money action: " + executable + ", is not a valid number!" @@ -310,7 +302,7 @@ public void run() { } catch (final NumberFormatException exception) { if (actionType == ActionType.TAKE_EXP) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Amount for take exp action: " + executable + ", is not a valid number!" @@ -318,7 +310,7 @@ public void run() { break; } - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Amount for give exp action: " + executable + ", is not a valid number!" @@ -327,27 +319,27 @@ public void run() { } case GIVE_PERM: - if (DeluxeMenus.getInstance().getVault() == null || !DeluxeMenus.getInstance().getVault().hooked()) { - DeluxeMenus.debug( + if (plugin.getVault() == null || !plugin.getVault().hooked()) { + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Vault not hooked! Cannot give permission: " + executable + "!"); break; } - DeluxeMenus.getInstance().getVault().givePermission(player, executable); + plugin.getVault().givePermission(player, executable); break; case TAKE_PERM: - if (DeluxeMenus.getInstance().getVault() == null || !DeluxeMenus.getInstance().getVault().hooked()) { - DeluxeMenus.debug( + if (plugin.getVault() == null || !plugin.getVault().hooked()) { + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Vault not hooked! Cannot take permission: " + executable + "!"); break; } - DeluxeMenus.getInstance().getVault().takePermission(player, executable); + plugin.getVault().takePermission(player, executable); break; case BROADCAST_SOUND: @@ -359,9 +351,9 @@ public void run() { if (!executable.contains(" ")) { try { - sound = Sound.valueOf(executable.toUpperCase()); + sound = SoundUtils.getSound(executable.toUpperCase()); } catch (final IllegalArgumentException exception) { - DeluxeMenus.printStacktrace( + plugin.printStacktrace( "Sound name given for sound action: " + executable + ", is not a valid sound!", exception ); @@ -371,9 +363,9 @@ public void run() { String[] parts = executable.split(" ", 3); try { - sound = Sound.valueOf(parts[0].toUpperCase()); + sound = SoundUtils.getSound(parts[0].toUpperCase()); } catch (final IllegalArgumentException exception) { - DeluxeMenus.printStacktrace( + plugin.printStacktrace( "Sound name given for sound action: " + parts[0] + ", is not a valid sound!", exception ); @@ -384,13 +376,13 @@ public void run() { try { pitch = Float.parseFloat(parts[2]); } catch (final NumberFormatException exception) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Pitch given for sound action: " + parts[2] + ", is not a valid number!" ); - DeluxeMenus.printStacktrace( + plugin.printStacktrace( "Pitch given for sound action: " + parts[2] + ", is not a valid number!", exception ); @@ -401,13 +393,13 @@ public void run() { try { volume = Float.parseFloat(parts[1]); } catch (final NumberFormatException exception) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Volume given for sound action: " + parts[1] + ", is not a valid number!" ); - DeluxeMenus.printStacktrace( + plugin.printStacktrace( "Volume given for sound action: " + parts[1] + ", is not a valid number!", exception ); @@ -437,4 +429,13 @@ public void run() { break; } } -} \ No newline at end of file + + public void runTaskLater(DeluxeMenus plugin, long delay) { + GlobalScheduler.get(plugin).runLater(this, delay); + } + + public void runTask(DeluxeMenus plugin) { + GlobalScheduler.get(plugin).run(this); + } + +} diff --git a/src/main/java/com/extendedclip/deluxemenus/command/DeluxeMenusCommand.java b/src/main/java/com/extendedclip/deluxemenus/command/DeluxeMenusCommand.java new file mode 100644 index 00000000..948a944e --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/command/DeluxeMenusCommand.java @@ -0,0 +1,84 @@ +package com.extendedclip.deluxemenus.command; + +import com.extendedclip.deluxemenus.DeluxeMenus; +import com.extendedclip.deluxemenus.command.subcommand.DumpCommand; +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.OpenCommand; +import com.extendedclip.deluxemenus.command.subcommand.ReloadCommand; +import com.extendedclip.deluxemenus.command.subcommand.SubCommand; +import com.extendedclip.deluxemenus.utils.Messages; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextReplacementConfig; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.PluginCommand; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static net.kyori.adventure.text.Component.text; + +public class DeluxeMenusCommand implements CommandExecutor { + + private static final TextReplacementConfig.Builder VERSION_REPLACER_BUILDER = TextReplacementConfig.builder().matchLiteral(""); + private static final TextReplacementConfig.Builder AUTHORS_REPLACER_BUILDER = TextReplacementConfig.builder().matchLiteral(""); + + private final DeluxeMenus plugin; + private final Map subCommands = new HashMap<>(); + + public DeluxeMenusCommand(final @NotNull DeluxeMenus plugin) { + this.plugin = plugin; + } + + public boolean register() { + final PluginCommand command = this.plugin.getCommand("deluxemenus"); + if (command == null) { + return false; + } + + command.setExecutor(this); + registerSubCommands(); + return true; + } + + @Override + public boolean onCommand( + final @NotNull CommandSender sender, + final @NotNull Command command, + final @NotNull String label, + final @NotNull String[] args + ) { + final List arguments = Arrays.asList(args); + + if (arguments.isEmpty()) { + plugin.sms(sender, Messages.PLUGIN_VERSION.message().replaceText(VERSION_REPLACER_BUILDER.replacement(plugin.getDescription().getVersion()).build()).replaceText(AUTHORS_REPLACER_BUILDER.replacement(plugin.getDescription().getAuthors().stream().map(author -> text(author, NamedTextColor.WHITE)).collect(Component.toComponent(text(", ", NamedTextColor.GRAY)))).build())); + return true; + } + + final SubCommand subCommand = subCommands.get(arguments.get(0).toLowerCase()); + + if (subCommand != null) { + subCommand.execute(sender, arguments.subList(1, arguments.size())); + return true; + } + + plugin.sms(sender, Messages.WRONG_USAGE); + return true; + } + + private void registerSubCommands() { + subCommands.put("dump", new DumpCommand(plugin)); + subCommands.put("execute", new ExecuteCommand(plugin)); + subCommands.put("help", new HelpCommand(plugin)); + subCommands.put("list", new ListCommand(plugin)); + subCommands.put("open", new OpenCommand(plugin)); + subCommands.put("reload", new ReloadCommand(plugin)); + } +} diff --git a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/DumpCommand.java b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/DumpCommand.java new file mode 100644 index 00000000..013af95b --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/DumpCommand.java @@ -0,0 +1,55 @@ +package com.extendedclip.deluxemenus.command.subcommand; + +import com.extendedclip.deluxemenus.DeluxeMenus; +import com.extendedclip.deluxemenus.utils.DumpUtils; +import com.extendedclip.deluxemenus.utils.Messages; +import net.kyori.adventure.text.event.ClickEvent; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +import static net.kyori.adventure.text.Component.text; + +public class DumpCommand extends SubCommand { + + public DumpCommand(final @NotNull DeluxeMenus plugin) { + super(plugin); + } + + @Override + public void execute(final @NotNull CommandSender sender, final @NotNull List args) { + if (!sender.hasPermission("deluxemenus.admin")) { + plugin.sms(sender, Messages.NO_PERMISSION); + return; + } + + if (args.size() != 1) { + plugin.sms(sender, Messages.WRONG_USAGE_DUMP_COMMAND); + return; + } + + String dump = ""; + try { + dump = DumpUtils.createDump(plugin, args.get(0)); + } catch (final RuntimeException ignored) { + } + + if (dump.isBlank()) { + plugin.sms(sender, Messages.DUMP_FAILED); + return; + } + + DumpUtils.postDump(dump).whenComplete((result, error) -> { + if (error != null) { + plugin.printStacktrace("Something went wrong while trying to create and post a dump!", error); + plugin.sms(sender, Messages.DUMP_FAILED); + return; + } + + final var link = text(DumpUtils.URL + result).clickEvent(ClickEvent.openUrl(DumpUtils.URL + result)); + + plugin.sms(sender, Messages.DUMP_SUCCESS.message().append(link)); + }); + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/ExecuteCommand.java b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/ExecuteCommand.java new file mode 100644 index 00000000..533ca1b6 --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/ExecuteCommand.java @@ -0,0 +1,94 @@ +package com.extendedclip.deluxemenus.command.subcommand; + +import com.extendedclip.deluxemenus.DeluxeMenus; +import com.extendedclip.deluxemenus.action.ActionType; +import com.extendedclip.deluxemenus.action.ClickAction; +import com.extendedclip.deluxemenus.action.ClickActionTask; +import com.extendedclip.deluxemenus.config.DeluxeMenusConfig; +import com.extendedclip.deluxemenus.menu.Menu; +import com.extendedclip.deluxemenus.menu.MenuHolder; +import com.extendedclip.deluxemenus.utils.Messages; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ExecuteCommand extends SubCommand { + + public ExecuteCommand(final @NotNull DeluxeMenus plugin) { + super(plugin); + } + + @Override + public void execute(final @NotNull CommandSender sender, final @NotNull List args) { + if (!sender.isOp()) { + plugin.sms(sender, Messages.NO_PERMISSION); + return; + } + + if (args.size() < 2) { + plugin.sms(sender, Messages.WRONG_USAGE_EXECUTE_COMMAND); + return; + } + + Player target = Bukkit.getPlayerExact(args.get(0)); + if (target == null) { + plugin.sms(sender, Messages.PLAYER_IS_NOT_ONLINE.message().replaceText(PLAYER_REPLACER_BUILDER.replacement(args.get(1)).build())); + return; + } + + String executable = String.join(" ", args.subList(1, args.size())); + + ActionType type = ActionType.getByStart(executable); + + if (type == null) { + plugin.sms(sender, Messages.WRONG_ACTION_TYPE); + return; + } + + executable = executable.replaceFirst(Pattern.quote(type.getIdentifier()), "").trim(); + + ClickAction action = new ClickAction(type, executable); + + Matcher d = DeluxeMenusConfig.DELAY_MATCHER.matcher(executable); + + if (d.find()) { + action.setDelay(d.group(1)); + executable = executable.replaceFirst(Pattern.quote(d.group()), ""); + } + + Matcher ch = DeluxeMenusConfig.CHANCE_MATCHER.matcher(executable); + + if (ch.find()) { + action.setChance(ch.group(1)); + executable = executable.replaceFirst(Pattern.quote(ch.group()), ""); + } + + action.setExecutable(executable); + + MenuHolder holder = Menu.getMenuHolder(target).orElse(new MenuHolder(plugin, target)); + + if (!action.checkChance(holder)) { + plugin.sms(sender, Messages.CHANCE_FAIL); + return; + } + + final ClickActionTask actionTask = new ClickActionTask(plugin, target.getUniqueId(), action.getType(), action.getExecutable(), holder.getTypedArgs(), true, true); + + if (action.hasDelay()) { + actionTask.runTaskLater(plugin, action.getDelay(holder)); + + plugin.sms(sender, Messages.ACTION_TO_BE_EXECUTED.message().replaceText(AMOUNT_REPLACER_BUILDER.replacement(String.valueOf(action.getDelay(holder))).build())); + return; + } + + actionTask.runTask(plugin); + + plugin.sms(sender, Messages.ACTION_EXECUTED_FOR.message().replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build())); + + } +} diff --git a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/HelpCommand.java b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/HelpCommand.java new file mode 100644 index 00000000..1500c154 --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/HelpCommand.java @@ -0,0 +1,25 @@ +package com.extendedclip.deluxemenus.command.subcommand; + +import com.extendedclip.deluxemenus.DeluxeMenus; +import com.extendedclip.deluxemenus.utils.Messages; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class HelpCommand extends SubCommand { + + public HelpCommand(final @NotNull DeluxeMenus plugin) { + super(plugin); + } + + @Override + public void execute(final @NotNull CommandSender sender, final @NotNull List args) { + if (sender.hasPermission("deluxemenus.admin")) { + plugin.sms(sender, Messages.HELP_ADMIN); + return; + } + + plugin.sms(sender, Messages.HELP); + } +} diff --git a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/ListCommand.java b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/ListCommand.java new file mode 100644 index 00000000..a1858239 --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/ListCommand.java @@ -0,0 +1,318 @@ +package com.extendedclip.deluxemenus.command.subcommand; + +import com.extendedclip.deluxemenus.DeluxeMenus; +import com.extendedclip.deluxemenus.menu.Menu; +import com.extendedclip.deluxemenus.utils.Messages; +import com.google.common.primitives.Ints; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import static net.kyori.adventure.text.Component.newline; +import static net.kyori.adventure.text.Component.text; + +public class ListCommand extends SubCommand { + + public ListCommand(final @NotNull DeluxeMenus plugin) { + super(plugin); + } + + @Override + public void execute(final @NotNull CommandSender sender, final @NotNull List args) { + if (!sender.hasPermission("deluxemenus.list")) { + plugin.sms(sender, Messages.NO_PERMISSION); + return; + } + + if (!args.isEmpty() && args.get(0).equalsIgnoreCase("all")) { + final Collection menus = Menu.getAllMenus(); + if (menus.isEmpty()) { + plugin.sms(sender, Messages.MENUS_LOADED.message().replaceText(AMOUNT_REPLACER_BUILDER.replacement("There are no").build())); + return; + } + + sendSimpleMenuList(sender, Menu.getAllMenus()); + return; + } + + final Map> menus = Menu.getPathSortedMenus(); + if (menus.isEmpty() || menus.values().stream().allMatch(List::isEmpty)) { + plugin.sms(sender, Messages.MENUS_LOADED.message().replaceText(AMOUNT_REPLACER_BUILDER.replacement("There are no").build())); + return; + } + + final List configMenus = menus.remove("config"); + sendPaginatedMenuList(sender, menus, configMenus == null ? Collections.emptyList() : configMenus, args); + } + + private void sendSimpleMenuList(final @NotNull CommandSender sender, final @NotNull Collection menus) { + final TextComponent.Builder list = text(); + list.append(text("The following " + menus.size() + " menus are loaded on the server:", NamedTextColor.GOLD).append(newline())); + + if (sender instanceof ConsoleCommandSender) { + list.append(newline()); + + final Component menusList = menus.stream().map(menu -> { + final String menuCommand = getMenuDisplayCommand(menu); + return menuCommand == null + ? text(menu.options().name(), NamedTextColor.DARK_AQUA).append(text(" - ", NamedTextColor.GRAY)).append(text("No menu command", NamedTextColor.RED)) + : text(menu.options().name(), NamedTextColor.DARK_AQUA).append(text(" - ", NamedTextColor.GRAY)).append(text(menuCommand, NamedTextColor.GREEN)); + }).collect(Component.toComponent(text(" | ", NamedTextColor.WHITE))); + + plugin.sms(sender, list.append(menusList).build()); + return; + } + + list.append(text("**Hover menu name for more info**", NamedTextColor.GRAY)).append(newline()).append(newline()); + + final var menusList = menus.stream().map(menu -> { + final String menuCommand = getMenuDisplayCommand(menu); + + return menuCommand == null + ? text(menu.options().name(), NamedTextColor.DARK_AQUA).hoverEvent(HoverEvent.showText(text("No open command", NamedTextColor.GOLD))) + : text(menu.options().name(), NamedTextColor.DARK_AQUA).hoverEvent(HoverEvent.showText(text("Open Command: ", NamedTextColor.GOLD).append(text(menuCommand, NamedTextColor.YELLOW)))).clickEvent(ClickEvent.suggestCommand(menuCommand)); + }).collect(Component.toComponent(text(", ", NamedTextColor.WHITE))); + + list.append(menusList); + 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)); + } + + final int maxPages = (int) Math.ceil((double) totalMenusCount / plugin.getGeneralConfig().menusListPageSize()); + + if (page == null || page < 1) { + page = 1; + } + + if (page > maxPages) { + page = maxPages; + } + + final Map> paginatedMenus = getPaginatedMenus( + menus, + configMenus.stream().collect(TreeMap::new, (map, menu) -> map.put(menu.options().name(), menu), TreeMap::putAll), + page, + plugin.getGeneralConfig().menusListPageSize() + ); + + 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())); + + if (sender instanceof ConsoleCommandSender) { + final var menuList = createMenuListForConsole(pageMenusTree, 0); + + list.append(newline()).append(menuList).append(newline()).append(text("Use /dm list to view more menus", NamedTextColor.GRAY)); + plugin.sms(sender, list.build()); + return; + } + + list.append(text("**Hover menu name for more info**", NamedTextColor.GRAY)); + list.append(newline()).append(newline()); + + final var menuList = createMenuListForPlayer(pageMenusTree, 0); + + list.append(menuList); + + if (page > 1 || page < maxPages) { + list.append(newline()); + + if (page > 1) { + list.append(text("<< Previous", NamedTextColor.GOLD) + .hoverEvent(HoverEvent.showText( + text("Click to go to the previous page", NamedTextColor.GRAY) + .append(newline()).append(newline()) + .append(text("Executes: /dm list " + (page - 1), NamedTextColor.GRAY)) + )) + .clickEvent(ClickEvent.runCommand("/dm list " + (page - 1)))); + if (page < maxPages) { + list.append(text(" | ", NamedTextColor.GREEN)); + } + } + + if (page < maxPages) { + list.append(text("Next >>", NamedTextColor.GOLD) + .hoverEvent(HoverEvent.showText( + text("Click to go to the next page", NamedTextColor.GRAY) + .append(newline()).append(newline()) + .append(text("Executes: /dm list " + (page + 1), NamedTextColor.GRAY)) + )) + .clickEvent(ClickEvent.runCommand("/dm list " + (page + 1)))); + } + } + + plugin.sms(sender, list.build()); + } + + private Map> getPaginatedMenus(final Map> menus, + final @NotNull Map configMenus, + final int page, + final int pageSize + ) { + final Map> paginatedMenus = new LinkedHashMap<>(); + final int start = (page - 1) * pageSize; + final int end = start + pageSize; + + int count = 0; + int i = 0; + for (final Map.Entry entry : configMenus.entrySet()) { + if (count >= pageSize || i >= end) { + break; + } + + if (i < start) { + i++; + continue; + } + + paginatedMenus.computeIfAbsent("config", k -> new ArrayList<>()).add(entry.getValue()); + count++; + i++; + } + + for (final Map.Entry> entry : menus.entrySet()) { + if (count >= pageSize || i >= end) { + break; + } + + for (final Menu menu : entry.getValue()) { + if (count >= pageSize || i >= end) { + break; + } + + if (i < start) { + i++; + continue; + } + + paginatedMenus.computeIfAbsent(entry.getKey(), k -> new ArrayList<>()).add(menu); + count++; + i++; + } + } + + return paginatedMenus; + } + + @SuppressWarnings("unchecked") + private Component createMenuListForConsole(final Map tree, int tabs) { + final TextComponent.Builder list = text(); + + for (final Map.Entry entry : tree.entrySet()) { + if (entry.getValue() instanceof List) { + for (final Menu menu : (List) entry.getValue()) { + list.append(text(" ".repeat(tabs) + "- " + entry.getKey(), NamedTextColor.DARK_AQUA).append(text(" - ", NamedTextColor.GRAY)).append(text(menu.options().name(), NamedTextColor.GREEN)).append(newline())); + } + } else { + list.append(text(" ".repeat(tabs) + "|- " + entry.getKey(), NamedTextColor.DARK_AQUA)).append(newline()); + list.append(createMenuListForConsole((Map) entry.getValue(), tabs + 1)); + } + } + + return list.build(); + } + + @SuppressWarnings("unchecked") + private Component createMenuListForPlayer(final Map tree, int tabs) { + final TextComponent.Builder list = text(); + + for (final Map.Entry entry : tree.entrySet()) { + if (!(entry.getValue() instanceof List)) { + list.append(text(" ".repeat(tabs) + "|-" + entry.getKey(), NamedTextColor.DARK_AQUA)).append(newline()); + list.append(createMenuListForPlayer((Map) entry.getValue(), tabs + 1)); + continue; + } + + for (final Menu menu : (List) entry.getValue()) { + final String menuCommand = getMenuDisplayCommand(menu); + + list.append( + text(" ".repeat(tabs) + "- " + entry.getKey(), NamedTextColor.DARK_AQUA).append(text(" - ", NamedTextColor.GRAY)).append(text(menu.options().name(), NamedTextColor.GREEN)) + .hoverEvent(HoverEvent.showText(menuCommand != null ? text("Open Command: ", NamedTextColor.GOLD).append(text(menuCommand, NamedTextColor.YELLOW)) : text("No open command", NamedTextColor.GOLD))) + .clickEvent(ClickEvent.suggestCommand((menuCommand != null ? menuCommand : "No open command"))) + ).append(newline()); + } + } + + return list.build(); + } + + private Map convertMenusToTree(final Map> menus) { + final Map tree = new LinkedHashMap<>(); + + for (final Map.Entry> entry : menus.entrySet()) { + final String[] path = entry.getKey().split("/"); + addMenuToTreeRecursively(tree, List.of(path), entry.getValue(), 0); + } + + return tree; + } + + @SuppressWarnings("unchecked") + private void addMenuToTreeRecursively(final Map tree, final List path, final List menus, final int step) { + if (step < 0 || step >= path.size()) { + return; + } + + if (step == path.size() - 1) { + if (!tree.containsKey(path.get(step))) { + tree.put(path.get(step), new ArrayList<>(menus)); + } else { + final List list = (List) tree.get(path.get(step)); + list.addAll(menus); + } + return; + } + + final String value = path.get(step); + if (!tree.containsKey(value)) { + tree.put(value, new TreeMap<>()); + } + + addMenuToTreeRecursively((Map) tree.get(value), path, menus, step + 1); + } + + /** + * Get the command that can be used to open this menu. + * The response will be the first command in the list of commands for this menu. + * 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) { + final boolean useAdminCommand = this.plugin.getGeneralConfig().useAdminCommandsInMenusList(); + + if (useAdminCommand) { + return "/deluxemenus open " + menu.options().name(); + } + + if (menu.options().commands().isEmpty()) { + return null; + } + + return "/" + menu.options().commands().get(0); + } +} diff --git a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/OpenCommand.java b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/OpenCommand.java new file mode 100644 index 00000000..224c0bb6 --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/OpenCommand.java @@ -0,0 +1,128 @@ +package com.extendedclip.deluxemenus.command.subcommand; + +import com.extendedclip.deluxemenus.DeluxeMenus; +import com.extendedclip.deluxemenus.menu.Menu; +import com.extendedclip.deluxemenus.utils.Messages; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Optional; + +public class OpenCommand extends SubCommand { + + public OpenCommand(final @NotNull DeluxeMenus plugin) { + super(plugin); + } + + @Override + public void execute(final @NotNull CommandSender sender, final @NotNull List args) { + if (!sender.hasPermission("deluxemenus.open")) { + plugin.sms(sender, Messages.NO_PERMISSION); + return; + } + + boolean player = (sender instanceof Player); + + if (args.isEmpty()) { + plugin.sms(sender, Messages.WRONG_USAGE_OPEN_COMMAND); + return; + } + + if (Menu.getAllMenus().isEmpty()) { + plugin.sms(sender, Messages.MENUS_LOADED.message().replaceText(AMOUNT_REPLACER_BUILDER.replacement("There are no").build())); + return; + } + + Player viewer; + String placeholderPlayer = null; + + if (args.size() == 2 && args.get(1).startsWith("-p:")) { + if (!sender.hasPermission("deluxemenus.placeholdersfor")) { + plugin.sms(sender, Messages.NO_PERMISSION_PLAYER_ARGUMENT); + return; + } + + placeholderPlayer = args.get(1).replace("-p:", ""); + + } else if (args.size() >= 3 && args.get(2).startsWith("-p:")) { + if (!sender.hasPermission("deluxemenus.placeholdersfor")) { + plugin.sms(sender, Messages.NO_PERMISSION_PLAYER_ARGUMENT); + return; + } + + placeholderPlayer = args.get(2).replace("-p:", ""); + } + + if (args.size() >= 2) { + if (placeholderPlayer == null) { + if (player && !sender.hasPermission("deluxemenus.open.others")) { + plugin.sms(sender, Messages.NO_PERMISSION); + return; + } + + viewer = Bukkit.getPlayerExact(args.get(1)); + + } else { + if (args.size() >= 3) { + if (!sender.hasPermission("deluxemenus.open.others")) { + plugin.sms(sender, Messages.NO_PERMISSION); + return; + } + + viewer = Bukkit.getPlayerExact(args.get(1)); + + } else { + if (!player) { + plugin.sms(sender, Messages.MUST_SPECIFY_PLAYER); + return; + } + + viewer = (Player) sender; + } + } + + } else { + if (!player) { + plugin.sms(sender, Messages.MUST_SPECIFY_PLAYER); + return; + } + + viewer = (Player) sender; + } + + if (viewer == null) { + plugin.sms(sender, Messages.PLAYER_IS_NOT_ONLINE.message().replaceText(PLAYER_REPLACER_BUILDER.replacement(args.get(1)).build())); + return; + } + + Player placeholder = null; + + if (placeholderPlayer != null) { + placeholder = Bukkit.getPlayerExact(placeholderPlayer); + + if (placeholder == null) { + plugin.sms(sender, Messages.PLAYER_IS_NOT_ONLINE.message().replaceText(PLAYER_REPLACER_BUILDER.replacement(placeholderPlayer).build())); + return; + + } else { + if (placeholder.hasPermission("deluxemenus.placeholdersfor.exempt")) { + plugin.sms(sender, Messages.PLAYER_IS_EXEMPT.message().replaceText(PLAYER_REPLACER_BUILDER.replacement(placeholderPlayer).build())); + + return; + } + } + } + + Optional menu = Menu.getMenuByName(args.get(0)); + + if (menu.isEmpty()) { + plugin.sms(sender, Messages.INVALID_MENU.message().replaceText(MENU_REPLACER_BUILDER.replacement(args.get(0)).build())); + return; + } + + menu.get().openMenu(viewer, null, placeholder); + } +} diff --git a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/ReloadCommand.java b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/ReloadCommand.java new file mode 100644 index 00000000..8a664926 --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/ReloadCommand.java @@ -0,0 +1,64 @@ +package com.extendedclip.deluxemenus.command.subcommand; + +import com.extendedclip.deluxemenus.DeluxeMenus; +import com.extendedclip.deluxemenus.menu.Menu; +import com.extendedclip.deluxemenus.utils.Messages; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class ReloadCommand extends SubCommand { + + public ReloadCommand(final @NotNull DeluxeMenus plugin) { + super(plugin); + } + + @Override + public void execute(final @NotNull CommandSender sender, final @NotNull List args) { + if (!sender.hasPermission("deluxemenus.reload")) { + plugin.sms(sender, Messages.NO_PERMISSION); + return; + } + + if (plugin.getConfiguration().checkConfig(null, "config.yml", false) == null) { + plugin.sms(sender, Messages.RELOAD_FAIL); + return; + } + + if (!args.isEmpty()) { + if (Menu.getMenuByName(args.get(0)).isEmpty()) { + plugin.sms(sender, Messages.INVALID_MENU.message().replaceText(MENU_REPLACER_BUILDER.replacement(args.get(0)).build())); + return; + } + + Menu.unload(plugin, args.get(0)); + + if (plugin.getConfiguration().loadGUIMenu(args.get(0))) { + plugin.sms(sender, Messages.MENU_RELOADED.message().replaceText(MENU_REPLACER_BUILDER.replacement(args.get(0)).build())); + return; + } + + plugin.sms(sender, Messages.MENU_NOT_RELOADED.message().replaceText(MENU_REPLACER_BUILDER.replacement(args.get(0)).build())); + return; + + } + + plugin.clearCaches(); + plugin.reloadConfig(); + plugin.saveConfig(); + plugin.reload(); + Menu.unload(plugin); + plugin.getConfiguration().loadGUIMenus(); + plugin.sms(sender, Messages.RELOAD_SUCCESS); + + int gLoaded = Menu.getLoadedMenuSize(); + + if (gLoaded == 1) { + plugin.sms(sender, Messages.MENU_LOADED.message().replaceText(AMOUNT_REPLACER_BUILDER.replacement(String.valueOf(gLoaded)).build())); + return; + } + + plugin.sms(sender, Messages.MENUS_LOADED.message().replaceText(AMOUNT_REPLACER_BUILDER.replacement(String.valueOf(gLoaded)).build())); + } +} diff --git a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/SubCommand.java b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/SubCommand.java new file mode 100644 index 00000000..90bf406a --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/SubCommand.java @@ -0,0 +1,23 @@ +package com.extendedclip.deluxemenus.command.subcommand; + +import com.extendedclip.deluxemenus.DeluxeMenus; +import net.kyori.adventure.text.TextReplacementConfig; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +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 final DeluxeMenus plugin; + + public SubCommand(final @NotNull DeluxeMenus plugin) { + this.plugin = plugin; + } + + public abstract void execute(final @NotNull CommandSender sender, final @NotNull List args); +} diff --git a/src/main/java/com/extendedclip/deluxemenus/commands/DeluxeMenusCommands.java b/src/main/java/com/extendedclip/deluxemenus/commands/DeluxeMenusCommands.java deleted file mode 100644 index fa464e0c..00000000 --- a/src/main/java/com/extendedclip/deluxemenus/commands/DeluxeMenusCommands.java +++ /dev/null @@ -1,475 +0,0 @@ -package com.extendedclip.deluxemenus.commands; - -import com.extendedclip.deluxemenus.DeluxeMenus; -import com.extendedclip.deluxemenus.action.ActionType; -import com.extendedclip.deluxemenus.action.ClickAction; -import com.extendedclip.deluxemenus.action.ClickActionTask; -import com.extendedclip.deluxemenus.config.DeluxeMenusConfig; -import com.extendedclip.deluxemenus.menu.Menu; -import com.extendedclip.deluxemenus.menu.MenuHolder; -import com.extendedclip.deluxemenus.utils.DumpUtils; -import com.extendedclip.deluxemenus.utils.Messages; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Optional; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import io.github.projectunified.minelib.scheduler.common.scheduler.Scheduler; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.TextComponent; -import net.kyori.adventure.text.TextReplacementConfig; -import net.kyori.adventure.text.event.ClickEvent; -import net.kyori.adventure.text.event.HoverEvent; -import net.kyori.adventure.text.format.NamedTextColor; -import org.bukkit.Bukkit; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.bukkit.command.ConsoleCommandSender; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; - -import static net.kyori.adventure.text.Component.newline; -import static net.kyori.adventure.text.Component.text; - -public class DeluxeMenusCommands implements CommandExecutor { - - private static final TextReplacementConfig.Builder PLAYER_REPLACER_BUILDER = TextReplacementConfig.builder().matchLiteral(""); - private static final TextReplacementConfig.Builder VERSION_REPLACER_BUILDER = TextReplacementConfig.builder().matchLiteral(""); - private static final TextReplacementConfig.Builder AUTHORS_REPLACER_BUILDER = TextReplacementConfig.builder().matchLiteral(""); - private static final TextReplacementConfig.Builder AMOUNT_REPLACER_BUILDER = TextReplacementConfig.builder().matchLiteral(""); - private static final TextReplacementConfig.Builder MENU_REPLACER_BUILDER = TextReplacementConfig.builder().matchLiteral(""); - - private final DeluxeMenus plugin; - - public DeluxeMenusCommands(final @NotNull DeluxeMenus plugin) { - this.plugin = plugin; - this.plugin.getCommand("deluxemenus").setExecutor(this); - } - - @Override - public boolean onCommand( - final @NotNull CommandSender sender, - final @NotNull Command command, - final @NotNull String label, - final @NotNull String[] args - ) { - - if (args.length == 0) { - plugin.sms( - sender, - Messages.PLUGIN_VERSION.message() - .replaceText(VERSION_REPLACER_BUILDER.replacement(plugin.getDescription().getVersion()).build()) - .replaceText(AUTHORS_REPLACER_BUILDER.replacement( - plugin.getDescription() - .getAuthors() - .stream() - .map(author -> text(author, NamedTextColor.WHITE)) - .collect(Component.toComponent(text(", ", NamedTextColor.GRAY)))).build() - ) - ); - return true; - } - - if (args[0].equalsIgnoreCase("help")) { - if (sender.hasPermission("deluxemenus.admin")) { - plugin.sms(sender, Messages.HELP_ADMIN); - return true; - } - - plugin.sms(sender, Messages.HELP); - return true; - - } else if (args[0].equalsIgnoreCase("dump")) { - if (!sender.hasPermission("deluxemenus.admin")) { - plugin.sms(sender, Messages.NO_PERMISSION); - return true; - } - - if (args.length != 2) { - plugin.sms(sender, Messages.WRONG_USAGE_DUMP_COMMAND); - return true; - } - - String dump = ""; - try { - dump = DumpUtils.createDump(plugin, args[1]); - } catch (final RuntimeException ignored) { - } - - if (dump.isBlank()) { - plugin.sms(sender, Messages.DUMP_FAILED); - return true; - } - - DumpUtils.postDump(dump).whenComplete((result, error) -> { - if (error != null) { - DeluxeMenus.printStacktrace( - "Something went wrong while trying to create and post a dump!", - error - ); - plugin.sms(sender, Messages.DUMP_FAILED); - return; - } - - final var link = text(DumpUtils.URL + result) - .clickEvent(ClickEvent.openUrl(DumpUtils.URL + result)); - - plugin.sms(sender, Messages.DUMP_SUCCESS.message().append(link)); - }); - - return true; - } else if (args[0].equalsIgnoreCase("execute")) { - if (!sender.isOp()) { - plugin.sms(sender, Messages.NO_PERMISSION); - return true; - } - - if (args.length < 3) { - plugin.sms(sender, Messages.WRONG_USAGE_EXECUTE_COMMAND); - return true; - } - - Player target = Bukkit.getPlayer(args[1]); - if (target == null) { - plugin.sms( - sender, - Messages.PLAYER_IS_NOT_ONLINE.message().replaceText(PLAYER_REPLACER_BUILDER.replacement(args[1]).build()) - ); - return true; - } - - String executable = String.join(" ", Arrays.asList(args).subList(2, args.length)); - - ActionType type = ActionType.getByStart(executable); - - if (type == null) { - plugin.sms(sender, Messages.WRONG_ACTION_TYPE); - return true; - } - - executable = executable.replaceFirst(Pattern.quote(type.getIdentifier()), "").trim(); - - ClickAction action = new ClickAction(type, executable); - - Matcher d = DeluxeMenusConfig.DELAY_MATCHER.matcher(executable); - - if (d.find()) { - action.setDelay(d.group(1)); - executable = executable.replaceFirst(Pattern.quote(d.group()), ""); - } - - Matcher ch = DeluxeMenusConfig.CHANCE_MATCHER.matcher(executable); - - if (ch.find()) { - action.setChance(ch.group(1)); - executable = executable.replaceFirst(Pattern.quote(ch.group()), ""); - } - - action.setExecutable(executable); - - MenuHolder holder = Menu.getMenuHolder(target).orElse(new MenuHolder(target)); - - if (!action.checkChance(holder)) { - plugin.sms(sender, Messages.CHANCE_FAIL); - return true; - } - - final ClickActionTask actionTask = new ClickActionTask( - plugin, - target.getUniqueId(), - action.getType(), - action.getExecutable(), - holder.getTypedArgs(), - true, - true - ); - - Scheduler scheduler = action.getType().getScheduler().getScheduler(holder.getViewer()); - - if (action.hasDelay()) { - scheduler.runLater(actionTask, action.getDelay(holder)); - - plugin.sms( - sender, - Messages.ACTION_TO_BE_EXECUTED.message().replaceText( - AMOUNT_REPLACER_BUILDER.replacement(String.valueOf(action.getDelay(holder))).build()) - ); - return true; - } - - scheduler.run(actionTask); - - plugin.sms( - sender, - Messages.ACTION_EXECUTED_FOR.message().replaceText( - PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) - ); - return true; - - } else if (args[0].equalsIgnoreCase("reload")) { - if (!sender.hasPermission("deluxemenus.reload")) { - plugin.sms(sender, Messages.NO_PERMISSION); - return true; - } - - if (plugin.getConfiguration().checkConfig(null, "config.yml", false) == null) { - plugin.sms(sender, Messages.RELOAD_FAIL); - return true; - } - - if (args.length > 1) { - if (Menu.getMenuByName(args[1]).isEmpty()) { - plugin.sms( - sender, - Messages.INVALID_MENU.message().replaceText( - MENU_REPLACER_BUILDER.replacement(args[1]).build()) - ); - return true; - } - - Menu.unload(args[1]); - - if (plugin.getConfiguration().loadGUIMenu(args[1])) { - plugin.sms( - sender, - Messages.MENU_RELOADED.message().replaceText( - MENU_REPLACER_BUILDER.replacement(args[1]).build()) - ); - return true; - } - - - plugin.sms( - sender, - Messages.MENU_NOT_RELOADED.message().replaceText( - MENU_REPLACER_BUILDER.replacement(args[1]).build()) - ); - return true; - - } - - plugin.clearCaches(); - plugin.reloadConfig(); - plugin.saveConfig(); - DeluxeMenus.debugLevel(plugin.getConfiguration().debugLevel()); - Menu.unload(); - plugin.getConfiguration().loadGUIMenus(); - plugin.sms(sender, Messages.RELOAD_SUCCESS); - - int gLoaded = Menu.getLoadedMenuSize(); - - if (gLoaded == 1) { - plugin.sms( - sender, - Messages.MENU_LOADED.message().replaceText( - AMOUNT_REPLACER_BUILDER.replacement(String.valueOf(gLoaded)).build()) - ); - } else { - plugin.sms( - sender, - Messages.MENUS_LOADED.message().replaceText( - AMOUNT_REPLACER_BUILDER.replacement(String.valueOf(gLoaded)).build()) - ); - } - return true; - - } else if (args[0].equalsIgnoreCase("list")) { - - if (!sender.hasPermission("deluxemenus.list")) { - plugin.sms(sender, Messages.NO_PERMISSION); - return true; - } - - Collection menus = Menu.getAllMenus(); - - if (menus.isEmpty()) { - plugin.sms( - sender, - Messages.MENUS_LOADED.message().replaceText( - AMOUNT_REPLACER_BUILDER.replacement("There are no").build()) - ); - return true; - } - - // Component builder start - final TextComponent.Builder list = text(); - - // Header - list.append(text("The following " + menus.size() + " menus are loaded on the server:", NamedTextColor.GOLD) - .append(newline())); - - if (sender instanceof ConsoleCommandSender) { - list.append(newline()); - - // Add all the menus using streams - final var menusList = menus.stream().map(menu -> menu.options().commands().isEmpty() - ? text(menu.options().name(), NamedTextColor.DARK_AQUA) - .append(text(" - ", NamedTextColor.GRAY)) - .append(text("No menu command", NamedTextColor.RED)) - - : text(menu.options().name(), NamedTextColor.DARK_AQUA) - .append(text(" - ", NamedTextColor.GRAY)) - .append(text("/" + menu.options().commands().get(0), NamedTextColor.GREEN)) - ).collect(Component.toComponent(text(" | ", NamedTextColor.WHITE))); - - plugin.sms(sender, list.append(menusList).build()); - return true; - } - - // Inform they can hover - list.append(text("**Hover menu name for more info**", NamedTextColor.GRAY)); - - // Extra space below - list.append(newline()).append(newline()); - - // Add all the menus using streams - final var menusList = menus.stream().map(menu -> menu.options().commands().isEmpty() - ? text(menu.options().name(), NamedTextColor.DARK_AQUA).hoverEvent( - HoverEvent.showText(text("No open command", NamedTextColor.GOLD))) - : text(menu.options().name(), NamedTextColor.DARK_AQUA).hoverEvent( - HoverEvent.showText(text("Open Command: ", NamedTextColor.GOLD) - .append(text("/" + menu.options().commands().get(0), NamedTextColor.YELLOW)))) - .clickEvent( - ClickEvent.suggestCommand("/" + menu.options().commands().get(0))) - ).collect(Component.toComponent(text(", ", NamedTextColor.WHITE))); - - list.append(menusList); - - plugin.sms(sender, list.build()); - return true; - - } else if (args[0].equalsIgnoreCase("open")) { - - if (!sender.hasPermission("deluxemenus.open")) { - plugin.sms(sender, Messages.NO_PERMISSION); - return true; - } - - boolean player = (sender instanceof Player); - - if (args.length < 2) { - plugin.sms(sender, Messages.WRONG_USAGE_OPEN_COMMAND); - return true; - } - - if (Menu.getAllMenus().isEmpty()) { - plugin.sms( - sender, - Messages.MENUS_LOADED.message().replaceText( - AMOUNT_REPLACER_BUILDER.replacement("There are no").build()) - ); - return true; - } - - Player viewer; - String placeholderPlayer = null; - - if (args.length == 3 && args[2].startsWith("-p:")) { - if (!sender.hasPermission("deluxemenus.placeholdersfor")) { - plugin.sms(sender, Messages.NO_PERMISSION_PLAYER_ARGUMENT); - return true; - } - - placeholderPlayer = args[2].replace("-p:", ""); - - } else if (args.length >= 4 && args[3].startsWith("-p:")) { - if (!sender.hasPermission("deluxemenus.placeholdersfor")) { - plugin.sms(sender, Messages.NO_PERMISSION_PLAYER_ARGUMENT); - return true; - } - - placeholderPlayer = args[3].replace("-p:", ""); - } - - if (args.length >= 3) { - if (placeholderPlayer == null) { - if (player && !sender.hasPermission("deluxemenus.open.others")) { - plugin.sms(sender, Messages.NO_PERMISSION); - return true; - } - - viewer = Bukkit.getPlayer(args[2]); - - } else { - if (args.length >= 4) { - if (!sender.hasPermission("deluxemenus.open.others")) { - plugin.sms(sender, Messages.NO_PERMISSION); - return true; - } - - viewer = Bukkit.getPlayer(args[2]); - - } else { - if (!player) { - plugin.sms(sender, Messages.MUST_SPECIFY_PLAYER); - return true; - } - - viewer = (Player) sender; - } - } - - } else { - if (!player) { - plugin.sms(sender, Messages.MUST_SPECIFY_PLAYER); - return true; - } - - viewer = (Player) sender; - } - - if (viewer == null) { - plugin.sms( - sender, - Messages.PLAYER_IS_NOT_ONLINE.message().replaceText(PLAYER_REPLACER_BUILDER.replacement(args[2]).build()) - ); - return true; - } - - Player placeholder = null; - - if (placeholderPlayer != null) { - placeholder = Bukkit.getPlayer(placeholderPlayer); - - if (placeholder == null) { - plugin.sms( - sender, - Messages.PLAYER_IS_NOT_ONLINE.message().replaceText(PLAYER_REPLACER_BUILDER.replacement(placeholderPlayer).build()) - ); - return true; - - } else { - if (placeholder.hasPermission("deluxemenus.placeholdersfor.exempt")) { - plugin.sms( - sender, - Messages.PLAYER_IS_EXEMPT.message().replaceText(PLAYER_REPLACER_BUILDER.replacement(placeholderPlayer).build()) - ); - - return true; - } - } - } - - Optional menu = Menu.getMenuByName(args[1]); - - if (menu.isEmpty()) { - plugin.sms( - sender, - Messages.INVALID_MENU.message().replaceText( - MENU_REPLACER_BUILDER.replacement(args[1]).build()) - ); - return true; - } - - menu.get().openMenu(viewer, null, placeholder); - return true; - - } else { - plugin.sms(sender, Messages.WRONG_USAGE); - } - return true; - } -} diff --git a/src/main/java/com/extendedclip/deluxemenus/config/DeluxeMenusConfig.java b/src/main/java/com/extendedclip/deluxemenus/config/DeluxeMenusConfig.java index 0e2e3776..22b7e6bf 100644 --- a/src/main/java/com/extendedclip/deluxemenus/config/DeluxeMenusConfig.java +++ b/src/main/java/com/extendedclip/deluxemenus/config/DeluxeMenusConfig.java @@ -6,13 +6,27 @@ import com.extendedclip.deluxemenus.action.ClickActionTask; import com.extendedclip.deluxemenus.action.ClickHandler; import com.extendedclip.deluxemenus.hooks.ItemHook; -import com.extendedclip.deluxemenus.menu.LoreAppendMode; +import com.extendedclip.deluxemenus.menu.options.LoreAppendMode; import com.extendedclip.deluxemenus.menu.Menu; import com.extendedclip.deluxemenus.menu.MenuHolder; import com.extendedclip.deluxemenus.menu.MenuItem; import com.extendedclip.deluxemenus.menu.options.MenuItemOptions; import com.extendedclip.deluxemenus.menu.options.MenuOptions; -import com.extendedclip.deluxemenus.requirement.*; +import com.extendedclip.deluxemenus.requirement.HasExpRequirement; +import com.extendedclip.deluxemenus.requirement.HasItemRequirement; +import com.extendedclip.deluxemenus.requirement.HasMetaRequirement; +import com.extendedclip.deluxemenus.requirement.HasMoneyRequirement; +import com.extendedclip.deluxemenus.requirement.HasPermissionRequirement; +import com.extendedclip.deluxemenus.requirement.HasPermissionsRequirement; +import com.extendedclip.deluxemenus.requirement.InputResultRequirement; +import com.extendedclip.deluxemenus.requirement.IsNearRequirement; +import com.extendedclip.deluxemenus.requirement.IsObjectRequirement; +import com.extendedclip.deluxemenus.requirement.JavascriptRequirement; +import com.extendedclip.deluxemenus.requirement.RegexMatchesRequirement; +import com.extendedclip.deluxemenus.requirement.Requirement; +import com.extendedclip.deluxemenus.requirement.RequirementList; +import com.extendedclip.deluxemenus.requirement.RequirementType; +import com.extendedclip.deluxemenus.requirement.StringLengthRequirement; import com.extendedclip.deluxemenus.requirement.wrappers.ItemWrapper; import com.extendedclip.deluxemenus.utils.DebugLevel; import com.extendedclip.deluxemenus.utils.ItemUtils; @@ -48,6 +62,12 @@ import org.bukkit.potion.PotionEffectType; import org.jetbrains.annotations.NotNull; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -58,6 +78,11 @@ import java.util.Optional; import java.util.Set; import java.util.TreeMap; +import java.util.function.BiConsumer; +import java.util.logging.Level; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; import static com.extendedclip.deluxemenus.utils.Constants.PLACEHOLDER_PREFIX; import static com.extendedclip.deluxemenus.utils.Constants.PLAYER_ITEMS; @@ -67,24 +92,38 @@ public class DeluxeMenusConfig { public static final List VALID_MATERIALS = new ArrayList<>(); public static final List VALID_MATERIAL_PREFIXES = new ArrayList<>(); + public static final Pattern DELAY_MATCHER = Pattern.compile("]+)>", Pattern.CASE_INSENSITIVE); + public static final Pattern CHANCE_MATCHER = Pattern.compile("]+)>", Pattern.CASE_INSENSITIVE); + public static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("%((?[a-zA-Z0-9]+)_)(?[^%]+)%"); + private static final List VALID_INVENTORY_TYPES = VersionHelper.getValidInventoryTypes(); static { VALID_MATERIALS.addAll(PLAYER_ITEMS); VALID_MATERIALS.add(WATER_BOTTLE); VALID_MATERIAL_PREFIXES.add(PLACEHOLDER_PREFIX); - VALID_MATERIAL_PREFIXES.addAll( - DeluxeMenus.getInstance().getItemHooks().values() - .stream() - .map(ItemHook::getPrefix) - .collect(Collectors.toList()) - ); } - public static final Pattern DELAY_MATCHER = Pattern.compile("]+)>", Pattern.CASE_INSENSITIVE); - public static final Pattern CHANCE_MATCHER = Pattern.compile("]+)>", Pattern.CASE_INSENSITIVE); - public static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("%((?[a-zA-Z0-9]+)_)(?[^%]+)%"); - private static final List VALID_INVENTORY_TYPES = VersionHelper.getValidInventoryTypes(); + private final String separator = File.separator; + private final File menuDirectory; + private final DeluxeMenus plugin; + private final List exampleMenus = Arrays.asList("basics_menu", "advanced_menu", "requirements_menu" + // more example menus here + ); + + public DeluxeMenusConfig(@NotNull final DeluxeMenus plugin) { + VALID_MATERIAL_PREFIXES.addAll(plugin.getItemHooks().values().stream().map(ItemHook::getPrefix).collect(Collectors.toList())); + + this.plugin = plugin; + menuDirectory = new File(this.plugin.getDataFolder() + separator + "gui_menus"); + try { + if (menuDirectory.mkdirs()) { + plugin.debug(DebugLevel.HIGH, Level.INFO, "Individual menus directory did not exist.", "Created directory: plugins" + separator + "DeluxeMenus" + separator + "gui_menus"); + } + } catch (SecurityException e) { + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Something went wrong while creating directory: plugins" + separator + "DeluxeMenus" + separator + "gui_menus"); + } + } private static boolean isValidMaterial(final @NotNull String material) { final String lowercaseMaterial = material.toLowerCase(Locale.ROOT); @@ -106,37 +145,8 @@ private static boolean isValidMaterial(final @NotNull String material) { return false; } - private final String separator = File.separator; - private final File menuDirectory; - private final DeluxeMenus plugin; - - private final List exampleMenus = Arrays.asList( - "basics_menu", - "advanced_menu", - "requirements_menu" - // more example menus here - ); - - public DeluxeMenusConfig(DeluxeMenus plugin) { - this.plugin = plugin; - menuDirectory = new File(this.plugin.getDataFolder() + separator + "gui_menus"); - try { - if (menuDirectory.mkdirs()) { - DeluxeMenus.debug( - DebugLevel.HIGH, - Level.INFO, - "Individual menus directory did not exist.", - "Created directory: plugins" + separator + "DeluxeMenus" + separator + "gui_menus" - ); - } - } catch (SecurityException e) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Something went wrong while creating directory: plugins" + separator + "DeluxeMenus" + separator - + "gui_menus" - ); - } + public static boolean containsPlaceholders(String text) { + return PLACEHOLDER_PATTERN.matcher(text).find(); } private List getStringListFromConfig(FileConfiguration config, String path) { @@ -151,10 +161,6 @@ private List getStringListFromConfig(FileConfiguration config, String pa } } - public static boolean containsPlaceholders(String text) { - return PLACEHOLDER_PATTERN.matcher(text).find(); - } - public boolean loadDefConfig() { if (checkConfig(null, "config.yml", true) == null) { return false; @@ -162,16 +168,17 @@ public boolean loadDefConfig() { FileConfiguration c = plugin.getConfig(); - c.options().header("DeluxeMenus " + plugin.getDescription().getVersion() - + " main configuration file" - + "\n" - + "\nA full wiki on how to use this plugin can be found at:" - + "\nhttps://wiki.helpch.at/clips-plugins/deluxemenus" - + "\n" - + c.options().header( + "DeluxeMenus " + plugin.getDescription().getVersion() + " main configuration file" + + "\n" + + "\nA full wiki on how to use this plugin can be found at:" + + "\nhttps://wiki.helpch.at/helpchat-plugins/deluxemenus" + + "\n" ); - c.addDefault("debug", "HIGHEST"); + c.addDefault("debug", "LOW"); c.addDefault("check_updates", true); + c.addDefault("use_admin_commands_in_menus_list", false); + c.addDefault("menus_list_page_size", 10); c.options().copyDefaults(true); if (!c.contains("gui_menus")) { @@ -190,10 +197,7 @@ private void createMenuExamples(FileConfiguration c) { try { menuFile.createNewFile(); } catch (IOException e) { - DeluxeMenus.printStacktrace( - "Failed to create example menus!", - e - ); + plugin.printStacktrace("Failed to create example menus!", e); continue; } saveResourceToFile(name + ".yml", menuFile); @@ -212,15 +216,8 @@ private boolean saveResourceToFile(String resource, File file) { os.write(buffer); return true; } catch (NullPointerException | IOException ex) { - DeluxeMenus.printStacktrace( - "Failed to update file: " + resource, - ex - ); - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.SEVERE, - "Failed to save default settings for:" + file.getName() + " from resource:" + resource - ); + plugin.printStacktrace("Failed to update file: " + resource, ex); + plugin.debug(DebugLevel.HIGHEST, Level.SEVERE, "Failed to save default settings for:" + file.getName() + " from resource:" + resource); } return false; } @@ -247,10 +244,7 @@ public FileConfiguration checkConfig(String folder, String fileName, boolean cre try { configFile.createNewFile(); } catch (IOException e) { - DeluxeMenus.printStacktrace( - "Failed to create file: " + fileName, - e - ); + plugin.printStacktrace("Failed to create file: " + fileName, e); return null; } } @@ -269,28 +263,14 @@ private FileConfiguration checkConfig(File f) { config.load(f); return config; } catch (IOException e) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.SEVERE, - "Could not read file: " + f.getName() - ); - - DeluxeMenus.printStacktrace( - "Could not read file: " + f.getName(), - e - ); + plugin.debug(DebugLevel.HIGHEST, Level.SEVERE, "Could not read file: " + f.getName()); + + plugin.printStacktrace("Could not read file: " + f.getName(), e); return null; } catch (InvalidConfigurationException e) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.SEVERE, - "Detected invalid configuration in file: " + f.getName() - ); - - DeluxeMenus.printStacktrace( - "Detected invalid configuration in file: " + f.getName(), - e - ); + plugin.debug(DebugLevel.HIGHEST, Level.SEVERE, "Detected invalid configuration in file: " + f.getName()); + + plugin.printStacktrace("Detected invalid configuration in file: " + f.getName(), e); return null; } } @@ -323,7 +303,7 @@ public boolean loadGUIMenu(String menu) { if (c.contains("gui_menus." + menu + ".file")) { loadMenuFromFile(menu); } else { - loadMenu(c, menu, true); + loadMenu(c, menu, true, "config"); } return true; @@ -358,7 +338,7 @@ public int loadGUIMenus() { loadMenuFromFile(key); } else { - loadMenu(c, key, true); + loadMenu(c, key, true, "config"); } } return Menu.getLoadedMenuSize(); @@ -369,24 +349,14 @@ public boolean loadMenuFromFile(String menuName) { String fileName = plugin.getConfig().getString("gui_menus." + menuName + ".file"); if (!fileName.endsWith(".yml")) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.SEVERE, - "Filename specified for menu: " + menuName + " is not a .yml file!", - "Make sure that the file name to load this menu from is specified as a .yml file!", - "Skipping loading of menu: " + menuName - ); + plugin.debug(DebugLevel.HIGHEST, Level.SEVERE, "Filename specified for menu: " + menuName + " is not a .yml file!", "Make sure that the file name to load this menu from is specified as a .yml file!", "Skipping loading of menu: " + menuName); return false; } File f = new File(menuDirectory.getPath(), fileName); if (!f.exists()) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.INFO, - f.getName() + " does not exist!" - ); + plugin.debug(DebugLevel.HIGHEST, Level.INFO, f.getName() + " does not exist!"); try { File folder = f.getParentFile(); @@ -394,26 +364,12 @@ public boolean loadMenuFromFile(String menuName) { f.createNewFile(); if (!saveResourceToFile("default_menu.yml", f)) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Failed to create a default menu file for menu: " + menuName, - "Skipping loading menu: " + menuName - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Failed to create a default menu file for menu: " + menuName, "Skipping loading menu: " + menuName); return false; } - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.INFO, - f.getName() + " created! Add your menu options to this file and use /dm reload to load it!" - ); + plugin.debug(DebugLevel.HIGHEST, Level.INFO, f.getName() + " created! Add your menu options to this file and use /dm reload to load it!"); } catch (IOException e) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.SEVERE, - "Could not create menu file: plugins" + separator + "DeluxeMenus" + separator + "gui_menus" - + separator + fileName - ); + plugin.debug(DebugLevel.HIGHEST, Level.SEVERE, "Could not create menu file: plugins" + separator + "DeluxeMenus" + separator + "gui_menus" + separator + fileName); return false; } } @@ -421,39 +377,27 @@ public boolean loadMenuFromFile(String menuName) { FileConfiguration cfg = checkConfig(f); if (cfg == null) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Menu: " + menuName + " in file: " + fileName + " not loaded." - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Menu: " + menuName + " in file: " + fileName + " not loaded."); return false; } if (cfg.getKeys(false).isEmpty()) { - DeluxeMenus.debug( - DebugLevel.HIGH, - Level.INFO, - "Menu config: " + f.getName() + " is empty! Creating default config example..." - ); + plugin.debug(DebugLevel.HIGH, Level.INFO, "Menu config: " + f.getName() + " is empty! Creating default config example..."); saveResourceToFile("default_menu.yml", f); return false; } - loadMenu(cfg, menuName, false); + final Path guiMenusPath = menuDirectory.toPath(); + final Path menuPath = f.toPath(); + final Path relativePath = guiMenusPath.relativize(menuPath); + + loadMenu(cfg, menuName, false, relativePath.toString()); return Menu.getMenuByName(menuName).isPresent(); } - public void loadMenu(FileConfiguration c, String key, boolean mainConfig) { - + public void loadMenu(FileConfiguration c, String key, boolean mainConfig, final @NotNull String path) { if (mainConfig) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Menu: " + key + " does not have a file specified in config.yml! Creating menus in the " + - "config.yml file is deprecated and will be removed in a future version! Please migrate your " + - "menus to individual files in the gui_menus directory! For more information see: " + - "https://wiki.helpch.at/clips-plugins/deluxemenus/external-menus" - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Menu: " + key + " does not have a file specified in config.yml! Creating menus in the " + "config.yml file is deprecated and will be removed in a future version! Please migrate your " + "menus to individual files in the gui_menus directory! For more information see: " + "https://wiki.helpch.at/clips-plugins/deluxemenus/external-menus"); } String pre = "gui_menus." + key + "."; @@ -463,12 +407,7 @@ public void loadMenu(FileConfiguration c, String key, boolean mainConfig) { } if (!c.contains(pre + "menu_title")) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.SEVERE, - "Menu title for menu: " + key + " is not present!", - "Skipping menu: " + key - ); + plugin.debug(DebugLevel.HIGHEST, Level.SEVERE, "Menu title for menu: " + key + " is not present!", "Skipping menu: " + key); return; } @@ -481,12 +420,7 @@ public void loadMenu(FileConfiguration c, String key, boolean mainConfig) { } if (title == null || title.isEmpty()) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.SEVERE, - "Menu title for menu: " + key + " is invalid!", - "Skipping menu: " + key - ); + plugin.debug(DebugLevel.HIGHEST, Level.SEVERE, "Menu title for menu: " + key + " is invalid!", "Skipping menu: " + key); return; } @@ -499,13 +433,7 @@ public void loadMenu(FileConfiguration c, String key, boolean mainConfig) { final InventoryType inventoryType = InventoryType.valueOf(c.getString(pre + "inventory_type").toUpperCase()); type = !VALID_INVENTORY_TYPES.contains(inventoryType) ? InventoryType.CHEST : inventoryType; } catch (Exception ex) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Inventory type for menu: " + key + " is invalid!", - "Valid Inventory types: " + Arrays.toString(VALID_INVENTORY_TYPES.toArray()), - "Defaulting to CHEST inventory type." - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Inventory type for menu: " + key + " is invalid!", "Valid Inventory types: " + Arrays.toString(VALID_INVENTORY_TYPES.toArray()), "Defaulting to CHEST inventory type."); } } @@ -519,22 +447,12 @@ public void loadMenu(FileConfiguration c, String key, boolean mainConfig) { String cmd = c.getString(pre + "open_command"); if (cmd == null) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.SEVERE, - "open_command specified for menu: " + key + " is null!", - "Skipping menu: " + key - ); + plugin.debug(DebugLevel.HIGHEST, Level.SEVERE, "open_command specified for menu: " + key + " is null!", "Skipping menu: " + key); return; } if (Menu.isMenuCommand(cmd)) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.SEVERE, - "open_command specified for menu: " + key + " already exists for another menu!", - "Skipping menu: " + key - ); + plugin.debug(DebugLevel.HIGHEST, Level.SEVERE, "open_command specified for menu: " + key + " already exists for another menu!", "Skipping menu: " + key); return; } @@ -546,12 +464,7 @@ public void loadMenu(FileConfiguration c, String key, boolean mainConfig) { for (String cmd : cmds) { if (Menu.isMenuCommand(cmd)) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "command: " + cmd + " specified for menu: " + key + " already exists for another menu!", - "Skipping command: " + cmd + " in menu: " + key - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "command: " + cmd + " specified for menu: " + key + " already exists for another menu!", "Skipping command: " + cmd + " in menu: " + key); } else { openCommands.add(cmd.toLowerCase()); } @@ -562,94 +475,68 @@ public void loadMenu(FileConfiguration c, String key, boolean mainConfig) { if (!openCommands.isEmpty()) { builder.commands(openCommands); builder.registerCommands(c.getBoolean(pre + "register_command", false)); + } - List argumentNames = new ArrayList<>(); - List argumentRequirements = new ArrayList<>(); - - if (c.contains(pre + "args")) { - // New requirements parsing - if (c.isConfigurationSection(pre + "args")) { - Set mapList = c.getConfigurationSection(pre + "args").getKeys(false); - debug("found args"); - for (String arg : mapList) { - debug("arg: " + arg); - // If it has requirements, add them - if (c.contains(pre + "args." + arg + ".requirements")) { - debug("arg has requirements: " + arg); - argumentRequirements.add(this.getRequirements(c, pre + "args." + arg)); - } - // Always add the arg itself - argumentNames.add(arg); + List argumentNames = new ArrayList<>(); + List argumentRequirements = new ArrayList<>(); + + if (c.contains(pre + "args")) { + // New requirements parsing + if (c.isConfigurationSection(pre + "args")) { + Set mapList = c.getConfigurationSection(pre + "args").getKeys(false); + debug("found args"); + for (String arg : mapList) { + debug("arg: " + arg); + // If it has requirements, add them + if (c.contains(pre + "args." + arg + ".requirements")) { + debug("arg has requirements: " + arg); + argumentRequirements.add(this.getRequirements(c, pre + "args." + arg)); } - // Old list parsing - } else if (c.isList(pre + "args")) { - argumentNames.addAll(c.getStringList(pre + "args")); - // Old singular item parsing - } else if (c.isString(pre + "args")) { - argumentNames.add(c.getString(pre + "args")); + // Always add the arg itself + argumentNames.add(arg); } + // Old list parsing + } else if (c.isList(pre + "args")) { + argumentNames.addAll(c.getStringList(pre + "args")); + // Old singular item parsing + } else if (c.isString(pre + "args")) { + argumentNames.add(c.getString(pre + "args")); } - - builder.arguments(argumentNames); - builder.argumentRequirements(argumentRequirements); - builder.argumentsUsageMessage(c.getString(pre + "args_usage_message", null)); } + builder.arguments(argumentNames); + builder.argumentRequirements(argumentRequirements); + builder.argumentsUsageMessage(c.getString(pre + "args_usage_message", null)); + int size = 54; if (type == InventoryType.CHEST) { if (!c.contains(pre + "size")) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.INFO, - "Menu size for menu: " + key + " is not present!", - "Using default size of 54" - ); + plugin.debug(DebugLevel.HIGHEST, Level.INFO, "Menu size for menu: " + key + " is not present!", "Using default size of 54"); } else { size = c.getInt(pre + "size"); - if ((size + 1) % 9 == 0) - size++; + if ((size + 1) % 9 == 0) size++; - if ((size - 1) % 9 == 0) - size--; + if ((size - 1) % 9 == 0) size--; if (size < 9) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.INFO, - "Menu size for menu: " + key + " is lower than 9", - "Defaulting to 9." - ); + plugin.debug(DebugLevel.HIGHEST, Level.INFO, "Menu size for menu: " + key + " is lower than 9", "Defaulting to 9."); size = 9; } if (size > 54) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Menu size for menu: " + key + " is higher than 54", - "Defaulting to 54." - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Menu size for menu: " + key + " is higher than 54", "Defaulting to 54."); size = 54; } if (size % 9 != 0) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Menu size for menu: " + key + " is not a multiple of 9", - "Defaulting to 54." - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Menu size for menu: " + key + " is not a multiple of 9", "Defaulting to 54."); size = 54; } } } else { size = type.getDefaultSize(); - DeluxeMenus.debug( - DebugLevel.LOWEST, - Level.INFO, - "TYPE IS: " + type + ". Setting size to:" + type.getDefaultSize() - ); + plugin.debug(DebugLevel.LOWEST, Level.INFO, "TYPE IS: " + type + ". Setting size to:" + type.getDefaultSize()); } builder.size(size); @@ -675,12 +562,7 @@ public void loadMenu(FileConfiguration c, String key, boolean mainConfig) { Map> items = loadMenuItems(c, key, mainConfig); if (items == null || items.isEmpty()) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.SEVERE, - "Failed to load menu items for menu: " + key, - "Skipping menu: " + key - ); + plugin.debug(DebugLevel.HIGHEST, Level.SEVERE, "Failed to load menu items for menu: " + key, "Skipping menu: " + key); return; } @@ -688,11 +570,10 @@ public void loadMenu(FileConfiguration c, String key, boolean mainConfig) { builder.parsePlaceholdersAfterArguments(c.getBoolean(pre + "parse_placeholders_after_arguments", false)); // Don't need to register the menu since it's done in the constructor - new Menu(builder.build(), items); + new Menu(plugin, builder.build(), items, path); } - private Map> loadMenuItems(FileConfiguration c, String name, - boolean mainConfig) { + private Map> loadMenuItems(FileConfiguration c, String name, boolean mainConfig) { String itemsPath = "gui_menus." + name + ".items"; if (!mainConfig) { @@ -716,24 +597,14 @@ private Map> loadMenuItems(FileConfiguration String currentPath = itemsPath + "." + key + "."; if (!c.contains(currentPath + "material")) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Material for item: " + key + " in menu: " + name + " is not present!", - "Skipping item: " + key - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Material for item: " + key + " in menu: " + name + " is not present!", "Skipping item: " + key); continue; } final String material = c.getString(currentPath + "material"); final String lowercaseMaterial = material.toLowerCase(Locale.ROOT); if (!isValidMaterial(lowercaseMaterial)) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Material for item: " + key + " in menu: " + name + " is not valid!", - "Skipping item: " + key - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Material for item: " + key + " in menu: " + name + " is not valid!", "Skipping item: " + key); continue; } @@ -760,10 +631,19 @@ private Map> loadMenuItems(FileConfiguration .hideUnbreakable(c.getBoolean(currentPath + "hide_unbreakable", false)) .hideEnchants(c.getBoolean(currentPath + "hide_enchantments", false)) .nbtString(c.getString(currentPath + "nbt_string", null)) + .nbtByte(c.getString(currentPath + "nbt_byte", null)) + .nbtShort(c.getString(currentPath + "nbt_short", null)) .nbtInt(c.getString(currentPath + "nbt_int", null)) .nbtStrings(c.getStringList(currentPath + "nbt_strings")) + .nbtBytes(c.getStringList(currentPath + "nbt_bytes")) + .nbtShorts(c.getStringList(currentPath + "nbt_shorts")) .nbtInts(c.getStringList(currentPath + "nbt_ints")) - .priority(c.getInt(currentPath + "priority", 1)); + .priority(c.getInt(currentPath + "priority", 1)) + .hideTooltip(c.getString(currentPath + "hide_tooltip", null)) + .enchantmentGlintOverride(c.getString(currentPath + "enchantment_glint_override", null)) + .rarity(c.getString(currentPath + "rarity", null)) + .tooltipStyle(c.getString(currentPath + "tooltip_style", null)) + .itemModel(c.getString(currentPath + "item_model", null)); // Lore Append Mode if (c.contains(currentPath + "lore_append_mode")) { @@ -772,12 +652,7 @@ private Map> loadMenuItems(FileConfiguration builder.loreAppendMode(LoreAppendMode.valueOf(loreAppendMode)); } catch (IllegalArgumentException | NullPointerException ignored) { builder.loreAppendMode(LoreAppendMode.OVERRIDE); // Defaults to override in case of invalid append mode - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Lore append mode: " + loreAppendMode + " for item: " + key + " in menu: " + name - + " is not a valid lore append mode!" - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Lore append mode: " + loreAppendMode + " for item: " + key + " in menu: " + name + " is not a valid lore append mode!"); } } @@ -789,12 +664,7 @@ private Map> loadMenuItems(FileConfiguration ItemFlag flag = Enums.getIfPresent(ItemFlag.class, flagAsString.toUpperCase()).orNull(); if (flag == null) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Item flag: " + flagAsString + " for item: " + key + " in menu: " + name - + " is not a valid item flag!" - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Item flag: " + flagAsString + " for item: " + key + " in menu: " + name + " is not a valid item flag!"); continue; } @@ -817,22 +687,14 @@ private Map> loadMenuItems(FileConfiguration for (String e : c.getStringList(currentPath + "banner_meta")) { if (!e.contains(";")) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Banner Meta for item: " + key + ", meta entry: " + e + " is invalid! Skipping this entry!" - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Banner Meta for item: " + key + ", meta entry: " + e + " is invalid! Skipping this entry!"); continue; } String[] metaParts = e.split(";", 2); if (metaParts.length != 2) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Banner Meta for item: " + key + ", meta entry: " + e + " is invalid! Skipping this entry!" - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Banner Meta for item: " + key + ", meta entry: " + e + " is invalid! Skipping this entry!"); continue; } @@ -843,16 +705,9 @@ private Map> loadMenuItems(FileConfiguration color = DyeColor.valueOf(metaParts[0].toUpperCase()); type = PatternType.valueOf(metaParts[1].toUpperCase()); } catch (IllegalArgumentException exception) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Banner Meta for item: " + key + ", meta entry: " + e + " is invalid! Skipping this entry!" - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Banner Meta for item: " + key + ", meta entry: " + e + " is invalid! Skipping this entry!"); - DeluxeMenus.printStacktrace( - "Banner Meta for item: " + key + ", meta entry: " + e + " is invalid! Skipping this entry!", - exception - ); + plugin.printStacktrace("Banner Meta for item: " + key + ", meta entry: " + e + " is invalid! Skipping this entry!", exception); continue; } @@ -870,22 +725,14 @@ private Map> loadMenuItems(FileConfiguration try { if (!e.contains(";")) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Potion Meta for item: " + key + ", meta entry: " + e + " is invalid! Skipping this entry!" - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Potion Meta for item: " + key + ", meta entry: " + e + " is invalid! Skipping this entry!"); continue; } String[] metaParts = e.split(";", 3); if (metaParts.length != 3) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Potion Meta for item: " + key + ", meta entry: " + e + " is invalid! Skipping this entry!" - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Potion Meta for item: " + key + ", meta entry: " + e + " is invalid! Skipping this entry!"); continue; } @@ -894,22 +741,14 @@ private Map> loadMenuItems(FileConfiguration int amplifier = Integer.parseInt(metaParts[2]); if (type == null) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Potion Meta for item: " + key + ", meta entry: " + e + " is invalid! Skipping this entry!" - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Potion Meta for item: " + key + ", meta entry: " + e + " is invalid! Skipping this entry!"); continue; } potionEffects.add(type.createEffect(duration, amplifier)); } catch (Exception ex) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Potion Meta for item: " + key + ", meta entry: " + e + " is invalid! Skipping this entry!" - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Potion Meta for item: " + key + ", meta entry: " + e + " is invalid! Skipping this entry!"); } } if (!potionEffects.isEmpty()) { @@ -926,50 +765,42 @@ private Map> loadMenuItems(FileConfiguration if (c.contains(currentPath + "click_commands")) { builder.clickHandler(getClickHandler(c, currentPath + "click_commands")); if (c.contains(currentPath + "click_requirement")) { - builder.clickRequirements( - this.getRequirements(c, currentPath + "click_requirement")); + builder.clickRequirements(this.getRequirements(c, currentPath + "click_requirement")); } } if (c.contains(currentPath + "left_click_commands")) { builder.leftClickHandler(getClickHandler(c, currentPath + "left_click_commands")); if (c.contains(currentPath + "left_click_requirement")) { - builder.leftClickRequirements( - this.getRequirements(c, currentPath + "left_click_requirement")); + builder.leftClickRequirements(this.getRequirements(c, currentPath + "left_click_requirement")); } } if (c.contains(currentPath + "right_click_commands")) { builder.rightClickHandler(getClickHandler(c, currentPath + "right_click_commands")); if (c.contains(currentPath + "right_click_requirement")) { - builder.rightClickRequirements( - this.getRequirements(c, currentPath + "right_click_requirement")); + builder.rightClickRequirements(this.getRequirements(c, currentPath + "right_click_requirement")); } } if (c.contains(currentPath + "shift_left_click_commands")) { - builder.shiftLeftClickHandler( - getClickHandler(c, currentPath + "shift_left_click_commands")); + builder.shiftLeftClickHandler(getClickHandler(c, currentPath + "shift_left_click_commands")); if (c.contains(currentPath + "shift_left_click_requirement")) { - builder.shiftLeftClickRequirements( - this.getRequirements(c, currentPath + "shift_left_click_requirement")); + builder.shiftLeftClickRequirements(this.getRequirements(c, currentPath + "shift_left_click_requirement")); } } if (c.contains(currentPath + "shift_right_click_commands")) { - builder.shiftRightClickHandler( - getClickHandler(c, currentPath + "shift_right_click_commands")); + builder.shiftRightClickHandler(getClickHandler(c, currentPath + "shift_right_click_commands")); if (c.contains(currentPath + "shift_right_click_requirement")) { - builder.shiftRightClickRequirements( - this.getRequirements(c, currentPath + "shift_right_click_requirement")); + builder.shiftRightClickRequirements(this.getRequirements(c, currentPath + "shift_right_click_requirement")); } } if (c.contains(currentPath + "middle_click_commands")) { builder.middleClickHandler(getClickHandler(c, currentPath + "middle_click_commands")); if (c.contains(currentPath + "middle_click_requirement")) { - builder.middleClickRequirements( - this.getRequirements(c, currentPath + "middle_click_requirement")); + builder.middleClickRequirements(this.getRequirements(c, currentPath + "middle_click_requirement")); } } @@ -991,7 +822,7 @@ private Map> loadMenuItems(FileConfiguration slots.add(c.getInt(currentPath + "slot", 0)); } - final MenuItem menuItem = new MenuItem(builder.build()); + final MenuItem menuItem = new MenuItem(plugin, builder.build()); for (int slot : slots) { TreeMap slotPriorityMap; @@ -1001,10 +832,7 @@ private Map> loadMenuItems(FileConfiguration } else { slotPriorityMap = menuItems.get(slot); } - slotPriorityMap.put( - menuItem.options().priority(), - new MenuItem(menuItem.options().asBuilder().slot(slot).build()) - ); + slotPriorityMap.put(menuItem.options().priority(), new MenuItem(plugin, menuItem.options().asBuilder().slot(slot).build())); } } return menuItems; @@ -1027,11 +855,7 @@ private RequirementList getRequirements(FileConfiguration c, String path) { debug("requirement: " + key + " from requirements list"); String rPath = path + ".requirements." + key; if (!c.contains(rPath + ".type")) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "No type set for requirement: " + key + " for path: " + rPath - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "No type set for requirement: " + key + " for path: " + rPath); continue; } @@ -1039,11 +863,7 @@ private RequirementList getRequirements(FileConfiguration c, String path) { RequirementType type = RequirementType.getType(stringType); if (type == null) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Requirement type '" + stringType + "' at path '" + rPath + "' is not valid!" - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Requirement type '" + stringType + "' at path '" + rPath + "' is not valid!"); continue; } @@ -1057,24 +877,21 @@ private RequirementList getRequirements(FileConfiguration c, String path) { case DOES_NOT_HAVE_ITEM: ItemWrapper wrapper = new ItemWrapper(); if (c.contains(rPath + ".material")) { + String materialName = c.getString(rPath + ".material"); try { - if (!containsPlaceholders(c.getString(rPath + ".material"))) - Material.valueOf(c.getString(rPath + ".material").toUpperCase()); + if (!containsPlaceholders(materialName) && plugin.getItemHooks().values() + .stream() + .filter(x -> materialName.toLowerCase().startsWith(x.getPrefix())) + .findFirst() + .orElse(null) == null) + Material.valueOf(materialName.toUpperCase()); } catch (Exception ex) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "has item requirement at path: " + rPath + " does not specify a valid Material name!" - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "has item requirement at path: " + rPath + " does not specify a valid Material name!"); break; } - wrapper.setMaterial(c.getString(rPath + ".material")); + wrapper.setMaterial(materialName); } else { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "has item requirement at path: " + rPath + " does not contain a material: entry!" - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "has item requirement at path: " + rPath + " does not contain a material: entry!"); break; } wrapper.setAmount(c.getInt(rPath + ".amount", 1)); @@ -1128,7 +945,7 @@ private RequirementList getRequirements(FileConfiguration c, String path) { } invert = type == RequirementType.DOES_NOT_HAVE_ITEM; - req = new HasItemRequirement(wrapper, invert); + req = new HasItemRequirement(plugin, wrapper, invert); break; case HAS_PERMISSION: case DOES_NOT_HAVE_PERMISSION: @@ -1136,22 +953,52 @@ private RequirementList getRequirements(FileConfiguration c, String path) { invert = type == RequirementType.DOES_NOT_HAVE_PERMISSION; req = new HasPermissionRequirement(c.getString(rPath + ".permission"), invert); } else { - DeluxeMenus.debug( + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Has Permission requirement at path: " + rPath + " does not contain a permission: entry"); + } + break; + case HAS_PERMISSIONS: + case DOES_NOT_HAVE_PERMISSIONS: + if (c.contains(rPath + ".permissions")) { + invert = type == RequirementType.DOES_NOT_HAVE_PERMISSIONS; + int minimum = -1; + if (c.contains(rPath + ".minimum") && (minimum = c.getInt(rPath + ".minimum")) < 1) { + plugin.debug( + DebugLevel.HIGHEST, + Level.WARNING, + "Has Permissions requirement at path: " + rPath + " has a minimum lower than 1. All permissions will be checked" + ); + minimum = -1; + } + List permissions = c.getStringList(rPath + ".permissions"); + if (permissions.isEmpty()) { + plugin.debug( + DebugLevel.HIGHEST, + Level.WARNING, + "Has Permissions requirement at path: " + rPath + " has no permissions to check. Ignoring..." + ); + break; + } else if (minimum > permissions.size()) { + plugin.debug( + DebugLevel.HIGHEST, + Level.WARNING, + "Has Permissions requirement at path: " + rPath + " has a minimum higher than the amount of permissions. Using "+permissions.size()+" instead" + ); + minimum = permissions.size(); + } + req = new HasPermissionsRequirement(permissions, minimum, invert); + } else { + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, - "Has Permission requirement at path: " + rPath + " does not contain a permission: entry" + "Has Permissions requirement at path: " + rPath + " does not contain permissions: entry" ); } break; case JAVASCRIPT: if (c.contains(rPath + ".expression")) { - req = new JavascriptRequirement(c.getString(rPath + ".expression")); + req = new JavascriptRequirement(plugin, c.getString(rPath + ".expression")); } else { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Javascript requirement at path: " + rPath + " does not contain an expression: entry" - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Javascript requirement at path: " + rPath + " does not contain an expression: entry"); } break; case EQUAL_TO: @@ -1167,49 +1014,31 @@ private RequirementList getRequirements(FileConfiguration c, String path) { case STRING_DOES_NOT_EQUAL: case STRING_DOES_NOT_EQUAL_IGNORECASE: if (c.contains(rPath + ".input") && c.contains(rPath + ".output")) { - req = new InputResultRequirement(type, - c.getString(rPath + ".input"), c.getString(rPath + ".output")); + req = new InputResultRequirement(type, c.getString(rPath + ".input"), c.getString(rPath + ".output")); } else { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Requirement at path: " + rPath + " does not contain the input: and/or the output: entries" - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Requirement at path: " + rPath + " does not contain the input: and/or the output: entries"); } break; case HAS_MONEY: case DOES_NOT_HAVE_MONEY: if (c.contains(rPath + ".amount") || c.contains(rPath + ".placeholder")) { invert = type == RequirementType.DOES_NOT_HAVE_MONEY; - req = new HasMoneyRequirement(c.getDouble(rPath + ".amount"), invert, - c.getString(rPath + ".placeholder", null)); + req = new HasMoneyRequirement(plugin, c.getDouble(rPath + ".amount"), invert, c.getString(rPath + ".placeholder", null)); } else { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Has Money requirement at path: " + rPath + " does not contain an amount: entry" - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Has Money requirement at path: " + rPath + " does not contain an amount: entry"); } break; case HAS_EXP: case DOES_NOT_HAVE_EXP: if (c.contains(rPath + ".amount")) { if (!containsPlaceholders(c.getString(rPath + ".amount")) && !c.isInt(rPath + ".amount")) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Value at path: " + rPath + ".amount is not a placeholder or a number" - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Value at path: " + rPath + ".amount is not a placeholder or a number"); break; } invert = type == RequirementType.DOES_NOT_HAVE_EXP; - req = new HasExpRequirement(c.getString(rPath + ".amount"), invert, c.getBoolean(rPath + ".level")); + req = new HasExpRequirement(plugin, c.getString(rPath + ".amount"), invert, c.getBoolean(rPath + ".level")); } else { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Has Exp requirement at path: " + rPath + " does not contain an amount: entry" - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Has Exp requirement at path: " + rPath + " does not contain an amount: entry"); } break; case REGEX_MATCHES: @@ -1217,14 +1046,9 @@ private RequirementList getRequirements(FileConfiguration c, String path) { if (c.contains(rPath + ".input") && c.contains(rPath + ".regex")) { Pattern p = Pattern.compile(c.getString(rPath + ".regex")); invert = type == RequirementType.REGEX_DOES_NOT_MATCH; - req = new RegexMatchesRequirement(p, - c.getString(rPath + ".input"), invert); + req = new RegexMatchesRequirement(p, c.getString(rPath + ".input"), invert); } else { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Regex requirement at path: " + rPath + " does not contain a input: or regex: entry" - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Regex requirement at path: " + rPath + " does not contain a input: or regex: entry"); } break; case IS_NEAR: @@ -1233,44 +1057,25 @@ private RequirementList getRequirements(FileConfiguration c, String path) { invert = type == RequirementType.IS_NOT_NEAR; Location loc = LocationUtils.deserializeLocation(c.getString(rPath + ".location")); if (loc == null) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "requirement at path: " + rPath + " has an invalid location. Valid Format is: ,,," - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "requirement at path: " + rPath + " has an invalid location. Valid Format is: ,,,"); } req = new IsNearRequirement(loc, c.getInt(rPath + ".distance"), invert); } else { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Is Near requirement at path: " + rPath + " does not contain a location: or distance: entry" - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Is Near requirement at path: " + rPath + " does not contain a location: or distance: entry"); } break; case HAS_META: case DOES_NOT_HAVE_META: if (!VersionHelper.IS_PDC_VERSION) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Has Meta requirement is not available for your server version!" - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Has Meta requirement is not available for your server version!"); break; } - if (c.contains(rPath + ".key") && c.contains(rPath + ".meta_type") && c - .contains(rPath + ".value")) { + if (c.contains(rPath + ".key") && c.contains(rPath + ".meta_type") && c.contains(rPath + ".value")) { String metaKey = c.getString(rPath + ".key"); invert = type == RequirementType.DOES_NOT_HAVE_META; - req = new HasMetaRequirement(metaKey, c.getString(rPath + ".meta_type").toUpperCase(), - c.getString(rPath + ".value"), invert); + req = new HasMetaRequirement(plugin, metaKey, c.getString(rPath + ".meta_type").toUpperCase(), c.getString(rPath + ".value"), invert); } else { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Has Meta requirement at path: " + rPath - + " does not contain the key:, meta_type: and/or value: entries!" - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Has Meta requirement at path: " + rPath + " does not contain the key:, meta_type: and/or value: entries!"); } break; case STRING_LENGTH: @@ -1282,22 +1087,14 @@ private RequirementList getRequirements(FileConfiguration c, String path) { } req = new StringLengthRequirement(c.getString(rPath + ".input"), min, max); } else { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "String length requirement at path: " + rPath + " does not contain an input: or one of (min: or max:)" - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "String length requirement at path: " + rPath + " does not contain an input: or one of (min: or max:)"); } break; case IS_OBJECT: if (c.contains(rPath + ".input") && c.contains(rPath + ".object")) { req = new IsObjectRequirement(c.getString(rPath + ".input"), c.getString(rPath + ".object")); } else { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "String length requirement at path: " + rPath + " does not contain an input: or object:" - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "String length requirement at path: " + rPath + " does not contain an input: or object:"); } break; default: @@ -1416,15 +1213,7 @@ public void onClick(@NotNull final MenuHolder holder) { continue; } - final ClickActionTask actionTask = new ClickActionTask( - plugin, - holder.getViewer().getUniqueId(), - action.getType(), - action.getExecutable(), - holder.getTypedArgs(), - holder.parsePlaceholdersInArguments(), - holder.parsePlaceholdersAfterArguments() - ); + final ClickActionTask actionTask = new ClickActionTask(plugin, holder.getViewer().getUniqueId(), action.getType(), action.getExecutable(), holder.getTypedArgs(), holder.parsePlaceholdersInArguments(), holder.parsePlaceholdersAfterArguments()); Scheduler scheduler = action.getType().getScheduler().getScheduler(holder.getViewer()); @@ -1448,14 +1237,7 @@ private void checkForDeprecatedItemOptions(ConfigurationSection config, String m return; } - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - String.format( - "Option '%s' of item '%s' in menu '%s' is deprecated and will be removed in the future. Replace it with item_flags: [%s].", - option, config.getName(), menuName, itemFlag - ) - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, String.format("Option '%s' of item '%s' in menu '%s' is deprecated and will be removed in the future. Replace it with item_flags: [%s].", option, config.getName(), menuName, itemFlag)); }; oldItemFlagOptionCheck.accept("hide_attributes", ItemFlag.HIDE_ATTRIBUTES); @@ -1464,7 +1246,7 @@ private void checkForDeprecatedItemOptions(ConfigurationSection config, String m } public void debug(String... messages) { - DeluxeMenus.debug(DebugLevel.LOWEST, Level.INFO, messages); + plugin.debug(DebugLevel.LOWEST, Level.INFO, messages); } public @NotNull DebugLevel debugLevel() { @@ -1485,7 +1267,6 @@ public void debug(String... messages) { public File getMenuDirector() { return menuDirectory; } - public void addEnchantmentsOptionToBuilder(final FileConfiguration c, final String currentPath, final String itemKey, final String menuName, final MenuItemOptions.MenuItemOptionsBuilder builder) { @@ -1498,7 +1279,7 @@ public void addEnchantmentsOptionToBuilder(final FileConfiguration c, final Stri for (final String configEnchantment : configEnchantments) { if (configEnchantment == null || !configEnchantment.contains(";")) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Enchantment format '" + configEnchantment + "' is incorrect for item " + itemKey + @@ -1510,7 +1291,7 @@ public void addEnchantmentsOptionToBuilder(final FileConfiguration c, final Stri String[] parts = configEnchantment.split(";", 2); if (parts.length != 2 || parts[0] == null || parts[1] == null) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Enchantment format '" + configEnchantment + "' is incorrect for item " + itemKey + @@ -1522,7 +1303,7 @@ public void addEnchantmentsOptionToBuilder(final FileConfiguration c, final Stri final Enchantment enchantment = Enchantment.getByName(parts[0].strip().toUpperCase()); if (enchantment == null) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Enchantment '" + parts[0].strip() + "' for item " + itemKey + @@ -1533,7 +1314,7 @@ public void addEnchantmentsOptionToBuilder(final FileConfiguration c, final Stri Integer level = Ints.tryParse(parts[1].strip()); if (level == null) { level = 1; - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Enchantment level '" + parts[1].strip() + "' is incorrect for item " + itemKey + @@ -1559,20 +1340,10 @@ public void addDamageOptionToBuilder(final FileConfiguration c, final String cur key = "data"; if (c.contains(currentPath + key)) { if (!damageOptionIsPresent) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Found 'data' option for item: " + itemKey + " in menu: " + menuName+ ". This option " + - "is deprecated and will be removed soon. Please use 'damage' instead." - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Found 'data' option for item: " + itemKey + " in menu: " + menuName + ". This option " + "is deprecated and will be removed soon. Please use 'damage' instead."); damageValue = c.getString(currentPath + key, ""); } else { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Found 'data' and 'damage' options for item: " + itemKey + " in menu: " + menuName + - ". 'data' option is deprecated and will be ignored. Using 'damage' instead." - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Found 'data' and 'damage' options for item: " + itemKey + " in menu: " + menuName + ". 'data' option is deprecated and will be ignored. Using 'damage' instead."); } } @@ -1585,28 +1356,13 @@ public void addDamageOptionToBuilder(final FileConfiguration c, final String cur } if (!ItemUtils.isPlaceholderOption(damageValue) && Ints.tryParse(damageValue) == null) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Found invalid value for '" + key + "' option for item: " + itemKey + " in menu: " + - menuName + ".", - "The correct formats for '" + key + "' are:", - " -> ", - " -> placeholder-", - "Ignoring the invalid value." - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Found invalid value for '" + key + "' option for item: " + itemKey + " in menu: " + menuName + ".", "The correct formats for '" + key + "' are:", " -> ", " -> placeholder-", "Ignoring the invalid value."); return; } final String[] parts = damageValue.split("-", 2); if (parts.length >= 2 && !containsPlaceholders(parts[1])) { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Could not find placeholder for '" + key + "' option for item: " + itemKey + " in menu: " + - menuName + ".", - "Ignoring the invalid value." - ); + plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Could not find placeholder for '" + key + "' option for item: " + itemKey + " in menu: " + menuName + ".", "Ignoring the invalid value."); return; } diff --git a/src/main/java/com/extendedclip/deluxemenus/config/GeneralConfig.java b/src/main/java/com/extendedclip/deluxemenus/config/GeneralConfig.java new file mode 100644 index 00000000..d8f25066 --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/config/GeneralConfig.java @@ -0,0 +1,72 @@ +package com.extendedclip.deluxemenus.config; + +import com.extendedclip.deluxemenus.DeluxeMenus; +import com.extendedclip.deluxemenus.utils.DebugLevel; +import org.jetbrains.annotations.NotNull; + +public class GeneralConfig { + private final DeluxeMenus plugin; + + private boolean checkForUpdates = true; + private DebugLevel debugLevel = getDefaultDebugLevel(); + private boolean useAdminCommandsInMenusList = false; + private int menusListPageSize = 10; + + public GeneralConfig(final @NotNull DeluxeMenus plugin) { + this.plugin = plugin; + } + + public void load() { + plugin.getConfig().addDefault("check_updates", checkForUpdates); + plugin.getConfig().addDefault("debug", debugLevel.name()); + plugin.getConfig().addDefault("use_admin_commands_in_menus_list", false); + plugin.getConfig().addDefault("menus_list_page_size", menusListPageSize); + + 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); + } + + public void reload() { + plugin.reloadConfig(); + load(); + } + + public boolean checkForUpdates() { + return checkForUpdates; + } + + public DebugLevel debugLevel() { + return debugLevel; + } + + public boolean useAdminCommandsInMenusList() { + return useAdminCommandsInMenusList; + } + + public int menusListPageSize() { + return menusListPageSize; + } + + private @NotNull DebugLevel loadDebugLevel() { + String configDebugLevel = plugin.getConfig().getString("debug", "HIGHEST"); + + if (configDebugLevel.equalsIgnoreCase("true")) { + configDebugLevel = "LOWEST"; + plugin.getConfig().set("debug", "LOWEST"); + } + + if (configDebugLevel.equalsIgnoreCase("false")) { + configDebugLevel = "HIGHEST"; + plugin.getConfig().set("debug", "HIGHEST"); + } + + final DebugLevel debugLevel = DebugLevel.getByName(configDebugLevel); + return debugLevel == null ? getDefaultDebugLevel() : debugLevel; + } + + private DebugLevel getDefaultDebugLevel() { + return DebugLevel.LOW; + } +} diff --git a/src/main/java/com/extendedclip/deluxemenus/dupe/DupeFixer.java b/src/main/java/com/extendedclip/deluxemenus/dupe/DupeFixer.java index cac42e9b..0d2c7155 100644 --- a/src/main/java/com/extendedclip/deluxemenus/dupe/DupeFixer.java +++ b/src/main/java/com/extendedclip/deluxemenus/dupe/DupeFixer.java @@ -1,10 +1,10 @@ package com.extendedclip.deluxemenus.dupe; import com.extendedclip.deluxemenus.DeluxeMenus; +import com.extendedclip.deluxemenus.listener.Listener; import com.extendedclip.deluxemenus.utils.DebugLevel; import io.github.projectunified.minelib.scheduler.global.GlobalScheduler; import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityPickupItemEvent; import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerLoginEvent; @@ -13,15 +13,17 @@ import java.util.logging.Level; -public class DupeFixer implements Listener { +/** + * Prevents duplication of items created by DeluxeMenus. Items created by DeluxeMenus are marked and removed if found + * outside the inventory they were created in. + */ +public class DupeFixer extends Listener { - private final DeluxeMenus plugin; private final MenuItemMarker marker; public DupeFixer(@NotNull final DeluxeMenus plugin, @NotNull final MenuItemMarker marker) { - this.plugin = plugin; + super(plugin); this.marker = marker; - plugin.getServer().getPluginManager().registerEvents(this, plugin); } @EventHandler @@ -30,7 +32,7 @@ private void onPickup(@NotNull final EntityPickupItemEvent event) { return; } - DeluxeMenus.debug( + plugin.debug( DebugLevel.LOWEST, Level.INFO, "Someone picked up a DeluxeMenus item. Removing it." @@ -44,7 +46,7 @@ private void onDrop(@NotNull final PlayerDropItemEvent event) { return; } - DeluxeMenus.debug( + plugin.debug( DebugLevel.LOWEST, Level.INFO, "A DeluxeMenus item was dropped in the world. Removing it." @@ -60,7 +62,7 @@ private void onLogin(@NotNull final PlayerLoginEvent event) { if (itemStack == null) continue; if (!marker.isMarked(itemStack)) continue; - DeluxeMenus.debug( + plugin.debug( DebugLevel.LOWEST, Level.INFO, "Player logged in with a DeluxeMenus item in their inventory. Removing it." diff --git a/src/main/java/com/extendedclip/deluxemenus/dupe/MenuItemMarker.java b/src/main/java/com/extendedclip/deluxemenus/dupe/MenuItemMarker.java index cf71d746..6f439afb 100644 --- a/src/main/java/com/extendedclip/deluxemenus/dupe/MenuItemMarker.java +++ b/src/main/java/com/extendedclip/deluxemenus/dupe/MenuItemMarker.java @@ -12,6 +12,10 @@ import java.util.regex.Pattern; +/** + * Mark inventory items created by DeluxeMenus to prevent duplication. Marked items will be removed from all inventories + * except the one they were created in. + */ public class MenuItemMarker implements ItemMarker { private final static String DEFAULT_MARK = "DM"; diff --git a/src/main/java/com/extendedclip/deluxemenus/hooks/BaseHeadHook.java b/src/main/java/com/extendedclip/deluxemenus/hooks/BaseHeadHook.java index 778f0e70..0178ec75 100644 --- a/src/main/java/com/extendedclip/deluxemenus/hooks/BaseHeadHook.java +++ b/src/main/java/com/extendedclip/deluxemenus/hooks/BaseHeadHook.java @@ -11,24 +11,42 @@ public class BaseHeadHook implements ItemHook, SimpleCache { + private final DeluxeMenus plugin; private final Map cache = new ConcurrentHashMap<>(); + public BaseHeadHook(@NotNull final DeluxeMenus plugin) { + this.plugin = plugin; + } + @Override public ItemStack getItem(@NotNull final String... arguments) { if (arguments.length == 0) { - return DeluxeMenus.getInstance().getHead().clone(); + return plugin.getHead().clone(); } try { - return cache.computeIfAbsent(arguments[0], SkullUtils::getSkullByBase64EncodedTextureUrl).clone(); + return cache.computeIfAbsent(arguments[0], value -> SkullUtils.getSkullByBase64EncodedTextureUrl(plugin, value)).clone(); } catch (Exception exception) { - DeluxeMenus.printStacktrace( + plugin.printStacktrace( "Something went wrong while trying to get base64 head: " + arguments[0], exception ); } - return DeluxeMenus.getInstance().getHead().clone(); + return plugin.getHead().clone(); + } + + @Override + public boolean itemMatchesIdentifiers(@NotNull ItemStack item, @NotNull String... arguments) { + if (arguments.length == 0) { + return false; + } + String itemTexture = SkullUtils.getTextureFromSkull(plugin, item); + String texture = SkullUtils.decodeSkinUrl(arguments[0]); + if (itemTexture == null || texture == null) return false; + + texture = texture.substring("https://textures.minecraft.net/texture/".length()-1); + return texture.equals(itemTexture); } @Override diff --git a/src/main/java/com/extendedclip/deluxemenus/hooks/ExecutableBlocksHook.java b/src/main/java/com/extendedclip/deluxemenus/hooks/ExecutableBlocksHook.java index d0942d64..22184d73 100644 --- a/src/main/java/com/extendedclip/deluxemenus/hooks/ExecutableBlocksHook.java +++ b/src/main/java/com/extendedclip/deluxemenus/hooks/ExecutableBlocksHook.java @@ -28,6 +28,11 @@ public ItemStack getItem(@NotNull String... arguments) { return (item == null) ? new ItemStack(Material.STONE) : item.clone(); } + @Override + public boolean itemMatchesIdentifiers(@NotNull ItemStack item, @NotNull String... arguments) { + return false; + } + @Override public String getPrefix() { return "executableblocks-"; diff --git a/src/main/java/com/extendedclip/deluxemenus/hooks/ExecutableItemsHook.java b/src/main/java/com/extendedclip/deluxemenus/hooks/ExecutableItemsHook.java index 553b61dc..9bdf1266 100644 --- a/src/main/java/com/extendedclip/deluxemenus/hooks/ExecutableItemsHook.java +++ b/src/main/java/com/extendedclip/deluxemenus/hooks/ExecutableItemsHook.java @@ -5,6 +5,8 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; + +import com.ssomar.score.api.executableitems.config.ExecutableItemInterface; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -31,6 +33,16 @@ public ItemStack getItem(@NotNull String... arguments) { return (item == null) ? new ItemStack(Material.STONE) : item.clone(); } + @Override + public boolean itemMatchesIdentifiers(@NotNull ItemStack item, @NotNull String... arguments) { + if (arguments.length == 0) { + return false; + } + ExecutableItemInterface fromId = ExecutableItemsAPI.getExecutableItemsManager().getExecutableItem(arguments[0]).orElse(null); + ExecutableItemInterface fromItem = ExecutableItemsAPI.getExecutableItemsManager().getExecutableItem(item).orElse(null); + return fromItem != null && fromItem.equals(fromId); + } + @Override public String getPrefix() { return "executableitems-"; diff --git a/src/main/java/com/extendedclip/deluxemenus/hooks/HeadDatabaseHook.java b/src/main/java/com/extendedclip/deluxemenus/hooks/HeadDatabaseHook.java index 32e8c884..f1c4c421 100644 --- a/src/main/java/com/extendedclip/deluxemenus/hooks/HeadDatabaseHook.java +++ b/src/main/java/com/extendedclip/deluxemenus/hooks/HeadDatabaseHook.java @@ -7,33 +7,40 @@ public class HeadDatabaseHook implements ItemHook { - private final HeadDatabaseAPI api; + private final DeluxeMenus plugin; + private final HeadDatabaseAPI api; - public HeadDatabaseHook() { - api = new HeadDatabaseAPI(); - } - - @Override - public ItemStack getItem(@NotNull final String... arguments) { - if (arguments.length == 0) { - return DeluxeMenus.getInstance().getHead().clone(); + public HeadDatabaseHook(@NotNull final DeluxeMenus plugin) { + this.plugin = plugin; + api = new HeadDatabaseAPI(); } - try { - final ItemStack item = api.getItemHead(arguments[0]); - return item != null ? item : DeluxeMenus.getInstance().getHead().clone(); - } catch (NullPointerException exception) { - DeluxeMenus.printStacktrace( - "Something went wrong while trying to get head database head: " + arguments[0], - exception - ); - } + @Override + public ItemStack getItem(@NotNull final String... arguments) { + if (arguments.length == 0) { + return plugin.getHead().clone(); + } - return DeluxeMenus.getInstance().getHead().clone(); - } + try { + final ItemStack item = api.getItemHead(arguments[0]); + return item != null ? item : plugin.getHead().clone(); + } catch (NullPointerException exception) { + plugin.printStacktrace("Something went wrong while trying to get head database head: " + arguments[0], exception); + } + + return plugin.getHead().clone(); + } @Override - public String getPrefix() { - return "hdb-"; + public boolean itemMatchesIdentifiers(@NotNull ItemStack item, @NotNull String... arguments) { + if (arguments.length == 0) { + return false; + } + return arguments[0].equalsIgnoreCase(api.getItemID(item)); } + + @Override + public String getPrefix() { + return "hdb-"; + } } diff --git a/src/main/java/com/extendedclip/deluxemenus/hooks/ItemHook.java b/src/main/java/com/extendedclip/deluxemenus/hooks/ItemHook.java index b0c49fa5..d88d8f1d 100644 --- a/src/main/java/com/extendedclip/deluxemenus/hooks/ItemHook.java +++ b/src/main/java/com/extendedclip/deluxemenus/hooks/ItemHook.java @@ -7,6 +7,8 @@ public interface ItemHook { ItemStack getItem(@NotNull final String... arguments); + boolean itemMatchesIdentifiers(@NotNull ItemStack item, @NotNull final String... arguments); + String getPrefix(); } diff --git a/src/main/java/com/extendedclip/deluxemenus/hooks/ItemsAdderHook.java b/src/main/java/com/extendedclip/deluxemenus/hooks/ItemsAdderHook.java index 98823237..ac3760b0 100644 --- a/src/main/java/com/extendedclip/deluxemenus/hooks/ItemsAdderHook.java +++ b/src/main/java/com/extendedclip/deluxemenus/hooks/ItemsAdderHook.java @@ -36,6 +36,15 @@ public ItemStack getItem(@NotNull final String... arguments) { return item.clone(); } + @Override + public boolean itemMatchesIdentifiers(@NotNull ItemStack item, @NotNull String... arguments) { + if (arguments.length == 0) { + return false; + } + CustomStack stack = CustomStack.byItemStack(item); + return stack != null && stack.getId().equalsIgnoreCase(arguments[0]); + } + @Override public String getPrefix() { return "itemsadder-"; diff --git a/src/main/java/com/extendedclip/deluxemenus/hooks/MMOItemsHook.java b/src/main/java/com/extendedclip/deluxemenus/hooks/MMOItemsHook.java index 5305fc4b..0d856b92 100644 --- a/src/main/java/com/extendedclip/deluxemenus/hooks/MMOItemsHook.java +++ b/src/main/java/com/extendedclip/deluxemenus/hooks/MMOItemsHook.java @@ -19,6 +19,11 @@ public class MMOItemsHook implements ItemHook, SimpleCache { private final Map cache = new ConcurrentHashMap<>(); + private final DeluxeMenus plugin; + + public MMOItemsHook(final @NotNull DeluxeMenus plugin) { + this.plugin = plugin; + } @Override public ItemStack getItem(@NotNull final String... arguments) { @@ -57,12 +62,22 @@ public ItemStack getItem(@NotNull final String... arguments) { }); mmoItem = future.get(); } catch (InterruptedException | ExecutionException e) { - DeluxeMenus.debug(DebugLevel.HIGHEST, Level.SEVERE, "Error getting MMOItem synchronously."); + plugin.debug(DebugLevel.HIGHEST, Level.SEVERE, "Error getting MMOItem synchronously."); } return mmoItem == null ? new ItemStack(Material.STONE, 1) : mmoItem; } + @Override + public boolean itemMatchesIdentifiers(@NotNull ItemStack item, @NotNull String... arguments) { + if (arguments.length == 0) { + return false; + } + String[] splitArgs = arguments[0].split(":"); + if (splitArgs.length != 2) return false; + return splitArgs[0].equalsIgnoreCase(MMOItems.getTypeName(item)) && splitArgs[1].equalsIgnoreCase(MMOItems.getID(item)); + } + @Override public String getPrefix() { return "mmoitems-"; diff --git a/src/main/java/com/extendedclip/deluxemenus/hooks/NamedHeadHook.java b/src/main/java/com/extendedclip/deluxemenus/hooks/NamedHeadHook.java index 0d02f9cd..aa880ba5 100644 --- a/src/main/java/com/extendedclip/deluxemenus/hooks/NamedHeadHook.java +++ b/src/main/java/com/extendedclip/deluxemenus/hooks/NamedHeadHook.java @@ -1,56 +1,64 @@ -package com.extendedclip.deluxemenus.hooks; - -import com.extendedclip.deluxemenus.DeluxeMenus; -import com.extendedclip.deluxemenus.cache.SimpleCache; -import com.extendedclip.deluxemenus.utils.SkullUtils; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; - -public class NamedHeadHook implements ItemHook, Listener, SimpleCache { - - private final Map cache = new ConcurrentHashMap<>(); - - public NamedHeadHook(@NotNull final DeluxeMenus plugin) { - plugin.getServer().getPluginManager().registerEvents(this, plugin); - } - - @Override - public ItemStack getItem(@NotNull final String... arguments) { - if (arguments.length == 0) { - return DeluxeMenus.getInstance().getHead().clone(); - } - - try { - return cache.computeIfAbsent(arguments[0], SkullUtils::getSkullByName).clone(); - } catch (Exception exception) { - DeluxeMenus.printStacktrace( - "Something went wrong while trying to get a head by name" + - ": " + arguments[0], - exception - ); - } - - return DeluxeMenus.getInstance().getHead().clone(); - } - - @EventHandler(ignoreCancelled = true) - public void onPlayerQuit(final PlayerQuitEvent event) { - cache.remove(event.getPlayer().getName()); - } - - @Override - public String getPrefix() { - return "head-"; - } - - @Override - public void clearCache() { - cache.clear(); - } -} +package com.extendedclip.deluxemenus.hooks; + +import com.extendedclip.deluxemenus.DeluxeMenus; +import com.extendedclip.deluxemenus.cache.SimpleCache; +import com.extendedclip.deluxemenus.listener.Listener; +import com.extendedclip.deluxemenus.utils.SkullUtils; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public class NamedHeadHook extends Listener implements ItemHook, SimpleCache { + + private final Map cache = new ConcurrentHashMap<>(); + + public NamedHeadHook(@NotNull final DeluxeMenus plugin) { + super(plugin); + } + + @Override + public ItemStack getItem(@NotNull final String... arguments) { + if (arguments.length == 0) { + return plugin.getHead().clone(); + } + + try { + return cache.computeIfAbsent(arguments[0], value -> SkullUtils.getSkullByName(plugin, value)).clone(); + } catch (Exception exception) { + plugin.printStacktrace( + "Something went wrong while trying to get a head by name" + + ": " + arguments[0], + exception + ); + } + + return plugin.getHead().clone(); + } + + @Override + public boolean itemMatchesIdentifiers(@NotNull ItemStack item, @NotNull String... arguments) { + if (arguments.length == 0) { + return false; + } + return arguments[0].equalsIgnoreCase(SkullUtils.getSkullOwner(item)); + } + + @EventHandler(ignoreCancelled = true) + public void onPlayerQuit(final PlayerQuitEvent event) { + cache.remove(event.getPlayer().getName()); + } + + @Override + public String getPrefix() { + return "head-"; + } + + @Override + public void clearCache() { + cache.clear(); + } +} diff --git a/src/main/java/com/extendedclip/deluxemenus/hooks/OraxenHook.java b/src/main/java/com/extendedclip/deluxemenus/hooks/OraxenHook.java index 98a56aa5..166fd23e 100644 --- a/src/main/java/com/extendedclip/deluxemenus/hooks/OraxenHook.java +++ b/src/main/java/com/extendedclip/deluxemenus/hooks/OraxenHook.java @@ -28,6 +28,14 @@ public ItemStack getItem(@NotNull String... arguments) { return (item == null) ? new ItemStack(Material.STONE) : item.clone(); } + @Override + public boolean itemMatchesIdentifiers(@NotNull ItemStack item, @NotNull String... arguments) { + if (arguments.length == 0) { + return false; + } + return arguments[0].equalsIgnoreCase(OraxenItems.getIdByItem(item)); + } + @Override public String getPrefix() { return "oraxen-"; diff --git a/src/main/java/com/extendedclip/deluxemenus/hooks/TextureHeadHook.java b/src/main/java/com/extendedclip/deluxemenus/hooks/TextureHeadHook.java index c3c1917e..127a8356 100644 --- a/src/main/java/com/extendedclip/deluxemenus/hooks/TextureHeadHook.java +++ b/src/main/java/com/extendedclip/deluxemenus/hooks/TextureHeadHook.java @@ -3,41 +3,51 @@ import com.extendedclip.deluxemenus.DeluxeMenus; import com.extendedclip.deluxemenus.cache.SimpleCache; import com.extendedclip.deluxemenus.utils.SkullUtils; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + public class TextureHeadHook implements ItemHook, SimpleCache { - private final Map cache = new ConcurrentHashMap<>(); + private final DeluxeMenus plugin; + private final Map cache = new ConcurrentHashMap<>(); - @Override - public ItemStack getItem(@NotNull final String... arguments) { - if (arguments.length == 0) { - return DeluxeMenus.getInstance().getHead().clone(); + public TextureHeadHook(@NotNull final DeluxeMenus plugin) { + this.plugin = plugin; } - try { - return cache.computeIfAbsent(arguments[0], key -> SkullUtils.getSkullByBase64EncodedTextureUrl(SkullUtils.getEncoded(key))).clone(); - } catch (Exception exception) { - DeluxeMenus.printStacktrace( - "Something went wrong while trying to get texture head: " + arguments[0], - exception - ); - } + @Override + public ItemStack getItem(@NotNull final String... arguments) { + if (arguments.length == 0) { + return plugin.getHead().clone(); + } - return DeluxeMenus.getInstance().getHead().clone(); - } + try { + return cache.computeIfAbsent(arguments[0], key -> SkullUtils.getSkullByBase64EncodedTextureUrl(plugin, SkullUtils.getEncoded(key))).clone(); + } catch (Exception exception) { + plugin.printStacktrace("Something went wrong while trying to get texture head: " + arguments[0], exception); + } - @Override - public String getPrefix() { - return "texture-"; - } + return plugin.getHead().clone(); + } @Override - public void clearCache() { - cache.clear(); + public boolean itemMatchesIdentifiers(@NotNull ItemStack item, @NotNull String... arguments) { + if (arguments.length == 0) { + return false; + } + return arguments[0].equals(SkullUtils.getTextureFromSkull(plugin, item)); } + + @Override + public String getPrefix() { + return "texture-"; + } + + @Override + public void clearCache() { + cache.clear(); + } } diff --git a/src/main/java/com/extendedclip/deluxemenus/listener/Listener.java b/src/main/java/com/extendedclip/deluxemenus/listener/Listener.java new file mode 100644 index 00000000..a219218c --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/listener/Listener.java @@ -0,0 +1,16 @@ +package com.extendedclip.deluxemenus.listener; + +import com.extendedclip.deluxemenus.DeluxeMenus; +import org.jetbrains.annotations.NotNull; + +public abstract class Listener implements org.bukkit.event.Listener { + protected final DeluxeMenus plugin; + + public Listener(@NotNull final DeluxeMenus plugin) { + this.plugin = plugin; + } + + public void register() { + this.plugin.getServer().getPluginManager().registerEvents(this, this.plugin); + } +} diff --git a/src/main/java/com/extendedclip/deluxemenus/listener/PlayerListener.java b/src/main/java/com/extendedclip/deluxemenus/listener/PlayerListener.java index 0490bdd5..04c503a3 100644 --- a/src/main/java/com/extendedclip/deluxemenus/listener/PlayerListener.java +++ b/src/main/java/com/extendedclip/deluxemenus/listener/PlayerListener.java @@ -8,17 +8,10 @@ import com.extendedclip.deluxemenus.requirement.RequirementList; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; - -import java.util.Optional; -import java.util.UUID; -import java.util.concurrent.TimeUnit; - import io.github.projectunified.minelib.scheduler.global.GlobalScheduler; -import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; import org.bukkit.event.inventory.ClickType; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryCloseEvent; @@ -27,210 +20,202 @@ import org.bukkit.event.player.PlayerQuitEvent; import org.jetbrains.annotations.NotNull; -public class PlayerListener implements Listener { - - private final DeluxeMenus plugin; - private final Cache cache = CacheBuilder.newBuilder() - .expireAfterWrite(75, TimeUnit.MILLISECONDS).build(); - - // This is so dumb. Mojang fix your shit. - private final Cache shiftCache = CacheBuilder.newBuilder() - .expireAfterWrite(200, TimeUnit.MILLISECONDS).build(); +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.TimeUnit; - public PlayerListener(DeluxeMenus plugin) { - this.plugin = plugin; - Bukkit.getPluginManager().registerEvents(this, this.plugin); - } +public class PlayerListener extends Listener { - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) - public void onCommandExecute(PlayerCommandPreprocessEvent event) { + private final Cache cache = CacheBuilder.newBuilder().expireAfterWrite(75, TimeUnit.MILLISECONDS).build(); - final String cmd = event.getMessage().substring(1); - final Optional optionalMenu = Menu.getMenuByCommand(cmd.toLowerCase()); + // This is so dumb. Mojang fix your shit. + private final Cache shiftCache = CacheBuilder.newBuilder().expireAfterWrite(200, TimeUnit.MILLISECONDS).build(); - if (optionalMenu.isEmpty()) { - return; + public PlayerListener(@NotNull final DeluxeMenus plugin) { + super(plugin); } - final Menu menu = optionalMenu.get(); + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onCommandExecute(PlayerCommandPreprocessEvent event) { - if (menu.options().registerCommands()) { - return; - } + final String cmd = event.getMessage().substring(1); + final Optional optionalMenu = Menu.getMenuByCommand(cmd.toLowerCase()); - Player player = event.getPlayer(); - menu.openMenu(player); - event.setCancelled(true); - } + if (optionalMenu.isEmpty()) { + return; + } - @EventHandler - public void onLeave(PlayerQuitEvent event) { - Player player = event.getPlayer(); + final Menu menu = optionalMenu.get(); - if (Menu.isInMenu(player)) { - Menu.closeMenu(player, false); - } - } + if (menu.options().registerCommands()) { + return; + } - @EventHandler - public void onOpen(InventoryOpenEvent event) { - if (!(event.getPlayer() instanceof Player)) { - return; + Player player = event.getPlayer(); + menu.openMenu(player); + event.setCancelled(true); } - final Player player = (Player) event.getPlayer(); + @EventHandler + public void onLeave(PlayerQuitEvent event) { + Player player = event.getPlayer(); - if (player.isSleeping()) { - event.setCancelled(true); + if (Menu.isInMenu(player)) { + Menu.closeMenu(plugin, player, false); + } } - if (Menu.isInMenu(player)) { - Menu.closeMenu(player, true); - } - } + @EventHandler + public void onOpen(InventoryOpenEvent event) { + if (!(event.getPlayer() instanceof Player)) { + return; + } - @EventHandler - public void onClose(InventoryCloseEvent event) { + final Player player = (Player) event.getPlayer(); + + if (player.isSleeping()) { + event.setCancelled(true); + } - if (!(event.getPlayer() instanceof Player)) { - return; + if (Menu.isInMenu(player)) { + Menu.closeMenu(plugin, player, true); + } } - final Player player = (Player) event.getPlayer(); + @EventHandler + public void onClose(InventoryCloseEvent event) { - if (Menu.isInMenu(player)) { - Menu.closeMenu(player, false); - GlobalScheduler.get(plugin).runLater(() -> { - Menu.cleanInventory(player, plugin.getMenuItemMarker()); - player.updateInventory(); - }, 3L); - } - } + if (!(event.getPlayer() instanceof Player)) { + return; + } - @EventHandler(priority = EventPriority.LOW) - public void onClick(InventoryClickEvent event) { + final Player player = (Player) event.getPlayer(); - if (!(event.getWhoClicked() instanceof Player)) { - return; + if (Menu.isInMenu(player)) { + Menu.closeMenu(plugin, player, false); + GlobalScheduler.get(plugin).runLater(() -> { + Menu.cleanInventory(plugin, player); + player.updateInventory(); + }, 3L); + } } - final Player player = (Player) event.getWhoClicked(); + @EventHandler(priority = EventPriority.LOW) + public void onClick(InventoryClickEvent event) { - final Optional optionalHolder = Menu.getMenuHolder(player); + if (!(event.getWhoClicked() instanceof Player)) { + return; + } - if (optionalHolder.isEmpty()) { - return; - } + final Player player = (Player) event.getWhoClicked(); - final MenuHolder holder = optionalHolder.get(); + final Optional optionalHolder = Menu.getMenuHolder(player); - if (holder.getMenu().isEmpty()) { - Menu.closeMenu(player, true); - } + if (optionalHolder.isEmpty()) { + return; + } - if (holder.isUpdating()) { - event.setCancelled(true); - return; - } + final MenuHolder holder = optionalHolder.get(); + + if (holder.getMenu().isEmpty()) { + Menu.closeMenu(plugin, player, true); + } - event.setCancelled(true); + if (holder.isUpdating()) { + event.setCancelled(true); + return; + } - int slot = event.getRawSlot(); + event.setCancelled(true); - MenuItem item = holder.getItem(slot); + int slot = event.getRawSlot(); - if (item == null) { - return; - } + MenuItem item = holder.getItem(slot); - if (this.cache.getIfPresent(player.getUniqueId()) != null) { - return; - } + if (item == null) { + return; + } - if (this.shiftCache.getIfPresent(player.getUniqueId()) != null) { - return; - } + if (this.cache.getIfPresent(player.getUniqueId()) != null) { + return; + } - if (event.getClick() == ClickType.DOUBLE_CLICK) { - return; - } + if (this.shiftCache.getIfPresent(player.getUniqueId()) != null) { + return; + } - if (event.getClick() == ClickType.SHIFT_LEFT) { - this.shiftCache.put(player.getUniqueId(), System.currentTimeMillis()); - } + if (event.getClick() == ClickType.DOUBLE_CLICK) { + return; + } - if (handleClick(player, holder, item.options().clickHandler(), - item.options().clickRequirements())) { - return; - } + if (event.getClick() == ClickType.SHIFT_LEFT) { + this.shiftCache.put(player.getUniqueId(), System.currentTimeMillis()); + } - if (event.isShiftClick() && event.isLeftClick()) { - if (handleClick(player, holder, item.options().shiftLeftClickHandler(), - item.options().shiftLeftClickRequirements())) { - return; - } - } + if (handleClick(player, holder, item.options().clickHandler(), item.options().clickRequirements())) { + return; + } - if (event.isShiftClick() && event.isRightClick()) { - if (handleClick(player, holder, item.options().shiftRightClickHandler(), - item.options().shiftRightClickRequirements())) { - return; - } - } + if (event.isShiftClick() && event.isLeftClick()) { + if (handleClick(player, holder, item.options().shiftLeftClickHandler(), item.options().shiftLeftClickRequirements())) { + return; + } + } - if (event.getClick() == ClickType.LEFT) { - if (handleClick(player, holder, item.options().leftClickHandler(), - item.options().leftClickRequirements())) { - return; - } - } + if (event.isShiftClick() && event.isRightClick()) { + if (handleClick(player, holder, item.options().shiftRightClickHandler(), item.options().shiftRightClickRequirements())) { + return; + } + } - if (event.getClick() == ClickType.RIGHT) { - if (handleClick(player, holder, item.options().rightClickHandler(), - item.options().rightClickRequirements())) { - return; - } - } + if (event.getClick() == ClickType.LEFT) { + if (handleClick(player, holder, item.options().leftClickHandler(), item.options().leftClickRequirements())) { + return; + } + } - if (event.getClick() == ClickType.MIDDLE) { - if (handleClick(player, holder, item.options().middleClickHandler(), - item.options().middleClickRequirements())) { - return; - } - } - } - - /** - * Handles menu click by player - * @param player player who clicked - * @param holder menu holder - * @param handler click handler - * @param requirements click requirements - * @return true if click was handled successfully. will ever return false if no click handler was found - */ - private boolean handleClick(final @NotNull Player player, final @NotNull MenuHolder holder, - final @NotNull Optional handler, - final @NotNull Optional requirements) { - if (handler.isEmpty()) { - return false; + if (event.getClick() == ClickType.RIGHT) { + if (handleClick(player, holder, item.options().rightClickHandler(), item.options().rightClickRequirements())) { + return; + } + } + + if (event.getClick() == ClickType.MIDDLE) { + if (handleClick(player, holder, item.options().middleClickHandler(), item.options().middleClickRequirements())) { + } + } } - if (requirements.isPresent()) { - final ClickHandler denyHandler = requirements.get().getDenyHandler(); + /** + * Handles menu click by player + * + * @param player player who clicked + * @param holder menu holder + * @param handler click handler + * @param requirements click requirements + * @return true if click was handled successfully. will ever return false if no click handler was found + */ + private boolean handleClick(final @NotNull Player player, final @NotNull MenuHolder holder, final @NotNull Optional handler, final @NotNull Optional requirements) { + if (handler.isEmpty()) { + return false; + } + + if (requirements.isPresent()) { + final ClickHandler denyHandler = requirements.get().getDenyHandler(); + + if (!requirements.get().evaluate(holder)) { + if (denyHandler == null) { + return true; + } - if (!requirements.get().evaluate(holder)) { - if (denyHandler == null) { - return true; + denyHandler.onClick(holder); + return true; + } } - denyHandler.onClick(holder); + this.cache.put(player.getUniqueId(), System.currentTimeMillis()); + handler.get().onClick(holder); + return true; - } } - - this.cache.put(player.getUniqueId(), System.currentTimeMillis()); - handler.get().onClick(holder); - - return true; - } } diff --git a/src/main/java/com/extendedclip/deluxemenus/menu/Menu.java b/src/main/java/com/extendedclip/deluxemenus/menu/Menu.java index 8c9c0525..300bbf7e 100644 --- a/src/main/java/com/extendedclip/deluxemenus/menu/Menu.java +++ b/src/main/java/com/extendedclip/deluxemenus/menu/Menu.java @@ -1,13 +1,12 @@ package com.extendedclip.deluxemenus.menu; import com.extendedclip.deluxemenus.DeluxeMenus; -import com.extendedclip.deluxemenus.dupe.MenuItemMarker; +import com.extendedclip.deluxemenus.menu.command.RegistrableMenuCommand; import com.extendedclip.deluxemenus.menu.options.MenuOptions; import com.extendedclip.deluxemenus.requirement.RequirementList; import com.extendedclip.deluxemenus.utils.DebugLevel; import com.extendedclip.deluxemenus.utils.StringUtils; -import java.lang.reflect.Field; import java.util.*; import java.util.Map.Entry; import java.util.logging.Level; @@ -16,10 +15,6 @@ import io.github.projectunified.minelib.scheduler.global.GlobalScheduler; import me.clip.placeholderapi.util.Msg; import org.bukkit.Bukkit; -import org.bukkit.command.Command; -import org.bukkit.command.CommandMap; -import org.bukkit.command.CommandSender; -import org.bukkit.command.SimpleCommandMap; import org.bukkit.entity.Player; import org.bukkit.event.inventory.InventoryType; import org.bukkit.inventory.Inventory; @@ -27,66 +22,83 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public class Menu extends Command { +public class Menu { private static final Map menus = new HashMap<>(); private static final Set menuHolders = new HashSet<>(); private static final Map lastOpenedMenus = new HashMap<>(); - private static CommandMap commandMap = null; + private final DeluxeMenus plugin; private final MenuOptions options; private final Map> items; - - public Menu(final @NotNull MenuOptions options, final @NotNull Map> items) { - super(options.commands().isEmpty() ? options.name() : options.commands().get(0)); - + // menu path starting from the plugin directory + private final String path; + + private RegistrableMenuCommand command = null; + + public Menu( + final @NotNull DeluxeMenus plugin, + final @NotNull MenuOptions options, + final @NotNull Map> items, + final @NotNull String path + ) { + this.plugin = plugin; this.options = options; this.items = items; + this.path = path; if (this.options.registerCommands()) { - if (this.options.commands().size() > 1) { - this.setAliases(this.options.commands().subList(1, this.options.commands().size())); - } - - addCommand(); + this.command = new RegistrableMenuCommand(plugin, this); + this.command.register(); } + menus.put(this.options.name(), this); } - public static void unload(final @NotNull String name) { + public static void unload(final @NotNull DeluxeMenus plugin, final @NotNull String name) { for (Player p : Bukkit.getOnlinePlayers()) { if (isInMenu(p, name)) { - closeMenu(p, true); + closeMenu(plugin, p, true); } } - Optional menu = Menu.getMenuByName(name); - if (menu.isEmpty()) { + Optional optionalMenu = Menu.getMenuByName(name); + if (optionalMenu.isEmpty()) { return; } - menu.get().removeCommand(); + optionalMenu.get().unregisterCommand(); menus.remove(name); } - public static void unload() { + public static void unload(final @NotNull DeluxeMenus plugin) { for (Player p : Bukkit.getOnlinePlayers()) { if (isInMenu(p)) { - closeMenu(p, true); + closeMenu(plugin, p, true); } } for (Menu menu : Menu.getAllMenus()) { - menu.removeCommand(); + menu.unregisterCommand(); } menus.clear(); menuHolders.clear(); lastOpenedMenus.clear(); } - public static void unloadForShutdown() { + private void unregisterCommand() { + if (this.command != null) { + this.command.unregister(); + } + + // WARNING! A reference to the command is stored by CraftBukkit for their `/help` command. There is currently + // no way to remove this reference! + this.command = null; + } + + public static void unloadForShutdown(final @NotNull DeluxeMenus plugin) { for (Player player : Bukkit.getOnlinePlayers()) { if (isInMenu(player)) { - closeMenuForShutdown(player); + closeMenuForShutdown(plugin, player); } } menus.clear(); @@ -100,6 +112,25 @@ public static int getLoadedMenuSize() { return menus.values(); } + // Menus need to be stored in a list because config.yml can contain multiple menus. + // This can be changed once we remove support for menus inside the config file. + public static @NotNull TreeMap> getPathSortedMenus() { + return menus.values().stream().map(m -> Map.entry(m.path(), m)).collect( + TreeMap::new, (tree, entry) -> { + final List list = tree.computeIfAbsent(entry.getKey(), k -> new ArrayList<>()); + list.add(entry.getValue()); + tree.put(entry.getKey(), list); + }, + (tree1, tree2) -> { + for (Entry> entry : tree2.entrySet()) { + final List list = tree1.computeIfAbsent(entry.getKey(), k -> new ArrayList<>()); + list.addAll(entry.getValue()); + tree1.put(entry.getKey(), list); + } + } + ); + } + public static @NotNull Optional getMenuByName(final @NotNull String name) { return menus.entrySet().stream().filter(e -> e.getKey().equalsIgnoreCase(name)).findFirst().map(Entry::getValue); } @@ -132,12 +163,12 @@ public static Optional getLastMenu(final @NotNull Player player) { return Optional.ofNullable(lastOpenedMenus.get(player.getUniqueId())); } - public static void cleanInventory(final @NotNull Player player, final @NotNull MenuItemMarker marker) { + public static void cleanInventory(final @NotNull DeluxeMenus plugin, final @NotNull Player player) { for (final ItemStack itemStack : player.getInventory().getContents()) { if (itemStack == null) continue; - if (!marker.isMarked(itemStack)) continue; + if (!plugin.getMenuItemMarker().isMarked(itemStack)) continue; - DeluxeMenus.debug( + plugin.debug( DebugLevel.LOWEST, Level.INFO, "Found a DeluxeMenus item in a player's inventory. Removing it." @@ -147,7 +178,7 @@ public static void cleanInventory(final @NotNull Player player, final @NotNull M player.updateInventory(); } - public static void closeMenu(final @NotNull Player player, final boolean close, final boolean executeCloseActions) { + public static void closeMenu(final @NotNull DeluxeMenus plugin, final @NotNull Player player, final boolean close, final boolean executeCloseActions) { Optional optionalHolder = getMenuHolder(player); if (optionalHolder.isEmpty()) { return; @@ -164,119 +195,22 @@ public static void closeMenu(final @NotNull Player player, final boolean close, if (close) { GlobalScheduler.get(DeluxeMenus.getInstance()).run(() -> { player.closeInventory(); - cleanInventory(player, DeluxeMenus.getInstance().getMenuItemMarker()); + cleanInventory(plugin, player); }); } menuHolders.remove(holder); lastOpenedMenus.put(player.getUniqueId(), holder.getMenu().orElse(null)); } - public static void closeMenuForShutdown(final @NotNull Player player) { + public static void closeMenuForShutdown(final @NotNull DeluxeMenus plugin, final @NotNull Player player) { getMenuHolder(player).ifPresent(MenuHolder::stopPlaceholderUpdate); player.closeInventory(); - cleanInventory(player, DeluxeMenus.getInstance().getMenuItemMarker()); + cleanInventory(plugin, player); } - public static void closeMenu(final @NotNull Player player, final boolean close) { - closeMenu(player, close, false); - } - - private void addCommand() { - if (commandMap == null) { - try { - final Field f = Bukkit.getServer().getClass().getDeclaredField("commandMap"); - f.setAccessible(true); - commandMap = (CommandMap) f.get(Bukkit.getServer()); - } catch (final @NotNull Exception exception) { - DeluxeMenus.printStacktrace( - "Something went wrong while trying to register command: " + this.getName(), - exception - ); - return; - } - } - boolean registered = commandMap.register("DeluxeMenus", this); - if (registered) { - DeluxeMenus.debug( - DebugLevel.LOW, - Level.INFO, - "Registered command: " + this.getName() + " for menu: " + this.options.name() - ); - } - } - - private void removeCommand() { - if (commandMap != null && this.options.registerCommands()) { - Field cMap; - Field knownCommands; - try { - cMap = Bukkit.getServer().getClass().getDeclaredField("commandMap"); - cMap.setAccessible(true); - knownCommands = SimpleCommandMap.class.getDeclaredField("knownCommands"); - knownCommands.setAccessible(true); - ((Map) knownCommands.get((SimpleCommandMap) cMap.get(Bukkit.getServer()))) - .remove(this.getName()); - boolean unregistered = this.unregister((CommandMap) cMap.get(Bukkit.getServer())); - this.unregister(commandMap); - if (unregistered) { - DeluxeMenus.debug( - DebugLevel.HIGH, - Level.INFO, - "Successfully unregistered command: " + this.getName() - ); - } else { - DeluxeMenus.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Failed to unregister command: " + this.getName() - ); - } - } catch (final @NotNull Exception exception) { - DeluxeMenus.printStacktrace( - "Something went wrong while trying to unregister command: " + this.getName(), - exception - ); - } - } - } - - @Override - public boolean execute(final @NotNull CommandSender sender, final @NotNull String commandLabel, final @NotNull String[] typedArgs) { - if (!(sender instanceof Player)) { - Msg.msg(sender, "Menus can only be opened by players!"); - return true; - } - - Map argMap = null; - - if (!this.options.arguments().isEmpty()) { - DeluxeMenus.debug(DebugLevel.LOWEST, Level.INFO, "has args"); - if (typedArgs.length < this.options.arguments().size()) { - if (this.options.argumentsUsageMessage().isPresent()) { - Msg.msg(sender, this.options.argumentsUsageMessage().get()); - } - return true; - } - argMap = new HashMap<>(); - int index = 0; - for (String arg : this.options.arguments()) { - if (index + 1 == this.options.arguments().size()) { - String last = String.join(" ", Arrays.asList(typedArgs).subList(index, typedArgs.length)); - DeluxeMenus.debug(DebugLevel.LOWEST, Level.INFO, "arg: " + arg + " => " + last); - argMap.put(arg, last); - } else { - argMap.put(arg, typedArgs[index]); - DeluxeMenus.debug(DebugLevel.LOWEST, Level.INFO, "arg: " + arg + " => " + typedArgs[index]); - } - index++; - } - } - - Player player = (Player) sender; - DeluxeMenus.debug(DebugLevel.LOWEST, Level.INFO, "opening menu: " + this.options.name()); - openMenu(player, argMap, null); - return true; + public static void closeMenu(final @NotNull DeluxeMenus plugin, final @NotNull Player player, final boolean close) { + closeMenu(plugin, player, close, false); } private boolean hasOpenBypassPerm(final @NotNull Player viewer) { @@ -333,7 +267,7 @@ public void openMenu(final @NotNull Player viewer, final @Nullable Map= this.options.size()) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Item set to slot " + slot + " for menu: " + this.options.name() + " exceeds the inventory size!", @@ -415,12 +349,12 @@ public void openMenu(final @NotNull Player viewer, final @Nullable Map= this.options.size()) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Item set to slot " + slot + " for menu: " + this.options.name() + " exceeds the inventory size!", @@ -440,7 +374,7 @@ public void openMenu(final @NotNull Player viewer, final @Nullable Map { if (isInMenu(holder.getViewer())) { - closeMenu(holder.getViewer(), false); + closeMenu(plugin, holder.getViewer(), false); } viewer.openInventory(inventory); @@ -464,4 +398,8 @@ public void openMenu(final @NotNull Player viewer, final @Nullable Map activeItems; @@ -35,12 +37,14 @@ public class MenuHolder implements InventoryHolder { private boolean parsePlaceholdersAfterArguments; private Map typedArgs; - public MenuHolder(Player viewer) { + public MenuHolder(final @NotNull DeluxeMenus plugin, final @NotNull Player viewer) { + this.plugin = plugin; this.viewer = viewer; } - public MenuHolder(Player viewer, String menuName, - Set activeItems, Inventory inventory) { + public MenuHolder(final @NotNull DeluxeMenus plugin, final @NotNull Player viewer, final @NotNull String menuName, + final @NotNull Set<@NotNull MenuItem> activeItems, final @NotNull Inventory inventory) { + this.plugin = plugin; this.viewer = viewer; this.menuName = menuName; this.activeItems = activeItems; @@ -171,7 +175,7 @@ public void refreshMenu() { } if (active.isEmpty()) { - Menu.closeMenu(getViewer(), true); + Menu.closeMenu(plugin, getViewer(), true); } GlobalScheduler.get(DeluxeMenus.getInstance()).run(() -> { @@ -256,7 +260,7 @@ public void run() { amt = 1; } } catch (Exception exception) { - DeluxeMenus.printStacktrace( + plugin.printStacktrace( "Something went wrong while updating item in slot " + item.options().slot() + ". Invalid dynamic amount: " + setPlaceholdersAndArguments(item.options().dynamicAmount().get()), exception @@ -342,4 +346,8 @@ public void setPlaceholderPlayer(Player placeholderPlayer) { public Player getPlaceholderPlayer() { return placeholderPlayer; } + + public @NotNull DeluxeMenus getPlugin() { + return plugin; + } } diff --git a/src/main/java/com/extendedclip/deluxemenus/menu/MenuItem.java b/src/main/java/com/extendedclip/deluxemenus/menu/MenuItem.java index be8224f8..a786436c 100644 --- a/src/main/java/com/extendedclip/deluxemenus/menu/MenuItem.java +++ b/src/main/java/com/extendedclip/deluxemenus/menu/MenuItem.java @@ -3,6 +3,7 @@ import com.extendedclip.deluxemenus.DeluxeMenus; import com.extendedclip.deluxemenus.hooks.ItemHook; import com.extendedclip.deluxemenus.menu.options.HeadType; +import com.extendedclip.deluxemenus.menu.options.LoreAppendMode; import com.extendedclip.deluxemenus.menu.options.MenuItemOptions; import com.extendedclip.deluxemenus.nbt.NbtProvider; import com.extendedclip.deluxemenus.utils.DebugLevel; @@ -14,12 +15,14 @@ import org.bukkit.FireworkEffect; import org.bukkit.Material; import org.bukkit.Registry; +import org.bukkit.NamespacedKey; import org.bukkit.block.Banner; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.type.Light; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemRarity; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ArmorMeta; import org.bukkit.inventory.meta.BannerMeta; @@ -52,9 +55,11 @@ public class MenuItem { - private final @NotNull MenuItemOptions options; + private final DeluxeMenus plugin; + private final MenuItemOptions options; - public MenuItem(@NotNull final MenuItemOptions options) { + public MenuItem(@NotNull final DeluxeMenus plugin, @NotNull final MenuItemOptions options) { + this.plugin = plugin; this.options = options; } @@ -86,7 +91,7 @@ public ItemStack getItemStack(@NotNull final MenuHolder holder) { final int temporaryAmount = amount; final String finalMaterial = lowercaseStringMaterial; - final ItemHook pluginHook = DeluxeMenus.getInstance().getItemHooks().values() + final ItemHook pluginHook = plugin.getItemHooks().values() .stream() .filter(x -> finalMaterial.startsWith(x.getPrefix())) .findFirst() @@ -104,7 +109,7 @@ public ItemStack getItemStack(@NotNull final MenuHolder holder) { if (itemStack == null) { final Material material = Material.getMaterial(stringMaterial.toUpperCase(Locale.ROOT)); if (material == null) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Material: " + stringMaterial + " is not valid! Setting to Stone." @@ -186,7 +191,7 @@ public ItemStack getItemStack(@NotNull final MenuHolder holder) { } } } catch (final NumberFormatException exception) { - DeluxeMenus.printStacktrace( + plugin.printStacktrace( "Invalid damage found: " + parsedDamage + ".", exception ); @@ -259,6 +264,39 @@ public ItemStack getItemStack(@NotNull final MenuHolder holder) { itemMeta.setUnbreakable(true); } + if (VersionHelper.HAS_DATA_COMPONENTS) { + if (this.options.hideTooltip().isPresent()) { + String hideTooltip = holder.setPlaceholdersAndArguments(this.options.hideTooltip().get()); + itemMeta.setHideTooltip(Boolean.parseBoolean(hideTooltip)); + } + if (this.options.enchantmentGlintOverride().isPresent()) { + String enchantmentGlintOverride = holder.setPlaceholdersAndArguments(this.options.enchantmentGlintOverride().get()); + itemMeta.setEnchantmentGlintOverride(Boolean.parseBoolean(enchantmentGlintOverride)); + } + if (this.options.rarity().isPresent()) { + String rarity = holder.setPlaceholdersAndArguments(this.options.rarity().get()); + try { + itemMeta.setRarity(ItemRarity.valueOf(rarity.toUpperCase())); + } catch (IllegalArgumentException e) { + plugin.debug( + DebugLevel.HIGHEST, + Level.WARNING, + "Rarity " + rarity + " is not a valid!" + ); + } + } + } + if (VersionHelper.HAS_TOOLTIP_STYLE) { + if (this.options.tooltipStyle().isPresent()) { + NamespacedKey tooltipStyle = NamespacedKey.fromString(holder.setPlaceholdersAndArguments(this.options.tooltipStyle().get())); + if (tooltipStyle != null) itemMeta.setTooltipStyle(tooltipStyle); + } + if (this.options.itemModel().isPresent()) { + NamespacedKey itemModel = NamespacedKey.fromString(holder.setPlaceholdersAndArguments(this.options.itemModel().get())); + if (itemModel != null) itemMeta.setItemModel(itemModel); + } + } + if (VersionHelper.HAS_ARMOR_TRIMS && ItemUtils.hasArmorMeta(itemStack)) { final Optional trimMaterialName = this.options.trimMaterial(); final Optional trimPatternName = this.options.trimPattern(); @@ -274,7 +312,7 @@ public ItemStack getItemStack(@NotNull final MenuHolder holder) { itemStack.setItemMeta(armorMeta); } else { if (trimMaterial == null) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Trim material " + trimMaterialName.get() + " is not a valid!" @@ -282,7 +320,7 @@ public ItemStack getItemStack(@NotNull final MenuHolder holder) { } if (trimPattern == null) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Trim pattern " + trimPatternName.get() + " is not a valid!" @@ -290,13 +328,13 @@ public ItemStack getItemStack(@NotNull final MenuHolder holder) { } } } else if (trimMaterialName.isPresent()) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Trim pattern is not set for item with trim material " + trimMaterialName.get() ); } else if (trimPatternName.isPresent()) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Trim material is not set for item with trim pattern " + trimPatternName.get() @@ -314,7 +352,7 @@ public ItemStack getItemStack(@NotNull final MenuHolder holder) { Integer.parseInt(parts[2].trim()))); itemStack.setItemMeta(leatherArmorMeta); } catch (final Exception exception) { - DeluxeMenus.printStacktrace( + plugin.printStacktrace( "Invalid rgb colors found for leather armor: " + parts[0].trim() + ", " + parts[1].trim() + ", " + parts[2].trim(), exception @@ -330,7 +368,7 @@ public ItemStack getItemStack(@NotNull final MenuHolder holder) { Integer.parseInt(parts[1].trim()), Integer.parseInt(parts[2].trim()))).build()); itemStack.setItemMeta(fireworkEffectMeta); } catch (final Exception exception) { - DeluxeMenus.printStacktrace( + plugin.printStacktrace( "Invalid rgb colors found for firework or firework star: " + parts[0].trim() + ", " + parts[1].trim() + ", " + parts[2].trim(), exception @@ -341,7 +379,7 @@ public ItemStack getItemStack(@NotNull final MenuHolder holder) { for (final Map.Entry entry : this.options.enchantments().entrySet()) { final boolean result = enchantmentStorageMeta.addStoredEnchant(entry.getKey(), entry.getValue(), true); if (!result) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.INFO, "Failed to add enchantment " + entry.getKey().getName() + " to item " + itemStack.getType() @@ -367,14 +405,14 @@ public ItemStack getItemStack(@NotNull final MenuHolder holder) { final int lightLevel = Math.min(Integer.parseInt(parsedLightLevel), light.getMaximumLevel()); light.setLevel(Math.max(lightLevel, 0)); if (lightLevel < 0) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.MEDIUM, Level.WARNING, "Invalid light level found for light block: " + parsedLightLevel + ". Setting to 0." ); } if (lightLevel > light.getMaximumLevel()) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.MEDIUM, Level.WARNING, "Invalid light level found for light block: " + parsedLightLevel + ". Setting to " + light.getMaximumLevel() + "." @@ -384,7 +422,7 @@ public ItemStack getItemStack(@NotNull final MenuHolder holder) { blockDataMeta.setBlockData(light); itemStack.setItemMeta(blockDataMeta); } catch (final Exception exception) { - DeluxeMenus.printStacktrace( + plugin.printStacktrace( "Invalid light level found for light block: " + parsedLightLevel, exception ); @@ -413,6 +451,22 @@ public ItemStack getItemStack(@NotNull final MenuHolder holder) { } } + if (this.options.nbtByte().isPresent()) { + final String tag = holder.setPlaceholdersAndArguments(this.options.nbtByte().get()); + if (tag.contains(":")) { + final String[] parts = tag.split(":"); + itemStack = NbtProvider.setByte(itemStack, parts[0], Byte.parseByte(parts[1])); + } + } + + if (this.options.nbtShort().isPresent()) { + final String tag = holder.setPlaceholdersAndArguments(this.options.nbtShort().get()); + if (tag.contains(":")) { + final String[] parts = tag.split(":"); + itemStack = NbtProvider.setShort(itemStack, parts[0], Short.parseShort(parts[1])); + } + } + if (this.options.nbtInt().isPresent()) { final String tag = holder.setPlaceholdersAndArguments(this.options.nbtInt().get()); if (tag.contains(":")) { @@ -429,6 +483,22 @@ public ItemStack getItemStack(@NotNull final MenuHolder holder) { } } + for (String nbtTag : this.options.nbtBytes()) { + final String tag = holder.setPlaceholdersAndArguments(nbtTag); + if (tag.contains(":")) { + final String[] parts = tag.split(":"); + itemStack = NbtProvider.setByte(itemStack, parts[0], Byte.parseByte(parts[1])); + } + } + + for (String nbtTag : this.options.nbtShorts()) { + final String tag = holder.setPlaceholdersAndArguments(nbtTag); + if (tag.contains(":")) { + final String[] parts = tag.split(":"); + itemStack = NbtProvider.setShort(itemStack, parts[0], Short.parseShort(parts[1])); + } + } + for (String nbtTag : this.options.nbtInts()) { final String tag = holder.setPlaceholdersAndArguments(nbtTag); if (tag.contains(":")) { @@ -461,9 +531,7 @@ private boolean isHeadItem(@NotNull final String material) { } private @NotNull Optional getItemFromHook(String hookName, String... args) { - return DeluxeMenus.getInstance() - .getItemHook(hookName) - .map(itemHook -> itemHook.getItem(args)); + return plugin.getItemHook(hookName).map(itemHook -> itemHook.getItem(args)); } private List getMenuItemLore(@NotNull final MenuHolder holder, @NotNull final List lore) { diff --git a/src/main/java/com/extendedclip/deluxemenus/menu/command/RegistrableMenuCommand.java b/src/main/java/com/extendedclip/deluxemenus/menu/command/RegistrableMenuCommand.java new file mode 100644 index 00000000..3edf522b --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/menu/command/RegistrableMenuCommand.java @@ -0,0 +1,181 @@ +package com.extendedclip.deluxemenus.menu.command; + +import com.extendedclip.deluxemenus.DeluxeMenus; +import com.extendedclip.deluxemenus.menu.Menu; +import com.extendedclip.deluxemenus.utils.DebugLevel; +import me.clip.placeholderapi.util.Msg; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandMap; +import org.bukkit.command.CommandSender; +import org.bukkit.command.SimpleCommandMap; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.logging.Level; + +public class RegistrableMenuCommand extends Command { + + private static final String FALLBACK_PREFIX = "DeluxeMenus".toLowerCase(Locale.ROOT).trim(); + private static CommandMap commandMap = null; + + private final DeluxeMenus plugin; + + private Menu menu; + private boolean registered = false; + private boolean unregistered = false; + + public RegistrableMenuCommand(final @NotNull DeluxeMenus plugin, + final @NotNull Menu menu) { + super(menu.options().commands().isEmpty() ? menu.options().name() : menu.options().commands().get(0)); + this.plugin = plugin; + this.menu = menu; + + if (menu.options().commands().size() > 1) { + this.setAliases(menu.options().commands().subList(1, menu.options().commands().size())); + } + } + + @Override + public boolean execute(final @NotNull CommandSender sender, final @NotNull String commandLabel, final @NotNull String[] typedArgs) { + if (this.unregistered) { + throw new IllegalStateException("This command was unregistered!"); + } + + if (!(sender instanceof Player)) { + Msg.msg(sender, "Menus can only be opened by players!"); + return true; + } + + Map argMap = null; + + if (!menu.options().arguments().isEmpty()) { + plugin.debug(DebugLevel.LOWEST, Level.INFO, "has args"); + if (typedArgs.length < menu.options().arguments().size()) { + if (menu.options().argumentsUsageMessage().isPresent()) { + Msg.msg(sender, menu.options().argumentsUsageMessage().get()); + } + return true; + } + argMap = new HashMap<>(); + int index = 0; + for (String arg : menu.options().arguments()) { + if (index + 1 == menu.options().arguments().size()) { + String last = String.join(" ", Arrays.asList(typedArgs).subList(index, typedArgs.length)); + plugin.debug(DebugLevel.LOWEST, Level.INFO, "arg: " + arg + " => " + last); + argMap.put(arg, last); + } else { + argMap.put(arg, typedArgs[index]); + plugin.debug(DebugLevel.LOWEST, Level.INFO, "arg: " + arg + " => " + typedArgs[index]); + } + index++; + } + } + + Player player = (Player) sender; + plugin.debug(DebugLevel.LOWEST, Level.INFO, "opening menu: " + menu.options().name()); + menu.openMenu(player, argMap, null); + return true; + } + + public void register() { + if (registered) { + throw new IllegalStateException("This command was already registered!"); + } + + registered = true; + + if (commandMap == null) { + try { + final Field f = Bukkit.getServer().getClass().getDeclaredField("commandMap"); + f.setAccessible(true); + commandMap = (CommandMap) f.get(Bukkit.getServer()); + } catch (final @NotNull Exception exception) { + plugin.printStacktrace( + "Something went wrong while trying to register command: " + this.getName(), + exception + ); + return; + } + } + + boolean registered = commandMap.register(FALLBACK_PREFIX, this); + if (registered) { + plugin.debug( + DebugLevel.LOW, + Level.INFO, + "Registered command: " + this.getName() + " for menu: " + menu.options().name() + ); + } + } + + public void unregister() { + if (!registered) { + throw new IllegalStateException("This command was not registered!"); + } + + if (unregistered) { + throw new IllegalStateException("This command was already unregistered!"); + } + + unregistered = true; + + if (commandMap == null) { + this.menu = null; + return; + } + + Field cMap; + Field knownCommands; + try { + cMap = Bukkit.getServer().getClass().getDeclaredField("commandMap"); + cMap.setAccessible(true); + knownCommands = SimpleCommandMap.class.getDeclaredField("knownCommands"); + knownCommands.setAccessible(true); + + final Map knownCommandsMap = (Map) knownCommands.get(cMap.get(Bukkit.getServer())); + + // We need to remove every single alias because CommandMap#register() adds them all to the map. + // If we do not remove them, then we will have dangling references to the command. + knownCommandsMap.remove(this.getName()); + knownCommandsMap.remove(FALLBACK_PREFIX + ":" + this.getName()); + + for (String alias : this.getAliases()) { + knownCommandsMap.remove(alias); + knownCommandsMap.remove(FALLBACK_PREFIX + ":" + alias); + } + + boolean unregistered = this.unregister((CommandMap) cMap.get(Bukkit.getServer())); + this.unregister(commandMap); + if (unregistered) { + plugin.debug( + DebugLevel.HIGH, + Level.INFO, + "Successfully unregistered command: " + this.getName() + ); + } else { + plugin.debug( + DebugLevel.HIGHEST, + Level.WARNING, + "Failed to unregister command: " + this.getName() + ); + } + } catch (final @NotNull Exception exception) { + plugin.printStacktrace( + "Something went wrong while trying to unregister command: " + this.getName(), + exception + ); + } + + this.menu = null; + } + + public boolean registered() { + return registered; + } +} diff --git a/src/main/java/com/extendedclip/deluxemenus/menu/LoreAppendMode.java b/src/main/java/com/extendedclip/deluxemenus/menu/options/LoreAppendMode.java similarity index 60% rename from src/main/java/com/extendedclip/deluxemenus/menu/LoreAppendMode.java rename to src/main/java/com/extendedclip/deluxemenus/menu/options/LoreAppendMode.java index 9b0547d3..b59ce59a 100644 --- a/src/main/java/com/extendedclip/deluxemenus/menu/LoreAppendMode.java +++ b/src/main/java/com/extendedclip/deluxemenus/menu/options/LoreAppendMode.java @@ -1,4 +1,4 @@ -package com.extendedclip.deluxemenus.menu; +package com.extendedclip.deluxemenus.menu.options; public enum LoreAppendMode { OVERRIDE, diff --git a/src/main/java/com/extendedclip/deluxemenus/menu/options/MenuItemOptions.java b/src/main/java/com/extendedclip/deluxemenus/menu/options/MenuItemOptions.java index 3c0400e8..3377bec1 100644 --- a/src/main/java/com/extendedclip/deluxemenus/menu/options/MenuItemOptions.java +++ b/src/main/java/com/extendedclip/deluxemenus/menu/options/MenuItemOptions.java @@ -2,7 +2,6 @@ import com.extendedclip.deluxemenus.action.ClickHandler; import com.extendedclip.deluxemenus.config.DeluxeMenusConfig; -import com.extendedclip.deluxemenus.menu.LoreAppendMode; import com.extendedclip.deluxemenus.requirement.RequirementList; import org.bukkit.DyeColor; import org.bukkit.block.banner.Pattern; @@ -37,6 +36,12 @@ public class MenuItemOptions { private final String trimMaterial; private final String trimPattern; + private final String hideTooltip; + private final String enchantmentGlintOverride; + private final String rarity; + private final String tooltipStyle; + private final String itemModel; + private final Map enchantments; private final List potionEffects; private final List bannerMeta; @@ -50,8 +55,12 @@ public class MenuItemOptions { private final LoreAppendMode loreAppendMode; private final String nbtString; + private final String nbtByte; + private final String nbtShort; private final String nbtInt; private final List nbtStrings; + private final List nbtBytes; + private final List nbtShorts; private final List nbtInts; private final int slot; @@ -89,6 +98,11 @@ private MenuItemOptions(final @NotNull MenuItemOptionsBuilder builder) { this.rgb = builder.rgb; this.trimMaterial = builder.trimMaterial; this.trimPattern = builder.trimPattern; + this.hideTooltip = builder.hideTooltip; + this.enchantmentGlintOverride = builder.enchantmentGlintOverride; + this.rarity = builder.rarity; + this.tooltipStyle = builder.tooltipStyle; + this.itemModel = builder.itemModel; this.enchantments = builder.enchantments; this.potionEffects = builder.potionEffects; this.bannerMeta = builder.bannerMeta; @@ -97,8 +111,12 @@ private MenuItemOptions(final @NotNull MenuItemOptionsBuilder builder) { this.displayNameHasPlaceholders = builder.displayNameHasPlaceholders; this.loreHasPlaceholders = builder.loreHasPlaceholders; this.nbtString = builder.nbtString; + this.nbtByte = builder.nbtByte; + this.nbtShort = builder.nbtShort; this.nbtInt = builder.nbtInt; this.nbtStrings = builder.nbtStrings; + this.nbtBytes = builder.nbtBytes; + this.nbtShorts = builder.nbtShorts; this.nbtInts = builder.nbtInts; this.slot = builder.slot; this.priority = builder.priority; @@ -178,6 +196,26 @@ public void headType(final @Nullable HeadType headType) { return Optional.ofNullable(trimPattern); } + public @NotNull Optional hideTooltip() { + return Optional.ofNullable(hideTooltip); + } + + public @NotNull Optional enchantmentGlintOverride() { + return Optional.ofNullable(enchantmentGlintOverride); + } + + public @NotNull Optional rarity() { + return Optional.ofNullable(rarity); + } + + public @NotNull Optional tooltipStyle() { + return Optional.ofNullable(tooltipStyle); + } + + public @NotNull Optional itemModel() { + return Optional.ofNullable(itemModel); + } + public @NotNull Map enchantments() { return enchantments; } @@ -218,6 +256,14 @@ public boolean hasLore() { return Optional.ofNullable(nbtString); } + public @NotNull Optional nbtByte() { + return Optional.ofNullable(nbtByte); + } + + public @NotNull Optional nbtShort() { + return Optional.ofNullable(nbtShort); + } + public @NotNull Optional nbtInt() { return Optional.ofNullable(nbtInt); } @@ -226,6 +272,14 @@ public boolean hasLore() { return nbtStrings; } + public @NotNull List nbtBytes() { + return nbtBytes; + } + + public @NotNull List nbtShorts() { + return nbtShorts; + } + public @NotNull List nbtInts() { return nbtInts; } @@ -311,14 +365,23 @@ public boolean updatePlaceholders() { .rgb(this.rgb) .trimMaterial(this.trimMaterial) .trimPattern(this.trimPattern) + .hideTooltip(this.hideTooltip) + .enchantmentGlintOverride(this.enchantmentGlintOverride) + .rarity(this.rarity) + .tooltipStyle(this.tooltipStyle) + .itemModel(this.itemModel) .enchantments(this.enchantments) .potionEffects(this.potionEffects) .bannerMeta(this.bannerMeta) .itemFlags(this.itemFlags) .unbreakable(this.unbreakable) .nbtString(this.nbtString) + .nbtByte(this.nbtByte) + .nbtShort(this.nbtShort) .nbtInt(this.nbtInt) .nbtStrings(this.nbtStrings) + .nbtBytes(this.nbtBytes) + .nbtShorts(this.nbtShorts) .nbtInts(this.nbtInts) .slot(this.slot) .priority(this.priority) @@ -355,6 +418,12 @@ public static class MenuItemOptionsBuilder { private String trimMaterial; private String trimPattern; + private String hideTooltip; + private String enchantmentGlintOverride; + private String rarity; + private String tooltipStyle; + private String itemModel; + private Map enchantments = Collections.emptyMap(); private List potionEffects = Collections.emptyList(); private List bannerMeta = Collections.emptyList(); @@ -368,8 +437,12 @@ public static class MenuItemOptionsBuilder { private LoreAppendMode loreAppendMode; private String nbtString; + private String nbtByte; + private String nbtShort; private String nbtInt; private List nbtStrings = Collections.emptyList(); + private List nbtBytes = Collections.emptyList(); + private List nbtShorts = Collections.emptyList(); private List nbtInts = Collections.emptyList(); private int slot; @@ -463,6 +536,31 @@ public MenuItemOptionsBuilder trimPattern(final @Nullable String trimPattern) { return this; } + public MenuItemOptionsBuilder hideTooltip(final @Nullable String hideTooltip) { + this.hideTooltip = hideTooltip; + return this; + } + + public MenuItemOptionsBuilder enchantmentGlintOverride(final @Nullable String enchantmentGlintOverride) { + this.enchantmentGlintOverride = enchantmentGlintOverride; + return this; + } + + public MenuItemOptionsBuilder rarity(final @Nullable String rarity) { + this.rarity = rarity; + return this; + } + + public MenuItemOptionsBuilder tooltipStyle(final @Nullable String tooltipStyle) { + this.tooltipStyle = tooltipStyle; + return this; + } + + public MenuItemOptionsBuilder itemModel(final @Nullable String itemModel) { + this.itemModel = itemModel; + return this; + } + public MenuItemOptionsBuilder enchantments(final @NotNull Map enchantments) { this.enchantments = enchantments; return this; @@ -526,6 +624,16 @@ public MenuItemOptionsBuilder nbtString(final @Nullable String nbtString) { return this; } + public MenuItemOptionsBuilder nbtByte(final @Nullable String nbtByte) { + this.nbtByte = nbtByte; + return this; + } + + public MenuItemOptionsBuilder nbtShort(final @Nullable String nbtShort) { + this.nbtShort = nbtShort; + return this; + } + public MenuItemOptionsBuilder nbtInt(final @Nullable String nbtInt) { this.nbtInt = nbtInt; return this; @@ -536,6 +644,16 @@ public MenuItemOptionsBuilder nbtStrings(final @NotNull List nbtStrings) return this; } + public MenuItemOptionsBuilder nbtBytes(final @NotNull List nbtBytes) { + this.nbtBytes = nbtBytes; + return this; + } + + public MenuItemOptionsBuilder nbtShorts(final @NotNull List nbtShorts) { + this.nbtShorts = nbtShorts; + return this; + } + public MenuItemOptionsBuilder nbtInts(final @NotNull List nbtInts) { this.nbtInts = nbtInts; return this; diff --git a/src/main/java/com/extendedclip/deluxemenus/nbt/NbtProvider.java b/src/main/java/com/extendedclip/deluxemenus/nbt/NbtProvider.java index 418f9136..3e0c4262 100644 --- a/src/main/java/com/extendedclip/deluxemenus/nbt/NbtProvider.java +++ b/src/main/java/com/extendedclip/deluxemenus/nbt/NbtProvider.java @@ -15,6 +15,8 @@ public final class NbtProvider { private static Method getStringMethod; private static Method setStringMethod; private static Method setBooleanMethod; + private static Method setByteMethod; + private static Method setShortMethod; private static Method setIntMethod; private static Method removeTagMethod; private static Method hasTagMethod; @@ -36,6 +38,8 @@ public final class NbtProvider { getStringMethod = compoundClass.getMethod(VersionConstants.GET_STRING_METHOD_NAME, String.class); setStringMethod = compoundClass.getMethod(VersionConstants.SET_STRING_METHOD_NAME, String.class, String.class); setBooleanMethod = compoundClass.getMethod(VersionConstants.SET_BOOLEAN_METHOD_NAME, String.class, boolean.class); + setByteMethod = compoundClass.getMethod(VersionConstants.SET_BYTE_METHOD_NAME, String.class, byte.class); + setShortMethod = compoundClass.getMethod(VersionConstants.SET_SHORT_METHOD_NAME, String.class, short.class); setIntMethod = compoundClass.getMethod(VersionConstants.SET_INTEGER_METHOD_NAME, String.class, int.class); removeTagMethod = compoundClass.getMethod(VersionConstants.REMOVE_TAG_METHOD_NAME, String.class); hasTagMethod = itemStackClass.getMethod(VersionConstants.HAS_TAG_METHOD_NAME); @@ -116,6 +120,32 @@ public static String getString(final ItemStack itemStack, final String key) { return getString(itemCompound, key); } + public static ItemStack setByte(final ItemStack itemStack, final String key, final byte value) { + if (itemStack == null) return null; + if (itemStack.getType() == Material.AIR) return null; + + Object nmsItemStack = asNMSCopy(itemStack); + Object itemCompound = hasTag(nmsItemStack) ? getTag(nmsItemStack) : newNBTTagCompound(); + + setByte(itemCompound, key, value); + setTag(nmsItemStack, itemCompound); + + return asBukkitCopy(nmsItemStack); + } + + public static ItemStack setShort(final ItemStack itemStack, final String key, final short value) { + if (itemStack == null) return null; + if (itemStack.getType() == Material.AIR) return null; + + Object nmsItemStack = asNMSCopy(itemStack); + Object itemCompound = hasTag(nmsItemStack) ? getTag(nmsItemStack) : newNBTTagCompound(); + + setShort(itemCompound, key, value); + setTag(nmsItemStack, itemCompound); + + return asBukkitCopy(nmsItemStack); + } + public static ItemStack setInt(final ItemStack itemStack, final String key, final int value) { if (itemStack == null) return null; if (itemStack.getType() == Material.AIR) return null; @@ -176,6 +206,20 @@ private static void setBoolean(final Object itemCompound, final String key, fina } } + private static void setByte(final Object itemCompound, final String key, final byte value) { + try { + setByteMethod.invoke(itemCompound, key, value); + } catch (IllegalAccessException | InvocationTargetException ignored) { + } + } + + private static void setShort(final Object itemCompound, final String key, final short value) { + try { + setShortMethod.invoke(itemCompound, key, value); + } catch (IllegalAccessException | InvocationTargetException ignored) { + } + } + private static void setInt(final Object itemCompound, final String key, final int value) { try { setIntMethod.invoke(itemCompound, key, value); @@ -299,6 +343,8 @@ private static class VersionConstants { private final static String GET_STRING_METHOD_NAME = getStringMethodName(); private final static String SET_STRING_METHOD_NAME = setStringMethodName(); private final static String SET_BOOLEAN_METHOD_NAME = setBooleanMethodName(); + private final static String SET_BYTE_METHOD_NAME = setByteMethodName(); + private final static String SET_SHORT_METHOD_NAME = setShortMethodName(); private final static String SET_INTEGER_METHOD_NAME = setIntegerMethodName(); private final static String REMOVE_TAG_METHOD_NAME = removeTagMethodName(); private final static String HAS_TAG_METHOD_NAME = hasTagMethodName(); @@ -320,6 +366,16 @@ private static String setBooleanMethodName() { return "setBoolean"; } + private static String setByteMethodName() { + if (VersionHelper.HAS_OBFUSCATED_NAMES) return "a"; + return "setByte"; + } + + private static String setShortMethodName() { + if (VersionHelper.HAS_OBFUSCATED_NAMES) return "a"; + return "setShort"; + } + private static String setIntegerMethodName() { if (VersionHelper.HAS_OBFUSCATED_NAMES) return "a"; return "setInt"; diff --git a/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java b/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java index 366bb06a..45768932 100644 --- a/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java +++ b/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java @@ -16,6 +16,13 @@ import org.jetbrains.annotations.Nullable; public class PersistentMetaHandler { + + private final DeluxeMenus plugin; + + public PersistentMetaHandler(@NotNull final DeluxeMenus plugin) { + this.plugin = plugin; + } + /** * Get a {@link PersistentDataType} from its name. * @@ -50,7 +57,7 @@ public class PersistentMetaHandler { final String[] split = key.split(":", 2); namespacedKey = new NamespacedKey(split[0], split[1]); } else { - namespacedKey = new NamespacedKey(DeluxeMenus.getInstance(), key); + namespacedKey = new NamespacedKey(plugin, key); } return namespacedKey; @@ -75,7 +82,7 @@ public class PersistentMetaHandler { try { namespacedKey = getKey(key); } catch (final IllegalArgumentException e) { - DeluxeMenus.debug( + 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() @@ -92,7 +99,7 @@ public class PersistentMetaHandler { try { result = player.getPersistentDataContainer().get(namespacedKey, type); } catch (final IllegalArgumentException e) { - DeluxeMenus.debug( + 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) @@ -136,7 +143,7 @@ public boolean setMeta(@NotNull final Player player, @NotNull final String input try { namespacedKey = getKey(args[1]); } catch (final IllegalArgumentException e) { - DeluxeMenus.debug( + 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() diff --git a/src/main/java/com/extendedclip/deluxemenus/requirement/HasExpRequirement.java b/src/main/java/com/extendedclip/deluxemenus/requirement/HasExpRequirement.java index e5271485..275a0a10 100644 --- a/src/main/java/com/extendedclip/deluxemenus/requirement/HasExpRequirement.java +++ b/src/main/java/com/extendedclip/deluxemenus/requirement/HasExpRequirement.java @@ -3,14 +3,17 @@ import com.extendedclip.deluxemenus.DeluxeMenus; import com.extendedclip.deluxemenus.menu.MenuHolder; import com.extendedclip.deluxemenus.utils.ExpUtils; +import org.jetbrains.annotations.NotNull; public class HasExpRequirement extends Requirement { + private final DeluxeMenus plugin; private final boolean invert; private final boolean level; private final String amt; - public HasExpRequirement(String amt, boolean invert, boolean level) { + public HasExpRequirement(@NotNull final DeluxeMenus plugin, String amt, boolean invert, boolean level) { + this.plugin = plugin; this.amt = amt; this.invert = invert; this.level = level; @@ -23,7 +26,7 @@ public boolean evaluate(MenuHolder holder) { try { amount = Integer.parseInt(holder.setPlaceholdersAndArguments(amt)); } catch (final Exception exception) { - DeluxeMenus.printStacktrace( + plugin.printStacktrace( "Invalid amount found for has exp requirement: " + holder.setPlaceholdersAndArguments(amt), exception ); diff --git a/src/main/java/com/extendedclip/deluxemenus/requirement/HasItemRequirement.java b/src/main/java/com/extendedclip/deluxemenus/requirement/HasItemRequirement.java index d2967197..97b3d965 100644 --- a/src/main/java/com/extendedclip/deluxemenus/requirement/HasItemRequirement.java +++ b/src/main/java/com/extendedclip/deluxemenus/requirement/HasItemRequirement.java @@ -1,149 +1,162 @@ -package com.extendedclip.deluxemenus.requirement; - -import com.extendedclip.deluxemenus.DeluxeMenus; -import com.extendedclip.deluxemenus.menu.MenuHolder; -import com.extendedclip.deluxemenus.requirement.wrappers.ItemWrapper; -import com.extendedclip.deluxemenus.utils.StringUtils; -import com.extendedclip.deluxemenus.utils.VersionHelper; -import java.util.List; -import java.util.stream.Collectors; -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; - -public class HasItemRequirement extends Requirement { - - private final ItemWrapper wrapper; - private final boolean invert; - - public HasItemRequirement(ItemWrapper wrapper, boolean invert) { - this.wrapper = wrapper; - this.invert = invert; - } - - @Override - public boolean evaluate(MenuHolder holder) { - String materialName = holder.setPlaceholdersAndArguments(wrapper.getMaterial()).toUpperCase(); - Material material = DeluxeMenus.MATERIALS.get(materialName); - if (material == null) { - return invert; - } - - if (material == Material.AIR) return invert == (holder.getViewer().getInventory().firstEmpty() == -1); - - ItemStack[] armor = wrapper.checkArmor() ? holder.getViewer().getInventory().getArmorContents() : null; - ItemStack[] offHand = wrapper.checkOffhand() ? holder.getViewer().getInventory().getExtraContents() : null; - ItemStack[] inventory = holder.getViewer().getInventory().getStorageContents(); - - int total = 0; - for (ItemStack itemToCheck: inventory) { - if (!isRequiredItem(itemToCheck, holder, material)) continue; - total += itemToCheck.getAmount(); - } - - if (offHand != null) { - for (ItemStack itemToCheck: offHand) { - if (!isRequiredItem(itemToCheck, holder, material)) continue; - total += itemToCheck.getAmount(); - } - } - - if (armor != null) { - for (ItemStack itemToCheck: armor) { - if (!isRequiredItem(itemToCheck, holder, material)) continue; - total += itemToCheck.getAmount(); - } - } - - return invert == (total < wrapper.getAmount()); - } - - private boolean isRequiredItem(ItemStack itemToCheck, MenuHolder holder, Material material) { - if (itemToCheck == null || itemToCheck.getType() == Material.AIR) return false; - if (wrapper.getMaterial() != null && itemToCheck.getType() != material) return false; - if (wrapper.hasData() && itemToCheck.getDurability() != wrapper.getData()) return false; - - ItemMeta metaToCheck = itemToCheck.getItemMeta(); - if (wrapper.isStrict()) { - if (metaToCheck != null) { - if (VersionHelper.IS_CUSTOM_MODEL_DATA) { - if (metaToCheck.hasCustomModelData()) return false; - } - if (metaToCheck.hasLore()) return false; - return !metaToCheck.hasDisplayName(); - } - - } else { - if ((wrapper.getCustomData() != 0 || wrapper.getName() != null || wrapper.getLore() != null) && metaToCheck == null) - return false; - - if (wrapper.getCustomData() != 0) { - if (VersionHelper.IS_CUSTOM_MODEL_DATA) { - if (!metaToCheck.hasCustomModelData()) return false; - if (metaToCheck.getCustomModelData() != wrapper.getCustomData()) return false; - } - } - - if (wrapper.getName() != null) { - if (!metaToCheck.hasDisplayName()) return false; - - String name = StringUtils.color(holder.setPlaceholdersAndArguments(wrapper.getName())); - String nameToCheck = StringUtils.color(holder.setPlaceholdersAndArguments(metaToCheck.getDisplayName())); - - if (wrapper.checkNameContains() && wrapper.checkNameIgnoreCase()) { - if (!org.apache.commons.lang3.StringUtils.containsIgnoreCase(nameToCheck, name)) return false; - } - else if (wrapper.checkNameContains()) { - if (!nameToCheck.contains(name)) return false; - } - else if (wrapper.checkNameIgnoreCase()) { - if (!nameToCheck.equalsIgnoreCase(name)) return false; - } - else if (!nameToCheck.equals(name)) { - return false; - } - } - - if (wrapper.getLoreList() != null) { - List loreX = metaToCheck.getLore(); - if (loreX == null) return false; - - String lore = wrapper.getLoreList().stream().map(holder::setPlaceholdersAndArguments).map(StringUtils::color).collect(Collectors.joining("&&")); - String loreToCheck = loreX.stream().map(holder::setPlaceholdersAndArguments).map(StringUtils::color).collect(Collectors.joining("&&")); - - if (wrapper.checkLoreContains() && wrapper.checkLoreIgnoreCase()) { - if (!org.apache.commons.lang3.StringUtils.containsIgnoreCase(loreToCheck, lore)) return false; - } - else if (wrapper.checkLoreContains()) { - if (!loreToCheck.contains(lore)) return false; - } - else if (wrapper.checkLoreIgnoreCase()) { - if (!loreToCheck.equalsIgnoreCase(lore)) return false; - } - else if (!loreToCheck.equals(lore)) { - return false; - } - } - - if (wrapper.getLore() != null) { - List loreX = metaToCheck.getLore(); - if (loreX == null) return false; - - String lore = StringUtils.color(holder.setPlaceholdersAndArguments(wrapper.getLore())); - String loreToCheck = loreX.stream().map(holder::setPlaceholdersAndArguments).map(StringUtils::color).collect(Collectors.joining("&&")); - - if (wrapper.checkLoreContains() && wrapper.checkLoreIgnoreCase()) { - return org.apache.commons.lang3.StringUtils.containsIgnoreCase(loreToCheck, lore); - } - else if (wrapper.checkLoreContains()) { - return loreToCheck.contains(lore); - } - else if (wrapper.checkLoreIgnoreCase()) { - return loreToCheck.equalsIgnoreCase(lore); - } - else return loreToCheck.equals(lore); - } - } - return true; - } -} +package com.extendedclip.deluxemenus.requirement; + +import com.extendedclip.deluxemenus.DeluxeMenus; +import com.extendedclip.deluxemenus.hooks.ItemHook; +import com.extendedclip.deluxemenus.menu.MenuHolder; +import com.extendedclip.deluxemenus.requirement.wrappers.ItemWrapper; +import com.extendedclip.deluxemenus.utils.StringUtils; +import com.extendedclip.deluxemenus.utils.VersionHelper; +import java.util.List; +import java.util.stream.Collectors; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +public class HasItemRequirement extends Requirement { + + private final DeluxeMenus plugin; + private final ItemWrapper wrapper; + private final boolean invert; + + public HasItemRequirement(final DeluxeMenus plugin, final ItemWrapper wrapper, final boolean invert) { + this.plugin = plugin; + this.wrapper = wrapper; + this.invert = invert; + } + + @Override + public boolean evaluate(MenuHolder holder) { + String materialName = holder.setPlaceholdersAndArguments(wrapper.getMaterial()); + Material material = DeluxeMenus.MATERIALS.get(materialName.toUpperCase()); + ItemHook pluginHook = null; + if (material == null) { + pluginHook = plugin.getItemHooks().values() + .stream() + .filter(x -> materialName.toLowerCase().startsWith(x.getPrefix())) + .findFirst() + .orElse(null); + if (pluginHook == null) return invert; + } + + if (material == Material.AIR) return invert == (holder.getViewer().getInventory().firstEmpty() == -1); + + ItemStack[] armor = wrapper.checkArmor() ? holder.getViewer().getInventory().getArmorContents() : null; + ItemStack[] offHand = wrapper.checkOffhand() ? holder.getViewer().getInventory().getExtraContents() : null; + ItemStack[] inventory = holder.getViewer().getInventory().getStorageContents(); + + int total = 0; + for (ItemStack itemToCheck: inventory) { + if (!isRequiredItem(itemToCheck, holder, material, pluginHook)) continue; + total += itemToCheck.getAmount(); + } + + if (offHand != null) { + for (ItemStack itemToCheck: offHand) { + if (!isRequiredItem(itemToCheck, holder, material, pluginHook)) continue; + total += itemToCheck.getAmount(); + } + } + + if (armor != null) { + for (ItemStack itemToCheck: armor) { + if (!isRequiredItem(itemToCheck, holder, material, pluginHook)) continue; + total += itemToCheck.getAmount(); + } + } + + return invert == (total < wrapper.getAmount()); + } + + private boolean isRequiredItem(ItemStack itemToCheck, MenuHolder holder, Material material, ItemHook pluginHook) { + if (itemToCheck == null || itemToCheck.getType() == Material.AIR) return false; + + if (pluginHook != null) { + if (!pluginHook.itemMatchesIdentifiers(itemToCheck, holder.setPlaceholdersAndArguments(wrapper.getMaterial().substring(pluginHook.getPrefix().length())))) return false; + } + else if (wrapper.getMaterial() != null && itemToCheck.getType() != material) return false; + if (wrapper.hasData() && itemToCheck.getDurability() != wrapper.getData()) return false; + + ItemMeta metaToCheck = itemToCheck.getItemMeta(); + if (wrapper.isStrict()) { + if (metaToCheck != null) { + if (VersionHelper.IS_CUSTOM_MODEL_DATA) { + if (metaToCheck.hasCustomModelData()) return false; + } + if (metaToCheck.hasLore()) return false; + return !metaToCheck.hasDisplayName(); + } + + } else { + if ((wrapper.getCustomData() != 0 || wrapper.getName() != null || wrapper.getLore() != null) && metaToCheck == null) + return false; + + if (wrapper.getCustomData() != 0) { + if (VersionHelper.IS_CUSTOM_MODEL_DATA) { + if (!metaToCheck.hasCustomModelData()) return false; + if (metaToCheck.getCustomModelData() != wrapper.getCustomData()) return false; + } + } + + if (wrapper.getName() != null) { + if (!metaToCheck.hasDisplayName()) return false; + + String name = StringUtils.color(holder.setPlaceholdersAndArguments(wrapper.getName())); + String nameToCheck = StringUtils.color(holder.setPlaceholdersAndArguments(metaToCheck.getDisplayName())); + + if (wrapper.checkNameContains() && wrapper.checkNameIgnoreCase()) { + if (!org.apache.commons.lang3.StringUtils.containsIgnoreCase(nameToCheck, name)) return false; + } + else if (wrapper.checkNameContains()) { + if (!nameToCheck.contains(name)) return false; + } + else if (wrapper.checkNameIgnoreCase()) { + if (!nameToCheck.equalsIgnoreCase(name)) return false; + } + else if (!nameToCheck.equals(name)) { + return false; + } + } + + if (wrapper.getLoreList() != null) { + List loreX = metaToCheck.getLore(); + if (loreX == null) return false; + + String lore = wrapper.getLoreList().stream().map(holder::setPlaceholdersAndArguments).map(StringUtils::color).collect(Collectors.joining("&&")); + String loreToCheck = loreX.stream().map(holder::setPlaceholdersAndArguments).map(StringUtils::color).collect(Collectors.joining("&&")); + + if (wrapper.checkLoreContains() && wrapper.checkLoreIgnoreCase()) { + if (!org.apache.commons.lang3.StringUtils.containsIgnoreCase(loreToCheck, lore)) return false; + } + else if (wrapper.checkLoreContains()) { + if (!loreToCheck.contains(lore)) return false; + } + else if (wrapper.checkLoreIgnoreCase()) { + if (!loreToCheck.equalsIgnoreCase(lore)) return false; + } + else if (!loreToCheck.equals(lore)) { + return false; + } + } + + if (wrapper.getLore() != null) { + List loreX = metaToCheck.getLore(); + if (loreX == null) return false; + + String lore = StringUtils.color(holder.setPlaceholdersAndArguments(wrapper.getLore())); + String loreToCheck = loreX.stream().map(holder::setPlaceholdersAndArguments).map(StringUtils::color).collect(Collectors.joining("&&")); + + if (wrapper.checkLoreContains() && wrapper.checkLoreIgnoreCase()) { + return org.apache.commons.lang3.StringUtils.containsIgnoreCase(loreToCheck, lore); + } + else if (wrapper.checkLoreContains()) { + return loreToCheck.contains(lore); + } + else if (wrapper.checkLoreIgnoreCase()) { + return loreToCheck.equalsIgnoreCase(lore); + } + else return loreToCheck.equals(lore); + } + } + return true; + } +} diff --git a/src/main/java/com/extendedclip/deluxemenus/requirement/HasMetaRequirement.java b/src/main/java/com/extendedclip/deluxemenus/requirement/HasMetaRequirement.java index af9d9c88..c8f1108b 100644 --- a/src/main/java/com/extendedclip/deluxemenus/requirement/HasMetaRequirement.java +++ b/src/main/java/com/extendedclip/deluxemenus/requirement/HasMetaRequirement.java @@ -3,15 +3,18 @@ import com.extendedclip.deluxemenus.DeluxeMenus; import com.extendedclip.deluxemenus.menu.MenuHolder; import org.bukkit.entity.Player; +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; - public HasMetaRequirement(String key, String type, String value, 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; @@ -25,7 +28,7 @@ public boolean evaluate(MenuHolder holder) { return false; } String parsedKey = holder.setPlaceholdersAndArguments(key); - String metaVal = DeluxeMenus.getInstance().getPersistentMetaHandler() + String metaVal = plugin.getPersistentMetaHandler() .getMeta(player, parsedKey, type, null); if (metaVal == null) { return invert; diff --git a/src/main/java/com/extendedclip/deluxemenus/requirement/HasMoneyRequirement.java b/src/main/java/com/extendedclip/deluxemenus/requirement/HasMoneyRequirement.java index 9aae814b..81114b64 100644 --- a/src/main/java/com/extendedclip/deluxemenus/requirement/HasMoneyRequirement.java +++ b/src/main/java/com/extendedclip/deluxemenus/requirement/HasMoneyRequirement.java @@ -2,14 +2,17 @@ import com.extendedclip.deluxemenus.DeluxeMenus; import com.extendedclip.deluxemenus.menu.MenuHolder; +import org.jetbrains.annotations.NotNull; public class HasMoneyRequirement extends Requirement { + private final DeluxeMenus plugin; private final boolean invert; private final String placeholder; private double amount; - public HasMoneyRequirement(double amount, boolean invert, String placeholder) { + public HasMoneyRequirement(@NotNull final DeluxeMenus plugin, double amount, boolean invert, String placeholder) { + this.plugin = plugin; this.amount = amount; this.invert = invert; this.placeholder = placeholder; @@ -17,7 +20,7 @@ public HasMoneyRequirement(double amount, boolean invert, String placeholder) { @Override public boolean evaluate(MenuHolder holder) { - if (getInstance().getVault() == null) { + if (plugin.getVault() == null) { return false; } @@ -26,16 +29,16 @@ public boolean evaluate(MenuHolder holder) { String expected = holder.setPlaceholdersAndArguments(placeholder); amount = Double.parseDouble(expected); } catch (final NumberFormatException exception) { - DeluxeMenus.printStacktrace( + plugin.printStacktrace( "Invalid amount found for has money requirement: " + holder.setPlaceholdersAndArguments(placeholder), exception ); } } if (invert) { - return !getInstance().getVault().hasEnough(holder.getViewer(), amount); + return !plugin.getVault().hasEnough(holder.getViewer(), amount); } else { - return getInstance().getVault().hasEnough(holder.getViewer(), amount); + return plugin.getVault().hasEnough(holder.getViewer(), amount); } } } diff --git a/src/main/java/com/extendedclip/deluxemenus/requirement/HasPermissionsRequirement.java b/src/main/java/com/extendedclip/deluxemenus/requirement/HasPermissionsRequirement.java new file mode 100644 index 00000000..05fa0742 --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/requirement/HasPermissionsRequirement.java @@ -0,0 +1,32 @@ +package com.extendedclip.deluxemenus.requirement; + +import com.extendedclip.deluxemenus.menu.MenuHolder; + +import java.util.List; + +public class HasPermissionsRequirement extends Requirement { + + private final List permissions; + private final int minimum; + private final boolean invert; + + public HasPermissionsRequirement(List permissions, int minimum, boolean invert) { + this.permissions = permissions; + this.minimum = minimum; + this.invert = invert; + } + + @Override + public boolean evaluate(MenuHolder holder) { + final int count = permissions.stream() + .map(holder::setPlaceholdersAndArguments) + .map(holder.getViewer()::hasPermission) + .mapToInt(hasPermission -> hasPermission ? 1 : 0) + .sum(); + return invert + ? count + minimum <= permissions.size() + : count >= minimum; + } + + +} diff --git a/src/main/java/com/extendedclip/deluxemenus/requirement/InputResultRequirement.java b/src/main/java/com/extendedclip/deluxemenus/requirement/InputResultRequirement.java index 3a372794..29ef2b93 100644 --- a/src/main/java/com/extendedclip/deluxemenus/requirement/InputResultRequirement.java +++ b/src/main/java/com/extendedclip/deluxemenus/requirement/InputResultRequirement.java @@ -1,6 +1,5 @@ package com.extendedclip.deluxemenus.requirement; -import com.extendedclip.deluxemenus.DeluxeMenus; import com.extendedclip.deluxemenus.menu.MenuHolder; public class InputResultRequirement extends Requirement { @@ -44,7 +43,7 @@ public boolean evaluate(MenuHolder holder) { try { in = Double.parseDouble(parsedInput); } catch (final NumberFormatException exception) { - DeluxeMenus.printStacktrace( + holder.getPlugin().printStacktrace( "Input for comparison requirement is an invalid number: " + parsedInput, exception ); @@ -54,7 +53,7 @@ public boolean evaluate(MenuHolder holder) { try { res = Double.parseDouble(parsedResult); } catch (final NumberFormatException exception) { - DeluxeMenus.printStacktrace( + holder.getPlugin().printStacktrace( "Output for comparison requirement is an invalid number: " + parsedResult, exception ); diff --git a/src/main/java/com/extendedclip/deluxemenus/requirement/IsObjectRequirement.java b/src/main/java/com/extendedclip/deluxemenus/requirement/IsObjectRequirement.java index da1d2f8b..467908fa 100644 --- a/src/main/java/com/extendedclip/deluxemenus/requirement/IsObjectRequirement.java +++ b/src/main/java/com/extendedclip/deluxemenus/requirement/IsObjectRequirement.java @@ -1,6 +1,5 @@ package com.extendedclip.deluxemenus.requirement; -import com.extendedclip.deluxemenus.DeluxeMenus; import com.extendedclip.deluxemenus.menu.MenuHolder; import com.extendedclip.deluxemenus.utils.DebugLevel; import com.google.common.primitives.Doubles; @@ -44,7 +43,7 @@ public boolean evaluate(MenuHolder holder) { return false; } default: - DeluxeMenus.debug(DebugLevel.HIGHEST, Level.INFO, "Invalid object: " + object + " in \"is object\" check."); + holder.getPlugin().debug(DebugLevel.HIGHEST, Level.INFO, "Invalid object: " + object + " in \"is object\" check."); return false; } } diff --git a/src/main/java/com/extendedclip/deluxemenus/requirement/JavascriptRequirement.java b/src/main/java/com/extendedclip/deluxemenus/requirement/JavascriptRequirement.java index dc0b06b4..56a67760 100644 --- a/src/main/java/com/extendedclip/deluxemenus/requirement/JavascriptRequirement.java +++ b/src/main/java/com/extendedclip/deluxemenus/requirement/JavascriptRequirement.java @@ -11,6 +11,7 @@ import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.ServicePriority; import org.bukkit.plugin.ServicesManager; +import org.jetbrains.annotations.NotNull; import org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory; public class JavascriptRequirement extends Requirement { @@ -18,9 +19,11 @@ public class JavascriptRequirement extends Requirement { private final ScriptEngineFactory factory = new NashornScriptEngineFactory(); private final ServicesManager manager = Bukkit.getServer().getServicesManager(); private static ScriptEngineManager engine; + private final DeluxeMenus plugin; private final String expression; - public JavascriptRequirement(String expression) { + public JavascriptRequirement(final @NotNull DeluxeMenus plugin, String expression) { + this.plugin = plugin; this.expression = expression; if (engine == null) { if (manager.isProvidedFor(ScriptEngineManager.class)) { @@ -28,7 +31,7 @@ public JavascriptRequirement(String expression) { engine = (ScriptEngineManager) provider.getProvider(); } else { engine = new ScriptEngineManager(); - manager.register(ScriptEngineManager.class, engine, DeluxeMenus.getInstance(), ServicePriority.Highest); + manager.register(ScriptEngineManager.class, engine, plugin, ServicePriority.Highest); } engine.registerEngineName("JavaScript", factory); engine.put("BukkitServer", Bukkit.getServer()); @@ -45,7 +48,7 @@ public boolean evaluate(MenuHolder holder) { Object result = engine.getEngineByName("JavaScript").eval(exp); if (!(result instanceof Boolean)) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Requirement javascript <" + this.expression + "> is invalid and does not return a boolean!" @@ -56,13 +59,13 @@ public boolean evaluate(MenuHolder holder) { return (boolean) result; } catch (final ScriptException | NullPointerException exception) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Error in requirement javascript syntax - " + this.expression ); - DeluxeMenus.printStacktrace( + plugin.printStacktrace( "Error in requirement javascript syntax - " + this.expression, exception ); diff --git a/src/main/java/com/extendedclip/deluxemenus/requirement/Requirement.java b/src/main/java/com/extendedclip/deluxemenus/requirement/Requirement.java index 7c49198a..0f065ff4 100644 --- a/src/main/java/com/extendedclip/deluxemenus/requirement/Requirement.java +++ b/src/main/java/com/extendedclip/deluxemenus/requirement/Requirement.java @@ -1,6 +1,5 @@ package com.extendedclip.deluxemenus.requirement; -import com.extendedclip.deluxemenus.DeluxeMenus; import com.extendedclip.deluxemenus.action.ClickHandler; import com.extendedclip.deluxemenus.menu.MenuHolder; @@ -40,10 +39,6 @@ public void setOptional(boolean optional) { this.optional = optional; } - public DeluxeMenus getInstance() { - return DeluxeMenus.getInstance(); - } - public ClickHandler getSuccessHandler() { return successHandler; } diff --git a/src/main/java/com/extendedclip/deluxemenus/requirement/RequirementType.java b/src/main/java/com/extendedclip/deluxemenus/requirement/RequirementType.java index aeb15ae6..ef39bcc2 100644 --- a/src/main/java/com/extendedclip/deluxemenus/requirement/RequirementType.java +++ b/src/main/java/com/extendedclip/deluxemenus/requirement/RequirementType.java @@ -41,6 +41,12 @@ public enum RequirementType { Arrays.asList("!has permission", "!has perm", "!haspermission", "!hasperm", "!perm"), "Checks if a player does not have a specific permission", Collections.singletonList("permission")), + HAS_PERMISSIONS(Arrays.asList("has permissions", "has perms", "haspermissions", "hasperms", "perms"), + "Checks if a player has a set amount of permissions", Collections.singletonList("permissions")), + DOES_NOT_HAVE_PERMISSIONS( + Arrays.asList("!has permissions", "!has perms", "!haspermissions", "!hasperms", "!perms"), + "Checks if a player does not have a set amount of permission", + Arrays.asList("permissions", "minimum")), STRING_CONTAINS(Arrays.asList("string contains", "stringcontains", "contains"), "Checks if a string contains another string", Arrays.asList("input", "output")), STRING_DOES_NOT_CONTAIN(Arrays.asList("!string contains", "!stringcontains", "!contains"), diff --git a/src/main/java/com/extendedclip/deluxemenus/updatechecker/UpdateChecker.java b/src/main/java/com/extendedclip/deluxemenus/updatechecker/UpdateChecker.java index b7dbc97a..30dfdc2e 100644 --- a/src/main/java/com/extendedclip/deluxemenus/updatechecker/UpdateChecker.java +++ b/src/main/java/com/extendedclip/deluxemenus/updatechecker/UpdateChecker.java @@ -1,6 +1,7 @@ package com.extendedclip.deluxemenus.updatechecker; import com.extendedclip.deluxemenus.DeluxeMenus; +import com.extendedclip.deluxemenus.listener.Listener; import com.extendedclip.deluxemenus.utils.DebugLevel; import com.extendedclip.deluxemenus.utils.Messages; import java.io.BufferedReader; @@ -13,15 +14,13 @@ import io.github.projectunified.minelib.scheduler.async.AsyncScheduler; import io.github.projectunified.minelib.scheduler.global.GlobalScheduler; import net.kyori.adventure.text.TextReplacementConfig; -import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; import org.jetbrains.annotations.NotNull; -public class UpdateChecker implements Listener { +public class UpdateChecker extends Listener { private static final TextReplacementConfig.Builder LATEST_VERSION_REPLACER_BUILDER = TextReplacementConfig.builder().matchLiteral(""); @@ -29,12 +28,11 @@ public class UpdateChecker implements Listener { = TextReplacementConfig.builder().matchLiteral(""); final int resourceId = 11734; - private final DeluxeMenus plugin; private String latestVersion = null; private boolean updateAvailable = false; public UpdateChecker(final @NotNull DeluxeMenus instance) { - plugin = instance; + super(instance); AsyncScheduler.get(plugin).run(() -> { if (check()) { @@ -43,10 +41,6 @@ public UpdateChecker(final @NotNull DeluxeMenus instance) { }); } - private void register() { - Bukkit.getPluginManager().registerEvents(this, plugin); - } - @EventHandler(priority = EventPriority.MONITOR) public void onJoin(final @NotNull PlayerJoinEvent event) { Player player = event.getPlayer(); @@ -77,7 +71,7 @@ private String getSpigotVersion() { connection.setRequestMethod("GET"); return new BufferedReader(new InputStreamReader(connection.getInputStream())).readLine(); } catch (Exception ex) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGH, Level.INFO, "Failed to check for update on spigot!" diff --git a/src/main/java/com/extendedclip/deluxemenus/utils/AdventureUtils.java b/src/main/java/com/extendedclip/deluxemenus/utils/AdventureUtils.java index dff45571..c4773ff9 100644 --- a/src/main/java/com/extendedclip/deluxemenus/utils/AdventureUtils.java +++ b/src/main/java/com/extendedclip/deluxemenus/utils/AdventureUtils.java @@ -4,6 +4,7 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; public final class AdventureUtils { private final static GsonComponentSerializer gson = GsonComponentSerializer.gson(); @@ -12,8 +13,8 @@ private AdventureUtils() { throw new AssertionError("Util classes should not be initialized"); } - public static void sendJson(CommandSender sender, String json) { - DeluxeMenus.getInstance().adventure().sender(sender).sendMessage(fromJson(json)); + public static void sendJson(@NotNull final DeluxeMenus plugin, CommandSender sender, String json) { + plugin.audiences().sender(sender).sendMessage(fromJson(json)); } public static Component fromJson(String json) { diff --git a/src/main/java/com/extendedclip/deluxemenus/utils/DumpUtils.java b/src/main/java/com/extendedclip/deluxemenus/utils/DumpUtils.java index 53a037e1..1b70d88b 100644 --- a/src/main/java/com/extendedclip/deluxemenus/utils/DumpUtils.java +++ b/src/main/java/com/extendedclip/deluxemenus/utils/DumpUtils.java @@ -119,7 +119,7 @@ private static boolean createMenuDump( final var guiMenus = config.getConfigurationSection("gui_menus"); if (guiMenus == null) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "No gui_menus section found in config.yml!" @@ -131,7 +131,7 @@ private static boolean createMenuDump( final Set keys = guiMenus.getKeys(false); if (!keys.contains(menuName)) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "File for the " + menuName + " menu is not declared in config.yml!" @@ -143,7 +143,7 @@ private static boolean createMenuDump( final String fileName = plugin.getConfig().getString("gui_menus." + menuName + ".file"); if (fileName == null) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "File for the " + menuName + " menu is not declared in config.yml!" @@ -153,7 +153,7 @@ private static boolean createMenuDump( } if (!fileName.endsWith(".yml")) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "File for the " + menuName + " menu is not declared in config.yml!" @@ -173,7 +173,7 @@ private static boolean createMenuDump( final var menuFile = new File(plugin.getConfiguration().getMenuDirector(), fileName); if (!menuFile.exists() || !menuFile.isFile()) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Could not find the " + fileName + " file in " + @@ -188,13 +188,13 @@ private static boolean createMenuDump( builder.append(line).append(System.lineSeparator()) ); } catch (final IOException exception) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Something went wrong while reading the the file: " + fileName ); - DeluxeMenus.printStacktrace( + plugin.printStacktrace( "Something went wrong while reading the the file: " + fileName, exception ); @@ -211,7 +211,7 @@ private static boolean createConfigDump( final File configFile = new File(plugin.getDataFolder(), "config.yml"); if (!configFile.exists() || !configFile.isFile()) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Could not find the " + configFile + " file in " + plugin.getDataFolder().getPath() @@ -230,13 +230,13 @@ private static boolean createConfigDump( builder.append(line).append(System.lineSeparator()) ); } catch (final IOException exception) { - DeluxeMenus.debug( + plugin.debug( DebugLevel.HIGHEST, Level.WARNING, "Something went wrong while reading the the file: " + configFile ); - DeluxeMenus.printStacktrace( + plugin.printStacktrace( "Something went wrong while reading the the file: " + configFile, exception ); diff --git a/src/main/java/com/extendedclip/deluxemenus/utils/SkullUtils.java b/src/main/java/com/extendedclip/deluxemenus/utils/SkullUtils.java index de1167f9..6795a62c 100644 --- a/src/main/java/com/extendedclip/deluxemenus/utils/SkullUtils.java +++ b/src/main/java/com/extendedclip/deluxemenus/utils/SkullUtils.java @@ -6,12 +6,6 @@ import com.google.gson.JsonObject; import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.Property; -import java.lang.reflect.Field; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Base64; -import java.util.UUID; - import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.inventory.ItemStack; @@ -21,166 +15,222 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.lang.reflect.Field; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Base64; +import java.util.UUID; + public class SkullUtils { - private static final Gson GSON = new Gson(); - - /** - * Helper method to get the encoded bytes for a full MC Texture - * - * @param url the url of the texture - * @return fully encoded texture url - */ - @NotNull - public static String getEncoded(@NotNull final String url) { - final byte[] encodedData = Base64.getEncoder().encode(String - .format("{textures:{SKIN:{url:\"%s\"}}}", "https://textures.minecraft.net/texture/" + url) - .getBytes()); - return new String(encodedData); - } - - /** - * Get the skull from a base64 encoded texture url - * - * @param base64Url base64 encoded url to use - * @return skull - */ - @NotNull - public static ItemStack getSkullByBase64EncodedTextureUrl(@NotNull final String base64Url) { - final ItemStack head = DeluxeMenus.getInstance().getHead().clone(); - if (base64Url.isEmpty()) { - return head; + private static final Gson GSON = new Gson(); + + /** + * Helper method to get the encoded bytes for a full MC Texture + * + * @param url the url of the texture + * @return fully encoded texture url + */ + @NotNull + public static String getEncoded(@NotNull final String url) { + final byte[] encodedData = Base64.getEncoder().encode(String + .format("{textures:{SKIN:{url:\"%s\"}}}", "https://textures.minecraft.net/texture/" + url) + .getBytes()); + return new String(encodedData); } - final SkullMeta headMeta = (SkullMeta) head.getItemMeta(); - if (headMeta == null) { - return head; + /** + * Get the skull from a base64 encoded texture url + * + * @param base64Url base64 encoded url to use + * @return skull + */ + @NotNull + public static ItemStack getSkullByBase64EncodedTextureUrl(@NotNull final DeluxeMenus plugin, @NotNull final String base64Url) { + final ItemStack head = plugin.getHead().clone(); + if (base64Url.isEmpty()) { + return head; + } + + final SkullMeta headMeta = (SkullMeta) head.getItemMeta(); + if (headMeta == null) { + return head; + } + + if (VersionHelper.HAS_PLAYER_PROFILES) { + final PlayerProfile profile = getPlayerProfile(plugin, base64Url); + headMeta.setOwnerProfile(profile); + head.setItemMeta(headMeta); + return head; + } + + final GameProfile profile = getGameProfile(base64Url); + final Field profileField; + try { + profileField = headMeta.getClass().getDeclaredField("profile"); + profileField.setAccessible(true); + profileField.set(headMeta, profile); + } catch (final NoSuchFieldException | IllegalArgumentException | IllegalAccessException exception) { + plugin.printStacktrace( + "Failed to get head item from base64 texture url", + exception + ); + } + head.setItemMeta(headMeta); + return head; } - if (VersionHelper.HAS_PLAYER_PROFILES) { - final PlayerProfile profile = getPlayerProfile(base64Url); - headMeta.setOwnerProfile(profile); - head.setItemMeta(headMeta); - return head; + public static String getTextureFromSkull(final DeluxeMenus plugin, ItemStack item) { + if (!(item.getItemMeta() instanceof SkullMeta)) return null; + SkullMeta meta = (SkullMeta) item.getItemMeta(); + + if (VersionHelper.HAS_PLAYER_PROFILES) { + PlayerProfile profile = meta.getOwnerProfile(); + if (profile == null) return null; + + URL url = profile.getTextures().getSkin(); + if (url == null) return null; + + return url.toString().substring("https://textures.minecraft.net/texture/".length() - 1); + } + + GameProfile profile; + try { + final Field profileField = meta.getClass().getDeclaredField("profile"); + profileField.setAccessible(true); + profile = (GameProfile) profileField.get(meta); + } catch (final NoSuchFieldException | IllegalArgumentException | IllegalAccessException exception) { + plugin.printStacktrace( + "Failed to get base64 texture url from head item", + exception + ); + return null; + } + + for (Property property : profile.getProperties().get("textures")) { + if (property.getName().equals("textures")) { + return decodeSkinUrl(property.getValue()); + } + } + return null; } - final GameProfile profile = getGameProfile(base64Url); - final Field profileField; - try { - profileField = headMeta.getClass().getDeclaredField("profile"); - profileField.setAccessible(true); - profileField.set(headMeta, profile); - } catch (final NoSuchFieldException | IllegalArgumentException | IllegalAccessException exception) { - DeluxeMenus.printStacktrace( - "Failed to get head item from base64 texture url", - exception - ); - } - head.setItemMeta(headMeta); - return head; - } - - - /** - * Get the skull from a player name - * @param playerName the player name to use - * @return skull - */ - @NotNull - public static ItemStack getSkullByName(@NotNull final String playerName) { - final ItemStack head = DeluxeMenus.getInstance().getHead().clone(); - if (playerName.isEmpty()) { - return head; - } - final SkullMeta headMeta = (SkullMeta) head.getItemMeta(); - if (headMeta == null) { - return head; + /** + * Get the skull from a player name + * + * @param playerName the player name to use + * @return skull + */ + @NotNull + public static ItemStack getSkullByName(@NotNull final DeluxeMenus plugin, @NotNull final String playerName) { + final ItemStack head = plugin.getHead().clone(); + if (playerName.isEmpty()) { + return head; + } + + final SkullMeta headMeta = (SkullMeta) head.getItemMeta(); + if (headMeta == null) { + return head; + } + + final OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(playerName); + + if (!VersionHelper.IS_SKULL_OWNER_LEGACY) { + headMeta.setOwningPlayer(offlinePlayer); + } else { + headMeta.setOwner(offlinePlayer.getName()); + } + + head.setItemMeta(headMeta); + return head; } - final OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(playerName); + public static String getSkullOwner(ItemStack skull) { + if (skull == null || !(skull.getItemMeta() instanceof SkullMeta)) return null; + SkullMeta meta = (SkullMeta) skull.getItemMeta(); - if (!VersionHelper.IS_SKULL_OWNER_LEGACY) { - headMeta.setOwningPlayer(offlinePlayer); - } else { - headMeta.setOwner(offlinePlayer.getName()); - } + if (!VersionHelper.IS_SKULL_OWNER_LEGACY) { + if (meta.getOwningPlayer() == null) return null; + return meta.getOwningPlayer().getName(); + } - head.setItemMeta(headMeta); - return head; - } - - /** - * Create a game profile object - * - * @param base64Url the base64 encoded texture url to use - * @return game profile - */ - @NotNull - private static GameProfile getGameProfile(@NotNull final String base64Url) { - GameProfile profile = new GameProfile(UUID.randomUUID(), ""); - profile.getProperties().put("textures", new Property("textures", base64Url)); - return profile; - } - - /** - * Create a player profile object - * Player profile was introduced in 1.18.1+ - * @param base64Url the base64 encoded texture URL to use - * @return player profile - */ - @NotNull - private static PlayerProfile getPlayerProfile(@NotNull final String base64Url) { - final PlayerProfile profile = Bukkit.createPlayerProfile(UUID.randomUUID()); - - final String decodedBase64 = decodeSkinUrl(base64Url); - if (decodedBase64 == null) { - return profile; + return meta.getOwner(); } - final PlayerTextures textures = profile.getTextures(); - - try { - textures.setSkin(new URL(decodedBase64)); - } catch (final MalformedURLException exception) { - DeluxeMenus.printStacktrace("Something went horribly wrong trying to create basehead URL", exception); + /** + * Create a game profile object + * + * @param base64Url the base64 encoded texture url to use + * @return game profile + */ + @NotNull + private static GameProfile getGameProfile(@NotNull final String base64Url) { + GameProfile profile = new GameProfile(UUID.randomUUID(), ""); + profile.getProperties().put("textures", new Property("textures", base64Url)); + return profile; } - profile.setTextures(textures); - return profile; - } - - /** - * Decode a base64 string and extract the url of the skin. Example: - *
- * - Base64: {@code eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZGNlYjE3MDhkNTQwNGVmMzI2MTAzZTdiNjA1NTljOTE3OGYzZGNlNzI5MDA3YWM5YTBiNDk4YmRlYmU0NjEwNyJ9fX0=} - *
- * - JSON: {@code {"textures":{"SKIN":{"url":"http://textures.minecraft.net/texture/dceb1708d5404ef326103e7b60559c9178f3dce729007ac9a0b498bdebe46107"}}}} - *
- * - Result: {@code http://textures.minecraft.net/texture/dceb1708d5404ef326103e7b60559c9178f3dce729007ac9a0b498bdebe46107} - *
- * Credit: iGabyTM - * @param base64Texture the texture - * @return the url of the texture if found, otherwise {@code null} - */ - @Nullable - private static String decodeSkinUrl(@NotNull final String base64Texture) { - final String decoded = new String(Base64.getDecoder().decode(base64Texture)); - final JsonObject object = GSON.fromJson(decoded, JsonObject.class); - - final JsonElement textures = object.get("textures"); - - if (textures == null) { - return null; + /** + * Create a player profile object + * Player profile was introduced in 1.18.1+ + * + * @param base64Url the base64 encoded texture URL to use + * @return player profile + */ + @NotNull + private static PlayerProfile getPlayerProfile(@NotNull final DeluxeMenus plugin, @NotNull final String base64Url) { + final PlayerProfile profile = Bukkit.createPlayerProfile(UUID.randomUUID()); + + final String decodedBase64 = decodeSkinUrl(base64Url); + if (decodedBase64 == null) { + return profile; + } + + final PlayerTextures textures = profile.getTextures(); + + try { + textures.setSkin(new URL(decodedBase64)); + } catch (final MalformedURLException exception) { + plugin.printStacktrace("Something went horribly wrong trying to create basehead URL", exception); + } + + profile.setTextures(textures); + return profile; } - final JsonElement skin = textures.getAsJsonObject().get("SKIN"); - - if (skin == null) { - return null; + /** + * Decode a base64 string and extract the url of the skin. Example: + *
+ * - Base64: {@code eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZGNlYjE3MDhkNTQwNGVmMzI2MTAzZTdiNjA1NTljOTE3OGYzZGNlNzI5MDA3YWM5YTBiNDk4YmRlYmU0NjEwNyJ9fX0=} + *
+ * - JSON: {@code {"textures":{"SKIN":{"url":"http://textures.minecraft.net/texture/dceb1708d5404ef326103e7b60559c9178f3dce729007ac9a0b498bdebe46107"}}}} + *
+ * - Result: {@code http://textures.minecraft.net/texture/dceb1708d5404ef326103e7b60559c9178f3dce729007ac9a0b498bdebe46107} + *
+ * Credit: iGabyTM + * + * @param base64Texture the texture + * @return the url of the texture if found, otherwise {@code null} + */ + @Nullable + public static String decodeSkinUrl(@NotNull final String base64Texture) { + final String decoded = new String(Base64.getDecoder().decode(base64Texture)); + final JsonObject object = GSON.fromJson(decoded, JsonObject.class); + + final JsonElement textures = object.get("textures"); + + if (textures == null) { + return null; + } + + final JsonElement skin = textures.getAsJsonObject().get("SKIN"); + + if (skin == null) { + return null; + } + + final JsonElement url = skin.getAsJsonObject().get("url"); + return url == null ? null : url.getAsString(); } - - final JsonElement url = skin.getAsJsonObject().get("url"); - return url == null ? null : url.getAsString(); - } } diff --git a/src/main/java/com/extendedclip/deluxemenus/utils/SoundUtils.java b/src/main/java/com/extendedclip/deluxemenus/utils/SoundUtils.java new file mode 100644 index 00000000..3ffc4842 --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/utils/SoundUtils.java @@ -0,0 +1,21 @@ +package com.extendedclip.deluxemenus.utils; + +import org.bukkit.Sound; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class SoundUtils { + + public static Sound getSound(String name) { + try { + // As of Minecraft 1.21.3, the org.bukkit.Sound class type changed from Enum to Interface. + // This fixes java.lang.IncompatibleClassChangeError when trying to use versions prior to 1.21.3. + Method valueOfMethod = Class.forName("org.bukkit.Sound").getMethod("valueOf", String.class); + return (Sound) valueOfMethod.invoke(null, name); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + // Use the Sound#valueOf method if Reflection fails. + return Sound.valueOf(name); + } + } +} diff --git a/src/main/java/com/extendedclip/deluxemenus/utils/VersionHelper.java b/src/main/java/com/extendedclip/deluxemenus/utils/VersionHelper.java index 49b239e8..08e0d68f 100644 --- a/src/main/java/com/extendedclip/deluxemenus/utils/VersionHelper.java +++ b/src/main/java/com/extendedclip/deluxemenus/utils/VersionHelper.java @@ -21,6 +21,8 @@ public final class VersionHelper { private static final String PACKAGE_NAME = Bukkit.getServer().getClass().getPackage().getName(); public static final String NMS_VERSION = PACKAGE_NAME.substring(PACKAGE_NAME.lastIndexOf('.') + 1); + // Tooltip Style & Item Model + private static final int V1_21_2 = 1_21_2; // Data components private static final int V1_20_5 = 1_20_5; // ArmorTrims @@ -44,6 +46,11 @@ public final class VersionHelper { private static final boolean IS_PAPER = checkPaper(); + /** + * Checks if the current version includes the setTooltipStyle and setItemModel + */ + public static final boolean HAS_TOOLTIP_STYLE = CURRENT_VERSION >= V1_21_2; + /** * Checks if the current version includes the Data Components */