diff --git a/README.md b/README.md index ac63f750..501b014f 100644 --- a/README.md +++ b/README.md @@ -39,4 +39,3 @@ If you would like to contribute towards DeluxeMenus should you take a look at ou - [CI Server][ci] - [Spigot Page][spigot] - [Plugin Statistics][bstats] - diff --git a/build.gradle.kts b/build.gradle.kts index 6558bf29..5cf53320 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,7 @@ plugins { java - id("com.gradleup.shadow") version("8.3.5") - id("com.github.ben-manes.versions") version("0.51.0") + id("com.gradleup.shadow") version("9.6.1") + id("com.github.ben-manes.versions") version("0.54.0") } // Change to true when releasing @@ -14,18 +14,19 @@ version = "$majorVersion-$minorVersion" repositories { mavenCentral() - maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") + maven("https://repo.papermc.io/repository/maven-public/") 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.momirealms.net/releases/") maven("https://repo.nexomc.com/releases/") maven("https://repo.oraxen.com/releases") + maven("https://maven.devs.beer/") + maven("https://repo.papermc.io/repository/maven-public/") maven("https://jitpack.io") } dependencies { - compileOnly(libs.spigot) + compileOnly(libs.paper) compileOnly(libs.vault) compileOnly(libs.authlib) @@ -48,7 +49,7 @@ dependencies { implementation(libs.adventure.minimessage) implementation(libs.bstats) - compileOnly("org.jetbrains:annotations:23.0.0") + compileOnly("org.jetbrains:annotations:26.1.0") } tasks { @@ -59,14 +60,15 @@ tasks { relocate("org.bstats", "com.extendedclip.deluxemenus.libs.bstats") archiveFileName.set("DeluxeMenus-${rootProject.version}.jar") } + java { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 disableAutoTargetJvm() } processResources { - filesMatching("plugin.yml") { + filesMatching(listOf("plugin.yml", "paper-plugin.yml")) { expand("version" to rootProject.version) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e810b282..5d782e6b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,34 +1,32 @@ [versions] # Compile only -spigot = "1.21.5-R0.1-SNAPSHOT" +paper = "1.21.11-R0.1-SNAPSHOT" vault = "1.7.1" authlib = "1.5.25" headdb = "1.3.2" -craftengine = "26.5" -itemsadder = "3.6.3-beta-14" -nexo = "1.1.0" -oraxen = "1.190.0" +craftengine = "26.7.4" +itemsadder = "4.0.10" +nexo = "1.10.0" +oraxen = "1.212.0" mythiclib = "1.7.1-SNAPSHOT" -mmoitems = "6.10-SNAPSHOT" -papi = "2.11.6" +mmoitems = "6.10.1-SNAPSHOT" +papi = "2.12.3" score = "5.25.3.9" sig = "1.5.0" -bstats = "3.1.0" - +bstats = "3.2.1" # Implementation -nashorn = "15.6" +nashorn = "15.7" adventure-platform = "4.4.1" -adventure-minimessage = "4.24.0" - +adventure-minimessage = "4.26.1" [libraries] # Compile only -spigot = { module = "org.spigotmc:spigot-api", version.ref = "spigot" } +paper = { module = "io.papermc.paper:paper-api", version.ref = "paper" } 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" } craftengine-core = { module = "net.momirealms:craft-engine-core", version.ref = "craftengine"} craftengine-bukkit = { module = "net.momirealms:craft-engine-bukkit", version.ref = "craftengine"} -itemsadder = { module = "com.github.LoneDev6:api-itemsadder", version.ref = "itemsadder" } +itemsadder = { module = "dev.lone:api-itemsadder", version.ref = "itemsadder" } nexo = { module = "com.nexomc:nexo", version.ref = "nexo" } oraxen = { module = "io.th0rgal:oraxen", version.ref = "oraxen" } mythiclib = { module = "io.lumine:MythicLib-dist", version.ref = "mythiclib"} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e6441136..b1b8ef56 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 cea7a793..a9db1155 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,9 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip networkTimeout=10000 +retries=0 +retryBackOffMs=500 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index b740cf13..249efbb0 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,10 +15,12 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # -# Gradle start up script for POSIX generated by Gradle. +# gradlew start up script for POSIX generated by Gradle. # # Important for running: # @@ -27,7 +29,7 @@ # bash, then to run this script, type that shell name before the whole # command line, like: # -# ksh Gradle +# ksh gradlew # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/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/. @@ -84,7 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -170,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -203,15 +203,14 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_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" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 7101f8e4..8508ef68 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,16 +13,18 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem -@rem Gradle startup script for Windows +@rem gradlew startup script for Windows @rem @rem ########################################################################## -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal +@rem Set local scope for the variables, and ensure extensions are enabled +setlocal EnableExtensions set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @@ -49,7 +51,7 @@ 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 +"%COMSPEC%" /c exit 1 :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% @@ -63,30 +65,18 @@ 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 +"%COMSPEC%" /c exit 1 :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% -:mainEnd -if "%OS%"=="Windows_NT" endlocal +@rem Execute gradlew +@rem endlocal doesn't take effect until after the line is parsed and variables are expanded +@rem which allows us to clear the local environment before executing the java command +endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel -:omega +:exitWithErrorLevel +@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts +"%COMSPEC%" /c exit %ERRORLEVEL% diff --git a/settings.gradle.kts b/settings.gradle.kts index a91e4ac4..b9a1575b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,3 +1,3 @@ rootProject.name = "DeluxeMenus" -enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") \ No newline at end of file +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") diff --git a/src/main/java/com/extendedclip/deluxemenus/DeluxeMenus.java b/src/main/java/com/extendedclip/deluxemenus/DeluxeMenus.java index a5b4b354..12d79301 100644 --- a/src/main/java/com/extendedclip/deluxemenus/DeluxeMenus.java +++ b/src/main/java/com/extendedclip/deluxemenus/DeluxeMenus.java @@ -15,6 +15,8 @@ import com.extendedclip.deluxemenus.nbt.NbtProvider; import com.extendedclip.deluxemenus.persistentmeta.PersistentMetaHandler; import com.extendedclip.deluxemenus.placeholder.Expansion; +import com.extendedclip.deluxemenus.scheduler.UniversalScheduler; +import com.extendedclip.deluxemenus.scheduler.scheduling.schedulers.TaskScheduler; import com.extendedclip.deluxemenus.updatechecker.UpdateChecker; import com.extendedclip.deluxemenus.utils.DebugLevel; import com.extendedclip.deluxemenus.utils.Messages; @@ -28,14 +30,23 @@ import org.bstats.charts.SingleLineChart; import org.bukkit.Bukkit; import org.bukkit.Material; +import org.bukkit.NamespacedKey; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.event.HandlerList; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.persistence.PersistentDataType; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.TreeMap; import java.util.function.Function; import java.util.logging.Level; import java.util.stream.Collectors; @@ -59,6 +70,9 @@ public class DeluxeMenus extends JavaPlugin { private final GeneralConfig generalConfig = new GeneralConfig(this); private DeluxeMenusConfig menuConfig; + @NotNull + private final TaskScheduler scheduler = UniversalScheduler.getScheduler(this); + @Override public void onLoad() { if (NbtProvider.isAvailable()) { @@ -113,7 +127,7 @@ public void onEnable() { public void onDisable() { Bukkit.getMessenger().unregisterOutgoingPluginChannel(this, "BungeeCord"); - Bukkit.getScheduler().cancelTasks(this); + scheduler.cancelTasks(this); if (this.audiences != null) { this.audiences.close(); @@ -217,6 +231,10 @@ public GeneralConfig getGeneralConfig() { return generalConfig; } + public @NotNull TaskScheduler getScheduler() { + return scheduler; + } + private boolean hookIntoPlaceholderAPI() { final boolean canHook = Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null; if (!canHook) { @@ -338,7 +356,7 @@ private void setUpMetrics() { .map(Menu::getMenuItems) .flatMap(c -> c.values().stream().map(TreeMap::values).flatMap(Collection::stream)) .map(MenuItem::options) - .collect(Collectors.toList()); + .toList(); results.put("Byte", options.stream().filter(option -> option.nbtByte().isPresent()).mapToInt(b -> 1).sum()); results.put("Bytes", options.stream().filter(option -> !option.nbtBytes().isEmpty()).mapToInt(b -> 1).sum()); results.put("Short", options.stream().filter(option -> option.nbtShort().isPresent()).mapToInt(s -> 1).sum()); @@ -351,4 +369,23 @@ private void setUpMetrics() { return results; })); } + + public boolean isDupeProtectionFlagged(@NotNull ItemStack item) { + if (!item.hasItemMeta()) return false; + + return item.getItemMeta().getPersistentDataContainer() + .has(new NamespacedKey(this, "deluxemenus.item.dupeprotection"), PersistentDataType.BYTE); + } + + public void markDupeProtection(@NotNull ItemStack item) { + ItemMeta meta = item.getItemMeta(); + if (meta == null) return; + + meta.getPersistentDataContainer().set( + new NamespacedKey(this, "deluxemenus.item.dupeprotection"), + PersistentDataType.BYTE, + (byte) 1 + ); + item.setItemMeta(meta); + } } diff --git a/src/main/java/com/extendedclip/deluxemenus/action/ClickAction.java b/src/main/java/com/extendedclip/deluxemenus/action/ClickAction.java index 59d61685..aefda3e0 100644 --- a/src/main/java/com/extendedclip/deluxemenus/action/ClickAction.java +++ b/src/main/java/com/extendedclip/deluxemenus/action/ClickAction.java @@ -76,7 +76,6 @@ public void setDelay(@Nullable final String delay) { this.delay = delay; } - /** * Get the unparsed chance of this action. * @@ -102,7 +101,6 @@ public void setChance(@Nullable final String chance) { * @param holder the holder to parse placeholders in the delay for. * @return the parsed delay */ - @SuppressWarnings("UnstableApiUsage") public long getDelay(@NotNull final MenuHolder holder) { if (delay == null || delay.isEmpty()) { return 0; @@ -119,7 +117,6 @@ public long getDelay(@NotNull final MenuHolder holder) { * @param holder the holder to parse placeholders in the chance for. * @return true if the chance has passed, false otherwise */ - @SuppressWarnings("UnstableApiUsage") public boolean checkChance(@NotNull final MenuHolder holder) { if (chance == null) { return true; diff --git a/src/main/java/com/extendedclip/deluxemenus/action/ClickActionTask.java b/src/main/java/com/extendedclip/deluxemenus/action/ClickActionTask.java index 6d728108..e763168f 100644 --- a/src/main/java/com/extendedclip/deluxemenus/action/ClickActionTask.java +++ b/src/main/java/com/extendedclip/deluxemenus/action/ClickActionTask.java @@ -4,22 +4,34 @@ import com.extendedclip.deluxemenus.menu.Menu; import com.extendedclip.deluxemenus.menu.MenuHolder; import com.extendedclip.deluxemenus.persistentmeta.PersistentMetaHandler; -import com.extendedclip.deluxemenus.utils.*; +import com.extendedclip.deluxemenus.scheduler.UniversalRunnable; +import com.extendedclip.deluxemenus.scheduler.scheduling.schedulers.TaskScheduler; +import com.extendedclip.deluxemenus.utils.AdventureUtils; +import com.extendedclip.deluxemenus.utils.DebugLevel; +import com.extendedclip.deluxemenus.utils.ExpUtils; +import com.extendedclip.deluxemenus.utils.SoundUtils; +import com.extendedclip.deluxemenus.utils.StringUtils; +import com.extendedclip.deluxemenus.utils.VersionHelper; import net.kyori.adventure.text.minimessage.MiniMessage; import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.Bukkit; import org.bukkit.Sound; import org.bukkit.entity.Player; -import org.bukkit.scheduler.BukkitRunnable; import org.jetbrains.annotations.NotNull; -import java.util.*; +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.logging.Level; -public class ClickActionTask extends BukkitRunnable { +public class ClickActionTask extends UniversalRunnable { private final DeluxeMenus plugin; + private final TaskScheduler scheduler; private final UUID uuid; private final ActionType actionType; private final String exec; @@ -38,6 +50,7 @@ public ClickActionTask( final boolean parsePlaceholdersAfterArguments ) { this.plugin = plugin; + this.scheduler = plugin.getScheduler(); this.uuid = uuid; this.actionType = actionType; this.exec = exec; @@ -58,7 +71,6 @@ public void run() { ? holder.get().getPlaceholderPlayer() : player; - final String executable = StringUtils.replacePlaceholdersAndArguments( this.exec, this.arguments, @@ -67,66 +79,37 @@ public void run() { this.parsePlaceholdersAfterArguments); switch (actionType) { - case META: + case META -> { if (!VersionHelper.IS_PDC_VERSION || plugin.getPersistentMetaHandler() == null) { plugin.debug(DebugLevel.HIGHEST, Level.INFO, "Meta action not supported on this server version."); break; } final PersistentMetaHandler.OperationResult result = plugin.getPersistentMetaHandler().parseAndExecuteMetaActionFromString(player, executable); switch (result) { - case INVALID_SYNTAX: - plugin.debug(DebugLevel.HIGHEST, Level.INFO, "Invalid meta action! Make sure you have the right syntax."); - break; - case NEW_VALUE_IS_DIFFERENT_TYPE: - plugin.debug(DebugLevel.HIGHEST, Level.INFO, "Invalid meta action! New value is a different type than the old value!"); - break; - case INVALID_TYPE: - plugin.debug(DebugLevel.HIGHEST, Level.INFO, "Invalid meta action! The specified type is not supported for the specified action!"); - break; - case EXISTENT_VALUE_IS_DIFFERENT_TYPE: - plugin.debug(DebugLevel.HIGHEST, Level.INFO, "Invalid meta action! Existent value is a different type than the new value!"); - break; - case VALUE_NOT_FOUND: - case SUCCESS: - default: - break; + case INVALID_SYNTAX -> + plugin.debug(DebugLevel.HIGHEST, Level.INFO, "Invalid meta action! Make sure you have the right syntax."); + case NEW_VALUE_IS_DIFFERENT_TYPE -> + plugin.debug(DebugLevel.HIGHEST, Level.INFO, "Invalid meta action! New value is a different type than the old value!"); + case INVALID_TYPE -> + plugin.debug(DebugLevel.HIGHEST, Level.INFO, "Invalid meta action! The specified type is not supported for the specified action!"); + case EXISTENT_VALUE_IS_DIFFERENT_TYPE -> + plugin.debug(DebugLevel.HIGHEST, Level.INFO, "Invalid meta action! Existent value is a different type than the new value!"); + default -> { + } } - break; - - case PLAYER: - case PLAYER_COMMAND_EVENT: - player.chat("/" + executable); - break; - - case PLACEHOLDER: - holder.ifPresent(it -> it.setPlaceholders(executable)); - break; - - case CHAT: - player.chat(executable); - break; - - case CONSOLE: - Bukkit.dispatchCommand(Bukkit.getConsoleSender(), executable); - break; - - case MINI_MESSAGE: - plugin.audiences().player(player).sendMessage(MiniMessage.miniMessage().deserialize(executable)); - break; - - case MINI_BROADCAST: - plugin.audiences().all().sendMessage(MiniMessage.miniMessage().deserialize(executable)); - break; - - case MESSAGE: - player.sendMessage(StringUtils.color(executable)); - break; - - case ACTION_BAR: - player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(StringUtils.color(executable))); - break; - - case LOG: + } + case PLAYER, PLAYER_COMMAND_EVENT -> player.chat("/" + executable); + case PLACEHOLDER -> holder.ifPresent(it -> it.setPlaceholders(executable)); + case CHAT -> player.chat(executable); + case CONSOLE -> scheduler.runTask(() -> Bukkit.dispatchCommand(Bukkit.getConsoleSender(), executable)); + case MINI_MESSAGE -> + plugin.audiences().player(player).sendMessage(MiniMessage.miniMessage().deserialize(executable)); + case MINI_BROADCAST -> + plugin.audiences().all().sendMessage(MiniMessage.miniMessage().deserialize(executable)); + case MESSAGE -> player.sendMessage(StringUtils.color(executable)); + case ACTION_BAR -> + player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(StringUtils.color(executable))); + case LOG -> { final String[] logParts = executable.split(" ", 2); if (logParts.length == 0 || logParts[0].isBlank()) { @@ -152,18 +135,10 @@ public void run() { } plugin.getLogger().log(logLevel, String.format("[%s]: %s", holder.map(MenuHolder::getMenuName).orElse("Unknown Menu"), message)); - break; - - case BROADCAST: - Bukkit.broadcastMessage(StringUtils.color(executable)); - break; - - case CLOSE: - Menu.closeMenu(plugin, player, true, true); - break; - - case OPEN_GUI_MENU: - case OPEN_MENU: + } + case BROADCAST -> Bukkit.broadcastMessage(StringUtils.color(executable)); + case CLOSE -> Menu.closeMenu(plugin, player, true, true, false); + case OPEN_GUI_MENU, OPEN_MENU -> { final String temporaryExecutable = executable.replaceAll("\\s+", " ").replace(" ", " "); final String[] executableParts = temporaryExecutable.split(" ", 2); @@ -193,9 +168,9 @@ public void run() { if (menuArgumentNames.isEmpty()) { if (passedArgumentValues != null && passedArgumentValues.length > 0) { plugin.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Arguments were given for menu " + menuName + " in action [openguimenu] or [openmenu], but the menu does not support arguments!" + DebugLevel.HIGHEST, + Level.WARNING, + "Arguments were given for menu " + menuName + " in action [openguimenu] or [openmenu], but the menu does not support arguments!" ); } @@ -221,9 +196,9 @@ public void run() { if (passedArgumentValues.length < menuArgumentNames.size()) { plugin.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Not enough arguments given for menu " + menuName + " when opening using the [openguimenu] or [openmenu] action!" + DebugLevel.HIGHEST, + Level.WARNING, + "Not enough arguments given for menu " + menuName + " when opening using the [openguimenu] or [openmenu] action!" ); break; } @@ -241,9 +216,9 @@ public void run() { if (passedArgumentValues.length <= index) { // This should never be the case! plugin.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Not enough arguments given for menu " + menuName + " when opening using the [openguimenu] or [openmenu] action!" + DebugLevel.HIGHEST, + Level.WARNING, + "Not enough arguments given for menu " + menuName + " when opening using the [openguimenu] or [openmenu] action!" ); break; } @@ -264,35 +239,24 @@ public void run() { } menuToOpen.openMenu(player, argumentsMap, holder.get().getPlaceholderPlayer()); - break; - - case CONNECT: - plugin.connect(player, executable); - break; - - case JSON_MESSAGE: - AdventureUtils.sendJson(plugin, player, executable); - break; - - case JSON_BROADCAST: - case BROADCAST_JSON: - plugin.audiences().all().sendMessage(AdventureUtils.fromJson(executable)); - break; - - case REFRESH: + } + case CONNECT -> plugin.connect(player, executable); + case JSON_MESSAGE -> AdventureUtils.sendJson(plugin, player, executable); + case JSON_BROADCAST, BROADCAST_JSON -> + plugin.audiences().all().sendMessage(AdventureUtils.fromJson(executable)); + case REFRESH -> { if (holder.isEmpty()) { plugin.debug( - DebugLevel.MEDIUM, - Level.WARNING, - player.getName() + " does not have menu open! Nothing to refresh!" + DebugLevel.MEDIUM, + Level.WARNING, + player.getName() + " does not have menu open! Nothing to refresh!" ); break; } holder.get().refreshMenu(); - break; - - case TAKE_MONEY: + } + case TAKE_MONEY -> { if (plugin.getVault() == null || !plugin.getVault().hooked()) { plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Vault not hooked! Cannot take money!"); break; @@ -302,14 +266,13 @@ public void run() { plugin.getVault().takeMoney(player, Double.parseDouble(executable)); } catch (final NumberFormatException exception) { plugin.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Amount for take money action: " + executable + ", is not a valid number!" + DebugLevel.HIGHEST, + Level.WARNING, + "Amount for take money action: " + executable + ", is not a valid number!" ); } - break; - - case GIVE_MONEY: + } + case GIVE_MONEY -> { if (plugin.getVault() == null || !plugin.getVault().hooked()) { plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Vault not hooked! Cannot give money!"); break; @@ -319,19 +282,17 @@ public void run() { plugin.getVault().giveMoney(player, Double.parseDouble(executable)); } catch (final NumberFormatException exception) { plugin.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Amount for give money action: " + executable + ", is not a valid number!" + DebugLevel.HIGHEST, + Level.WARNING, + "Amount for give money action: " + executable + ", is not a valid number!" ); } - break; - - case TAKE_EXP: - case GIVE_EXP: + } + case TAKE_EXP, GIVE_EXP -> { final String lowerCaseExecutable = executable.toLowerCase(); try { - if (Integer.parseInt(lowerCaseExecutable.replaceAll("l", "")) <= 0) break; + if (Integer.parseInt(lowerCaseExecutable.replace("l", "")) <= 0) break; if (actionType == ActionType.TAKE_EXP) { ExpUtils.setExp(player, "-" + lowerCaseExecutable); @@ -339,56 +300,48 @@ public void run() { } ExpUtils.setExp(player, lowerCaseExecutable); - break; } catch (final NumberFormatException exception) { if (actionType == ActionType.TAKE_EXP) { plugin.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Amount for take exp action: " + executable + ", is not a valid number!" + DebugLevel.HIGHEST, + Level.WARNING, + "Amount for take exp action: " + executable + ", is not a valid number!" ); break; } plugin.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Amount for give exp action: " + executable + ", is not a valid number!" + DebugLevel.HIGHEST, + Level.WARNING, + "Amount for give exp action: " + executable + ", is not a valid number!" ); - break; } - - case GIVE_PERM: + } + case GIVE_PERM -> { if (plugin.getVault() == null || !plugin.getVault().hooked()) { plugin.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Vault not hooked! Cannot give permission: " + executable + "!"); + DebugLevel.HIGHEST, + Level.WARNING, + "Vault not hooked! Cannot give permission: " + executable + "!"); break; } plugin.getVault().givePermission(player, executable); - break; - - case TAKE_PERM: + } + case TAKE_PERM -> { if (plugin.getVault() == null || !plugin.getVault().hooked()) { plugin.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Vault not hooked! Cannot take permission: " + executable + "!"); + DebugLevel.HIGHEST, + Level.WARNING, + "Vault not hooked! Cannot take permission: " + executable + "!"); break; } plugin.getVault().takePermission(player, executable); - break; - - case BROADCAST_SOUND: - case BROADCAST_RAW_SOUND: - case BROADCAST_WORLD_SOUND: - case BROADCAST_WORLD_RAW_SOUND: - case PLAY_RAW_SOUND: - case PLAY_SOUND: + } + case BROADCAST_SOUND, BROADCAST_RAW_SOUND, BROADCAST_WORLD_SOUND, BROADCAST_WORLD_RAW_SOUND, PLAY_RAW_SOUND, + PLAY_SOUND -> { boolean isRaw = isRaw(actionType); Sound sound = null; @@ -402,8 +355,8 @@ public void run() { sound = SoundUtils.getSound(executable.toUpperCase()); } catch (final IllegalArgumentException exception) { plugin.printStacktrace( - "Sound name given for sound action: " + executable + ", is not a valid sound!", - exception + "Sound name given for sound action: " + executable + ", is not a valid sound!", + exception ); break; } @@ -417,8 +370,8 @@ public void run() { sound = SoundUtils.getSound(parts[0].toUpperCase()); } catch (final IllegalArgumentException exception) { plugin.printStacktrace( - "Sound name given for sound action: " + parts[0] + ", is not a valid sound!", - exception + "Sound name given for sound action: " + parts[0] + ", is not a valid sound!", + exception ); break; } @@ -429,101 +382,91 @@ public void run() { pitch = Float.parseFloat(parts[2]); } catch (final NumberFormatException exception) { plugin.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Pitch given for sound action: " + parts[2] + ", is not a valid number!" + DebugLevel.HIGHEST, + Level.WARNING, + "Pitch given for sound action: " + parts[2] + ", is not a valid number!" ); plugin.printStacktrace( - "Pitch given for sound action: " + parts[2] + ", is not a valid number!", - exception + "Pitch given for sound action: " + parts[2] + ", is not a valid number!", + exception ); } } - try { volume = Float.parseFloat(parts[1]); } catch (final NumberFormatException exception) { plugin.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Volume given for sound action: " + parts[1] + ", is not a valid number!" + DebugLevel.HIGHEST, + Level.WARNING, + "Volume given for sound action: " + parts[1] + ", is not a valid number!" ); plugin.printStacktrace( - "Volume given for sound action: " + parts[1] + ", is not a valid number!", - exception + "Volume given for sound action: " + parts[1] + ", is not a valid number!", + exception ); } } switch (actionType) { - case BROADCAST_WORLD_RAW_SOUND: + case BROADCAST_WORLD_RAW_SOUND -> { for (final Player broadcastTarget : player.getWorld().getPlayers()) { broadcastTarget.playSound(broadcastTarget.getLocation(), soundName, volume, pitch); } - break; - - case BROADCAST_RAW_SOUND: + } + case BROADCAST_RAW_SOUND -> { for (final Player broadcastTarget : Bukkit.getOnlinePlayers()) { broadcastTarget.playSound(broadcastTarget.getLocation(), soundName, volume, pitch); } - break; - - case PLAY_RAW_SOUND: - player.playSound(player.getLocation(), soundName, volume, pitch); - break; - - case BROADCAST_SOUND: + } + case PLAY_RAW_SOUND -> player.playSound(player.getLocation(), soundName, volume, pitch); + case BROADCAST_SOUND -> { if (sound == null) { plugin.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Sound name given for sound action: " + executable + ", is not a valid sound!" + DebugLevel.HIGHEST, + Level.WARNING, + "Sound name given for sound action: " + executable + ", is not a valid sound!" ); break; } for (final Player broadcastTarget : Bukkit.getOnlinePlayers()) { broadcastTarget.playSound(broadcastTarget.getLocation(), sound, volume, pitch); } - break; - - case BROADCAST_WORLD_SOUND: + } + case BROADCAST_WORLD_SOUND -> { if (sound == null) { plugin.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Sound name given for sound action: " + executable + ", is not a valid sound!" + DebugLevel.HIGHEST, + Level.WARNING, + "Sound name given for sound action: " + executable + ", is not a valid sound!" ); break; } for (final Player broadcastTarget : player.getWorld().getPlayers()) { broadcastTarget.playSound(broadcastTarget.getLocation(), sound, volume, pitch); } - break; - - case PLAY_SOUND: + } + case PLAY_SOUND -> { if (sound == null) { plugin.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Sound name given for sound action: " + executable + ", is not a valid sound!" + DebugLevel.HIGHEST, + Level.WARNING, + "Sound name given for sound action: " + executable + ", is not a valid sound!" ); break; } player.playSound(player.getLocation(), sound, volume, pitch); - break; + } } - break; - - default: - break; + } + default -> { + } } } private boolean isRaw(ActionType actionType) { return actionType == ActionType.PLAY_RAW_SOUND || actionType == ActionType.BROADCAST_RAW_SOUND || actionType == ActionType.BROADCAST_WORLD_RAW_SOUND; } - -} \ No newline at end of file +} diff --git a/src/main/java/com/extendedclip/deluxemenus/command/DeluxeMenusCommand.java b/src/main/java/com/extendedclip/deluxemenus/command/DeluxeMenusCommand.java index 9aa91a97..f0870718 100644 --- a/src/main/java/com/extendedclip/deluxemenus/command/DeluxeMenusCommand.java +++ b/src/main/java/com/extendedclip/deluxemenus/command/DeluxeMenusCommand.java @@ -2,18 +2,19 @@ import com.extendedclip.deluxemenus.DeluxeMenus; import com.extendedclip.deluxemenus.command.subcommand.*; -import com.extendedclip.deluxemenus.utils.DebugLevel; import com.extendedclip.deluxemenus.utils.Messages; +import io.papermc.paper.command.brigadier.BasicCommand; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents; 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.CommandSender; -import org.bukkit.command.PluginCommand; -import org.bukkit.command.TabExecutor; import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.NonNull; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -22,7 +23,7 @@ import static net.kyori.adventure.text.Component.text; -public class DeluxeMenusCommand implements TabExecutor { +public class DeluxeMenusCommand implements BasicCommand { private static final TextReplacementConfig.Builder VERSION_REPLACER_BUILDER = TextReplacementConfig.builder().matchLiteral(""); private static final TextReplacementConfig.Builder AUTHORS_REPLACER_BUILDER = TextReplacementConfig.builder().matchLiteral(""); @@ -35,48 +36,47 @@ public DeluxeMenusCommand(final @NotNull DeluxeMenus plugin) { } public boolean register() { - final PluginCommand command = this.plugin.getCommand("deluxemenus"); - if (command == null) { - return false; - } - - command.setExecutor(this); registerSubCommands(); + plugin.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, event -> + event.registrar().register( + "deluxemenus", + "DeluxeMenus main command", + List.of("dm"), + this + ) + ); return true; } @Override - public boolean onCommand( - final @NotNull CommandSender sender, - final @NotNull Command command, - final @NotNull String label, - final @NotNull String[] args + public void execute( + final @NotNull CommandSourceStack stack, + final @NotNull String @NonNull [] args ) { + final CommandSender sender = stack.getSender(); final List arguments = Arrays.asList(args); - if (arguments.isEmpty()) { + if (arguments.isEmpty() || (arguments.size() == 1 && arguments.getFirst().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; + return; } - final SubCommand subCommand = subCommands.get(arguments.get(0).toLowerCase()); + final SubCommand subCommand = subCommands.get(arguments.getFirst().toLowerCase()); if (subCommand != null) { subCommand.execute(sender, arguments.subList(1, arguments.size())); - return true; + return; } plugin.sms(sender, Messages.WRONG_USAGE); - return true; } @Override - public List onTabComplete( - final @NotNull CommandSender sender, - final @NotNull Command command, - final @NotNull String label, - final @NotNull String[] args + public @NotNull Collection suggest( + final @NotNull CommandSourceStack stack, + final @NotNull String @NonNull [] args ) { + final CommandSender sender = stack.getSender(); final List arguments = Arrays.asList(args); return subCommands.values() diff --git a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/DumpCommand.java b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/DumpCommand.java index e59e0d05..8d0e3075 100644 --- a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/DumpCommand.java +++ b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/DumpCommand.java @@ -42,7 +42,7 @@ public void execute(final @NotNull CommandSender sender, final @NotNull List menus = Menu.getAllMenus(); if (menus.isEmpty()) { plugin.sms(sender, Messages.MENUS_LOADED.message().replaceText(AMOUNT_REPLACER_BUILDER.replacement("There are no").build())); @@ -85,11 +85,11 @@ public void execute(@NotNull final CommandSender sender, @NotNull final List> paginatedMenus = getPaginatedMenus( @@ -373,6 +373,6 @@ private void addMenuToTreeRecursively(final Map tree, final List return null; } - return "/" + menu.options().commands().get(0); + return "/" + menu.options().commands().getFirst(); } } diff --git a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java index 631aa206..65cbafc0 100644 --- a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java +++ b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/MetaCommand.java @@ -61,10 +61,10 @@ public void execute(@NotNull final CommandSender sender, @NotNull final List type = DataType.getSupportedTypeByName(typeName); if (type == null) { plugin.sms(sender, Messages.META_TYPE_UNSUPPORTED.message().replaceText(TYPE_REPLACER_BUILDER.replacement(typeName).build())); @@ -318,7 +318,7 @@ private void handleShowMeta(@NotNull final CommandSender sender, @NotNull final return; } - final String keyName = arguments.get(0); + final String keyName = arguments.getFirst(); final NamespacedKey namespacedKey = plugin.getPersistentMetaHandler().getKey(keyName); if (namespacedKey == null) { plugin.sms(sender, Messages.META_KEY_INVALID.message().replaceText(KEY_REPLACER_BUILDER.replacement(keyName).build())); @@ -366,25 +366,18 @@ private void handleSetMeta(@NotNull final CommandSender sender, @NotNull final P final PersistentMetaHandler.OperationResult result = plugin.getPersistentMetaHandler().setMetaValue(target, namespacedKey, type, parsedValue); switch (result) { - case SUCCESS: - plugin.sms(sender, Messages.META_VALUE_SET.message() - .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build()) - .replaceText(TYPE_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build()) - .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build()) - .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) - ); - return; - case NEW_VALUE_IS_DIFFERENT_TYPE: - plugin.sms(sender, Messages.META_VALUE_TYPE_MISMATCH.message() - .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build()) - .replaceText(TYPE_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build())); - return; - case EXISTENT_VALUE_IS_DIFFERENT_TYPE: - plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE.message() - .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build())); - return; - default: - plugin.sms(sender, Messages.WRONG_USAGE_META_SET_COMMAND); + case SUCCESS -> plugin.sms(sender, Messages.META_VALUE_SET.message() + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build()) + .replaceText(TYPE_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build()) + .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build()) + .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) + ); + case NEW_VALUE_IS_DIFFERENT_TYPE -> plugin.sms(sender, Messages.META_VALUE_TYPE_MISMATCH.message() + .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build()) + .replaceText(TYPE_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build())); + case EXISTENT_VALUE_IS_DIFFERENT_TYPE -> plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE.message() + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build())); + default -> plugin.sms(sender, Messages.WRONG_USAGE_META_SET_COMMAND); } } @@ -394,26 +387,19 @@ private void handleRemoveMeta(@NotNull final CommandSender sender, @NotNull fina @NotNull final Map context) { final PersistentMetaHandler.OperationResult result = plugin.getPersistentMetaHandler().removeMetaValue(target, namespacedKey, type); switch (result) { - case SUCCESS: - plugin.sms(sender, Messages.META_VALUE_REMOVED.message() - .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build()) - .replaceText(TYPE_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build()) - .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) - ); - return; - case EXISTENT_VALUE_IS_DIFFERENT_TYPE: - plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE.message() - .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build())); - return; - case VALUE_NOT_FOUND: - plugin.sms(sender, Messages.NO_META_VALUE.message() - .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build()) - .replaceText(TYPE_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build()) - .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) - ); - return; - default: - plugin.sms(sender, Messages.WRONG_USAGE_META_REMOVE_COMMAND); + case SUCCESS -> plugin.sms(sender, Messages.META_VALUE_REMOVED.message() + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build()) + .replaceText(TYPE_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build()) + .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) + ); + case EXISTENT_VALUE_IS_DIFFERENT_TYPE -> plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE.message() + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build())); + case VALUE_NOT_FOUND -> plugin.sms(sender, Messages.NO_META_VALUE.message() + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build()) + .replaceText(TYPE_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build()) + .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) + ); + default -> plugin.sms(sender, Messages.WRONG_USAGE_META_REMOVE_COMMAND); } } @@ -422,21 +408,18 @@ private void handleSwitchMeta(@NotNull final CommandSender sender, @NotNull fina final PersistentMetaHandler.OperationResult result = plugin.getPersistentMetaHandler().switchMetaValue(target, namespacedKey); switch (result) { - case SUCCESS: + case SUCCESS -> { final Object newValue = plugin.getPersistentMetaHandler().getMetaValue(target, namespacedKey, DataType.BOOLEAN); plugin.sms(sender, Messages.META_VALUE_SWITCHED.message() - .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build()) - .replaceText(NEW_VALUE_REPLACER_BUILDER.replacement(String.valueOf(newValue)).build()) - .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build()) + .replaceText(NEW_VALUE_REPLACER_BUILDER.replacement(String.valueOf(newValue)).build()) + .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) ); - return; - case EXISTENT_VALUE_IS_DIFFERENT_TYPE: - plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE.message() - .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build())); - return; - default: - plugin.sms(sender, Messages.WRONG_USAGE_META_SWITCH_COMMAND); + } + case EXISTENT_VALUE_IS_DIFFERENT_TYPE -> plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE.message() + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build())); + default -> plugin.sms(sender, Messages.WRONG_USAGE_META_SWITCH_COMMAND); } } @@ -449,34 +432,29 @@ private void handleAddMeta(@NotNull final CommandSender sender, @NotNull final P if (!(parsedValue instanceof Number)) { plugin.sms(sender, Messages.META_ADD_TYPE_MISMATCH.message() - .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(value)).build())); + .replaceText(VALUE_REPLACER_BUILDER.replacement(value).build())); return; } final PersistentMetaHandler.OperationResult result = plugin.getPersistentMetaHandler().addMetaValue(target, namespacedKey, type, (Number) parsedValue); switch (result) { - case SUCCESS: + case SUCCESS -> { final Object newValue = plugin.getPersistentMetaHandler().getMetaValue(target, namespacedKey, type); plugin.sms(sender, Messages.META_VALUE_ADDED.message() - .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build()) - .replaceText(TYPE_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build()) - .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build()) - .replaceText(NEW_VALUE_REPLACER_BUILDER.replacement(String.valueOf(newValue)).build()) - .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build()) + .replaceText(TYPE_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build()) + .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build()) + .replaceText(NEW_VALUE_REPLACER_BUILDER.replacement(String.valueOf(newValue)).build()) + .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) ); - return; - case INVALID_TYPE: - plugin.sms(sender, Messages.META_ADD_TYPE_MISMATCH.message() - .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build())); - return; - case EXISTENT_VALUE_IS_DIFFERENT_TYPE: - plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE.message() - .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build())); - return; - default: - plugin.sms(sender, Messages.WRONG_USAGE_META_ADD_COMMAND); + } + case INVALID_TYPE -> plugin.sms(sender, Messages.META_ADD_TYPE_MISMATCH.message() + .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build())); + case EXISTENT_VALUE_IS_DIFFERENT_TYPE -> plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE.message() + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build())); + default -> plugin.sms(sender, Messages.WRONG_USAGE_META_ADD_COMMAND); } } @@ -489,34 +467,29 @@ private void handleSubtractMeta(@NotNull final CommandSender sender, @NotNull fi if (!(parsedValue instanceof Number)) { plugin.sms(sender, Messages.META_SUBTRACT_TYPE_MISMATCH.message() - .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(value)).build())); + .replaceText(VALUE_REPLACER_BUILDER.replacement(value).build())); return; } final PersistentMetaHandler.OperationResult result = plugin.getPersistentMetaHandler().subtractMetaValue(target, namespacedKey, type, (Number) parsedValue); switch (result) { - case SUCCESS: + case SUCCESS -> { final Object newValue = plugin.getPersistentMetaHandler().getMetaValue(target, namespacedKey, type); plugin.sms(sender, Messages.META_VALUE_SUBTRACTED.message() - .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build()) - .replaceText(TYPE_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build()) - .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build()) - .replaceText(NEW_VALUE_REPLACER_BUILDER.replacement(String.valueOf(newValue)).build()) - .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build()) + .replaceText(TYPE_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.TYPE_NAME, type.getComplexType().getSimpleName())).build()) + .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build()) + .replaceText(NEW_VALUE_REPLACER_BUILDER.replacement(String.valueOf(newValue)).build()) + .replaceText(PLAYER_REPLACER_BUILDER.replacement(target.getName()).build()) ); - return; - case INVALID_TYPE: - plugin.sms(sender, Messages.META_SUBTRACT_TYPE_MISMATCH.message() - .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build())); - return; - case EXISTENT_VALUE_IS_DIFFERENT_TYPE: - plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE.message() - .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build())); - return; - default: - plugin.sms(sender, Messages.WRONG_USAGE_META_SUBTRACT_COMMAND); + } + case INVALID_TYPE -> plugin.sms(sender, Messages.META_SUBTRACT_TYPE_MISMATCH.message() + .replaceText(VALUE_REPLACER_BUILDER.replacement(String.valueOf(parsedValue)).build())); + case EXISTENT_VALUE_IS_DIFFERENT_TYPE -> plugin.sms(sender, Messages.META_EXISTENT_VALUE_WRONG_TYPE.message() + .replaceText(KEY_REPLACER_BUILDER.replacement(context.getOrDefault(ContextKeys.KEY_NAME, namespacedKey.toString())).build())); + default -> plugin.sms(sender, Messages.WRONG_USAGE_META_SUBTRACT_COMMAND); } } @@ -527,23 +500,12 @@ private void sendWrongUsageMessage(@NotNull final CommandSender sender, @Nullabl } switch (action) { - case SET: - plugin.sms(sender, Messages.WRONG_USAGE_META_SET_COMMAND); - return; - case REMOVE: - plugin.sms(sender, Messages.WRONG_USAGE_META_REMOVE_COMMAND); - return; - case ADD: - plugin.sms(sender, Messages.WRONG_USAGE_META_ADD_COMMAND); - return; - case SUBTRACT: - plugin.sms(sender, Messages.WRONG_USAGE_META_SUBTRACT_COMMAND); - return; - case SWITCH: - plugin.sms(sender, Messages.WRONG_USAGE_META_SWITCH_COMMAND); - return; - default: - plugin.sms(sender, Messages.WRONG_USAGE_META_COMMAND); + case SET -> plugin.sms(sender, Messages.WRONG_USAGE_META_SET_COMMAND); + case REMOVE -> plugin.sms(sender, Messages.WRONG_USAGE_META_REMOVE_COMMAND); + case ADD -> plugin.sms(sender, Messages.WRONG_USAGE_META_ADD_COMMAND); + case SUBTRACT -> plugin.sms(sender, Messages.WRONG_USAGE_META_SUBTRACT_COMMAND); + case SWITCH -> plugin.sms(sender, Messages.WRONG_USAGE_META_SWITCH_COMMAND); + default -> plugin.sms(sender, Messages.WRONG_USAGE_META_COMMAND); } } diff --git a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/OpenCommand.java b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/OpenCommand.java index 33cb874c..1e58ed91 100644 --- a/src/main/java/com/extendedclip/deluxemenus/command/subcommand/OpenCommand.java +++ b/src/main/java/com/extendedclip/deluxemenus/command/subcommand/OpenCommand.java @@ -127,10 +127,10 @@ public void execute(final @NotNull CommandSender sender, final @NotNull List menu = Menu.getMenuByName(arguments.get(0)); + Optional menu = Menu.getMenuByName(arguments.getFirst()); if (menu.isEmpty()) { - plugin.sms(sender, Messages.INVALID_MENU.message().replaceText(MENU_REPLACER_BUILDER.replacement(arguments.get(0)).build())); + plugin.sms(sender, Messages.INVALID_MENU.message().replaceText(MENU_REPLACER_BUILDER.replacement(arguments.getFirst()).build())); return; } @@ -152,11 +152,11 @@ public void execute(final @NotNull CommandSender sender, final @NotNull List menu = Menu.getMenuByName(arguments.get(0)); + Optional menu = Menu.getMenuByName(arguments.getFirst()); if (menu.isEmpty()) { - plugin.sms(sender, Messages.INVALID_MENU.message().replaceText(MENU_REPLACER_BUILDER.replacement(arguments.get(0)).build())); + plugin.sms(sender, Messages.INVALID_MENU.message().replaceText(MENU_REPLACER_BUILDER.replacement(arguments.getFirst()).build())); return; } menu.get().refreshForAll(); - if(arguments.size() < 2 || !arguments.get(1).equalsIgnoreCase("-s")) { + if (arguments.size() < 2 || !arguments.get(1).equalsIgnoreCase("-s")) { plugin.sms(sender, Messages.MENU_REFRESHED.message() .replaceText(MENU_REPLACER_BUILDER.replacement(menu.get().options().name()).build()) .replaceText(AMOUNT_REPLACER_BUILDER.replacement(String.valueOf(menu.get().activeViewers())).build()) @@ -74,11 +74,11 @@ public void execute(final @NotNull CommandSender sender, final @NotNull List guiCloseCommands = new ArrayList<>(); + if (c.isList(pre + "gui_close_command")) { + guiCloseCommands.addAll(c.getStringList(pre + "gui_close_command")); + } else { + guiCloseCommands.add(c.getString(pre + "gui_close_command")); + } + builder.guiCloseCommands(guiCloseCommands); + } + + if (c.contains(pre + "gui_open_command")) { + final List guiOpenCommands = new ArrayList<>(); + if (c.isList(pre + "gui_open_command")) { + guiOpenCommands.addAll(c.getStringList(pre + "gui_open_command")); + } else { + guiOpenCommands.add(c.getString(pre + "gui_open_command")); + } + builder.guiOpenCommands(guiOpenCommands); + } + int size = 54; if (type == InventoryType.CHEST) { if (!c.contains(pre + "size")) { @@ -877,17 +895,16 @@ private RequirementList getRequirements(FileConfiguration c, String path) { boolean invert; switch (type) { - case HAS_ITEM: - case DOES_NOT_HAVE_ITEM: + case HAS_ITEM, DOES_NOT_HAVE_ITEM -> { ItemWrapper wrapper = new ItemWrapper(); if (c.contains(rPath + ".material")) { String materialName = c.getString(rPath + ".material"); try { if (!containsPlaceholders(materialName) && plugin.getItemHooks().values() - .stream() - .filter(x -> materialName.toLowerCase().startsWith(x.getPrefix())) - .findFirst() - .orElse(null) == null) + .stream() + .filter(x -> materialName.toLowerCase().startsWith(x.getPrefix())) + .findFirst() + .orElse(null) == null) Material.valueOf(materialName.toUpperCase()); } catch (Exception ex) { plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "has item requirement at path: " + rPath + " does not specify a valid Material name!"); @@ -928,11 +945,11 @@ private RequirementList getRequirements(FileConfiguration c, String path) { final ConfigurationSection modelDataComponent = c.getConfigurationSection(rPath + ".model_data_component"); if (modelDataComponent != null) { wrapper.setCustomModelDataComponent( - CustomModelDataComponent.builder() - .colors(modelDataComponent.getStringList("colors")) - .flags(modelDataComponent.getStringList("flags")) - .floats(modelDataComponent.getStringList("floats")) - .strings(modelDataComponent.getStringList("strings"))); + CustomModelDataComponent.builder() + .colors(modelDataComponent.getStringList("colors")) + .flags(modelDataComponent.getStringList("flags")) + .floats(modelDataComponent.getStringList("floats")) + .strings(modelDataComponent.getStringList("strings"))); } } @@ -962,92 +979,78 @@ private RequirementList getRequirements(FileConfiguration c, String path) { invert = type == RequirementType.DOES_NOT_HAVE_ITEM; req = new HasItemRequirement(plugin, wrapper, invert); - break; - case HAS_PERMISSION: - case DOES_NOT_HAVE_PERMISSION: + } + case HAS_PERMISSION, DOES_NOT_HAVE_PERMISSION -> { if (c.contains(rPath + ".permission")) { invert = type == RequirementType.DOES_NOT_HAVE_PERMISSION; req = new HasPermissionRequirement(c.getString(rPath + ".permission"), invert); } else { 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: + } + case HAS_PERMISSIONS, 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" + 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..." + 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" + 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 Permissions requirement at path: " + rPath + " does not contain permissions: entry" + DebugLevel.HIGHEST, + Level.WARNING, + "Has Permissions requirement at path: " + rPath + " does not contain permissions: entry" ); } - break; - case JAVASCRIPT: + } + case JAVASCRIPT -> { if (c.contains(rPath + ".expression")) { req = new JavascriptRequirement(plugin, c.getString(rPath + ".expression")); } else { plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Javascript requirement at path: " + rPath + " does not contain an expression: entry"); } - break; - case EQUAL_TO: - case NOT_EQUAL_TO: - case GREATER_THAN: - case GREATER_THAN_EQUAL_TO: - case LESS_THAN: - case LESS_THAN_EQUAL_TO: - case STRING_CONTAINS: - case STRING_EQUALS: - case STRING_EQUALS_IGNORECASE: - case STRING_DOES_NOT_CONTAIN: - case STRING_DOES_NOT_EQUAL: - case STRING_DOES_NOT_EQUAL_IGNORECASE: - case STRING_CONTAINS_IGNORECASE: - case STRING_DOES_NOT_CONTAIN_IGNORECASE: + } + case EQUAL_TO, NOT_EQUAL_TO, GREATER_THAN, GREATER_THAN_EQUAL_TO, LESS_THAN, LESS_THAN_EQUAL_TO, + STRING_CONTAINS, STRING_EQUALS, STRING_EQUALS_IGNORECASE, STRING_DOES_NOT_CONTAIN, + STRING_DOES_NOT_EQUAL, STRING_DOES_NOT_EQUAL_IGNORECASE, STRING_CONTAINS_IGNORECASE, + STRING_DOES_NOT_CONTAIN_IGNORECASE -> { if (c.contains(rPath + ".input") && c.contains(rPath + ".output")) { req = new InputResultRequirement(type, c.getString(rPath + ".input"), c.getString(rPath + ".output")); } else { 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: + } + case HAS_MONEY, DOES_NOT_HAVE_MONEY -> { if (c.contains(rPath + ".amount") || c.contains(rPath + ".placeholder")) { invert = type == RequirementType.DOES_NOT_HAVE_MONEY; req = new HasMoneyRequirement(plugin, c.getDouble(rPath + ".amount"), invert, c.getString(rPath + ".placeholder", null)); } else { 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: + } + case HAS_EXP, DOES_NOT_HAVE_EXP -> { if (c.contains(rPath + ".amount")) { if (!containsPlaceholders(c.getString(rPath + ".amount")) && !c.isInt(rPath + ".amount")) { plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Value at path: " + rPath + ".amount is not a placeholder or a number"); @@ -1058,9 +1061,8 @@ private RequirementList getRequirements(FileConfiguration c, String path) { } else { plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Has Exp requirement at path: " + rPath + " does not contain an amount: entry"); } - break; - case REGEX_MATCHES: - case REGEX_DOES_NOT_MATCH: + } + case REGEX_MATCHES, REGEX_DOES_NOT_MATCH -> { if (c.contains(rPath + ".input") && c.contains(rPath + ".regex")) { Pattern p = Pattern.compile(c.getString(rPath + ".regex")); invert = type == RequirementType.REGEX_DOES_NOT_MATCH; @@ -1068,9 +1070,8 @@ private RequirementList getRequirements(FileConfiguration c, String path) { } else { plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Regex requirement at path: " + rPath + " does not contain a input: or regex: entry"); } - break; - case IS_NEAR: - case IS_NOT_NEAR: + } + case IS_NEAR, IS_NOT_NEAR -> { if (c.contains(rPath + ".location") && c.contains(rPath + ".distance")) { invert = type == RequirementType.IS_NOT_NEAR; Location loc = LocationUtils.deserializeLocation(c.getString(rPath + ".location")); @@ -1081,9 +1082,8 @@ private RequirementList getRequirements(FileConfiguration c, String path) { } else { 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: + } + case HAS_META, DOES_NOT_HAVE_META -> { if (!VersionHelper.IS_PDC_VERSION) { plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "Has Meta requirement is not available for your server version!"); break; @@ -1095,8 +1095,8 @@ private RequirementList getRequirements(FileConfiguration c, String path) { } else { 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: + } + case STRING_LENGTH -> { if (c.contains(rPath + ".input") && (c.contains(rPath + ".min") || c.contains(rPath + ".max"))) { int min = c.getInt(rPath + ".min", 0); Integer max = null; @@ -1107,16 +1107,16 @@ private RequirementList getRequirements(FileConfiguration c, String path) { } else { 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: + } + case IS_OBJECT -> { if (c.contains(rPath + ".input") && c.contains(rPath + ".object")) { req = new IsObjectRequirement(c.getString(rPath + ".input"), c.getString(rPath + ".object")); } else { plugin.debug(DebugLevel.HIGHEST, Level.WARNING, "String length requirement at path: " + rPath + " does not contain an input: or object:"); } - break; - default: - break; + } + default -> { + } } if (req != null) { @@ -1220,26 +1220,22 @@ private ClickHandler getClickHandler(FileConfiguration c, String configPath) { if (!actions.isEmpty()) { - handler = new ClickHandler() { - - @Override - public void onClick(@NotNull final MenuHolder holder) { - - for (ClickAction action : actions) { + handler = holder -> { - if (!action.checkChance(holder)) { - continue; - } + for (ClickAction action : actions) { - final ClickActionTask actionTask = new ClickActionTask(plugin, holder.getViewer().getUniqueId(), action.getType(), action.getExecutable(), holder.getTypedArgs(), holder.parsePlaceholdersInArguments(), holder.parsePlaceholdersAfterArguments()); + if (!action.checkChance(holder)) { + continue; + } - if (action.hasDelay()) { - actionTask.runTaskLater(plugin, action.getDelay(holder)); - continue; - } + final ClickActionTask actionTask = new ClickActionTask(plugin, holder.getViewer().getUniqueId(), action.getType(), action.getExecutable(), holder.getTypedArgs(), holder.parsePlaceholdersInArguments(), holder.parsePlaceholdersAfterArguments()); - actionTask.runTask(plugin); + if (action.hasDelay()) { + plugin.getScheduler().runTaskLater(holder.getViewer(), actionTask, action.getDelay(holder)); + continue; } + + plugin.getScheduler().runTask(holder.getViewer(), actionTask); } }; } diff --git a/src/main/java/com/extendedclip/deluxemenus/dupe/DupeFixer.java b/src/main/java/com/extendedclip/deluxemenus/dupe/DupeFixer.java index 5bbd1e15..9285ad92 100644 --- a/src/main/java/com/extendedclip/deluxemenus/dupe/DupeFixer.java +++ b/src/main/java/com/extendedclip/deluxemenus/dupe/DupeFixer.java @@ -2,6 +2,8 @@ import com.extendedclip.deluxemenus.DeluxeMenus; import com.extendedclip.deluxemenus.listener.Listener; +import com.extendedclip.deluxemenus.nbt.NbtProvider; +import com.extendedclip.deluxemenus.scheduler.scheduling.schedulers.TaskScheduler; import com.extendedclip.deluxemenus.utils.DebugLevel; import org.bukkit.event.EventHandler; import org.bukkit.event.entity.EntityPickupItemEvent; @@ -18,16 +20,19 @@ */ public class DupeFixer extends Listener { + private final TaskScheduler scheduler; private final MenuItemMarker marker; public DupeFixer(@NotNull final DeluxeMenus plugin, @NotNull final MenuItemMarker marker) { super(plugin); + this.scheduler = plugin.getScheduler(); this.marker = marker; } @EventHandler private void onPickup(@NotNull final EntityPickupItemEvent event) { - if (!marker.isMarked(event.getItem().getItemStack())) { + ItemStack stack = event.getItem().getItemStack(); + if (!marker.isMarked(stack) && isDupeProtectionFlagged(stack)) { return; } @@ -41,7 +46,8 @@ private void onPickup(@NotNull final EntityPickupItemEvent event) { @EventHandler private void onDrop(@NotNull final PlayerDropItemEvent event) { - if (!marker.isMarked(event.getItemDrop().getItemStack())) { + ItemStack stack = event.getItemDrop().getItemStack(); + if (!marker.isMarked(stack) && isDupeProtectionFlagged(stack)) { return; } @@ -55,21 +61,28 @@ private void onDrop(@NotNull final PlayerDropItemEvent event) { @EventHandler private void onLogin(@NotNull final PlayerJoinEvent event) { - plugin.getServer().getScheduler().runTaskLater( - plugin, - () -> { - for (final ItemStack itemStack : event.getPlayer().getInventory().getContents()) { - if (itemStack == null) continue; - if (!marker.isMarked(itemStack)) continue; + scheduler.runTaskLater(() -> { + for (final ItemStack itemStack : event.getPlayer().getInventory().getContents()) { + if (itemStack == null) continue; + if (!marker.isMarked(itemStack) && isDupeProtectionFlagged(itemStack)) continue; - plugin.debug( - DebugLevel.LOWEST, - Level.INFO, - "Player logged in with a DeluxeMenus item in their inventory. Removing it." - ); - event.getPlayer().getInventory().remove(itemStack); - }}, - 10L + plugin.debug( + DebugLevel.LOWEST, + Level.INFO, + "Player logged in with a DeluxeMenus item in their inventory. Removing it." + ); + event.getPlayer().getInventory().remove(itemStack); + } + }, 10L ); } -} \ No newline at end of file + + private boolean isDupeProtectionFlagged(ItemStack itemStack) { + if (NbtProvider.isAvailable()) { + String value = NbtProvider.getString(itemStack, "deluxemenus.item.dupeprotection"); + return !"true".equals(value); + } + + return true; + } +} diff --git a/src/main/java/com/extendedclip/deluxemenus/events/DeluxeMenusOpenMenuEvent.java b/src/main/java/com/extendedclip/deluxemenus/events/DeluxeMenusOpenMenuEvent.java index 655c4831..ec8ca03e 100644 --- a/src/main/java/com/extendedclip/deluxemenus/events/DeluxeMenusOpenMenuEvent.java +++ b/src/main/java/com/extendedclip/deluxemenus/events/DeluxeMenusOpenMenuEvent.java @@ -1,6 +1,5 @@ package com.extendedclip.deluxemenus.events; -import com.extendedclip.deluxemenus.menu.Menu; import com.extendedclip.deluxemenus.menu.MenuHolder; import org.bukkit.entity.Player; import org.bukkit.event.HandlerList; diff --git a/src/main/java/com/extendedclip/deluxemenus/hooks/BaseHeadHook.java b/src/main/java/com/extendedclip/deluxemenus/hooks/BaseHeadHook.java index 0178ec75..5aaf2128 100644 --- a/src/main/java/com/extendedclip/deluxemenus/hooks/BaseHeadHook.java +++ b/src/main/java/com/extendedclip/deluxemenus/hooks/BaseHeadHook.java @@ -5,7 +5,6 @@ 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; diff --git a/src/main/java/com/extendedclip/deluxemenus/hooks/ExecutableBlocksHook.java b/src/main/java/com/extendedclip/deluxemenus/hooks/ExecutableBlocksHook.java index 58ec1bf2..d92173b2 100644 --- a/src/main/java/com/extendedclip/deluxemenus/hooks/ExecutableBlocksHook.java +++ b/src/main/java/com/extendedclip/deluxemenus/hooks/ExecutableBlocksHook.java @@ -15,7 +15,6 @@ public class ExecutableBlocksHook implements ItemHook, SimpleCache { private final Map cache = new ConcurrentHashMap<>(); - @SuppressWarnings("deprecation") @Override public ItemStack getItem(@NotNull String... arguments) { if (arguments.length == 0) { diff --git a/src/main/java/com/extendedclip/deluxemenus/hooks/ExecutableItemsHook.java b/src/main/java/com/extendedclip/deluxemenus/hooks/ExecutableItemsHook.java index 9bdf1266..3054c1da 100644 --- a/src/main/java/com/extendedclip/deluxemenus/hooks/ExecutableItemsHook.java +++ b/src/main/java/com/extendedclip/deluxemenus/hooks/ExecutableItemsHook.java @@ -5,7 +5,6 @@ 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; diff --git a/src/main/java/com/extendedclip/deluxemenus/hooks/ItemsAdderHook.java b/src/main/java/com/extendedclip/deluxemenus/hooks/ItemsAdderHook.java index ac3760b0..bae2d184 100644 --- a/src/main/java/com/extendedclip/deluxemenus/hooks/ItemsAdderHook.java +++ b/src/main/java/com/extendedclip/deluxemenus/hooks/ItemsAdderHook.java @@ -4,7 +4,6 @@ import dev.lone.itemsadder.api.CustomStack; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; - import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/com/extendedclip/deluxemenus/hooks/MMOItemsHook.java b/src/main/java/com/extendedclip/deluxemenus/hooks/MMOItemsHook.java index d458c7bf..fad24825 100644 --- a/src/main/java/com/extendedclip/deluxemenus/hooks/MMOItemsHook.java +++ b/src/main/java/com/extendedclip/deluxemenus/hooks/MMOItemsHook.java @@ -2,15 +2,14 @@ import com.extendedclip.deluxemenus.DeluxeMenus; import com.extendedclip.deluxemenus.cache.SimpleCache; +import com.extendedclip.deluxemenus.scheduler.scheduling.schedulers.TaskScheduler; import com.extendedclip.deluxemenus.utils.DebugLevel; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.logging.Level; - import net.Indyuce.mmoitems.MMOItems; import net.Indyuce.mmoitems.api.Type; -import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; @@ -19,9 +18,11 @@ public class MMOItemsHook implements ItemHook, SimpleCache { private final Map cache = new ConcurrentHashMap<>(); private final DeluxeMenus plugin; + private final TaskScheduler scheduler; public MMOItemsHook(final @NotNull DeluxeMenus plugin) { this.plugin = plugin; + this.scheduler = plugin.getScheduler(); } @Override @@ -47,7 +48,7 @@ public ItemStack getItem(@NotNull final String... arguments) { ItemStack mmoItem = null; try { - mmoItem = Bukkit.getScheduler().callSyncMethod(plugin, () -> { + mmoItem = scheduler.callSyncMethod(() -> { ItemStack item = MMOItems.plugin.getItem(itemType, splitArgs[1]); if (item == null) { diff --git a/src/main/java/com/extendedclip/deluxemenus/hooks/NamedHeadHook.java b/src/main/java/com/extendedclip/deluxemenus/hooks/NamedHeadHook.java index aa880ba5..4b306fd9 100644 --- a/src/main/java/com/extendedclip/deluxemenus/hooks/NamedHeadHook.java +++ b/src/main/java/com/extendedclip/deluxemenus/hooks/NamedHeadHook.java @@ -6,7 +6,6 @@ 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; diff --git a/src/main/java/com/extendedclip/deluxemenus/hooks/SimpleItemGeneratorHook.java b/src/main/java/com/extendedclip/deluxemenus/hooks/SimpleItemGeneratorHook.java index 58e7eccb..8ec99f25 100644 --- a/src/main/java/com/extendedclip/deluxemenus/hooks/SimpleItemGeneratorHook.java +++ b/src/main/java/com/extendedclip/deluxemenus/hooks/SimpleItemGeneratorHook.java @@ -15,7 +15,6 @@ public class SimpleItemGeneratorHook implements ItemHook, SimpleCache { - private final Map cache = new ConcurrentHashMap<>(); private final DeluxeMenus plugin; @@ -34,9 +33,9 @@ public ItemStack getItem(@NotNull Player holder, @NotNull String... arguments) { return errorItem("Item arguments are absent."); } - final ItemStack item = cache.computeIfAbsent(arguments[0], id -> { - return SimpleItemGenerator.get().bakeItem(id, holder).orElse(null); - }); + final ItemStack item = cache.computeIfAbsent(arguments[0], id -> + SimpleItemGenerator.get().bakeItem(id, holder).orElse(null) + ); return item == null? errorItem("Item %s wasn't found by SimpleItemGenerator.", arguments[0]) : item.clone(); } diff --git a/src/main/java/com/extendedclip/deluxemenus/listener/PlayerListener.java b/src/main/java/com/extendedclip/deluxemenus/listener/PlayerListener.java index af69231b..830b1471 100644 --- a/src/main/java/com/extendedclip/deluxemenus/listener/PlayerListener.java +++ b/src/main/java/com/extendedclip/deluxemenus/listener/PlayerListener.java @@ -6,9 +6,10 @@ import com.extendedclip.deluxemenus.menu.MenuHolder; import com.extendedclip.deluxemenus.menu.MenuItem; import com.extendedclip.deluxemenus.requirement.RequirementList; +import com.extendedclip.deluxemenus.scheduler.scheduling.schedulers.TaskScheduler; +import com.extendedclip.deluxemenus.utils.DebugLevel; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; -import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -18,14 +19,17 @@ import org.bukkit.event.inventory.InventoryOpenEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import java.util.Optional; import java.util.UUID; import java.util.concurrent.TimeUnit; +import java.util.logging.Level; public class PlayerListener extends Listener { + private final TaskScheduler scheduler; private final Cache cache = CacheBuilder.newBuilder().expireAfterWrite(75, TimeUnit.MILLISECONDS).build(); // This is so dumb. Mojang fix your shit. @@ -33,9 +37,10 @@ public class PlayerListener extends Listener { public PlayerListener(@NotNull final DeluxeMenus plugin) { super(plugin); + this.scheduler = plugin.getScheduler(); } - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onCommandExecute(PlayerCommandPreprocessEvent event) { final String cmd = event.getMessage().substring(1); @@ -67,48 +72,74 @@ public void onLeave(PlayerQuitEvent event) { @EventHandler public void onOpen(InventoryOpenEvent event) { - if (!(event.getPlayer() instanceof Player)) { + if (!(event.getPlayer() instanceof Player player)) { return; } - final Player player = (Player) event.getPlayer(); - if (player.isSleeping()) { event.setCancelled(true); } if (Menu.isInMenu(player)) { - Menu.closeMenu(plugin, player, true); + Menu.closeMenu(plugin, player, false); } } @EventHandler public void onClose(InventoryCloseEvent event) { - - if (!(event.getPlayer() instanceof Player)) { + if (!(event.getPlayer() instanceof Player player)) { return; } - final Player player = (Player) event.getPlayer(); - if (Menu.isInMenu(player)) { Menu.closeMenu(plugin, player, false); - Bukkit.getScheduler().runTaskLater(plugin, () -> { - Menu.cleanInventory(plugin, player); - player.updateInventory(); - }, 3L); } + + scheduler.runTaskLater(player, () -> { + Menu.cleanInventory(plugin, player); + + boolean removed = false; + + for (ItemStack itemStack : player.getInventory().getContents()) { + if (itemStack == null) continue; + if (!plugin.getMenuItemMarker().isMarked(itemStack)) continue; + if (!plugin.isDupeProtectionFlagged(itemStack)) continue; + + player.getInventory().remove(itemStack); + removed = true; + + plugin.debug( + DebugLevel.LOWEST, + Level.INFO, + "DeluxeMenus item found in main inventory on close. Removing it." + ); + } + + ItemStack offhand = player.getInventory().getItemInOffHand(); + if (plugin.getMenuItemMarker().isMarked(offhand) && plugin.isDupeProtectionFlagged(offhand)) { + player.getInventory().setItemInOffHand(null); + removed = true; + + plugin.debug( + DebugLevel.LOWEST, + Level.INFO, + "DeluxeMenus item found in offhand on close. Removing it." + ); + } + + if (removed) { + player.updateInventory(); + } + }, 1L); } @EventHandler(priority = EventPriority.LOW) public void onClick(InventoryClickEvent event) { - if (!(event.getWhoClicked() instanceof Player)) { + if (!(event.getWhoClicked() instanceof Player player)) { return; } - final Player player = (Player) event.getWhoClicked(); - final Optional optionalHolder = Menu.getMenuHolder(player); if (optionalHolder.isEmpty()) { @@ -181,8 +212,7 @@ public void onClick(InventoryClickEvent event) { } if (event.getClick() == ClickType.MIDDLE) { - if (handleClick(player, holder, item.options().middleClickHandler(), item.options().middleClickRequirements())) { - } + handleClick(player, holder, item.options().middleClickHandler(), item.options().middleClickRequirements()); } } diff --git a/src/main/java/com/extendedclip/deluxemenus/menu/Menu.java b/src/main/java/com/extendedclip/deluxemenus/menu/Menu.java index 615bedf7..cbe3d37d 100644 --- a/src/main/java/com/extendedclip/deluxemenus/menu/Menu.java +++ b/src/main/java/com/extendedclip/deluxemenus/menu/Menu.java @@ -1,19 +1,32 @@ package com.extendedclip.deluxemenus.menu; import com.extendedclip.deluxemenus.DeluxeMenus; -import com.extendedclip.deluxemenus.action.ClickHandler; -import com.extendedclip.deluxemenus.dupe.MenuItemMarker; +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.events.DeluxeMenusOpenMenuEvent; import com.extendedclip.deluxemenus.events.DeluxeMenusPreOpenMenuEvent; import com.extendedclip.deluxemenus.menu.command.RegistrableMenuCommand; import com.extendedclip.deluxemenus.menu.options.MenuOptions; import com.extendedclip.deluxemenus.requirement.RequirementList; +import com.extendedclip.deluxemenus.scheduler.scheduling.schedulers.TaskScheduler; import com.extendedclip.deluxemenus.utils.DebugLevel; import com.extendedclip.deluxemenus.utils.StringUtils; - -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; +import java.util.Optional; +import java.util.Set; +import java.util.TreeMap; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -25,11 +38,12 @@ 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 final Map menus = new ConcurrentHashMap<>(); + private static final Set menuHolders = ConcurrentHashMap.newKeySet(); + private static final Map lastOpenedMenus = new ConcurrentHashMap<>(); private final DeluxeMenus plugin; + private final TaskScheduler scheduler; private final MenuOptions options; private final Map> items; // menu path starting from the plugin directory @@ -44,6 +58,7 @@ public Menu( final @NotNull String path ) { this.plugin = plugin; + this.scheduler = plugin.getScheduler(); this.options = options; this.items = items; this.path = path; @@ -183,7 +198,7 @@ public static void cleanInventory(final @NotNull DeluxeMenus plugin, final @NotN player.updateInventory(); } - public static void closeMenu(final @NotNull DeluxeMenus plugin, 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, final boolean runCloseCommmands) { Optional optionalHolder = getMenuHolder(player); if (optionalHolder.isEmpty()) { return; @@ -198,14 +213,26 @@ public static void closeMenu(final @NotNull DeluxeMenus plugin, final @NotNull P holder.getMenu().map(Menu::options).map(MenuOptions::closeHandler).flatMap(h -> h).ifPresent(h -> h.onClick(holder)); } + menuHolders.remove(holder); + lastOpenedMenus.put(player.getUniqueId(), holder.getMenu().orElse(null)); + if (close) { - Bukkit.getScheduler().runTask(plugin, () -> { + final TaskScheduler scheduler = plugin.getScheduler(); + final Runnable closeInventory = () -> { player.closeInventory(); cleanInventory(plugin, player); - }); + }; + + if (scheduler.isEntityThread(player)) { + closeInventory.run(); + } else { + scheduler.runTask(player, closeInventory); + } + } + + if (runCloseCommmands) { + holder.getMenu().map(Menu::options).map(MenuOptions::guiCloseCommands).ifPresent(commands -> executeCommands(plugin, player, commands, holder)); } - menuHolders.remove(holder); - lastOpenedMenus.put(player.getUniqueId(), holder.getMenu().orElse(null)); } public static void closeMenuForShutdown(final @NotNull DeluxeMenus plugin, final @NotNull Player player) { @@ -216,7 +243,7 @@ public static void closeMenuForShutdown(final @NotNull DeluxeMenus plugin, final } public static void closeMenu(final @NotNull DeluxeMenus plugin, final @NotNull Player player, final boolean close) { - closeMenu(plugin, player, close, false); + closeMenu(plugin, player, close, true, true); } private boolean hasOpenBypassPerm(final @NotNull Player viewer) { @@ -274,11 +301,13 @@ public void openMenu(final @NotNull Player viewer, final @Nullable Map { + scheduler.runTaskAsynchronously(() -> { Set activeItems = new HashSet<>(); @@ -361,6 +390,7 @@ public void openMenu(final @NotNull Player viewer, final @Nullable Map { - if(options.refresh()) { + holder.getMenu().map(Menu::options).map(MenuOptions::guiOpenCommands).ifPresent(commands -> executeCommands(plugin, viewer, commands, holder)); + + scheduler.runTask(viewer, () -> { + if (options.refresh()) { holder.startRefreshTask(); } @@ -395,17 +427,60 @@ public void openMenu(final @NotNull Player viewer, final @Nullable Map { + DeluxeMenusOpenMenuEvent openEvent = new DeluxeMenusOpenMenuEvent(viewer, holder); + Bukkit.getPluginManager().callEvent(openEvent); + }); + }); + } + + private static void executeCommands(final @NotNull DeluxeMenus plugin, final @NotNull Player viewer, final @NotNull List commands, final @NotNull MenuHolder holder) { + for (String command : commands) { + ActionType type = ActionType.getByStart(command); + if (type == null) continue; + + command = command.replaceFirst(Pattern.quote(type.getIdentifier()), "").trim(); + + ClickAction action = new ClickAction(type, command); + + Matcher d = DeluxeMenusConfig.DELAY_MATCHER.matcher(command); + + if (d.find()) { + action.setDelay(d.group(1)); + command = command.replaceFirst(Pattern.quote(d.group()), ""); + } + + Matcher ch = DeluxeMenusConfig.CHANCE_MATCHER.matcher(command); + + if (ch.find()) { + action.setChance(ch.group(1)); + command = command.replaceFirst(Pattern.quote(ch.group()), ""); + } + + action.setExecutable(command); + + final ClickActionTask actionTask = new ClickActionTask( + plugin, + viewer.getUniqueId(), + action.getType(), + command, + holder.getTypedArgs(), + true, + true + ); + + if (action.hasDelay()) { + plugin.getScheduler().runTaskLater(viewer, actionTask, action.getDelay(holder)); + } else { + plugin.getScheduler().runTask(viewer, actionTask); + } } - }); - - Bukkit.getScheduler().runTask(plugin, () -> { - DeluxeMenusOpenMenuEvent openEvent = new DeluxeMenusOpenMenuEvent(viewer, holder); - Bukkit.getPluginManager().callEvent(openEvent); - }); - }); - } + } public void refreshForAll() { menuHolders.stream().filter(menuHolder -> menuHolder.getMenuName().equalsIgnoreCase(options.name())).forEach(MenuHolder::refreshMenu); diff --git a/src/main/java/com/extendedclip/deluxemenus/menu/MenuHolder.java b/src/main/java/com/extendedclip/deluxemenus/menu/MenuHolder.java index b101a365..34afac38 100644 --- a/src/main/java/com/extendedclip/deluxemenus/menu/MenuHolder.java +++ b/src/main/java/com/extendedclip/deluxemenus/menu/MenuHolder.java @@ -2,20 +2,16 @@ import com.extendedclip.deluxemenus.DeluxeMenus; import com.extendedclip.deluxemenus.menu.options.MenuOptions; +import com.extendedclip.deluxemenus.scheduler.scheduling.schedulers.TaskScheduler; +import com.extendedclip.deluxemenus.scheduler.scheduling.tasks.MyScheduledTask; import com.extendedclip.deluxemenus.utils.StringUtils; -import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.scheduler.BukkitRunnable; -import org.bukkit.scheduler.BukkitTask; import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -24,27 +20,32 @@ public class MenuHolder implements InventoryHolder { private final DeluxeMenus plugin; + private final TaskScheduler scheduler; private final Player viewer; private Player placeholderPlayer; private String menuName; private Set activeItems; - private BukkitTask updateTask = null; - private BukkitTask refreshTask = null; + private MyScheduledTask updateTask = null; + private MyScheduledTask refreshTask = null; private Inventory inventory; - private boolean updating; + + private volatile boolean updating; + private boolean parsePlaceholdersInArguments; private boolean parsePlaceholdersAfterArguments; private Map typedArgs; public MenuHolder(final @NotNull DeluxeMenus plugin, final @NotNull Player viewer) { this.plugin = plugin; + this.scheduler = plugin.getScheduler(); this.viewer = viewer; } 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.scheduler = plugin.getScheduler(); this.viewer = viewer; this.menuName = menuName; this.activeItems = activeItems; @@ -55,7 +56,7 @@ public String getViewerName() { return viewer.getName(); } - public BukkitTask getUpdateTask() { + public MyScheduledTask getUpdateTask() { return updateTask; } @@ -104,7 +105,7 @@ public Optional getMenu() { } public @NotNull String setPlaceholders(final @NotNull String string) { - final Player player = this.placeholderPlayer != null ? this.placeholderPlayer : this.getViewer(); + final Player player = this.placeholderPlayer != null ? this.placeholderPlayer : this.viewer; if (player == null) { return string; } @@ -113,7 +114,7 @@ public Optional getMenu() { } public @NotNull String setArguments(final @NotNull String string) { - final Player player = this.placeholderPlayer != null ? this.placeholderPlayer : this.getViewer(); + final Player player = this.placeholderPlayer != null ? this.placeholderPlayer : this.viewer; return StringUtils.replaceArguments( string, @@ -136,82 +137,89 @@ public void refreshMenu() { return; } - setUpdating(true); - - Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> { + scheduler.runTaskAsynchronously(() -> { final Set active = new HashSet<>(); + final Set slotsToClear = new HashSet<>(); for (int i = 0; i < getInventory().getSize(); i++) { TreeMap e = menu.getMenuItems().get(i); if (e == null) { - getInventory().setItem(i, null); + slotsToClear.add(i); continue; } - boolean m = false; + boolean matched = false; for (MenuItem item : e.values()) { if (item.options().viewRequirements().isPresent()) { if (item.options().viewRequirements().get().evaluate(this)) { - m = true; + matched = true; active.add(item); break; } } else { - m = true; + matched = true; active.add(item); break; } } - if (!m) { - getInventory().setItem(i, null); + if (!matched) { + slotsToClear.add(i); } } if (active.isEmpty()) { - Menu.closeMenu(plugin, getViewer(), true); + scheduler.runTask(viewer, () -> Menu.closeMenu(plugin, viewer, true)); + return; } - Bukkit.getScheduler().runTask(plugin, () -> { + scheduler.runTask(viewer, () -> { + setUpdating(true); - boolean update = false; + try { + for (int slot : slotsToClear) { + getInventory().setItem(slot, null); + } - for (MenuItem item : active) { + boolean update = false; - ItemStack iStack = item.getItemStack(this); + for (MenuItem item : active) { - if (iStack == null) { - continue; - } + ItemStack iStack = item.getItemStack(this); - iStack = plugin.getMenuItemMarker().mark(iStack); + if (iStack == null) { + continue; + } - int slot = item.options().slot(); + iStack = plugin.getMenuItemMarker().mark(iStack); - if (slot >= menu.options().size()) { - continue; - } + int slot = item.options().slot(); - if (item.options().updatePlaceholders()) { - update = true; - } + if (slot >= menu.options().size()) { + continue; + } - getInventory().setItem(item.options().slot(), iStack); - } + if (item.options().updatePlaceholders()) { + update = true; + } - setActiveItems(active); + getInventory().setItem(item.options().slot(), iStack); + } - if (update && updateTask == null) { - startUpdatePlaceholdersTask(); - } else if(!update && updateTask != null) { - stopPlaceholderUpdate(); - } + setActiveItems(active); - setUpdating(false); + if (update && updateTask == null) { + startUpdatePlaceholdersTask(); + } else if (!update && updateTask != null) { + stopPlaceholderUpdate(); + } + } finally { + setUpdating(false); + } }); }); } @@ -227,7 +235,7 @@ public void stopPlaceholderUpdate() { } public void stopRefreshTask() { - if(refreshTask != null) { + if (refreshTask != null) { try { refreshTask.cancel(); } catch (Exception ignored) { @@ -237,20 +245,21 @@ public void stopRefreshTask() { } public void startRefreshTask() { - if(refreshTask != null) { + if (refreshTask != null) { stopRefreshTask(); } - refreshTask = new BukkitRunnable() { - @Override - public void run() { - refreshMenu(); - } - }.runTaskTimerAsynchronously(plugin, 20L, - 20L * Menu.getMenuByName(menuName) - .map(Menu::options) - .map(MenuOptions::refreshInterval) - .orElse(10)); + long initialDelay = 20L; + long period = 20L * Menu.getMenuByName(menuName) + .map(Menu::options) + .map(MenuOptions::refreshInterval) + .orElse(10); + + refreshTask = scheduler.runTaskTimerAsynchronously( + this::refreshMenu, + initialDelay, + period + ); } public void startUpdatePlaceholdersTask() { @@ -259,69 +268,70 @@ public void startUpdatePlaceholdersTask() { stopPlaceholderUpdate(); } - updateTask = new BukkitRunnable() { + long initialDelay = 20L; + long period = 20L * Menu.getMenuByName(menuName) + .map(Menu::options) + .map(MenuOptions::updateInterval) + .orElse(10); - @Override - public void run() { + updateTask = scheduler.runTaskTimer( + viewer, + () -> { - if (updating) { - return; - } - - Set items = getActiveItems(); + if (updating) { + return; + } - if (items == null) { - return; - } + Set items = getActiveItems(); - for (MenuItem item : items) { + if (items == null) { + return; + } - if (item.options().updatePlaceholders()) { + for (MenuItem item : items) { - ItemStack i = inventory.getItem(item.options().slot()); + if (item.options().updatePlaceholders()) { - if (i == null) { - continue; - } + ItemStack i = inventory.getItem(item.options().slot()); - int amt = i.getAmount(); + if (i == null) { + continue; + } - if (item.options().dynamicAmount().isPresent()) { - try { - amt = Integer.parseInt(setPlaceholdersAndArguments(item.options().dynamicAmount().get())); - if (amt <= 0) { - amt = 1; + int amt = i.getAmount(); + + if (item.options().dynamicAmount().isPresent()) { + try { + amt = Integer.parseInt(setPlaceholdersAndArguments(item.options().dynamicAmount().get())); + if (amt <= 0) { + amt = 1; + } + } catch (Exception exception) { + plugin.printStacktrace( + "Something went wrong while updating item in slot " + item.options().slot() + + ". Invalid dynamic amount: " + setPlaceholdersAndArguments(item.options().dynamicAmount().get()), + exception + ); } - } catch (Exception exception) { - plugin.printStacktrace( - "Something went wrong while updating item in slot " + item.options().slot() + - ". Invalid dynamic amount: " + setPlaceholdersAndArguments(item.options().dynamicAmount().get()), - exception - ); } - } - ItemMeta meta = i.getItemMeta(); + ItemMeta meta = i.getItemMeta(); - if (item.options().displayNameHasPlaceholders() && item.options().displayName().isPresent()) { - meta.setDisplayName(StringUtils.color(setPlaceholdersAndArguments(item.options().displayName().get()))); - } + if (item.options().displayNameHasPlaceholders() && item.options().displayName().isPresent()) { + meta.setDisplayName(StringUtils.color(setPlaceholdersAndArguments(item.options().displayName().get()))); + } - if (item.options().loreHasPlaceholders()) { - meta.setLore(item.getMenuItemLore(getHolder(), item.options().lore())); - } + if (item.options().loreHasPlaceholders()) { + meta.setLore(item.getMenuItemLore(getHolder(), item.options().lore())); + } - i.setItemMeta(meta); - i.setAmount(amt); + i.setItemMeta(meta); + i.setAmount(amt); + } } - } - } - }.runTaskTimerAsynchronously(plugin, 20L, - 20L * Menu.getMenuByName(menuName) - .map(Menu::options) - .map(MenuOptions::updateInterval) - .orElse(10)); + }, initialDelay, period + ); } public boolean isUpdating() { diff --git a/src/main/java/com/extendedclip/deluxemenus/menu/MenuItem.java b/src/main/java/com/extendedclip/deluxemenus/menu/MenuItem.java index 79fbd8eb..a896cdb8 100644 --- a/src/main/java/com/extendedclip/deluxemenus/menu/MenuItem.java +++ b/src/main/java/com/extendedclip/deluxemenus/menu/MenuItem.java @@ -2,10 +2,10 @@ import com.extendedclip.deluxemenus.DeluxeMenus; import com.extendedclip.deluxemenus.hooks.ItemHook; +import com.extendedclip.deluxemenus.menu.options.CustomModelDataComponent; 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.menu.options.CustomModelDataComponent; import com.extendedclip.deluxemenus.nbt.NbtProvider; import com.extendedclip.deluxemenus.utils.DebugLevel; import com.extendedclip.deluxemenus.utils.ItemUtils; @@ -15,8 +15,8 @@ import org.bukkit.Color; import org.bukkit.FireworkEffect; import org.bukkit.Material; -import org.bukkit.Registry; import org.bukkit.NamespacedKey; +import org.bukkit.Registry; import org.bukkit.block.Banner; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.type.Light; @@ -45,14 +45,14 @@ import java.io.ByteArrayInputStream; import java.io.IOException; -import java.util.Base64; -import java.util.Arrays; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Optional; import java.util.Objects; +import java.util.Optional; import java.util.logging.Level; import java.util.stream.Collectors; @@ -81,14 +81,11 @@ public static ItemStack base64ToItemStack(String data) { return (ItemStack) object; } return null; - } catch (IllegalArgumentException e) { - return null; - } catch (IOException e) { - return null; - } catch (ClassNotFoundException e) { + } catch (IllegalArgumentException | ClassNotFoundException | IOException e) { return null; } } + public ItemStack getItemStack(@NotNull final MenuHolder holder) { final Player viewer = holder.getViewer(); @@ -112,7 +109,6 @@ public ItemStack getItemStack(@NotNull final MenuHolder holder) { } } - if (ItemUtils.isPlayerItem(lowercaseStringMaterial)) { final ItemStack playerItem = INVENTORY_ITEM_ACCESSORS.get(lowercaseStringMaterial).apply(viewer.getInventory()); @@ -282,20 +278,18 @@ public ItemStack getItemStack(@NotNull final MenuHolder holder) { LoreAppendMode mode = this.options.loreAppendMode().orElse(LoreAppendMode.OVERRIDE); if (!this.options.hasLore() && this.options.loreAppendMode().isEmpty()) mode = LoreAppendMode.IGNORE; switch (mode) { - case IGNORE: // DM lore is not added at all - lore.addAll(itemLore); - break; - case TOP: // DM lore is added at the top + case IGNORE -> // DM lore is not added at all + lore.addAll(itemLore); + case TOP -> { lore.addAll(getMenuItemLore(holder, this.options.lore())); lore.addAll(itemLore); - break; - case BOTTOM: // DM lore is bottom at the bottom + } + case BOTTOM -> { lore.addAll(itemLore); lore.addAll(getMenuItemLore(holder, this.options.lore())); - break; - case OVERRIDE: // Lore from DM overrides the lore from the item - lore.addAll(getMenuItemLore(holder, this.options.lore())); - break; + } + case OVERRIDE -> // Lore from DM overrides the lore from the item + lore.addAll(getMenuItemLore(holder, this.options.lore())); } itemMeta.setLore(lore); @@ -373,69 +367,65 @@ public ItemStack getItemStack(@NotNull final MenuHolder holder) { Level.WARNING, "Trim pattern is not set for item with trim material " + trimMaterialName.get() ); - } else if (trimPatternName.isPresent()) { - plugin.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Trim material is not set for item with trim pattern " + trimPatternName.get() - ); - } + } else trimPatternName.ifPresent(s -> plugin.debug( + DebugLevel.HIGHEST, + Level.WARNING, + "Trim material is not set for item with trim pattern " + s + )); } - if (itemMeta instanceof LeatherArmorMeta && this.options.rgb().isPresent()) { - final LeatherArmorMeta leatherArmorMeta = (LeatherArmorMeta) itemMeta; + switch (itemMeta) { + case LeatherArmorMeta leatherArmorMeta when this.options.rgb().isPresent() -> { - final Color color = parseRGBColor(holder.setPlaceholdersAndArguments(this.options.rgb().get())); - if (color != null) { - leatherArmorMeta.setColor(color); - } else { - plugin.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Invalid rgb colors found for leather armor: " + this.options.rgb().get() - ); - } + final Color color = parseRGBColor(holder.setPlaceholdersAndArguments(this.options.rgb().get())); + if (color != null) { + leatherArmorMeta.setColor(color); + } else { + plugin.debug( + DebugLevel.HIGHEST, + Level.WARNING, + "Invalid rgb colors found for leather armor: " + this.options.rgb().get() + ); + } - itemStack.setItemMeta(leatherArmorMeta); - } else if (itemMeta instanceof FireworkEffectMeta && this.options.rgb().isPresent()) { - final FireworkEffectMeta fireworkEffectMeta = (FireworkEffectMeta) itemMeta; - final Color color = parseRGBColor(holder.setPlaceholdersAndArguments(this.options.rgb().get())); - if (color != null) { - fireworkEffectMeta.setEffect(FireworkEffect.builder().withColor(color).build()); - } else { - plugin.debug( - DebugLevel.HIGHEST, - Level.WARNING, - "Invalid RGB color found for firework or firework star: " + this.options.rgb().get() - ); + itemStack.setItemMeta(leatherArmorMeta); } - itemStack.setItemMeta(fireworkEffectMeta); - } else if (itemMeta instanceof EnchantmentStorageMeta && !this.options.enchantments().isEmpty()) { - final EnchantmentStorageMeta enchantmentStorageMeta = (EnchantmentStorageMeta) itemMeta; - for (final Map.Entry entry : this.options.enchantments().entrySet()) { - final boolean result = enchantmentStorageMeta.addStoredEnchant(entry.getKey(), entry.getValue(), true); - if (!result) { + case FireworkEffectMeta fireworkEffectMeta when this.options.rgb().isPresent() -> { + final Color color = parseRGBColor(holder.setPlaceholdersAndArguments(this.options.rgb().get())); + if (color != null) { + fireworkEffectMeta.setEffect(FireworkEffect.builder().withColor(color).build()); + } else { plugin.debug( - DebugLevel.HIGHEST, - Level.INFO, - "Failed to add enchantment " + entry.getKey().getName() + " to item " + itemStack.getType() + DebugLevel.HIGHEST, + Level.WARNING, + "Invalid RGB color found for firework or firework star: " + this.options.rgb().get() ); } + itemStack.setItemMeta(fireworkEffectMeta); } - itemStack.setItemMeta(enchantmentStorageMeta); - } else { - itemStack.setItemMeta(itemMeta); + case EnchantmentStorageMeta enchantmentStorageMeta when !this.options.enchantments().isEmpty() -> { + for (final Map.Entry entry : this.options.enchantments().entrySet()) { + final boolean result = enchantmentStorageMeta.addStoredEnchant(entry.getKey(), entry.getValue(), true); + if (!result) { + plugin.debug( + DebugLevel.HIGHEST, + Level.INFO, + "Failed to add enchantment " + entry.getKey().getName() + " to item " + itemStack.getType() + ); + } + } + itemStack.setItemMeta(enchantmentStorageMeta); + } + default -> itemStack.setItemMeta(itemMeta); } if (!(itemMeta instanceof EnchantmentStorageMeta) && !this.options.enchantments().isEmpty()) { this.options.enchantments().forEach((enchantment, level) -> itemMeta.addEnchant(enchantment, level, true)); } - if (this.options.lightLevel().isPresent() && itemMeta instanceof BlockDataMeta) { - final BlockDataMeta blockDataMeta = (BlockDataMeta) itemMeta; + if (this.options.lightLevel().isPresent() && itemMeta instanceof BlockDataMeta blockDataMeta) { final BlockData blockData = blockDataMeta.getBlockData(itemStack.getType()); - if (blockData instanceof Light) { - final Light light = (Light) blockData; + if (blockData instanceof Light light) { final String parsedLightLevel = holder.setPlaceholdersAndArguments(this.options.lightLevel().get()); try { final int lightLevel = Math.min(Integer.parseInt(parsedLightLevel), light.getMaximumLevel()); @@ -541,6 +531,10 @@ public ItemStack getItemStack(@NotNull final MenuHolder holder) { itemStack = NbtProvider.setInt(itemStack, parts[0], Integer.parseInt(parts[1])); } } + + if (NbtProvider.isAvailable()) { + itemStack = NbtProvider.setString(itemStack, "deluxemenus.item.dupeprotection", "true"); + } } return itemStack; diff --git a/src/main/java/com/extendedclip/deluxemenus/menu/command/RegistrableMenuCommand.java b/src/main/java/com/extendedclip/deluxemenus/menu/command/RegistrableMenuCommand.java index 286877dd..06feefd4 100644 --- a/src/main/java/com/extendedclip/deluxemenus/menu/command/RegistrableMenuCommand.java +++ b/src/main/java/com/extendedclip/deluxemenus/menu/command/RegistrableMenuCommand.java @@ -12,6 +12,7 @@ import org.bukkit.command.SimpleCommandMap; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.NonNull; import java.lang.reflect.Field; import java.util.Arrays; @@ -33,7 +34,7 @@ public class RegistrableMenuCommand extends Command { public RegistrableMenuCommand(final @NotNull DeluxeMenus plugin, final @NotNull Menu menu) { - super(menu.options().commands().isEmpty() ? menu.options().name() : menu.options().commands().get(0)); + super(menu.options().commands().isEmpty() ? menu.options().name() : menu.options().commands().getFirst()); this.plugin = plugin; this.menu = menu; @@ -43,12 +44,12 @@ public RegistrableMenuCommand(final @NotNull DeluxeMenus plugin, } @Override - public boolean execute(final @NotNull CommandSender sender, final @NotNull String commandLabel, final @NotNull String[] typedArgs) { + public boolean execute(final @NotNull CommandSender sender, final @NotNull String commandLabel, final @NotNull String @NonNull [] typedArgs) { if (this.unregistered) { throw new IllegalStateException("This command was unregistered!"); } - if (!(sender instanceof Player)) { + if (!(sender instanceof Player player)) { Msg.msg(sender, "Menus can only be opened by players!"); return true; } @@ -60,7 +61,7 @@ public boolean execute(final @NotNull CommandSender sender, final @NotNull Strin if (typedArgs.length < menu.options().arguments().size()) { if (menu.options().argumentsUsageMessage().isPresent()) { String usageMessage = menu.options().argumentsUsageMessage().get(); - Msg.msg(sender, StringUtils.replacePlaceholders(usageMessage, (Player) sender)); + Msg.msg(sender, StringUtils.replacePlaceholders(usageMessage, player)); } return true; } @@ -79,7 +80,6 @@ public boolean execute(final @NotNull CommandSender sender, final @NotNull Strin } } - Player player = (Player) sender; plugin.debug(DebugLevel.LOWEST, Level.INFO, "opening menu: " + menu.options().name()); menu.openMenu(player, argMap, null); return true; @@ -89,26 +89,9 @@ public void register() { if (registered) { throw new IllegalStateException("This command was already registered!"); } - if (registered) { - throw new IllegalStateException("This command was already registered!"); - } - registered = true; 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; - } - } if (commandMap == null) { try { final Field f = Bukkit.getServer().getClass().getDeclaredField("commandMap"); @@ -164,6 +147,7 @@ public void unregister() { knownCommands = SimpleCommandMap.class.getDeclaredField("knownCommands"); knownCommands.setAccessible(true); + //noinspection unchecked 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. diff --git a/src/main/java/com/extendedclip/deluxemenus/menu/options/MenuOptions.java b/src/main/java/com/extendedclip/deluxemenus/menu/options/MenuOptions.java index c5c4df0a..2e4e2f90 100644 --- a/src/main/java/com/extendedclip/deluxemenus/menu/options/MenuOptions.java +++ b/src/main/java/com/extendedclip/deluxemenus/menu/options/MenuOptions.java @@ -23,6 +23,8 @@ public class MenuOptions { private final boolean enableBypassPerm; private final List commands; + private final List guiCloseCommands; + private final List guiOpenCommands; private final boolean registerCommands; private final List arguments; private final List argumentRequirements; @@ -45,6 +47,8 @@ private MenuOptions(final @NotNull MenuOptionsBuilder builder) { this.enableBypassPerm = builder.enableBypassPerm; this.commands = builder.commands; + this.guiCloseCommands = builder.guiCloseCommands; + this.guiOpenCommands = builder.guiOpenCommands; this.registerCommands = builder.registerCommands; this.arguments = builder.arguments; this.argumentRequirements = builder.argumentRequirements; @@ -103,6 +107,14 @@ public boolean enableBypassPerm() { return this.commands; } + public @Nullable List guiCloseCommands() { + return this.guiCloseCommands; + } + + public @Nullable List guiOpenCommands() { + return this.guiOpenCommands; + } + public boolean registerCommands() { return this.registerCommands; } @@ -142,6 +154,8 @@ public boolean registerCommands() { .parsePlaceholdersAfterArguments(this.parsePlaceholdersAfterArguments) .enableBypassPerm(this.enableBypassPerm) .commands(this.commands) + .guiCloseCommands(this.guiCloseCommands) + .guiOpenCommands(this.guiOpenCommands) .registerCommands(this.registerCommands) .arguments(this.arguments) .argumentRequirements(this.argumentRequirements) @@ -165,6 +179,8 @@ public static class MenuOptionsBuilder { private boolean enableBypassPerm = false; private List commands = List.of(); + private List guiCloseCommands; + private List guiOpenCommands; private boolean registerCommands = false; private List arguments = List.of(); private List argumentRequirements = List.of(); @@ -234,6 +250,16 @@ public MenuOptionsBuilder commands(final @NotNull List<@NotNull String> commands return this; } + public MenuOptionsBuilder guiCloseCommands(final @Nullable List guiCloseCommands) { + this.guiCloseCommands = guiCloseCommands; + return this; + } + + public MenuOptionsBuilder guiOpenCommands(final @Nullable List guiOpenCommands) { + this.guiOpenCommands = guiOpenCommands; + return this; + } + public MenuOptionsBuilder registerCommands(final boolean registerCommands) { this.registerCommands = registerCommands; return this; diff --git a/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java b/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java index bab7d857..f6c077ee 100644 --- a/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java +++ b/src/main/java/com/extendedclip/deluxemenus/persistentmeta/PersistentMetaHandler.java @@ -120,9 +120,9 @@ public Map getMetaValues( return player.getPersistentDataContainer().getKeys().stream() .filter(key -> player.getPersistentDataContainer().has(key, type.getPDType())) .map(key -> Pair.of(key.toString(), player.getPersistentDataContainer().get(key, type.getPDType()))) - .filter(entry -> entry.getValue() != null) - .filter(entry -> type.isSupported(entry.getValue())) - .collect(Collectors.toMap(Pair::getKey, Pair::getValue)); + .filter(entry -> entry.value() != null) + .filter(entry -> type.isSupported(entry.value())) + .collect(Collectors.toMap(Pair::key, Pair::value)); } /** @@ -223,7 +223,7 @@ public Map getMetaValues( return OperationResult.EXISTENT_VALUE_IS_DIFFERENT_TYPE; } - player.getPersistentDataContainer().set(key, DataType.BOOLEAN.getPDType(), currentValue.equalsIgnoreCase("true") ? "false" : "true"); + player.getPersistentDataContainer().set(key, DataType.BOOLEAN.getPDType(), Boolean.toString(!currentValue.equalsIgnoreCase("true"))); return OperationResult.SUCCESS; } @@ -352,30 +352,32 @@ public Map getMetaValues( final Object parsedValue = parseValueByType(type, args.length >= 4 ? args[3] : null); - switch (action) { - case SET: + return switch (action) { + case SET -> { if (parsedValue == null) { - return OperationResult.NEW_VALUE_IS_DIFFERENT_TYPE; + yield OperationResult.NEW_VALUE_IS_DIFFERENT_TYPE; } - return setMetaValue(player, key, type, parsedValue); - case REMOVE: - return removeMetaValue(player, key, type); - case ADD: + yield setMetaValue(player, key, type, parsedValue); + } + case REMOVE -> removeMetaValue(player, key, type); + case ADD -> { if (!(parsedValue instanceof Number)) { - return OperationResult.NEW_VALUE_IS_DIFFERENT_TYPE; + yield OperationResult.NEW_VALUE_IS_DIFFERENT_TYPE; } - return addMetaValue(player, key, type, (Number) parsedValue); - case SUBTRACT: + yield addMetaValue(player, key, type, (Number) parsedValue); + } + case SUBTRACT -> { if (!(parsedValue instanceof Number)) { - return OperationResult.NEW_VALUE_IS_DIFFERENT_TYPE; + yield OperationResult.NEW_VALUE_IS_DIFFERENT_TYPE; } - return subtractMetaValue(player, key, type, (Number) parsedValue); - } + yield subtractMetaValue(player, key, type, (Number) parsedValue); + } + default -> OperationResult.INVALID_SYNTAX; + }; - return OperationResult.INVALID_SYNTAX; } /** @@ -424,7 +426,6 @@ public Map getMetaValues( * @param key The string to parse. * @return The {@link NamespacedKey} or null if the key could not be parsed. */ - @SuppressWarnings("UnstableApiUsage") public @Nullable NamespacedKey getKey(@NotNull final String key) { final NamespacedKey namespacedKey; diff --git a/src/main/java/com/extendedclip/deluxemenus/placeholder/Expansion.java b/src/main/java/com/extendedclip/deluxemenus/placeholder/Expansion.java index 6a1a6ea0..ac467470 100644 --- a/src/main/java/com/extendedclip/deluxemenus/placeholder/Expansion.java +++ b/src/main/java/com/extendedclip/deluxemenus/placeholder/Expansion.java @@ -70,13 +70,13 @@ public boolean persist() { final String parsedInputLower = parsedInput.toLowerCase(); switch (parsedInputLower) { - case "is_in_menu": { + case "is_in_menu" -> { return getBooleanAsString(Menu.getMenuHolder(onlinePlayer).isPresent()); } - case "opened_menu": { + case "opened_menu" -> { return Menu.getOpenMenu(onlinePlayer).map(Menu::options).map(MenuOptions::name).orElse(""); } - case "last_menu": { + case "last_menu" -> { return Menu.getLastMenu(onlinePlayer).map(Menu::options).map(MenuOptions::name).orElse(""); } } diff --git a/src/main/java/com/extendedclip/deluxemenus/requirement/HasItemRequirement.java b/src/main/java/com/extendedclip/deluxemenus/requirement/HasItemRequirement.java index 9c1a1442..8a33e741 100644 --- a/src/main/java/com/extendedclip/deluxemenus/requirement/HasItemRequirement.java +++ b/src/main/java/com/extendedclip/deluxemenus/requirement/HasItemRequirement.java @@ -187,7 +187,7 @@ private boolean itemModelComponentContains(MenuHolder holder, @NotNull final Cus .map(holder::setPlaceholdersAndArguments) .map(StringUtils::parseRGBColor) .filter(Objects::nonNull) - .collect(Collectors.toList()); + .toList(); for (Color color : colors) { if (!modelData.getColors().contains(color)) { @@ -201,7 +201,7 @@ private boolean itemModelComponentContains(MenuHolder holder, @NotNull final Cus .stream() .map(holder::setPlaceholdersAndArguments) .map(Boolean::parseBoolean) - .collect(Collectors.toList()); + .toList(); for (Boolean flag : flags) { if (!modelData.getFlags().contains(flag)) { @@ -215,7 +215,7 @@ private boolean itemModelComponentContains(MenuHolder holder, @NotNull final Cus .stream() .map(holder::setPlaceholdersAndArguments) .map(Float::parseFloat) - .collect(Collectors.toList()); + .toList(); for (Float floatValue : floats) { if (!modelData.getFloats().contains(floatValue)) { @@ -228,7 +228,7 @@ private boolean itemModelComponentContains(MenuHolder holder, @NotNull final Cus final List strings = wrapper.strings() .stream() .map(holder::setPlaceholdersAndArguments) - .collect(Collectors.toList()); + .toList(); for (String string : strings) { if (!modelData.getStrings().contains(string)) { diff --git a/src/main/java/com/extendedclip/deluxemenus/requirement/InputResultRequirement.java b/src/main/java/com/extendedclip/deluxemenus/requirement/InputResultRequirement.java index 93932422..8b11ce83 100644 --- a/src/main/java/com/extendedclip/deluxemenus/requirement/InputResultRequirement.java +++ b/src/main/java/com/extendedclip/deluxemenus/requirement/InputResultRequirement.java @@ -21,24 +21,32 @@ public boolean evaluate(MenuHolder holder) { String parsedResult = holder.setPlaceholdersAndArguments(this.result); switch (type) { - case STRING_CONTAINS: - return parsedInput.contains(parsedResult); - case STRING_EQUALS: - return parsedInput.equals(parsedResult); - case STRING_EQUALS_IGNORECASE: - return parsedInput.equalsIgnoreCase(parsedResult); - case STRING_DOES_NOT_CONTAIN: - return !parsedInput.contains(parsedResult); - case STRING_DOES_NOT_EQUAL: - return !parsedInput.equals(parsedResult); - case STRING_DOES_NOT_EQUAL_IGNORECASE: - return !parsedInput.equalsIgnoreCase(parsedResult); - case STRING_CONTAINS_IGNORECASE: - return parsedInput.toLowerCase().contains(parsedResult.toLowerCase()); - case STRING_DOES_NOT_CONTAIN_IGNORECASE: - return !parsedInput.toLowerCase().contains(parsedResult.toLowerCase()); - default: - break; + case STRING_CONTAINS -> { + return parsedInput.contains(parsedResult); + } + case STRING_EQUALS -> { + return parsedInput.equals(parsedResult); + } + case STRING_EQUALS_IGNORECASE -> { + return parsedInput.equalsIgnoreCase(parsedResult); + } + case STRING_DOES_NOT_CONTAIN -> { + return !parsedInput.contains(parsedResult); + } + case STRING_DOES_NOT_EQUAL -> { + return !parsedInput.equals(parsedResult); + } + case STRING_DOES_NOT_EQUAL_IGNORECASE -> { + return !parsedInput.equalsIgnoreCase(parsedResult); + } + case STRING_CONTAINS_IGNORECASE -> { + return parsedInput.toLowerCase().contains(parsedResult.toLowerCase()); + } + case STRING_DOES_NOT_CONTAIN_IGNORECASE -> { + return !parsedInput.toLowerCase().contains(parsedResult.toLowerCase()); + } + default -> { + } } double in; @@ -64,23 +72,14 @@ public boolean evaluate(MenuHolder holder) { return false; } - switch (type) { - case GREATER_THAN: - return in > res; - case GREATER_THAN_EQUAL_TO: - return in >= res; - case EQUAL_TO: - return in == res; - case NOT_EQUAL_TO: - return in != res; - case LESS_THAN_EQUAL_TO: - return in <= res; - case LESS_THAN: - return in < res; - default: - break; - } - return false; + return switch (type) { + case GREATER_THAN -> in > res; + case GREATER_THAN_EQUAL_TO -> in >= res; + case EQUAL_TO -> in == res; + case NOT_EQUAL_TO -> in != res; + case LESS_THAN_EQUAL_TO -> in <= res; + case LESS_THAN -> in < res; + default -> false; + }; } } - diff --git a/src/main/java/com/extendedclip/deluxemenus/requirement/IsNearRequirement.java b/src/main/java/com/extendedclip/deluxemenus/requirement/IsNearRequirement.java index d207aba7..e5b575a5 100644 --- a/src/main/java/com/extendedclip/deluxemenus/requirement/IsNearRequirement.java +++ b/src/main/java/com/extendedclip/deluxemenus/requirement/IsNearRequirement.java @@ -24,6 +24,6 @@ public boolean evaluate(MenuHolder holder) { if (holder.getViewer().getWorld().getName().equals(location.getWorld().getName())) { withinRange = holder.getViewer().getLocation().distance(location) < distance; } - return invert ? !withinRange : withinRange; + return invert != withinRange; } } diff --git a/src/main/java/com/extendedclip/deluxemenus/requirement/IsObjectRequirement.java b/src/main/java/com/extendedclip/deluxemenus/requirement/IsObjectRequirement.java index 467908fa..f9a1b79a 100644 --- a/src/main/java/com/extendedclip/deluxemenus/requirement/IsObjectRequirement.java +++ b/src/main/java/com/extendedclip/deluxemenus/requirement/IsObjectRequirement.java @@ -24,27 +24,32 @@ public boolean evaluate(MenuHolder holder) { String toCheck = holder.setPlaceholdersAndArguments(input); switch (object) { - case "int": + case "int" -> { return Ints.tryParse(toCheck) != null; - case "double": + } + case "double" -> { return Doubles.tryParse(toCheck) != null; - case "player": + } + case "player" -> { try { UUID id = UUID.fromString(toCheck); return Bukkit.getPlayer(id) != null; } catch (IllegalArgumentException e) { return Bukkit.getPlayerExact(toCheck) != null; } - case "uuid": + } + case "uuid" -> { try { UUID.fromString(toCheck); return true; } catch (IllegalArgumentException e) { return false; } - default: + } + default -> { 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 56a67760..0958d0cd 100644 --- a/src/main/java/com/extendedclip/deluxemenus/requirement/JavascriptRequirement.java +++ b/src/main/java/com/extendedclip/deluxemenus/requirement/JavascriptRequirement.java @@ -3,10 +3,10 @@ import com.extendedclip.deluxemenus.DeluxeMenus; import com.extendedclip.deluxemenus.menu.MenuHolder; import com.extendedclip.deluxemenus.utils.DebugLevel; -import java.util.logging.Level; import javax.script.ScriptEngineFactory; import javax.script.ScriptEngineManager; import javax.script.ScriptException; +import java.util.logging.Level; import org.bukkit.Bukkit; import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.ServicePriority; @@ -27,8 +27,8 @@ public JavascriptRequirement(final @NotNull DeluxeMenus plugin, String expressio this.expression = expression; if (engine == null) { if (manager.isProvidedFor(ScriptEngineManager.class)) { - final RegisteredServiceProvider provider = manager.getRegistration(ScriptEngineManager.class); - engine = (ScriptEngineManager) provider.getProvider(); + final RegisteredServiceProvider provider = manager.getRegistration(ScriptEngineManager.class); + engine = provider.getProvider(); } else { engine = new ScriptEngineManager(); manager.register(ScriptEngineManager.class, engine, plugin, ServicePriority.Highest); diff --git a/src/main/java/com/extendedclip/deluxemenus/requirement/RequirementType.java b/src/main/java/com/extendedclip/deluxemenus/requirement/RequirementType.java index d36213f4..61ce60b1 100644 --- a/src/main/java/com/extendedclip/deluxemenus/requirement/RequirementType.java +++ b/src/main/java/com/extendedclip/deluxemenus/requirement/RequirementType.java @@ -31,10 +31,10 @@ public enum RequirementType { Arrays.asList("amount", "placeholder")), HAS_EXP(Arrays.asList("has exp", "hasexp", "exp"), "Checks if a player has enough exp", - Arrays.asList("amount")), + List.of("amount")), DOES_NOT_HAVE_EXP(Arrays.asList("!has exp", "!hasexp", "!exp"), "Checks if a player has enough exp", - Arrays.asList("amount")), + List.of("amount")), HAS_PERMISSION(Arrays.asList("has permission", "has perm", "haspermission", "hasperm", "perm"), "Checks if a player has a specific permission", Collections.singletonList("permission")), DOES_NOT_HAVE_PERMISSION( @@ -86,10 +86,10 @@ public enum RequirementType { REGEX_DOES_NOT_MATCH(Arrays.asList("!regex matches", "!regex"), "Checks if a placeholder parsed string does not match a regex pattern", Arrays.asList("input", "regex")), - STRING_LENGTH(Arrays.asList("string length"), + STRING_LENGTH(List.of("string length"), "Checks if the given string's length is between the provided minimum and (optionally) maximum.", Arrays.asList("input", "min", "max")), - IS_OBJECT(Arrays.asList("is object"), + IS_OBJECT(List.of("is object"), "Checks if the given string can be parsed as a given Java object.", Arrays.asList("input", "object")); diff --git a/src/main/java/com/extendedclip/deluxemenus/scheduler/UniversalRunnable.java b/src/main/java/com/extendedclip/deluxemenus/scheduler/UniversalRunnable.java new file mode 100644 index 00000000..adcb1cff --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/scheduler/UniversalRunnable.java @@ -0,0 +1,176 @@ +/* + * MIT License + * + * Copyright (c) 2023 Sevastjan + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.extendedclip.deluxemenus.scheduler; + +import com.extendedclip.deluxemenus.scheduler.scheduling.schedulers.TaskScheduler; +import com.extendedclip.deluxemenus.scheduler.scheduling.tasks.MyScheduledTask; +import org.bukkit.plugin.Plugin; + +/** Just modified BukkitRunnable */ +public abstract class UniversalRunnable implements Runnable { + MyScheduledTask task; + + public synchronized void cancel() throws IllegalStateException { + checkScheduled(); + task.cancel(); + } + + /** + * Returns true if this task has been cancelled. + * + * @return true if the task has been cancelled + * @throws IllegalStateException if task was not scheduled yet + */ + public synchronized boolean isCancelled() throws IllegalStateException { + checkScheduled(); + return task.isCancelled(); + } + + /** + * Schedules this in the Bukkit scheduler to run on next tick. + * + * @param plugin the reference to the plugin scheduling task + * @return {@link MyScheduledTask} + * @throws IllegalArgumentException if plugin is null + * @throws IllegalStateException if this was already scheduled + * @see TaskScheduler#runTask(Runnable) + */ + + public synchronized MyScheduledTask runTask(Plugin plugin) throws IllegalArgumentException, IllegalStateException { + checkNotYetScheduled(); + return setupTask(UniversalScheduler.getScheduler(plugin).runTask(this)); + } + + /** + * Asynchronous tasks should never access any API in Bukkit. Great care + * should be taken to assure the thread-safety of asynchronous tasks. + *

