diff --git a/build.gradle.kts b/build.gradle.kts index fea4ebfa..46b52127 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") } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e06b3db8..6e4d66a1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,20 +1,20 @@ [versions] # Compile only -spigot = "1.21.3-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" -oraxen = "1.159.0" -mythiclib = "1.6.2-SNAPSHOT" -mmoitems = "6.9.5-SNAPSHOT" +headdb = "1.3.2" +itemsadder = "3.6.3-beta-14" +oraxen = "1.187.0" +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" +nashorn = "15.6" adventure-platform = "4.3.4" -adventure-minimessage = "4.17.0" +adventure-minimessage = "4.18.0" [libraries] # Compile only @@ -23,7 +23,7 @@ vault = { module = "com.github.milkbowl:VaultAPI", version.ref = "vault" } 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" } +oraxen = { module = "io.th0rgal: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" } 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/config/DeluxeMenusConfig.java b/src/main/java/com/extendedclip/deluxemenus/config/DeluxeMenusConfig.java index 16a79389..e2bdfa6c 100644 --- a/src/main/java/com/extendedclip/deluxemenus/config/DeluxeMenusConfig.java +++ b/src/main/java/com/extendedclip/deluxemenus/config/DeluxeMenusConfig.java @@ -6,7 +6,7 @@ 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; diff --git a/src/main/java/com/extendedclip/deluxemenus/menu/Menu.java b/src/main/java/com/extendedclip/deluxemenus/menu/Menu.java index 2de43f5d..23d776e2 100644 --- a/src/main/java/com/extendedclip/deluxemenus/menu/Menu.java +++ b/src/main/java/com/extendedclip/deluxemenus/menu/Menu.java @@ -1,22 +1,17 @@ package com.extendedclip.deluxemenus.menu; import com.extendedclip.deluxemenus.DeluxeMenus; +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; -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; @@ -24,12 +19,11 @@ 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; @@ -37,26 +31,24 @@ public class Menu extends Command { // 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 ) { - super(options.commands().isEmpty() ? options.name() : options.commands().get(0)); - 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); } @@ -67,12 +59,12 @@ public static void unload(final @NotNull DeluxeMenus plugin, final @NotNull Stri } } - 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); } @@ -83,13 +75,23 @@ public static void unload(final @NotNull DeluxeMenus plugin) { } } for (Menu menu : Menu.getAllMenus()) { - menu.removeCommand(); + menu.unregisterCommand(); } menus.clear(); menuHolders.clear(); lastOpenedMenus.clear(); } + 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)) { @@ -208,103 +210,6 @@ public static void closeMenu(final @NotNull DeluxeMenus plugin, final @NotNull P closeMenu(plugin, 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) { - plugin.printStacktrace( - "Something went wrong while trying to register command: " + this.getName(), - exception - ); - return; - } - } - boolean registered = commandMap.register("DeluxeMenus", this); - if (registered) { - plugin.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) { - 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 - ); - } - } - } - - @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()) { - plugin.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)); - 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: " + this.options.name()); - openMenu(player, argMap, null); - return true; - } - private boolean hasOpenBypassPerm(final @NotNull Player viewer) { return viewer.hasPermission("deluxemenus.openrequirement.bypass." + this.options.name()) || viewer.hasPermission("deluxemenus.openrequirement.bypass.*"); diff --git a/src/main/java/com/extendedclip/deluxemenus/menu/MenuItem.java b/src/main/java/com/extendedclip/deluxemenus/menu/MenuItem.java index dc28bd14..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; 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 21cdc158..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;