diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 3ac9fa2..692e3b7 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -9,6 +9,7 @@ dependencies { testImplementation("com.github.seeseemelk:MockBukkit-v1.17:1.13.0") testImplementation("org.reflections:reflections:0.10.2") + compileOnly("org.spigotmc:spigot-api:1.19.4-R0.1-SNAPSHOT") compileOnly("io.papermc:paperlib:1.0.4") // we include paperlib and relocate elsewhere compileOnly("com.github.MilkBowl:VaultAPI:1.7") // vault compileOnly("me.clip:placeholderapi:2.10.4") // PAPI diff --git a/api/src/main/java/net/islandearth/rpgregions/api/IRPGRegionsAPI.java b/api/src/main/java/net/islandearth/rpgregions/api/IRPGRegionsAPI.java index e783c84..d8997ab 100644 --- a/api/src/main/java/net/islandearth/rpgregions/api/IRPGRegionsAPI.java +++ b/api/src/main/java/net/islandearth/rpgregions/api/IRPGRegionsAPI.java @@ -2,6 +2,7 @@ package net.islandearth.rpgregions.api; import com.convallyria.languagy.api.language.Translator; import com.google.gson.Gson; +import net.islandearth.rpgregions.api.schedule.PlatformScheduler; import net.islandearth.rpgregions.managers.IRPGRegionsManagers; import org.bukkit.configuration.Configuration; @@ -29,4 +30,10 @@ public interface IRPGRegionsAPI { void debug(String debug); boolean hasHeadDatabase(); + + /** + * Gets the scheduler used for the current platform. + * @return the scheduler for this server platform + */ + PlatformScheduler getScheduler(); } diff --git a/api/src/main/java/net/islandearth/rpgregions/api/schedule/PlatformScheduler.java b/api/src/main/java/net/islandearth/rpgregions/api/schedule/PlatformScheduler.java new file mode 100644 index 0000000..d33c622 --- /dev/null +++ b/api/src/main/java/net/islandearth/rpgregions/api/schedule/PlatformScheduler.java @@ -0,0 +1,30 @@ +package net.islandearth.rpgregions.api.schedule; + +import net.islandearth.rpgregions.api.IRPGRegionsAPI; +import org.bukkit.entity.Entity; + +public abstract class PlatformScheduler { + + protected final T api; + + public PlatformScheduler(T api) { + this.api = api; + } + + public abstract void executeOnMain(Runnable runnable); + + public abstract void executeOnEntity(Entity entity, Runnable runnable); + + public abstract RPGRegionsTask executeRepeating(Runnable runnable, long delay, long period); + + public abstract void executeDelayed(Runnable runnable, long delay); + + public abstract void executeAsync(Runnable runnable); + + public abstract void registerInitTask(Runnable runnable); + + @FunctionalInterface + public interface RPGRegionsTask { + void cancel(); + } +} diff --git a/build.gradle.kts b/build.gradle.kts index a24541e..137307c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,11 +1,8 @@ plugins { - id("com.github.johnrengelman.shadow") version("7.1.2") + id("com.github.johnrengelman.shadow") version("8.1.1") id("java") } -java.sourceCompatibility = JavaVersion.VERSION_16 -java.targetCompatibility = JavaVersion.VERSION_16 - dependencies { testImplementation("junit:junit:4.13.2") implementation(project(":rpgregions", "shadow")) @@ -31,6 +28,10 @@ allprojects { apply(plugin = "com.github.johnrengelman.shadow") apply(plugin = "java") + java { + toolchain.languageVersion.set(JavaLanguageVersion.of(17)) + } + repositories { mavenCentral() mavenLocal() @@ -66,8 +67,7 @@ allprojects { } dependencies { - implementation("com.convallyria.languagy:api:3.0.1") - compileOnly("org.spigotmc:spigot-api:1.19.4-R0.1-SNAPSHOT") + implementation("com.convallyria.languagy:api:3.0.2") } tasks { @@ -99,6 +99,12 @@ allprojects { dependsOn(shadowJar) } + compileJava { + // Set the release flag. This configures what version bytecode the compiler will emit, as well as what JDK APIs are usable. + // See https://openjdk.java.net/jeps/247 for more information. + options.release.set(16) + } + processResources { filesMatching("plugin.yml") { expand("version" to project.version) diff --git a/folia/build.gradle.kts b/folia/build.gradle.kts new file mode 100644 index 0000000..794424a --- /dev/null +++ b/folia/build.gradle.kts @@ -0,0 +1,16 @@ +java { + disableAutoTargetJvm() +} + +repositories { + maven { + name = "papermc" + url = uri("https://repo.papermc.io/repository/maven-public/") + } +} + +dependencies { + compileOnly(project(":api")) + + compileOnly("dev.folia:folia-api:1.19.4-R0.1-SNAPSHOT") +} diff --git a/folia/src/main/java/net/islandearth/rpgregions/folia/schedule/FoliaScheduler.java b/folia/src/main/java/net/islandearth/rpgregions/folia/schedule/FoliaScheduler.java new file mode 100644 index 0000000..21aab29 --- /dev/null +++ b/folia/src/main/java/net/islandearth/rpgregions/folia/schedule/FoliaScheduler.java @@ -0,0 +1,79 @@ +package net.islandearth.rpgregions.folia.schedule; + +import io.papermc.paper.threadedregions.RegionizedServerInitEvent; +import io.papermc.paper.threadedregions.scheduler.ScheduledTask; +import net.islandearth.rpgregions.api.IRPGRegionsAPI; +import net.islandearth.rpgregions.api.schedule.PlatformScheduler; +import org.bukkit.Bukkit; +import org.bukkit.entity.Entity; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.plugin.Plugin; + +import java.util.ArrayList; +import java.util.List; + +public class FoliaScheduler extends PlatformScheduler implements Listener { + + public static final boolean RUNNING_FOLIA; + + static { + boolean found; + try { + Class.forName("io.papermc.paper.threadedregions.RegionizedServerInitEvent"); + found = true; + } catch (final ReflectiveOperationException e) { + found = false; + } + RUNNING_FOLIA = found; + } + + private List initTasks = new ArrayList<>(); + + public FoliaScheduler(IRPGRegionsAPI api) { + super(api); + Bukkit.getPluginManager().registerEvents(this, (Plugin) api); + } + + @EventHandler + public void onServerInit(RegionizedServerInitEvent event) { + for (final Runnable tasks : initTasks) { + tasks.run(); + } + initTasks = null; + } + + @Override + public void executeOnMain(Runnable runnable) { + Bukkit.getGlobalRegionScheduler().execute((Plugin) api, runnable); + } + + @Override + public void executeOnEntity(Entity entity, Runnable runnable) { + entity.getScheduler().run((Plugin) api, (s) -> runnable.run(), () -> {}); + } + + @Override + public RPGRegionsTask executeRepeating(Runnable runnable, long delay, long period) { + final ScheduledTask task = Bukkit.getGlobalRegionScheduler().runAtFixedRate((Plugin) api, (s) -> runnable.run(), delay, period); + return task::cancel; + } + + @Override + public void executeDelayed(Runnable runnable, long delay) { + Bukkit.getGlobalRegionScheduler().runDelayed((Plugin) api, (s) -> runnable.run(), delay); + } + + @Override + public void executeAsync(Runnable runnable) { + Bukkit.getAsyncScheduler().runNow((Plugin) api, (s) -> runnable.run()); + } + + @Override + public void registerInitTask(Runnable runnable) { + if (initTasks == null) { + throw new IllegalStateException("Server already initialised!"); + } + initTasks.add(runnable); + } +} diff --git a/gradle.properties b/gradle.properties index 8db8608..3286758 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,3 +2,6 @@ pluginGroup=net.islandearth pluginVersion=1.4.5 # Set to false if you don't have access to the UltraRegions API jar to make the plugin compilable. The purchased plugin has support for it. ultraRegionsSupport=true + +# Dependency management +cloud_version = 1.8.3 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 41d9927..ccebba7 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 41dfb87..bdc9a83 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c787..79a61d4 100644 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,10 +80,10 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' @@ -143,12 +143,16 @@ fi if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -205,6 +209,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index 107acd3..93e3f59 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +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! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +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 diff --git a/modern/build.gradle.kts b/modern/build.gradle.kts index 5611490..ef2bae7 100644 --- a/modern/build.gradle.kts +++ b/modern/build.gradle.kts @@ -12,6 +12,7 @@ dependencies { testImplementation("junit:junit:4.13.2") + compileOnly("org.spigotmc:spigot-api:1.19.4-R0.1-SNAPSHOT") compileOnly("com.sk89q.worldguard:worldguard-bukkit:7.0.4-SNAPSHOT") { exclude("com.destroystokyo.paper") exclude("org.spigotmc") diff --git a/rpgregions/build.gradle.kts b/rpgregions/build.gradle.kts index e41b74e..e9f4ff7 100644 --- a/rpgregions/build.gradle.kts +++ b/rpgregions/build.gradle.kts @@ -4,19 +4,31 @@ repositories { dependencies { implementation(project(":api")) + implementation(project(":folia")) testImplementation("junit:junit:4.13.2") testImplementation("com.github.seeseemelk:MockBukkit-v1.17:1.13.0") testImplementation("org.reflections:reflections:0.10.2") + implementation("cloud.commandframework:cloud-paper:${properties["cloud_version"]}") { + exclude("org.checkerframework") + } + implementation("cloud.commandframework:cloud-annotations:${properties["cloud_version"]}") { + exclude("org.checkerframework") + } + implementation("cloud.commandframework:cloud-minecraft-extras:${properties["cloud_version"]}") { + exclude("org.checkerframework") + exclude("net.kyori") + } + implementation("net.kyori:adventure-platform-bukkit:4.3.0") implementation("org.apache.commons:commons-lang3:3.12.0") implementation("net.wesjd:anvilgui:1.6.3-SNAPSHOT") // anvilgui implementation("com.github.stefvanschie.inventoryframework:IF:0.10.9") // inventory framework - implementation("co.aikar:acf-paper:0.5.1-SNAPSHOT") // commands implementation("co.aikar:idb-core:1.0.0-SNAPSHOT") // database implementation("org.bstats:bstats-bukkit:3.0.1") // plugin stats implementation("io.papermc:paperlib:1.0.7") // paperlib - async teleport on Paper + compileOnly("org.spigotmc:spigot-api:1.19.4-R0.1-SNAPSHOT") compileOnly("com.sk89q.worldguard:worldguard-bukkit:7.0.4-SNAPSHOT") { exclude("com.destroystokyo.paper") exclude("org.spigotmc") @@ -60,8 +72,9 @@ tasks { } shadowJar { - relocate("co.aikar.commands", "net.islandearth.rpgregions.libs.acf") - relocate("co.aikar.locales", "net.islandearth.rpgregions.libs.acf.locales") + relocate("net.kyori.adventure", "net.islandearth.rpgregions.libs.adventure") + relocate("cloud.commandframework", "net.islandearth.rpgregions.libs.commandframework") + relocate("io.leangen.geantyref", "net.islandearth.rpgregions.libs.typetoken") relocate("co.aikar.idb", "net.islandearth.rpgregions.libs.idb") relocate("com.github.stefvanschie.inventoryframework", "net.islandearth.rpgregions.libs.inventoryframework") relocate("org.bstats", "net.islandearth.rpgregions.libs.bstats") diff --git a/rpgregions/src/main/java/net/islandearth/rpgregions/RPGRegions.java b/rpgregions/src/main/java/net/islandearth/rpgregions/RPGRegions.java index 7b0f1cb..ece4ae7 100644 --- a/rpgregions/src/main/java/net/islandearth/rpgregions/RPGRegions.java +++ b/rpgregions/src/main/java/net/islandearth/rpgregions/RPGRegions.java @@ -1,27 +1,23 @@ package net.islandearth.rpgregions; -import co.aikar.commands.InvalidCommandArgument; -import co.aikar.commands.PaperCommandManager; import co.aikar.idb.DB; import com.convallyria.languagy.api.language.Language; import com.convallyria.languagy.api.language.Translator; -import com.google.common.collect.ImmutableList; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import net.islandearth.rpgregions.api.IRPGRegionsAPI; import net.islandearth.rpgregions.api.RPGRegionsAPI; import net.islandearth.rpgregions.api.integrations.rpgregions.RPGRegionsIntegration; import net.islandearth.rpgregions.api.integrations.rpgregions.region.RPGRegionsRegion; -import net.islandearth.rpgregions.commands.DiscoveriesCommand; -import net.islandearth.rpgregions.commands.RPGRegionsCommand; -import net.islandearth.rpgregions.commands.RPGRegionsDebugCommand; -import net.islandearth.rpgregions.commands.RPGRegionsExportCommand; +import net.islandearth.rpgregions.api.schedule.PlatformScheduler; +import net.islandearth.rpgregions.commands.Commands; import net.islandearth.rpgregions.effects.FogEffect; import net.islandearth.rpgregions.effects.PotionRegionEffect; import net.islandearth.rpgregions.effects.RegionEffect; import net.islandearth.rpgregions.effects.RegionEffectRegistry; import net.islandearth.rpgregions.effects.VanishEffect; import net.islandearth.rpgregions.exception.CouldNotStartException; +import net.islandearth.rpgregions.folia.schedule.FoliaScheduler; import net.islandearth.rpgregions.gson.AbstractAdapter; import net.islandearth.rpgregions.gson.ItemStackAdapter; import net.islandearth.rpgregions.gson.LocationAdapter; @@ -32,7 +28,6 @@ import net.islandearth.rpgregions.listener.RegionListener; import net.islandearth.rpgregions.listener.ServerReloadListener; import net.islandearth.rpgregions.listener.external.CustomStructuresListener; import net.islandearth.rpgregions.managers.RPGRegionsManagers; -import net.islandearth.rpgregions.managers.data.region.ConfiguredRegion; import net.islandearth.rpgregions.managers.registry.IRPGRegionsRegistry; import net.islandearth.rpgregions.requirements.AlonsoLevelRequirement; import net.islandearth.rpgregions.requirements.DependencyRequirement; @@ -59,37 +54,39 @@ import net.islandearth.rpgregions.rewards.QuestReward; import net.islandearth.rpgregions.rewards.RegionDiscoverReward; import net.islandearth.rpgregions.rewards.RegionRewardRegistry; import net.islandearth.rpgregions.rewards.TeleportReward; +import net.islandearth.rpgregions.schedule.BukkitScheduler; import net.islandearth.rpgregions.tasks.DynmapTask; import net.islandearth.rpgregions.translation.Translations; +import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.milkbowl.vault.economy.Economy; import org.bstats.bukkit.Metrics; import org.bstats.charts.SimplePie; import org.bstats.charts.SingleLineChart; import org.bukkit.Bukkit; import org.bukkit.Location; -import org.bukkit.OfflinePlayer; -import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.potion.PotionEffect; +import org.checkerframework.checker.nullness.qual.NonNull; -import java.io.File; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.ExecutionException; import java.util.logging.Level; public final class RPGRegions extends JavaPlugin implements IRPGRegionsAPI { + private BukkitAudiences adventure; + private PlatformScheduler scheduler; private RPGRegionsManagers managers; - private PaperCommandManager commandManager; private Economy ecoProvider; - public PaperCommandManager getCommandManager() { - return commandManager; + public @NonNull BukkitAudiences adventure() { + if (this.adventure == null) { + throw new IllegalStateException("Tried to access Adventure when the plugin was disabled!"); + } + return this.adventure; } @Override @@ -101,10 +98,11 @@ public final class RPGRegions extends JavaPlugin implements IRPGRegionsAPI { @Override public void onEnable() { + this.adventure = BukkitAudiences.create(this); + this.scheduler = FoliaScheduler.RUNNING_FOLIA ? new FoliaScheduler(this) : new BukkitScheduler(this); RPGRegionsAPI.setAPI(this); this.createConfig(); this.generateLang(); - this.commandManager = new PaperCommandManager(this); try { this.managers = new RPGRegionsManagers(this); } catch (ReflectiveOperationException | IOException | CouldNotStartException e) { @@ -112,12 +110,12 @@ public final class RPGRegions extends JavaPlugin implements IRPGRegionsAPI { Bukkit.getPluginManager().disablePlugin(this); return; } + this.registerCommands(); this.registerRewards(); this.registerRequirements(); this.registerEffects(); this.registerListeners(); - this.registerCommands(); - this.translator = Translator.of(this, "lang", Language.ENGLISH, debug()); + this.translator = Translator.of(this, "lang", Language.BRITISH_ENGLISH, debug()); this.registerTasks(); this.registerMetrics(); this.setupEconomy(); @@ -155,6 +153,11 @@ public final class RPGRegions extends JavaPlugin implements IRPGRegionsAPI { managers.getRegionsCache().getConfiguredRegions().forEach((id, region) -> region.save(this)); } + if (this.adventure != null) { + this.adventure.close(); + this.adventure = null; + } + RPGRegionsAPI.setAPI(null); DB.close(); } @@ -186,55 +189,7 @@ public final class RPGRegions extends JavaPlugin implements IRPGRegionsAPI { } private void registerCommands() { - final PaperCommandManager manager = commandManager; - manager.enableUnstableAPI("help"); - manager.getCommandCompletions().registerAsyncCompletion("regions", context -> ImmutableList.copyOf(getManagers().getRegionsCache().getConfiguredRegions().keySet())); - manager.getCommandCompletions().registerAsyncCompletion("integration-regions", context -> ImmutableList.copyOf(this.getManagers().getIntegrationManager().getAllRegionNames(context.getPlayer().getWorld()))); - manager.getCommandCompletions().registerAsyncCompletion("async", context -> ImmutableList.of("--async")); - manager.getCommandCompletions().registerAsyncCompletion("region-types", context -> ImmutableList.of("Cuboid", "Poly")); - manager.getCommandCompletions().registerAsyncCompletion("offline-players", context -> { - List players = new ArrayList<>(); - if (!getConfig().getBoolean("settings.server.tabcomplete.search-offline-players")) { - for (Player player : Bukkit.getOnlinePlayers()) { - players.add(player.getName()); - } - return ImmutableList.copyOf(players); - } - - for (OfflinePlayer offlinePlayer : Bukkit.getOfflinePlayers()) { - players.add(offlinePlayer.getName()); - } - return ImmutableList.copyOf(players); - }); - manager.getCommandCompletions().registerAsyncCompletion("schematics", context -> { - File schematicFolder = new File("plugins/WorldEdit/schematics/"); - List files = new ArrayList<>(); - for (File file : schematicFolder.listFiles()) { - files.add(file.getName()); - } - return files; - }); - manager.getCommandCompletions().registerAsyncCompletion("templates", context -> { - File templates = new File(this.getDataFolder() + File.separator + "templates"); - List files = new ArrayList<>(); - for (File file : templates.listFiles()) { - files.add(file.getName()); - } - return files; - }); - manager.getCommandContexts().registerContext(ConfiguredRegion.class, context -> { - String id = context.popFirstArg(); - for (ConfiguredRegion region : managers.getRegionsCache().getConfiguredRegions().values()) { - if (region.getId().equals(id)) { - return region; - } - } - throw new InvalidCommandArgument("Could not find a region with that id."); - }); - manager.registerCommand(new RPGRegionsCommand(this)); - manager.registerCommand(new DiscoveriesCommand(this)); - manager.registerCommand(new RPGRegionsDebugCommand(this)); - manager.registerCommand(new RPGRegionsExportCommand(this)); + new Commands(this); } private void registerRewards() { @@ -305,6 +260,11 @@ public final class RPGRegions extends JavaPlugin implements IRPGRegionsAPI { return Bukkit.getPluginManager().getPlugin("HeadDatabase") != null; } + @Override + public PlatformScheduler getScheduler() { + return scheduler; + } + private void setupEconomy() { if (getServer().getPluginManager().getPlugin("Vault") == null) { return; @@ -323,7 +283,7 @@ public final class RPGRegions extends JavaPlugin implements IRPGRegionsAPI { private void registerTasks() { if (Bukkit.getPluginManager().getPlugin("Dynmap") != null && getConfig().getBoolean("settings.external.dynmap")) { - Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new DynmapTask(this), 0L, 20L); + getScheduler().executeRepeating(new DynmapTask(this), 0L, 20L); getLogger().info("Registered support for Dynmap."); } } diff --git a/rpgregions/src/main/java/net/islandearth/rpgregions/api/integrations/rpgregions/RPGRegionsIntegration.java b/rpgregions/src/main/java/net/islandearth/rpgregions/api/integrations/rpgregions/RPGRegionsIntegration.java index c8240e5..d6b29ff 100644 --- a/rpgregions/src/main/java/net/islandearth/rpgregions/api/integrations/rpgregions/RPGRegionsIntegration.java +++ b/rpgregions/src/main/java/net/islandearth/rpgregions/api/integrations/rpgregions/RPGRegionsIntegration.java @@ -1,13 +1,10 @@ package net.islandearth.rpgregions.api.integrations.rpgregions; -import co.aikar.commands.InvalidCommandArgument; import com.google.gson.Gson; -import net.islandearth.rpgregions.RPGRegions; import net.islandearth.rpgregions.api.IRPGRegionsAPI; import net.islandearth.rpgregions.api.events.RegionsEnterEvent; import net.islandearth.rpgregions.api.integrations.IntegrationManager; import net.islandearth.rpgregions.api.integrations.rpgregions.region.RPGRegionsRegion; -import net.islandearth.rpgregions.commands.RPGRegionsIntegrationCommand; import net.islandearth.rpgregions.managers.data.region.ConfiguredRegion; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -39,20 +36,6 @@ public class RPGRegionsIntegration implements IntegrationManager { public RPGRegionsIntegration(IRPGRegionsAPI plugin) { this.plugin = plugin; this.regions = new HashMap<>(); - // Register command completions and command, it's here because we only want it to enable if enabled. - if (plugin instanceof RPGRegions rpgRegions) { - rpgRegions.getCommandManager().getCommandContexts().registerContext(RPGRegionsRegion.class, context -> { - String id = context.popFirstArg(); - if (rpgRegions.getManagers().getIntegrationManager() instanceof RPGRegionsIntegration rpgRegionsIntegration) { - Optional region = rpgRegionsIntegration.getRegion(id); - if (region.isPresent()) { - return region.get(); - } - } - throw new InvalidCommandArgument("Could not find a region with that id."); - }); - rpgRegions.getCommandManager().registerCommand(new RPGRegionsIntegrationCommand(rpgRegions)); - } plugin.getLogger().info("RPGRegions region integration has been enabled."); } diff --git a/rpgregions/src/main/java/net/islandearth/rpgregions/commands/Commands.java b/rpgregions/src/main/java/net/islandearth/rpgregions/commands/Commands.java new file mode 100644 index 0000000..d772583 --- /dev/null +++ b/rpgregions/src/main/java/net/islandearth/rpgregions/commands/Commands.java @@ -0,0 +1,146 @@ +package net.islandearth.rpgregions.commands; + +import cloud.commandframework.annotations.AnnotationParser; +import cloud.commandframework.arguments.parser.ParserParameters; +import cloud.commandframework.arguments.parser.ParserRegistry; +import cloud.commandframework.arguments.parser.StandardParameters; +import cloud.commandframework.bukkit.CloudBukkitCapabilities; +import cloud.commandframework.execution.CommandExecutionCoordinator; +import cloud.commandframework.extra.confirmation.CommandConfirmationManager; +import cloud.commandframework.meta.CommandMeta; +import cloud.commandframework.minecraft.extras.MinecraftExceptionHandler; +import cloud.commandframework.paper.PaperCommandManager; +import com.google.common.collect.ImmutableList; +import io.leangen.geantyref.TypeToken; +import net.islandearth.rpgregions.RPGRegions; +import net.islandearth.rpgregions.api.integrations.rpgregions.RPGRegionsIntegration; +import net.islandearth.rpgregions.api.integrations.rpgregions.region.RPGRegionsRegion; +import net.islandearth.rpgregions.commands.caption.RPGRegionsCaptionRegistry; +import net.islandearth.rpgregions.commands.caption.RPGRegionsCaptionRegistryFactory; +import net.islandearth.rpgregions.commands.parser.ConfiguredRegionArgument; +import net.islandearth.rpgregions.commands.parser.IntegrationRegionArgument; +import net.islandearth.rpgregions.managers.data.region.ConfiguredRegion; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; + +public class Commands { + + public Commands(RPGRegions plugin) { + + // This function maps the command sender type of our choice to the bukkit command sender. + final Function mapperFunction = Function.identity(); + + final PaperCommandManager manager; + try { + manager = new PaperCommandManager<>( + plugin, + CommandExecutionCoordinator.simpleCoordinator(), + mapperFunction, + mapperFunction + ); + } catch (Exception e) { + plugin.getLogger().severe("Failed to initialize the command manager"); + e.printStackTrace(); + return; + } + + // Register Brigadier mappings + if (manager.hasCapability(CloudBukkitCapabilities.BRIGADIER)) { + manager.registerBrigadier(); + } + + // Register asynchronous completions + if (manager.hasCapability(CloudBukkitCapabilities.ASYNCHRONOUS_COMPLETION)) { + manager.registerAsynchronousCompletions(); + } + + /* + * Create the confirmation manager. This allows us to require certain commands to be + * confirmed before they can be executed + */ + final CommandConfirmationManager confirmationManager = new CommandConfirmationManager<>(30L, TimeUnit.SECONDS, + /* Action when confirmation is required */ + context -> { + final String name = context.getCommand().getArguments().get(0).getName(); + context.getCommandContext().getSender().sendMessage( + ChatColor.RED + "Confirmation required. Confirm using " + ChatColor.YELLOW + "/" + name + " confirm" + ChatColor.RED + "."); + }, + /* Action when no confirmation is pending */ sender -> sender.sendMessage( + ChatColor.RED + "You don't have any pending commands.") + ); + + // Register the confirmation processor. This will enable confirmations for commands that require it + confirmationManager.registerConfirmationProcessor(manager); + + // This will allow you to decorate commands with descriptions + final Function commandMetaFunction = parserParameters -> + CommandMeta.simple() + .with(CommandMeta.DESCRIPTION, parserParameters.get(StandardParameters.DESCRIPTION, "No description")) + .build(); + + // Override the default exception handlers + // todo: change some stuff lmao + new MinecraftExceptionHandler() + .withInvalidSyntaxHandler() + .withInvalidSenderHandler() + .withNoPermissionHandler() + .withArgumentParsingHandler() + .apply(manager, player -> plugin.adventure().sender(player)); + + // Register our custom caption registry so we can define exception messages for parsers + final RPGRegionsCaptionRegistry captionRegistry = new RPGRegionsCaptionRegistryFactory().create(); + manager.captionRegistry(captionRegistry); + + // Register custom parsers + final ParserRegistry parserRegistry = manager.parserRegistry(); + parserRegistry.registerParserSupplier(TypeToken.get(ConfiguredRegion.class), parserParameters -> + new ConfiguredRegionArgument.ConfiguredRegionParser<>()); + parserRegistry.registerParserSupplier(TypeToken.get(RPGRegionsRegion.class), parserParameters -> + new IntegrationRegionArgument.IntegrationRegionParser<>()); + + parserRegistry.registerSuggestionProvider("region-types", (context, arg) -> ImmutableList.of("Cuboid", "Poly")); + + parserRegistry.registerSuggestionProvider("integration-regions", (context, arg) -> { + if (context.getSender() instanceof Player player) { + return ImmutableList.copyOf(plugin.getManagers().getIntegrationManager().getAllRegionNames(player.getWorld())); + } + return new ArrayList<>(); + }); + + parserRegistry.registerSuggestionProvider("templates", (context, arg) -> { + File templates = new File(plugin.getDataFolder() + File.separator + "templates"); + List files = new ArrayList<>(); + for (File file : templates.listFiles()) { + files.add(file.getName()); + } + return files; + }); + + parserRegistry.registerSuggestionProvider("schematics", (context, arg) -> { + File schematicFolder = new File("plugins/WorldEdit/schematics/"); + List files = new ArrayList<>(); + for (File file : schematicFolder.listFiles()) { + files.add(file.getName()); + } + return files; + }); + + parserRegistry.registerSuggestionProvider("async", (context, arg) -> List.of("--async")); + + final AnnotationParser annotationParser = new AnnotationParser<>(manager, CommandSender.class, commandMetaFunction); + annotationParser.parse(new DiscoveriesCommand(plugin)); + annotationParser.parse(new RPGRegionsCommand(plugin, manager)); + annotationParser.parse(new RPGRegionsDebugCommand(plugin)); + annotationParser.parse(new RPGRegionsExportCommand(plugin, manager)); + if (plugin.getManagers().getIntegrationManager() instanceof RPGRegionsIntegration) { + annotationParser.parse(new RPGRegionsIntegrationCommand(plugin, manager)); + } + } +} diff --git a/rpgregions/src/main/java/net/islandearth/rpgregions/commands/DiscoveriesCommand.java b/rpgregions/src/main/java/net/islandearth/rpgregions/commands/DiscoveriesCommand.java index 6fa44b8..b585ded 100644 --- a/rpgregions/src/main/java/net/islandearth/rpgregions/commands/DiscoveriesCommand.java +++ b/rpgregions/src/main/java/net/islandearth/rpgregions/commands/DiscoveriesCommand.java @@ -1,11 +1,9 @@ package net.islandearth.rpgregions.commands; -import co.aikar.commands.BaseCommand; -import co.aikar.commands.annotation.CommandAlias; -import co.aikar.commands.annotation.CommandCompletion; -import co.aikar.commands.annotation.CommandPermission; -import co.aikar.commands.annotation.Default; -import co.aikar.commands.annotation.Subcommand; +import cloud.commandframework.annotations.Argument; +import cloud.commandframework.annotations.CommandDescription; +import cloud.commandframework.annotations.CommandMethod; +import cloud.commandframework.annotations.CommandPermission; import net.islandearth.rpgregions.RPGRegions; import net.islandearth.rpgregions.api.events.RegionDiscoverEvent; import net.islandearth.rpgregions.gui.DiscoveryGUI; @@ -20,8 +18,7 @@ import org.bukkit.entity.Player; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -@CommandAlias("discoveries|discovery") -public class DiscoveriesCommand extends BaseCommand { +public class DiscoveriesCommand { private final RPGRegions plugin; @@ -29,16 +26,19 @@ public class DiscoveriesCommand extends BaseCommand { this.plugin = plugin; } - @Default + @CommandDescription("Opens the discovery GUI") @CommandPermission("rpgregions.list") + @CommandMethod("discoveries|discovery") public void onDefault(Player player) { new DiscoveryGUI(plugin, player).open(); } - @Subcommand("discover") + @CommandDescription("Discovers a region for a player") @CommandPermission("rpgregions.discover") - @CommandCompletion("@regions @offline-players") - public void onDiscover(CommandSender sender, ConfiguredRegion configuredRegion, OfflinePlayer target) { + @CommandMethod("discoveries|discovery discover ") + public void onDiscover(CommandSender sender, + @Argument("region") ConfiguredRegion configuredRegion, + @Argument("player") OfflinePlayer target) { plugin.getManagers().getStorageManager().getAccount(target.getUniqueId()).thenAccept(account -> { LocalDateTime date = LocalDateTime.now(); DateTimeFormatter format = DateTimeFormatter.ofPattern(plugin.getConfig().getString("settings.server.discoveries.date.format")); diff --git a/rpgregions/src/main/java/net/islandearth/rpgregions/commands/RPGRegionsCommand.java b/rpgregions/src/main/java/net/islandearth/rpgregions/commands/RPGRegionsCommand.java index 27720c6..f2340b3 100644 --- a/rpgregions/src/main/java/net/islandearth/rpgregions/commands/RPGRegionsCommand.java +++ b/rpgregions/src/main/java/net/islandearth/rpgregions/commands/RPGRegionsCommand.java @@ -1,13 +1,12 @@ package net.islandearth.rpgregions.commands; -import co.aikar.commands.BaseCommand; -import co.aikar.commands.CommandHelp; -import co.aikar.commands.annotation.CommandAlias; -import co.aikar.commands.annotation.CommandCompletion; -import co.aikar.commands.annotation.CommandPermission; -import co.aikar.commands.annotation.Default; -import co.aikar.commands.annotation.HelpCommand; -import co.aikar.commands.annotation.Subcommand; +import cloud.commandframework.annotations.Argument; +import cloud.commandframework.annotations.CommandDescription; +import cloud.commandframework.annotations.CommandMethod; +import cloud.commandframework.annotations.CommandPermission; +import cloud.commandframework.annotations.specifier.Greedy; +import cloud.commandframework.minecraft.extras.MinecraftHelp; +import cloud.commandframework.paper.PaperCommandManager; import net.islandearth.rpgregions.RPGRegions; import net.islandearth.rpgregions.api.RPGRegionsAPI; import net.islandearth.rpgregions.api.events.RPGRegionsReloadEvent; @@ -27,6 +26,7 @@ import org.bukkit.OfflinePlayer; import org.bukkit.World; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.checkerframework.checker.nullness.qual.NonNull; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -34,25 +34,30 @@ import java.io.File; import java.io.FileReader; import java.io.Reader; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; -@CommandAlias("rpgregions|rpgr") -public class RPGRegionsCommand extends BaseCommand { +public class RPGRegionsCommand { private final RPGRegions plugin; + private final MinecraftHelp help; private final List regenerateConfirm = new ArrayList<>(); private static final String WARNING_MESSAGE = ChatColor.RED + "" + ChatColor.ITALIC + "" + ChatColor.BOLD + "The regenerate configuration is very dangerous and can delete world sections if used wrongly."; - public RPGRegionsCommand(RPGRegions plugin) { + public RPGRegionsCommand(RPGRegions plugin, PaperCommandManager manager) { this.plugin = plugin; + this.help = new MinecraftHelp<>( + "/rpgregions help", + player -> plugin.adventure().sender(player), + manager + ); } - @Default + @CommandDescription("The default RPGRegions command") + @CommandMethod("rpgregions|rpgr") public void onDefault(CommandSender sender) { if (sender.hasPermission("rpgregions.noview") && !sender.isOp()) return; @@ -68,13 +73,13 @@ public class RPGRegionsCommand extends BaseCommand { + " regions are loaded with " + rewards + " rewards."); } - @HelpCommand - @Subcommand("help") - public void onHelp(final CommandHelp commandHelp) { - commandHelp.showHelp(); + @CommandMethod("rpgregions|rpgr help [query]") + public void onHelp(final CommandSender sender, @Argument("query") @Greedy @Nullable String query) { + help.queryCommands(query == null ? "" : query, sender); } - @Subcommand("about") + @CommandDescription("Debug information about the plugin") + @CommandMethod("rpgregions|rpgr about") public void onAbout(CommandSender sender) { sender.sendMessage(StringUtils.colour("&eRPGRegions v" + plugin.getDescription().getVersion() + ".")); sender.sendMessage(StringUtils.colour("&eOwner: https://www.spigotmc.org/members/%%__USER__%%/")); @@ -82,10 +87,13 @@ public class RPGRegionsCommand extends BaseCommand { sender.sendMessage(StringUtils.colour("&eIntegration: " + plugin.getManagers().getIntegrationManager().getClass().getName())); } - @Subcommand("add") + @CommandDescription("Creates a configured region from a region created in your integration") @CommandPermission("rpgregions.add") - @CommandCompletion("@integration-regions") - public void onAdd(CommandSender sender, String region, @co.aikar.commands.annotation.Optional @Nullable String worldName) { + @CommandMethod("rpgregions|rpgr add [world]") + public void onAdd(CommandSender sender, + @Argument(value = "region", suggestions = "integration-regions") String region, + @Argument("world") @Nullable World world) { + if (plugin.getManagers().getRegionsCache().getConfiguredRegion(region).isPresent()) { sender.sendMessage(StringUtils.colour("&cThat region is already configured.")); return; @@ -96,7 +104,6 @@ public class RPGRegionsCommand extends BaseCommand { location = player.getLocation(); } - final World world = worldName == null ? null : Bukkit.getWorld(worldName); if (world != null) { if (location == null) location = new Location(world, 0, 100, 0); else location.setWorld(world); @@ -127,59 +134,58 @@ public class RPGRegionsCommand extends BaseCommand { plugin.getManagers().getRegionsCache().addConfiguredRegion(configuredRegion); } - @Subcommand("setname") + @CommandDescription("Sets the display name of a region") @CommandPermission("rpgregions.setname") - @CommandCompletion("@regions") - public void onSetName(CommandSender sender, String region, String regionName) { - Optional configuredRegion = plugin.getManagers().getRegionsCache().getConfiguredRegion(region); - if (configuredRegion.isEmpty()) { - sender.sendMessage(StringUtils.colour("&cRegion '" + region + "' does not exist yet, create it first.")); - return; - } - configuredRegion.get().setCustomName(regionName); - sender.sendMessage(StringUtils.colour("&aSet name of region '" + region + "' to: " + regionName)); + @CommandMethod("rpgregions|rpgr setname ") + public void onSetName(CommandSender sender, + @Argument("region") ConfiguredRegion region, + @Argument("name") String name) { + region.setCustomName(name); + sender.sendMessage(StringUtils.colour("&aSet name of region '" + region + "' to: " + name)); } - @Subcommand("remove") + @CommandDescription("Removes a configured region. Does not delete it from your integration.") @CommandPermission("rpgregions.remove") - @CommandCompletion("@regions") - public void onRemove(CommandSender sender, ConfiguredRegion configuredRegion) { - if (configuredRegion != null) { - configuredRegion.delete(plugin); - plugin.getManagers().getRegionsCache().removeConfiguredRegion(configuredRegion.getId()); - sender.sendMessage(StringUtils.colour("&cRemoved configured region " + configuredRegion.getId() + "!")); + @CommandMethod("rpgregions|rpgr remove ") + public void onRemove(CommandSender sender, @Argument("region") ConfiguredRegion region) { + if (region != null) { + region.delete(plugin); + plugin.getManagers().getRegionsCache().removeConfiguredRegion(region.getId()); + sender.sendMessage(StringUtils.colour("&cRemoved configured region " + region.getId() + "!")); } else { sender.sendMessage(StringUtils.colour("&cA region by that name is not yet configured.")); } } - @Subcommand("edit") + @CommandDescription("Opens the editor GUI for a region") @CommandPermission("rpgregions.edit") - @CommandCompletion("@regions") - public void onEdit(Player player, ConfiguredRegion configuredRegion) { - new RegionCreateGUI(plugin, player, configuredRegion).open(); + @CommandMethod("rpgregions|rpgr edit ") + public void onEdit(Player player, @Argument("region") ConfiguredRegion region) { + new RegionCreateGUI(plugin, player, region).open(); } - @Subcommand("list|discoveries") + @CommandDescription("Opens the /discovery GUI") @CommandPermission("rpgregions.list") + @CommandMethod("rpgregions|rpgr list|discoveries") public void onList(Player player) { new DiscoveryGUI(plugin, player).open(); } - @Subcommand("additem") + @CommandDescription("Adds an item reward to a region (util command)") @CommandPermission("rpgregions.additem") - @CommandCompletion("@regions") - public void onAddItem(Player player, ConfiguredRegion configuredRegion) { - if (configuredRegion != null) { - configuredRegion.getRewards().add(new ItemReward(plugin, player.getInventory().getItemInMainHand())); + @CommandMethod("rpgregions|rpgr additem ") + public void onAddItem(Player player, @Argument("region") ConfiguredRegion region) { + if (region != null) { + region.getRewards().add(new ItemReward(plugin, player.getInventory().getItemInMainHand())); player.sendMessage(ChatColor.GREEN + "Item added to configuration!"); } else { player.sendMessage(ChatColor.RED + "No region exists by that name."); } } - @Subcommand("reload") + @CommandDescription("Reloads configured regions from the `/plugins/RPGRegions/regions` folder.") @CommandPermission("rpgregions.reload") + @CommandMethod("rpgregions|rpgr reload") public void onReload(CommandSender sender) { sender.sendMessage(ChatColor.GREEN + "Reloading region files..."); long startTime = System.currentTimeMillis(); @@ -208,11 +214,11 @@ public class RPGRegionsCommand extends BaseCommand { sender.sendMessage(ChatColor.GREEN + "Done! (" + totalTime + "ms)"); } - @Subcommand("save") + @CommandDescription("Saves configured regions to the `/plugins/RPGRegions/regions` folder. Also saves user data.") @CommandPermission("rpgregions.save") - @CommandCompletion("@async") - public void onSave(CommandSender sender, @co.aikar.commands.annotation.Optional String[] args) { - boolean async = Arrays.asList(args).contains("--async"); + @CommandMethod("rpgregions|rpgr save [async]") + public void onSave(CommandSender sender, @Argument(value = "async", suggestions = "async") String asyncArg) { + boolean async = asyncArg.equals("--async"); sender.sendMessage(ChatColor.GREEN + "Saving data..." + (async ? ChatColor.GOLD + " (async)" : "")); long startTime = System.currentTimeMillis(); @@ -236,68 +242,55 @@ public class RPGRegionsCommand extends BaseCommand { }); } - @Subcommand("reset") + @CommandDescription("Resets the data of a player") @CommandPermission("rpgregions.reset") - @CommandCompletion("@players @regions") - public void onReset(CommandSender sender, String[] args) { - @SuppressWarnings({"deprecation"}) // We know what we're doing. - OfflinePlayer player = Bukkit.getOfflinePlayer(args[0]); - if (!player.hasPlayedBefore()) { - sender.sendMessage(ChatColor.RED + "That player cannot be found."); - return; - } - - switch (args.length) { - case 1 -> { - plugin.getManagers().getStorageManager().clearDiscoveries(player.getUniqueId()); + @CommandMethod("rpgregions|rpgr reset [region]") + public void onReset(CommandSender sender, + @Argument("player") @NonNull OfflinePlayer player, + @Argument("region") @Nullable ConfiguredRegion region) { + if (region == null) { + plugin.getManagers().getStorageManager().clearDiscoveries(player.getUniqueId()); + if (!player.isOnline()) + plugin.getManagers().getStorageManager().removeCachedAccount(player.getUniqueId()); + sender.sendMessage(ChatColor.GREEN + "Players discoveries has been cleared."); + } else { + if (plugin.getManagers().getRegionsCache().getConfiguredRegions().containsKey(region.getId())) { + plugin.getManagers().getStorageManager().clearDiscovery(player.getUniqueId(), region.getId()); if (!player.isOnline()) plugin.getManagers().getStorageManager().removeCachedAccount(player.getUniqueId()); - sender.sendMessage(ChatColor.GREEN + "Players discoveries has been cleared."); + sender.sendMessage(ChatColor.GREEN + "Discovery cleared."); + } else { + sender.sendMessage(ChatColor.RED + "Region does not exist or is not configured."); } - case 2 -> { - String regionId = args[1]; - if (plugin.getManagers().getRegionsCache().getConfiguredRegions().containsKey(regionId)) { - plugin.getManagers().getStorageManager().clearDiscovery(player.getUniqueId(), regionId); - if (!player.isOnline()) - plugin.getManagers().getStorageManager().removeCachedAccount(player.getUniqueId()); - sender.sendMessage(ChatColor.GREEN + "Discovery cleared."); - } else { - sender.sendMessage(ChatColor.RED + "Region does not exist or is not configured."); - } - } - default -> sender.sendMessage(ChatColor.RED + "Usage: /rpgregions reset [region_id]"); } } - @Subcommand("delete") + + @CommandDescription("Deletes user accounts from the database") @CommandPermission("rpgregions.delete") - @CommandCompletion("@players") - public void onDelete(CommandSender sender, Player player) { - if (player != null) { - plugin.getManagers().getStorageManager().deleteAccount(player.getUniqueId()); - sender.sendMessage(ChatColor.GREEN + "Deleted account of player."); - } else { - sender.sendMessage(ChatColor.RED + "That player cannot be found."); - } + @CommandMethod("rpgregions|rpgr delete ") + public void onDelete(CommandSender sender, + @Argument("player") @NotNull OfflinePlayer player) { + plugin.getManagers().getStorageManager().deleteAccount(player.getUniqueId()); + sender.sendMessage(ChatColor.GREEN + "Deleted account of player."); } - @Subcommand("setlocation") + @CommandDescription("Sets the teleport location of a region") @CommandPermission("rpgregions.setlocation") - @CommandCompletion("@regions") - public void onSetLocation(Player player, ConfiguredRegion configuredRegion) { - if (configuredRegion != null) { - Location location = player.getLocation(); - configuredRegion.setLocation(location); - player.sendMessage(ChatColor.GREEN + "Location has been updated."); - } else { - player.sendMessage(StringUtils.colour("&cA region by that name is not yet configured.")); - } + @CommandMethod("rpgregions|rpgr setlocation ") + public void onSetLocation(Player player, + @Argument("region") ConfiguredRegion region) { + Location location = player.getLocation(); + region.setLocation(location); + player.sendMessage(ChatColor.GREEN + "Location has been updated."); } - @Subcommand("regenerate|regen") + //TODO: use confirmation api + @CommandDescription("If configured, regenerates a region to the set schematic") @CommandPermission("rpgregions.regenerate") - @CommandCompletion("@regions") - public void onRegenerate(Player player, ConfiguredRegion configuredRegion) { + @CommandMethod("rpgregions|rpgr regenerate ") + public void onRegenerate(Player player, + @Argument("region") ConfiguredRegion region) { IntegrationType integrationType = IntegrationType.valueOf(plugin.getConfig().getString("settings.integration.name").toUpperCase()); if (integrationType != IntegrationType.WORLDGUARD) { player.sendMessage(ChatColor.RED + "Regeneration only supports WorldGuard integrations."); @@ -306,13 +299,13 @@ public class RPGRegionsCommand extends BaseCommand { if (!regenerateConfirm.contains(player.getUniqueId())) { regenerateConfirm.add(player.getUniqueId()); - player.sendMessage(ChatColor.YELLOW + "Run /rpgregions regenerate " + configuredRegion.getId() + " again to confirm. Only use if you know what you are doing!"); + player.sendMessage(ChatColor.YELLOW + "Run /rpgregions regenerate " + region.getId() + " again to confirm. Only use if you know what you are doing!"); player.sendMessage(WARNING_MESSAGE); } else { regenerateConfirm.remove(player.getUniqueId()); player.sendMessage(ChatColor.GREEN + "Regenerating region..."); long startTime = System.currentTimeMillis(); - boolean done = RegenUtils.regenerate(configuredRegion); + boolean done = RegenUtils.regenerate(region); if (!done) { player.sendMessage(ChatColor.RED + "Unable to regenerate region. Check console for details."); } @@ -322,28 +315,30 @@ public class RPGRegionsCommand extends BaseCommand { } } - @Subcommand("setschematic") + @CommandDescription("Sets the regeneration schematic for a region") @CommandPermission("rpgregions.setschematic") - @CommandCompletion("@regions @schematics @nothing") - public void onAddSchematic(Player player, ConfiguredRegion configuredRegion, String schematicName) { + @CommandMethod("rpgregions|rpgr setschematic ") + public void onAddSchematic(Player player, + @Argument("region") ConfiguredRegion region, + @Argument("schematicName") String schematicName) { IntegrationType integrationType = IntegrationType.valueOf(plugin.getConfig().getString("settings.integration.name").toUpperCase()); if (integrationType != IntegrationType.WORLDGUARD) { player.sendMessage(ChatColor.RED + "Regeneration only supports WorldGuard integrations."); return; } - if (configuredRegion != null) { + if (region != null) { if (!regenerateConfirm.contains(player.getUniqueId())) { regenerateConfirm.add(player.getUniqueId()); player.sendMessage(ChatColor.YELLOW + "Run /rpgregions addschematic " + schematicName + " again to confirm. Only use if you know what you are doing!"); player.sendMessage(ChatColor.RED + "MAKE SURE YOU ARE STANDING WHERE YOU CREATED THE SCHEMATIC ORIGINALLY (//copy, //schematic save), OTHERWISE IT WILL NOT PASTE CORRECTLY."); } else { regenerateConfirm.remove(player.getUniqueId()); - Regenerate regenerate = configuredRegion.getRegenerate(); + Regenerate regenerate = region.getRegenerate(); if (regenerate == null) regenerate = new Regenerate(Integer.MAX_VALUE, false, new ArrayList<>()); regenerate.setSchematicName(schematicName); regenerate.setOrigin(player.getLocation()); - configuredRegion.setRegenerate(regenerate); + region.setRegenerate(regenerate); player.sendMessage(ChatColor.GREEN + "This region has had a regenerate section added, and schematicName set to " + schematicName + " and origin set to " + player.getLocation() + "."); player.sendMessage(ChatColor.YELLOW + "Run /rpgregions save and " + ChatColor.BOLD + "CONFIGURE BEFORE RELOADING OR RESTARTING THE SERVER."); } @@ -351,8 +346,9 @@ public class RPGRegionsCommand extends BaseCommand { } } - @Subcommand("forceupdateicons") + @CommandDescription("Resets the icons of all configured regions to the config default") @CommandPermission("rpgregions.forceupdate") + @CommandMethod("rpgregions|rpgr forceupdateicons") public void onForceUpdateIcons(Player player) { Optional defaultIcon = Optional.of(Material.valueOf(RPGRegionsAPI.getAPI().getConfig().getString("settings.server.gui.default_region_icon"))); defaultIcon.ifPresent(xMaterial -> { diff --git a/rpgregions/src/main/java/net/islandearth/rpgregions/commands/RPGRegionsDebugCommand.java b/rpgregions/src/main/java/net/islandearth/rpgregions/commands/RPGRegionsDebugCommand.java index 5704a42..5fd7b54 100644 --- a/rpgregions/src/main/java/net/islandearth/rpgregions/commands/RPGRegionsDebugCommand.java +++ b/rpgregions/src/main/java/net/islandearth/rpgregions/commands/RPGRegionsDebugCommand.java @@ -1,10 +1,8 @@ package net.islandearth.rpgregions.commands; -import co.aikar.commands.BaseCommand; -import co.aikar.commands.annotation.CommandAlias; -import co.aikar.commands.annotation.CommandPermission; -import co.aikar.commands.annotation.Default; -import co.aikar.commands.annotation.Subcommand; +import cloud.commandframework.annotations.Argument; +import cloud.commandframework.annotations.CommandMethod; +import cloud.commandframework.annotations.CommandPermission; import co.aikar.idb.DB; import co.aikar.idb.Database; import net.islandearth.rpgregions.RPGRegions; @@ -15,14 +13,14 @@ import net.islandearth.rpgregions.rewards.DiscoveryReward; import net.islandearth.rpgregions.thread.Blocking; import org.apache.commons.lang3.StringUtils; import org.bukkit.ChatColor; +import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; import java.sql.Connection; import java.sql.SQLException; -@CommandAlias("rpgregionsdebug|rpgrd") @CommandPermission("rpgregions.debug") -public class RPGRegionsDebugCommand extends BaseCommand { +public class RPGRegionsDebugCommand { private final RPGRegions plugin; @@ -30,7 +28,7 @@ public class RPGRegionsDebugCommand extends BaseCommand { this.plugin = plugin; } - @Default + @CommandMethod("rpgregionsdebug|rpgrd") public void onDefault(CommandSender sender) throws SQLException { IStorageManager storageManager = plugin.getManagers().getStorageManager(); sender.sendMessage(ChatColor.GOLD + "Database status:"); @@ -67,8 +65,7 @@ public class RPGRegionsDebugCommand extends BaseCommand { }); } - - @Subcommand("enable|disable") + @CommandMethod("rpgregionsdebug|rpgrd enable|disable|toggle") public void onEnable(CommandSender sender) { final boolean newValue = !plugin.debug(); plugin.getConfig().set("settings.dev.debug", newValue); @@ -76,7 +73,7 @@ public class RPGRegionsDebugCommand extends BaseCommand { else sender.sendMessage(ChatColor.RED + "Debug disabled."); } - @Subcommand("cache") + @CommandMethod("rpgregionsdebug|rpgrd cache") public void onCache(CommandSender sender) { IStorageManager storageManager = plugin.getManagers().getStorageManager(); sender.sendMessage(ChatColor.GOLD + "Database cache:"); @@ -84,4 +81,13 @@ public class RPGRegionsDebugCommand extends BaseCommand { sender.sendMessage(ChatColor.GRAY + " - " + uuid.toString()); }); } + + @CommandMethod("rpgregionsdebug|rpgrd removecached ") + public void onRemoveCached(CommandSender sender, @Argument("player") OfflinePlayer player) { + IStorageManager storageManager = plugin.getManagers().getStorageManager(); + sender.sendMessage(ChatColor.GREEN + "Removing from cache..."); + storageManager.removeCachedAccount(player.getUniqueId()).thenAccept((a) -> { + sender.sendMessage(ChatColor.GREEN + "Successfully removed and saved " + player.getName() + "'s account from the cache."); + }); + } } diff --git a/rpgregions/src/main/java/net/islandearth/rpgregions/commands/RPGRegionsExportCommand.java b/rpgregions/src/main/java/net/islandearth/rpgregions/commands/RPGRegionsExportCommand.java index d642722..ab3b192 100644 --- a/rpgregions/src/main/java/net/islandearth/rpgregions/commands/RPGRegionsExportCommand.java +++ b/rpgregions/src/main/java/net/islandearth/rpgregions/commands/RPGRegionsExportCommand.java @@ -1,13 +1,11 @@ package net.islandearth.rpgregions.commands; -import co.aikar.commands.BaseCommand; -import co.aikar.commands.CommandHelp; -import co.aikar.commands.annotation.CommandAlias; -import co.aikar.commands.annotation.CommandCompletion; -import co.aikar.commands.annotation.CommandPermission; -import co.aikar.commands.annotation.Default; -import co.aikar.commands.annotation.HelpCommand; -import co.aikar.commands.annotation.Subcommand; +import cloud.commandframework.annotations.Argument; +import cloud.commandframework.annotations.CommandDescription; +import cloud.commandframework.annotations.CommandMethod; +import cloud.commandframework.annotations.specifier.Greedy; +import cloud.commandframework.minecraft.extras.MinecraftHelp; +import cloud.commandframework.paper.PaperCommandManager; import net.islandearth.rpgregions.RPGRegions; import net.islandearth.rpgregions.effects.RegionEffect; import net.islandearth.rpgregions.managers.data.region.ConfiguredRegion; @@ -15,36 +13,42 @@ import net.islandearth.rpgregions.requirements.RegionRequirement; import net.islandearth.rpgregions.rewards.DiscoveryReward; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.FileReader; import java.io.Reader; import java.util.logging.Level; -@CommandAlias("rpgre") -@CommandPermission("rpgregions.export") -public class RPGRegionsExportCommand extends BaseCommand { +public class RPGRegionsExportCommand { private final RPGRegions plugin; + private final MinecraftHelp help; - public RPGRegionsExportCommand(RPGRegions plugin) { + public RPGRegionsExportCommand(RPGRegions plugin, PaperCommandManager manager) { this.plugin = plugin; + this.help = new MinecraftHelp<>( + "/rpgre help", + player -> plugin.adventure().sender(player), + manager + ); } - @Default + @CommandDescription("The default RPGRegions export command.") + @CommandMethod("rpgre") public void onDefault(CommandSender sender) { sender.sendMessage(ChatColor.GREEN + "Use this command to export/import a region template."); } - @HelpCommand - @Subcommand("help") - public void onHelp(final CommandHelp commandHelp) { - commandHelp.showHelp(); + @CommandMethod("rpgre help [query]") + public void onHelp(final CommandSender sender, @Argument("query") @Greedy @Nullable String query) { + help.queryCommands(query == null ? "" : query, sender); } - @Subcommand("export") - @CommandCompletion("@regions") - public void onExport(CommandSender sender, ConfiguredRegion region) { + @CommandDescription("Exports a region to a template file (`plugins/RPGRegions/templates`).") + @CommandMethod("rpgre export ") + public void onExport(CommandSender sender, + @Argument("region") ConfiguredRegion region) { File templates = new File(plugin.getDataFolder() + File.separator + "templates"); File target = new File(templates + File.separator + region.getId() + "_template.json"); region.save(plugin, target); @@ -53,9 +57,13 @@ public class RPGRegionsExportCommand extends BaseCommand { sender.sendMessage(ChatColor.YELLOW + "You may use /rpgre import " + region.getId() + " to import the requirements, rewards, etc. of this template to the target region."); } - @Subcommand("import") - @CommandCompletion("@templates @regions") - public void onImport(CommandSender sender, String template, ConfiguredRegion region) { + @CommandDescription("Imports a template region into an existing configured region") + @CommandMethod("rpgre import