+ * Schedules this in the Bukkit scheduler to run asynchronously. + * + * @param plugin the reference to the plugin scheduling task + * @return {@link MyScheduledTask} + * @throws IllegalArgumentException if plugin is null + * @throws IllegalStateException if this was already scheduled + * @see TaskScheduler#runTaskAsynchronously(Runnable) + */ + + public synchronized MyScheduledTask runTaskAsynchronously(Plugin plugin) throws IllegalArgumentException, IllegalStateException { + checkNotYetScheduled(); + return setupTask(UniversalScheduler.getScheduler(plugin).runTaskAsynchronously(this)); + } + + /** + * Schedules this to run after the specified number of server ticks. + * + * @param plugin the reference to the plugin scheduling task + * @param delay the ticks to wait before running the task + * @return {@link MyScheduledTask} + * @throws IllegalArgumentException if plugin is null + * @throws IllegalStateException if this was already scheduled + * @see TaskScheduler#runTaskLater(Runnable, long) + */ + + public synchronized MyScheduledTask runTaskLater(Plugin plugin, long delay) throws IllegalArgumentException, IllegalStateException { + checkNotYetScheduled(); + return setupTask(UniversalScheduler.getScheduler(plugin).runTaskLater(this, delay)); + } + + /** + * Asynchronous tasks should never access any API in Bukkit. Great care + * should be taken to assure the thread-safety of asynchronous tasks. + *

+ * Schedules this to run asynchronously after the specified number of + * server ticks. + * + * @param plugin the reference to the plugin scheduling task + * @param delay the ticks to wait before running the task + * @return {@link MyScheduledTask} + * @throws IllegalArgumentException if plugin is null + * @throws IllegalStateException if this was already scheduled + * @see TaskScheduler#runTaskLaterAsynchronously(Runnable, long) + */ + + public synchronized MyScheduledTask runTaskLaterAsynchronously(Plugin plugin, long delay) throws IllegalArgumentException, IllegalStateException { + checkNotYetScheduled(); + return setupTask(UniversalScheduler.getScheduler(plugin).runTaskLaterAsynchronously(this, delay)); + } + + /** + * Schedules this to repeatedly run until cancelled, starting after the + * specified number of server ticks. + * + * @param plugin the reference to the plugin scheduling task + * @param delay the ticks to wait before running the task + * @param period the ticks to wait between runs + * @return {@link MyScheduledTask} + * @throws IllegalArgumentException if plugin is null + * @throws IllegalStateException if this was already scheduled + * @see TaskScheduler#runTaskTimer(Runnable, long, long) + */ + + public synchronized MyScheduledTask runTaskTimer(Plugin plugin, long delay, long period) throws IllegalArgumentException, IllegalStateException { + checkNotYetScheduled(); + return setupTask(UniversalScheduler.getScheduler(plugin).runTaskTimer(this, delay, period)); + } + + /** + * Asynchronous tasks should never access any API in Bukkit. Great care + * should be taken to assure the thread-safety of asynchronous tasks. + *

+ * Schedules this to repeatedly run asynchronously until cancelled, + * starting after the specified number of server ticks. + * + * @param plugin the reference to the plugin scheduling task + * @param delay the ticks to wait before running the task for the first + * time + * @param period the ticks to wait between runs + * @return {@link MyScheduledTask} + * @throws IllegalArgumentException if plugin is null + * @throws IllegalStateException if this was already scheduled + * @see TaskScheduler#runTaskTimerAsynchronously(Runnable, long, long) + */ + + public synchronized MyScheduledTask runTaskTimerAsynchronously(Plugin plugin, long delay, long period) throws IllegalArgumentException, IllegalStateException { + checkNotYetScheduled(); + return setupTask(UniversalScheduler.getScheduler(plugin).runTaskTimerAsynchronously(this, delay, period)); + } + + private void checkScheduled() { + if (task == null) { + throw new IllegalStateException("Not scheduled yet"); + } + } + + private void checkNotYetScheduled() { + if (task != null) { + throw new IllegalStateException("Already scheduled"); + } + } + + private MyScheduledTask setupTask(final MyScheduledTask task) { + this.task = task; + return task; + } + +} diff --git a/src/main/java/com/extendedclip/deluxemenus/scheduler/UniversalScheduler.java b/src/main/java/com/extendedclip/deluxemenus/scheduler/UniversalScheduler.java new file mode 100644 index 00000000..8949a140 --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/scheduler/UniversalScheduler.java @@ -0,0 +1,42 @@ +/* + * MIT License + * + * Copyright (c) 2023 Sevastjan + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.extendedclip.deluxemenus.scheduler; + +import com.extendedclip.deluxemenus.scheduler.bukkit.BukkitScheduler; +import com.extendedclip.deluxemenus.scheduler.folia.FoliaScheduler; +import com.extendedclip.deluxemenus.scheduler.paper.PaperScheduler; +import com.extendedclip.deluxemenus.scheduler.scheduling.schedulers.TaskScheduler; +import com.extendedclip.deluxemenus.scheduler.utils.JavaUtil; +import org.bukkit.plugin.Plugin; + +public class UniversalScheduler { + private static final boolean IS_FOLIA = JavaUtil.classExists("io.papermc.paper.threadedregions.RegionizedServer"); + private static final boolean IS_CANVAS = JavaUtil.classExists("io.canvasmc.canvas.server.ThreadedServer"); + private static final boolean IS_EXPANDED_SCHEDULING_AVAILABLE = JavaUtil.classExists("io.papermc.paper.threadedregions.scheduler.ScheduledTask"); + + public static TaskScheduler getScheduler(Plugin plugin) { + return IS_FOLIA || IS_CANVAS ? new FoliaScheduler(plugin) : (IS_EXPANDED_SCHEDULING_AVAILABLE ? new PaperScheduler(plugin) : new BukkitScheduler(plugin)); + } + +} diff --git a/src/main/java/com/extendedclip/deluxemenus/scheduler/bukkit/BukkitScheduledTask.java b/src/main/java/com/extendedclip/deluxemenus/scheduler/bukkit/BukkitScheduledTask.java new file mode 100644 index 00000000..b79deffb --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/scheduler/bukkit/BukkitScheduledTask.java @@ -0,0 +1,71 @@ +/* + * MIT License + * + * Copyright (c) 2023 Sevastjan + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.extendedclip.deluxemenus.scheduler.bukkit; + +import com.extendedclip.deluxemenus.scheduler.scheduling.tasks.MyScheduledTask; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitTask; + +public class BukkitScheduledTask implements MyScheduledTask { + + BukkitTask task; + + boolean isRepeating; + + public BukkitScheduledTask(final BukkitTask task) { + this.task = task; + this.isRepeating = false; + } + + public BukkitScheduledTask(final BukkitTask task, boolean isRepeating) { + this.task = task; + this.isRepeating = isRepeating; + } + + @Override + public void cancel() { + task.cancel(); + } + + @Override + public boolean isCancelled() { + return task.isCancelled(); + } + + @Override + public Plugin getOwningPlugin() { + return task.getOwner(); + } + + @Override + public boolean isCurrentlyRunning() { + return Bukkit.getServer().getScheduler().isCurrentlyRunning(this.task.getTaskId()); //There's no other way. Fuck bukkit + } + + @Override + public boolean isRepeatingTask() { + return isRepeating; + } +} diff --git a/src/main/java/com/extendedclip/deluxemenus/scheduler/bukkit/BukkitScheduler.java b/src/main/java/com/extendedclip/deluxemenus/scheduler/bukkit/BukkitScheduler.java new file mode 100644 index 00000000..4c562dd0 --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/scheduler/bukkit/BukkitScheduler.java @@ -0,0 +1,129 @@ +/* + * MIT License + * + * Copyright (c) 2023 Sevastjan + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.extendedclip.deluxemenus.scheduler.bukkit; + +import com.extendedclip.deluxemenus.scheduler.scheduling.schedulers.TaskScheduler; +import com.extendedclip.deluxemenus.scheduler.scheduling.tasks.MyScheduledTask; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.plugin.Plugin; + +public class BukkitScheduler implements TaskScheduler { + final Plugin plugin; + + public BukkitScheduler(Plugin plugin) { + this.plugin = plugin; + } + + @Override + public boolean isGlobalThread() { + return Bukkit.getServer().isPrimaryThread(); + } + + @Override + public boolean isEntityThread(Entity entity) { + return Bukkit.getServer().isPrimaryThread(); + } + + @Override + public boolean isRegionThread(Location location) { + return Bukkit.getServer().isPrimaryThread(); + } + + @Override + public MyScheduledTask runTask(Runnable runnable) { + return new BukkitScheduledTask(Bukkit.getScheduler().runTask(plugin, runnable)); + } + + @Override + public MyScheduledTask runTaskLater(Runnable runnable, long delay) { + return new BukkitScheduledTask(Bukkit.getScheduler().runTaskLater(plugin, runnable, delay)); + } + + @Override + public MyScheduledTask runTaskTimer(Runnable runnable, long delay, long period) { + return new BukkitScheduledTask(Bukkit.getScheduler().runTaskTimer(plugin, runnable, delay, period)); + } + + @Override + public MyScheduledTask runTaskAsynchronously(Runnable runnable) { + return new BukkitScheduledTask(Bukkit.getScheduler().runTaskAsynchronously(plugin, runnable)); + } + + @Override + public MyScheduledTask runTaskLaterAsynchronously(Runnable runnable, long delay) { + return new BukkitScheduledTask(Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, runnable, delay)); + } + + @Override + public MyScheduledTask runTaskTimerAsynchronously(Runnable runnable, long delay, long period) { + return new BukkitScheduledTask(Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, runnable, delay, period)); + } + + //Useless? Or... + public MyScheduledTask runTask(Plugin plugin, Runnable runnable) { + return new BukkitScheduledTask(Bukkit.getScheduler().runTask(plugin, runnable)); + } + + @Override + public MyScheduledTask runTaskLater(Plugin plugin, Runnable runnable, long delay) { + return new BukkitScheduledTask(Bukkit.getScheduler().runTaskLater(plugin, runnable, delay)); + } + + @Override + public MyScheduledTask runTaskTimer(Plugin plugin, Runnable runnable, long delay, long period) { + return new BukkitScheduledTask(Bukkit.getScheduler().runTaskTimer(plugin, runnable, delay, period)); + } + + @Override + public MyScheduledTask runTaskAsynchronously(Plugin plugin, Runnable runnable) { + return new BukkitScheduledTask(Bukkit.getScheduler().runTaskAsynchronously(plugin, runnable)); + } + + @Override + public MyScheduledTask runTaskLaterAsynchronously(Plugin plugin, Runnable runnable, long delay) { + return new BukkitScheduledTask(Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, runnable, delay)); + } + + @Override + public MyScheduledTask runTaskTimerAsynchronously(Plugin plugin, Runnable runnable, long delay, long period) { + return new BukkitScheduledTask(Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, runnable, delay, period)); + } + + @Override + public void execute(Runnable runnable) { + Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, runnable); + } + + @Override + public void cancelTasks() { + Bukkit.getScheduler().cancelTasks(plugin); + } + + @Override + public void cancelTasks(Plugin plugin) { + Bukkit.getScheduler().cancelTasks(plugin); + } +} diff --git a/src/main/java/com/extendedclip/deluxemenus/scheduler/folia/FoliaScheduledTask.java b/src/main/java/com/extendedclip/deluxemenus/scheduler/folia/FoliaScheduledTask.java new file mode 100644 index 00000000..eb9f199d --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/scheduler/folia/FoliaScheduledTask.java @@ -0,0 +1,65 @@ +/* + * MIT License + * + * Copyright (c) 2023 Sevastjan + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.extendedclip.deluxemenus.scheduler.folia; + +import com.extendedclip.deluxemenus.scheduler.scheduling.tasks.MyScheduledTask; +import io.papermc.paper.threadedregions.scheduler.ScheduledTask; +import org.bukkit.plugin.Plugin; + +public class FoliaScheduledTask implements MyScheduledTask { + private final ScheduledTask task; + + public FoliaScheduledTask(final ScheduledTask task) { + this.task = task; + } + + public void cancel() { + if (this.task == null) { + return; + } + + this.task.cancel(); + } + + public boolean isCancelled() { + return this.task == null || this.task.isCancelled(); + } + + public Plugin getOwningPlugin() { + return this.task == null ? null : this.task.getOwningPlugin(); + } + + public boolean isCurrentlyRunning() { + if (this.task == null) { + return false; + } + + final ScheduledTask.ExecutionState state = this.task.getExecutionState(); + return state == ScheduledTask.ExecutionState.RUNNING || state == ScheduledTask.ExecutionState.CANCELLED_RUNNING; + } + + public boolean isRepeatingTask() { + return this.task != null && this.task.isRepeatingTask(); + } +} diff --git a/src/main/java/com/extendedclip/deluxemenus/scheduler/folia/FoliaScheduler.java b/src/main/java/com/extendedclip/deluxemenus/scheduler/folia/FoliaScheduler.java new file mode 100644 index 00000000..cbc3d26c --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/scheduler/folia/FoliaScheduler.java @@ -0,0 +1,220 @@ +/* + * MIT License + * + * Copyright (c) 2023 Sevastjan + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.extendedclip.deluxemenus.scheduler.folia; + +import com.extendedclip.deluxemenus.scheduler.scheduling.schedulers.TaskScheduler; +import com.extendedclip.deluxemenus.scheduler.scheduling.tasks.MyScheduledTask; +import io.papermc.paper.threadedregions.scheduler.AsyncScheduler; +import io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler; +import io.papermc.paper.threadedregions.scheduler.RegionScheduler; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.plugin.Plugin; + +import java.util.concurrent.TimeUnit; + +public class FoliaScheduler implements TaskScheduler { + + final Plugin plugin; + + public FoliaScheduler(Plugin plugin) { + this.plugin = plugin; + } + + private final RegionScheduler regionScheduler = Bukkit.getServer().getRegionScheduler(); + private final GlobalRegionScheduler globalRegionScheduler = Bukkit.getServer().getGlobalRegionScheduler(); + private final AsyncScheduler asyncScheduler = Bukkit.getServer().getAsyncScheduler(); + + @Override + public boolean isGlobalThread() { + return Bukkit.getServer().isGlobalTickThread(); + } + + @Override + public boolean isTickThread() { + return Bukkit.getServer().isPrimaryThread(); // The Paper implementation checks whether this is a tick thread, this method exists to avoid confusion. + } + + @Override + public boolean isEntityThread(Entity entity) { + return Bukkit.getServer().isOwnedByCurrentRegion(entity); + } + + @Override + public boolean isRegionThread(Location location) { + return Bukkit.getServer().isOwnedByCurrentRegion(location); + } + + @Override + public MyScheduledTask runTask(Runnable runnable) { + return new FoliaScheduledTask(globalRegionScheduler.run(plugin, task -> runnable.run())); + } + + @Override + public MyScheduledTask runTaskLater(Runnable runnable, long delay) { + //Folia exception: Delay ticks may not be <= 0 + if (delay <= 0) { + return runTask(runnable); + } + return new FoliaScheduledTask(globalRegionScheduler.runDelayed(plugin, task -> runnable.run(), delay)); + } + + @Override + public MyScheduledTask runTaskTimer(Runnable runnable, long delay, long period) { + //Folia exception: Delay ticks may not be <= 0 + delay = getOneIfNotPositive(delay); + return new FoliaScheduledTask(globalRegionScheduler.runAtFixedRate(plugin, task -> runnable.run(), delay, period)); + } + + @Override + public MyScheduledTask runTask(Plugin plugin, Runnable runnable) { + return new FoliaScheduledTask(globalRegionScheduler.run(plugin, task -> runnable.run())); + } + + @Override + public MyScheduledTask runTaskLater(Plugin plugin, Runnable runnable, long delay) { + //Folia exception: Delay ticks may not be <= 0 + if (delay <= 0) { + return runTask(plugin, runnable); + } + return new FoliaScheduledTask(globalRegionScheduler.runDelayed(plugin, task -> runnable.run(), delay)); + } + + @Override + public MyScheduledTask runTaskTimer(Plugin plugin, Runnable runnable, long delay, long period) { + //Folia exception: Delay ticks may not be <= 0 + delay = getOneIfNotPositive(delay); + return new FoliaScheduledTask(globalRegionScheduler.runAtFixedRate(plugin, task -> runnable.run(), delay, period)); + } + + @Override + public MyScheduledTask runTask(Location location, Runnable runnable) { + return new FoliaScheduledTask(regionScheduler.run(plugin, location, task -> runnable.run())); + } + + @Override + public MyScheduledTask runTaskLater(Location location, Runnable runnable, long delay) { + //Folia exception: Delay ticks may not be <= 0 + if (delay <= 0) { + return runTask(runnable); + } + return new FoliaScheduledTask(regionScheduler.runDelayed(plugin, location, task -> runnable.run(), delay)); + } + + @Override + public MyScheduledTask runTaskTimer(Location location, Runnable runnable, long delay, long period) { + //Folia exception: Delay ticks may not be <= 0 + delay = getOneIfNotPositive(delay); + return new FoliaScheduledTask(regionScheduler.runAtFixedRate(plugin, location, task -> runnable.run(), delay, period)); + } + + @Override + public MyScheduledTask runTask(Entity entity, Runnable runnable) { + return new FoliaScheduledTask(entity.getScheduler().run(plugin, task -> runnable.run(), null)); + } + + @Override + public MyScheduledTask runTaskLater(Entity entity, Runnable runnable, long delay) { + //Folia exception: Delay ticks may not be <= 0 + if (delay <= 0) { + return runTask(entity, runnable); + } + return new FoliaScheduledTask(entity.getScheduler().runDelayed(plugin, task -> runnable.run(), null, delay)); + } + + @Override + public MyScheduledTask runTaskTimer(Entity entity, Runnable runnable, long delay, long period) { + //Folia exception: Delay ticks may not be <= 0 + delay = getOneIfNotPositive(delay); + return new FoliaScheduledTask(entity.getScheduler().runAtFixedRate(plugin, task -> runnable.run(), null, delay, period)); + } + + @Override + public MyScheduledTask runTaskAsynchronously(Runnable runnable) { + return new FoliaScheduledTask(asyncScheduler.runNow(plugin, task -> runnable.run())); + } + + @Override + public MyScheduledTask runTaskLaterAsynchronously(Runnable runnable, long delay) { + //Folia exception: Delay ticks may not be <= 0 + delay = getOneIfNotPositive(delay); + return new FoliaScheduledTask(asyncScheduler.runDelayed(plugin, task -> runnable.run(), delay * 50L, TimeUnit.MILLISECONDS)); + } + + @Override + public MyScheduledTask runTaskTimerAsynchronously(Runnable runnable, long delay, long period) { + return new FoliaScheduledTask(asyncScheduler.runAtFixedRate(plugin, task -> runnable.run(), delay * 50, period * 50, TimeUnit.MILLISECONDS)); + } + + @Override + public MyScheduledTask runTaskAsynchronously(Plugin plugin, Runnable runnable) { + return new FoliaScheduledTask(asyncScheduler.runNow(plugin, task -> runnable.run())); + } + + @Override + public MyScheduledTask runTaskLaterAsynchronously(Plugin plugin, Runnable runnable, long delay) { + //Folia exception: Delay ticks may not be <= 0 + delay = getOneIfNotPositive(delay); + return new FoliaScheduledTask(asyncScheduler.runDelayed(plugin, task -> runnable.run(), delay * 50L, TimeUnit.MILLISECONDS)); + } + + @Override + public MyScheduledTask runTaskTimerAsynchronously(Plugin plugin, Runnable runnable, long delay, long period) { + //Folia exception: Delay ticks may not be <= 0 + delay = getOneIfNotPositive(delay); + return new FoliaScheduledTask(asyncScheduler.runAtFixedRate(plugin, task -> runnable.run(), delay * 50, period * 50, TimeUnit.MILLISECONDS)); + } + + @Override + public void execute(Runnable runnable) { + globalRegionScheduler.execute(plugin, runnable); + } + + @Override + public void execute(Location location, Runnable runnable) { + regionScheduler.execute(plugin, location, runnable); + } + + @Override + public void execute(Entity entity, Runnable runnable) { + entity.getScheduler().execute(plugin, runnable, null, 1L); + } + + @Override + public void cancelTasks() { + globalRegionScheduler.cancelTasks(plugin); + asyncScheduler.cancelTasks(plugin); + } + + @Override + public void cancelTasks(Plugin plugin) { + globalRegionScheduler.cancelTasks(plugin); + asyncScheduler.cancelTasks(plugin); + } + + private long getOneIfNotPositive(long x) { + return x <= 0 ? 1L : x; + } +} diff --git a/src/main/java/com/extendedclip/deluxemenus/scheduler/paper/PaperScheduler.java b/src/main/java/com/extendedclip/deluxemenus/scheduler/paper/PaperScheduler.java new file mode 100644 index 00000000..6920ff97 --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/scheduler/paper/PaperScheduler.java @@ -0,0 +1,41 @@ +/* + * MIT License + * + * Copyright (c) 2023 Sevastjan + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.extendedclip.deluxemenus.scheduler.paper; + +import com.extendedclip.deluxemenus.scheduler.folia.FoliaScheduler; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; + +// Thanks to Towny +public class PaperScheduler extends FoliaScheduler { + public PaperScheduler(Plugin plugin) { + super(plugin); + } + + @Override + public boolean isGlobalThread() { + // isGlobalThread does not exist on paper, match the bukkit task scheduler's behaviour. + return Bukkit.getServer().isPrimaryThread(); + } +} diff --git a/src/main/java/com/extendedclip/deluxemenus/scheduler/scheduling/schedulers/TaskScheduler.java b/src/main/java/com/extendedclip/deluxemenus/scheduler/scheduling/schedulers/TaskScheduler.java new file mode 100644 index 00000000..22b77198 --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/scheduler/scheduling/schedulers/TaskScheduler.java @@ -0,0 +1,346 @@ +/* + * MIT License + * + * Copyright (c) 2023 Sevastjan + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.extendedclip.deluxemenus.scheduler.scheduling.schedulers; + +import com.extendedclip.deluxemenus.scheduler.scheduling.tasks.MyScheduledTask; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.plugin.Plugin; + +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; + +public interface TaskScheduler { + + /** + * Folia: Returns whether the current thread is ticking the global region
+ * Paper & Bukkit: Returns {@link org.bukkit.Server#isPrimaryThread} + */ + boolean isGlobalThread(); + + /** + * @return {@link org.bukkit.Server#isPrimaryThread} + */ + default boolean isTickThread() { + return Bukkit.getServer().isPrimaryThread(); + } + + /** + * Folia & Paper: Returns whether the current thread is ticking a region and that the region + * being ticked owns the specified entity. Note that this function is the only appropriate method of + * checking for ownership of an entity, as retrieving the entity's location is undefined unless the + * entity is owned by the current region + *

+ * Bukkit: returns {@link org.bukkit.Server#isPrimaryThread} + * + * @param entity Specified entity + */ + boolean isEntityThread(Entity entity); + + /** + * Folia & Paper: Returns whether the current thread is ticking a region and that the region + * being ticked owns the chunk at the specified world and block position as included in the specified location + *

+ * Bukkit: returns {@link org.bukkit.Server#isPrimaryThread} + * + * @param location Specified location, must have a non-null world. + */ + boolean isRegionThread(Location location); + + /** + * Schedules a task to be executed on the next tick
+ * Folia & Paper: ...on the global region
+ * Bukkit: ...on the main thread + * + * @param runnable The task to execute + */ + MyScheduledTask runTask(Runnable runnable); + + /** + * Schedules a task to be executed after the specified delay in ticks
+ * Folia & Paper: ...on the global region
+ * Bukkit: ...on the main thread + * + * @param runnable The task to execute + * @param delay The delay, in ticks + */ + MyScheduledTask runTaskLater(Runnable runnable, long delay); + + /** + * Schedules a repeating task to be executed after the initial delay with the specified period
+ * Folia & Paper: ...on the global region
+ * Bukkit: ...on the main thread + * + * @param runnable The task to execute + * @param delay The initial delay, in ticks. + * @param period The period, in ticks. + */ + MyScheduledTask runTaskTimer(Runnable runnable, long delay, long period); + + /** + * Deprecated: use {@link #runTask(Runnable)} + */ + @Deprecated + default MyScheduledTask runTask(Plugin plugin, Runnable runnable) { + return runTask(runnable); + } + + /** + * Deprecated: use {@link #runTaskLater(Runnable, long)} + */ + @Deprecated + default MyScheduledTask runTaskLater(Plugin plugin, Runnable runnable, long delay) { + return runTaskLater(runnable, delay); + } + + /** + * Deprecated: use {@link #runTaskTimer(Runnable, long, long)} + */ + @Deprecated + default MyScheduledTask runTaskTimer(Plugin plugin, Runnable runnable, long delay, long period) { + return runTaskTimer(runnable, delay, period); + } + + /** + * Folia & Paper: Schedules a task to be executed on the region which owns the location on the next tick + *

+ * Bukkit: same as {@link #runTask(Runnable)} + * + * @param location The location which the region executing should own + * @param runnable The task to execute + */ + default MyScheduledTask runTask(Location location, Runnable runnable) { + return runTask(runnable); + } + + /** + * Folia & Paper: Schedules a task to be executed on the region which owns the location after the + * specified delay in ticks + *

+ * Bukkit: same as {@link #runTaskLater(Runnable, long)} + * + * @param location The location which the region executing should own + * @param runnable The task to execute + * @param delay The delay, in ticks. + */ + default MyScheduledTask runTaskLater(Location location, Runnable runnable, long delay) { + return runTaskLater(runnable, delay); + } + + /** + * Folia & Paper: Schedules a repeating task to be executed on the region which owns the location + * after the initial delay with the specified period + *

+ * Bukkit: same as {@link #runTaskTimer(Runnable, long, long)} + * + * @param location The location which the region executing should own + * @param runnable The task to execute + * @param delay The initial delay, in ticks. + * @param period The period, in ticks. + */ + default MyScheduledTask runTaskTimer(Location location, Runnable runnable, long delay, long period) { + return runTaskTimer(runnable, delay, period); + } + + /** + * Deprecated: use {@link #runTaskLater(Runnable, long)} + */ + @Deprecated + default MyScheduledTask scheduleSyncDelayedTask(Runnable runnable, long delay) { + return runTaskLater(runnable, delay); + } + + /** + * Deprecated: use {@link #execute(Runnable)} or {@link #runTask(Runnable)} + */ + @Deprecated + default MyScheduledTask scheduleSyncDelayedTask(Runnable runnable) { + return runTask(runnable); + } + + /** + * Deprecated: use {@link #runTaskTimer(Runnable, long, long)} + */ + @Deprecated + default MyScheduledTask scheduleSyncRepeatingTask(Runnable runnable, long delay, long period) { + return runTaskTimer(runnable, delay, period); + } + + /** + * Folia & Paper: Schedules a task to be executed on the region which owns the location + * of given entity on the next tick + *

+ * Bukkit: same as {@link #runTask(Runnable)} + * + * @param entity The entity whose location the region executing should own + * @param runnable The task to execute + */ + default MyScheduledTask runTask(Entity entity, Runnable runnable) { + return runTask(runnable); + } + + /** + * Folia & Paper: Schedules a task to be executed on the region which owns the location + * of given entity after the specified delay in ticks + *

+ * Bukkit: same as {@link #runTaskLater(Runnable, long)} + * + * @param entity The entity whose location the region executing should own + * @param runnable The task to execute + * @param delay The delay, in ticks. + */ + default MyScheduledTask runTaskLater(Entity entity, Runnable runnable, long delay) { + return runTaskLater(runnable, delay); + } + + /** + * Folia & Paper: Schedules a repeating task to be executed on the region which owns the + * location of given entity after the initial delay with the specified period + *

+ * Bukkit: same as {@link #runTaskTimer(Runnable, long, long)} + * + * @param entity The entity whose location the region executing should own + * @param runnable The task to execute + * @param delay The initial delay, in ticks. + * @param period The period, in ticks. + */ + default MyScheduledTask runTaskTimer(Entity entity, Runnable runnable, long delay, long period) { + return runTaskTimer(runnable, delay, period); + } + + /** + * Schedules the specified task to be executed asynchronously immediately + * + * @param runnable The task to execute + * @return The {@link MyScheduledTask} that represents the scheduled task + */ + MyScheduledTask runTaskAsynchronously(Runnable runnable); + + /** + * Schedules the specified task to be executed asynchronously after the time delay has passed + * + * @param runnable The task to execute + * @param delay The time delay to pass before the task should be executed + * @return The {@link MyScheduledTask} that represents the scheduled task + */ + MyScheduledTask runTaskLaterAsynchronously(Runnable runnable, long delay); + + /** + * Schedules the specified task to be executed asynchronously after the initial delay has passed, + * and then periodically executed with the specified period + * + * @param runnable The task to execute + * @param delay The time delay to pass before the first execution of the task, in ticks + * @param period The time between task executions after the first execution of the task, in ticks + * @return The {@link MyScheduledTask} that represents the scheduled task + */ + MyScheduledTask runTaskTimerAsynchronously(Runnable runnable, long delay, long period); + + /** + * Deprecated: use {@link #runTaskAsynchronously(Runnable)} + */ + @Deprecated + default MyScheduledTask runTaskAsynchronously(Plugin plugin, Runnable runnable) { + return runTaskAsynchronously(runnable); + } + + /** + * Deprecated: use {@link #runTaskLaterAsynchronously(Runnable, long)} + */ + @Deprecated + default MyScheduledTask runTaskLaterAsynchronously(Plugin plugin, Runnable runnable, long delay) { + return runTaskLaterAsynchronously(runnable, delay); + } + + /** + * Deprecated: use {@link #runTaskTimerAsynchronously(Runnable, long, long)} + */ + @Deprecated + default MyScheduledTask runTaskTimerAsynchronously(Plugin plugin, Runnable runnable, long delay, long period) { + return runTaskTimerAsynchronously(runnable, delay, period); + } + + /** + * Calls a method on the main thread and returns a Future object. This task will be executed + * by the main(Bukkit)/global(Folia&Paper) server thread. + *

+ * Note: The Future.get() methods must NOT be called from the main thread. + *

+ * Note2: There is at least an average of 10ms latency until the isDone() method returns true. + * + * @param task Task to be executed + */ + default Future callSyncMethod(final Callable task) { + CompletableFuture completableFuture = new CompletableFuture<>(); + execute(() -> { + try { + completableFuture.complete(task.call()); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + return completableFuture; + } + + /** + * Schedules a task to be executed on the global region + * + * @param runnable The task to execute + */ + void execute(Runnable runnable); + + /** + * Schedules a task to be executed on the region which owns the location + * + * @param location The location which the region executing should own + * @param runnable The task to execute + */ + default void execute(Location location, Runnable runnable) { + execute(runnable); + } + + /** + * Schedules a task to be executed on the region which owns the location of given entity + * + * @param entity The entity which location the region executing should own + * @param runnable The task to execute + */ + default void execute(Entity entity, Runnable runnable) { + execute(runnable); + } + + /** + * Attempts to cancel all tasks scheduled by this plugin + */ + void cancelTasks(); + + /** + * Attempts to cancel all tasks scheduled by the specified plugin + * + * @param plugin specified plugin + */ + void cancelTasks(Plugin plugin); +} diff --git a/src/main/java/com/extendedclip/deluxemenus/scheduler/scheduling/tasks/MyScheduledTask.java b/src/main/java/com/extendedclip/deluxemenus/scheduler/scheduling/tasks/MyScheduledTask.java new file mode 100644 index 00000000..47ae40fa --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/scheduler/scheduling/tasks/MyScheduledTask.java @@ -0,0 +1,54 @@ +/* + * MIT License + * + * Copyright (c) 2023 Sevastjan + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.extendedclip.deluxemenus.scheduler.scheduling.tasks; + +import org.bukkit.plugin.Plugin; + +public interface MyScheduledTask { + + /** + * Cancels executing task + */ + void cancel(); + + /** + * @return true if task is cancelled, false otherwise + */ + boolean isCancelled(); + + /** + * @return The plugin under which the task was scheduled. + */ + Plugin getOwningPlugin(); + + /** + * @return true if task is currently executing, false otherwise + */ + boolean isCurrentlyRunning(); + + /** + * @return true if task is repeating, false otherwise + */ + boolean isRepeatingTask(); +} diff --git a/src/main/java/com/extendedclip/deluxemenus/scheduler/utils/JavaUtil.java b/src/main/java/com/extendedclip/deluxemenus/scheduler/utils/JavaUtil.java new file mode 100644 index 00000000..b5b85152 --- /dev/null +++ b/src/main/java/com/extendedclip/deluxemenus/scheduler/utils/JavaUtil.java @@ -0,0 +1,12 @@ +package com.extendedclip.deluxemenus.scheduler.utils; + +public class JavaUtil { + public static boolean classExists(String className) { + try { + Class.forName(className); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } +} diff --git a/src/main/java/com/extendedclip/deluxemenus/updatechecker/UpdateChecker.java b/src/main/java/com/extendedclip/deluxemenus/updatechecker/UpdateChecker.java index e30fe77b..4ebd0d6d 100644 --- a/src/main/java/com/extendedclip/deluxemenus/updatechecker/UpdateChecker.java +++ b/src/main/java/com/extendedclip/deluxemenus/updatechecker/UpdateChecker.java @@ -2,6 +2,7 @@ import com.extendedclip.deluxemenus.DeluxeMenus; import com.extendedclip.deluxemenus.listener.Listener; +import com.extendedclip.deluxemenus.scheduler.scheduling.schedulers.TaskScheduler; import com.extendedclip.deluxemenus.utils.DebugLevel; import com.extendedclip.deluxemenus.utils.Messages; import java.io.BufferedReader; @@ -15,7 +16,6 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.player.PlayerJoinEvent; -import org.bukkit.scheduler.BukkitRunnable; import org.jetbrains.annotations.NotNull; public class UpdateChecker extends Listener { @@ -25,28 +25,20 @@ public class UpdateChecker extends Listener { private static final TextReplacementConfig.Builder CURRENT_VERSION_REPLACER_BUILDER = TextReplacementConfig.builder().matchLiteral(""); - final int resourceId = 11734; + private static final int RESOURCE_ID = 11734; + private final TaskScheduler scheduler; private String latestVersion = null; private boolean updateAvailable = false; public UpdateChecker(final @NotNull DeluxeMenus instance) { super(instance); + this.scheduler = plugin.getScheduler(); - new BukkitRunnable() { - @Override - public void run() { - if (check()) { - new BukkitRunnable() { - - @Override - public void run() { - register(); - } - }.runTask(plugin); - } + scheduler.runTaskAsynchronously(() -> { + if (check()) { + scheduler.runTask(this::register); } - - }.runTaskAsynchronously(plugin); + }); } @EventHandler(priority = EventPriority.MONITOR) @@ -74,7 +66,7 @@ public void onJoin(final @NotNull PlayerJoinEvent event) { private String getSpigotVersion() { try { HttpURLConnection connection = (HttpURLConnection) new URL( - "https://api.spigotmc.org/legacy/update.php?resource=" + resourceId).openConnection(); + "https://api.spigotmc.org/legacy/update.php?resource=" + RESOURCE_ID).openConnection(); connection.setDoOutput(true); connection.setRequestMethod("GET"); return new BufferedReader(new InputStreamReader(connection.getInputStream())).readLine(); diff --git a/src/main/java/com/extendedclip/deluxemenus/utils/DebugLevel.java b/src/main/java/com/extendedclip/deluxemenus/utils/DebugLevel.java index d21e06a7..d89e701f 100644 --- a/src/main/java/com/extendedclip/deluxemenus/utils/DebugLevel.java +++ b/src/main/java/com/extendedclip/deluxemenus/utils/DebugLevel.java @@ -17,7 +17,7 @@ public enum DebugLevel { private final String[] names; private final int priority; - private DebugLevel(final int priority, @NotNull final String... names) { + DebugLevel(final int priority, @NotNull final String... names) { this.priority = priority; this.names = names; } diff --git a/src/main/java/com/extendedclip/deluxemenus/utils/ExpUtils.java b/src/main/java/com/extendedclip/deluxemenus/utils/ExpUtils.java index e1d0f4f1..023a5cc6 100644 --- a/src/main/java/com/extendedclip/deluxemenus/utils/ExpUtils.java +++ b/src/main/java/com/extendedclip/deluxemenus/utils/ExpUtils.java @@ -26,7 +26,7 @@ public static void setExp( final String lowerCase = stringAmount.toLowerCase(Locale.ENGLISH); if (stringAmount.contains("l")) { - final int neededLevel = Integer.parseInt(lowerCase.replaceAll("l", "")) + target.getLevel(); + final int neededLevel = Integer.parseInt(lowerCase.replace("l", "")) + target.getLevel(); amount = getExpToLevel(neededLevel) + (getTotalExperience(target) - getExpToLevel(target.getLevel())); setTotalExperience(target, 0); } diff --git a/src/main/java/com/extendedclip/deluxemenus/utils/ItemUtils.java b/src/main/java/com/extendedclip/deluxemenus/utils/ItemUtils.java index 870e8312..08de3186 100644 --- a/src/main/java/com/extendedclip/deluxemenus/utils/ItemUtils.java +++ b/src/main/java/com/extendedclip/deluxemenus/utils/ItemUtils.java @@ -33,7 +33,7 @@ public static boolean isPlaceholderOption(@NotNull final String material) { /** * Checks if the string starts with the substring "stack-". The check is case-insensitive. * - * @param itemstack The string to check + * @param material The string to check * @return true if the string starts with "stack-", false otherwise */ public static boolean isItemStackOption(@NotNull final String material) { diff --git a/src/main/java/com/extendedclip/deluxemenus/utils/Pair.java b/src/main/java/com/extendedclip/deluxemenus/utils/Pair.java index 3ba367cc..78bd7a43 100644 --- a/src/main/java/com/extendedclip/deluxemenus/utils/Pair.java +++ b/src/main/java/com/extendedclip/deluxemenus/utils/Pair.java @@ -2,11 +2,9 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.NonNull; -public class Pair { - - private final K key; - private final V value; +public record Pair(K key, V value) { public static Pair of(@NotNull final K key, @Nullable final V value) { return new Pair<>(key, value); @@ -21,16 +19,18 @@ public Pair(@NotNull final K key, @Nullable final V value) { this.value = value; } - public @NotNull K getKey() { + @Override + public @NotNull K key() { return key; } - public @Nullable V getValue() { + @Override + public @Nullable V value() { return value; } @Override - public String toString() { + public @NonNull String toString() { return "Pair{" + "key=" + key + ", value=" + key + '}'; } } diff --git a/src/main/java/com/extendedclip/deluxemenus/utils/SkullUtils.java b/src/main/java/com/extendedclip/deluxemenus/utils/SkullUtils.java index 38ad9457..c25c64f7 100644 --- a/src/main/java/com/extendedclip/deluxemenus/utils/SkullUtils.java +++ b/src/main/java/com/extendedclip/deluxemenus/utils/SkullUtils.java @@ -81,8 +81,7 @@ public static ItemStack getSkullByBase64EncodedTextureUrl(@NotNull final DeluxeM } public static String getTextureFromSkull(final DeluxeMenus plugin, ItemStack item) { - if (!(item.getItemMeta() instanceof SkullMeta)) return null; - SkullMeta meta = (SkullMeta) item.getItemMeta(); + if (!(item.getItemMeta() instanceof SkullMeta meta)) return null; if (VersionHelper.HAS_PLAYER_PROFILES) { PlayerProfile profile = meta.getOwnerProfile(); @@ -150,8 +149,7 @@ public static ItemStack getSkullByName(@NotNull final DeluxeMenus plugin, @NotNu } public static String getSkullOwner(ItemStack skull) { - if (skull == null || !(skull.getItemMeta() instanceof SkullMeta)) return null; - SkullMeta meta = (SkullMeta) skull.getItemMeta(); + if (skull == null || !(skull.getItemMeta() instanceof SkullMeta meta)) return null; if (!VersionHelper.IS_SKULL_OWNER_LEGACY) { if (meta.getOwningPlayer() == null) return null; @@ -183,7 +181,7 @@ private static GameProfile getGameProfile(@NotNull final String base64Url) { */ @NotNull private static PlayerProfile getPlayerProfile(@NotNull final DeluxeMenus plugin, @NotNull final String base64Url) { - final PlayerProfile profile = Bukkit.createPlayerProfile(UUID.randomUUID()); + final PlayerProfile profile = Bukkit.createProfile(UUID.randomUUID()); final String decodedBase64 = decodeSkinUrl(base64Url); if (decodedBase64 == null) { diff --git a/src/main/java/com/extendedclip/deluxemenus/utils/StringUtils.java b/src/main/java/com/extendedclip/deluxemenus/utils/StringUtils.java index 6ae95804..81fd30f8 100644 --- a/src/main/java/com/extendedclip/deluxemenus/utils/StringUtils.java +++ b/src/main/java/com/extendedclip/deluxemenus/utils/StringUtils.java @@ -3,7 +3,6 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; - import me.clip.placeholderapi.PlaceholderAPI; import net.md_5.bungee.api.ChatColor; import org.bukkit.Color; diff --git a/src/main/java/com/extendedclip/deluxemenus/utils/VersionHelper.java b/src/main/java/com/extendedclip/deluxemenus/utils/VersionHelper.java index 87e9a943..a676f056 100644 --- a/src/main/java/com/extendedclip/deluxemenus/utils/VersionHelper.java +++ b/src/main/java/com/extendedclip/deluxemenus/utils/VersionHelper.java @@ -185,7 +185,6 @@ private static int getCurrentVersion() { else stringBuilder.append(patch.replace(".", "")); } - //noinspection UnstableApiUsage final Integer version = Ints.tryParse(stringBuilder.toString()); // Should never fail diff --git a/src/main/resources/paper-plugin.yml b/src/main/resources/paper-plugin.yml new file mode 100644 index 00000000..6a38f0e2 --- /dev/null +++ b/src/main/resources/paper-plugin.yml @@ -0,0 +1,55 @@ +name: DeluxeMenus +main: com.extendedclip.deluxemenus.DeluxeMenus +version: '${version}' +api-version: '1.21' +folia-supported: true +dependencies: + server: + PlaceholderAPI: + load: BEFORE + required: true + join-classpath: true + Vault: + load: BEFORE + required: false + join-classpath: true + HeadDatabase: + load: BEFORE + required: false + join-classpath: true + CraftEngine: + load: BEFORE + required: false + join-classpath: true + ItemsAdder: + load: BEFORE + required: false + join-classpath: true + Nexo: + load: BEFORE + required: false + join-classpath: true + Oraxen: + load: BEFORE + required: false + join-classpath: true + MMOItems: + load: BEFORE + required: false + join-classpath: true + ExecutableItems: + load: BEFORE + required: false + join-classpath: true + ExecutableBlocks: + load: BEFORE + required: false + join-classpath: true + Score: + load: BEFORE + required: false + join-classpath: true + SimpleItemGenerator: + load: BEFORE + required: false + join-classpath: true diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index c57c869c..ec50f892 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,7 +1,8 @@ -api-version: 1.13 name: DeluxeMenus main: com.extendedclip.deluxemenus.DeluxeMenus version: ${version} +api-version: 1.13 +folia-supported: true authors: [ HelpChat ] softdepend: [ PlaceholderAPI, Vault, HeadDatabase, CraftEngine, ItemsAdder, Nexo, Oraxen, ExecutableItems, ExecutableBlocks, Score, SimpleItemGenerator, MMOItems ] description: All in one inventory menu system