diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index f2d4cba..4b27bf6 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -21,7 +21,7 @@ jobs: uses: actions/setup-java@v4 with: distribution: "temurin" - java-version: 17 + java-version: 21 - uses: gradle/gradle-build-action@v2 with: # allow master and *-dev branches to write caches (default is only master/main) diff --git a/code/build.gradle.kts b/code/build.gradle.kts index f883304..b0d3912 100644 --- a/code/build.gradle.kts +++ b/code/build.gradle.kts @@ -4,13 +4,20 @@ plugins { dependencies { implementation(libs.cloud.core) + + // Minecraft implementation(libs.cloud.minecraft.extras) + implementation(libs.cloud.minecraft.paper) + implementation(libs.paper) + + // Processors + implementation(libs.cloud.processors.cooldown) } indra { javaVersions { - minimumToolchain(8) - target(8) + minimumToolchain(21) + target(21) } } diff --git a/code/gradle/libs.versions.toml b/code/gradle/libs.versions.toml index beb8c46..91d3038 100644 --- a/code/gradle/libs.versions.toml +++ b/code/gradle/libs.versions.toml @@ -1,12 +1,22 @@ [versions] indra = "3.1.3" -cloud = "2.0.0-beta.2" -cloudMinecraft = "2.0.0-beta.2" +cloud = "2.0.0-rc.2" +cloudMinecraft = "2.0.0-beta.8" +cloudProcessors = "1.0.0-beta.3" + +paper = "1.20.6-R0.1-SNAPSHOT" [plugins] indra = { id = "net.kyori.indra", version.ref = "indra" } [libraries] cloud-core = { group = "org.incendo", name = "cloud-core", version.ref = "cloud" } + +# Minecraft cloud-minecraft-extras = { group = "org.incendo", name = "cloud-minecraft-extras", version.ref = "cloudMinecraft" } +cloud-minecraft-paper = { group = "org.incendo", name = "cloud-paper", version.ref = "cloudMinecraft" } +paper = { group = "io.papermc.paper", name = "paper-api", version.ref = "paper" } + +# Processors +cloud-processors-cooldown = { group = "org.incendo", name = "cloud-processors-cooldown", version.ref = "cloudProcessors" } diff --git a/code/settings.gradle.kts b/code/settings.gradle.kts index 8abe398..4af1412 100644 --- a/code/settings.gradle.kts +++ b/code/settings.gradle.kts @@ -22,6 +22,9 @@ dependencyResolutionManagement { name = "dv8tion" mavenContent { releasesOnly() } } + maven("https://repo.papermc.io/repository/maven-public/") { + name = "papermc" + } } } diff --git a/code/src/main/java/org/incendo/cloud/snippet/minecraft/MinecraftHelpExample.java b/code/src/main/java/org/incendo/cloud/snippet/minecraft/MinecraftHelpExample.java index 89a28f8..6f9bde0 100644 --- a/code/src/main/java/org/incendo/cloud/snippet/minecraft/MinecraftHelpExample.java +++ b/code/src/main/java/org/incendo/cloud/snippet/minecraft/MinecraftHelpExample.java @@ -70,7 +70,7 @@ public void helpCommand(final @NonNull CommandManager commandM .entries() .stream() .map(CommandEntry::syntax) - .map(Suggestion::simple) + .map(Suggestion::suggestion) .collect(Collectors.toList()) ) ) diff --git a/code/src/main/java/org/incendo/cloud/snippet/minecraft/PaperExample.java b/code/src/main/java/org/incendo/cloud/snippet/minecraft/PaperExample.java new file mode 100644 index 0000000..4db0ebf --- /dev/null +++ b/code/src/main/java/org/incendo/cloud/snippet/minecraft/PaperExample.java @@ -0,0 +1,63 @@ +package org.incendo.cloud.snippet.minecraft; + +import io.papermc.paper.command.brigadier.CommandSourceStack; +import org.bukkit.command.CommandSender; +import org.bukkit.plugin.java.JavaPlugin; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.SenderMapper; +import org.incendo.cloud.execution.ExecutionCoordinator; +import org.incendo.cloud.paper.LegacyPaperCommandManager; +import org.incendo.cloud.paper.PaperCommandManager; + +public class PaperExample { + + public void exampleLegacyNative(final @NonNull JavaPlugin yourPlugin) { + final ExecutionCoordinator executionCoordinator = ExecutionCoordinator.simpleCoordinator(); + // --8<-- [start:legacy_native] + LegacyPaperCommandManager commandManager = LegacyPaperCommandManager.createNative( + yourPlugin, /* 1 */ + executionCoordinator /* 2 */ + ); + // --8<-- [end:legacy_native] + } + + public void exampleLegacyCustom( + final @NonNull JavaPlugin yourPlugin, + final @NonNull SenderMapper senderMapper + ) { + final ExecutionCoordinator executionCoordinator = ExecutionCoordinator.simpleCoordinator(); + // --8<-- [start:legacy_custom] + LegacyPaperCommandManager commandManager = new LegacyPaperCommandManager<>( + yourPlugin, /* 1 */ + executionCoordinator, /* 2 */ + senderMapper /* 3 */ + ); + // --8<-- [end:legacy_custom] + } + + public void exampleModernNative(final @NonNull JavaPlugin javaPlugin) { + final ExecutionCoordinator executionCoordinator = ExecutionCoordinator.simpleCoordinator(); + // --8<-- [start:modern_native] + PaperCommandManager commandManager = PaperCommandManager.builder() + .executionCoordinator(executionCoordinator) + .buildOnEnable(javaPlugin); + // or: .buildBootstrapped(bootstrapContext); + // --8<-- [end:modern_native] + } + + public void exampleModernCustom( + final @NonNull JavaPlugin javaPlugin, + final @NonNull SenderMapper senderMapper + ) { + final ExecutionCoordinator executionCoordinator = ExecutionCoordinator.simpleCoordinator(); + // --8<-- [start:modern_custom] + PaperCommandManager commandManager = PaperCommandManager.builder(senderMapper) + .executionCoordinator(executionCoordinator) + .buildOnEnable(javaPlugin); + // or: .buildBootstrapped(bootstrapContext); + // --8<-- [end:modern_custom] + } + + public record YourSenderType() { + } +} diff --git a/code/src/main/java/org/incendo/cloud/snippet/processors/CooldownExample.java b/code/src/main/java/org/incendo/cloud/snippet/processors/CooldownExample.java new file mode 100644 index 0000000..5877883 --- /dev/null +++ b/code/src/main/java/org/incendo/cloud/snippet/processors/CooldownExample.java @@ -0,0 +1,90 @@ +package org.incendo.cloud.snippet.processors; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.incendo.cloud.CommandManager; +import org.incendo.cloud.processors.cooldown.Cooldown; +import org.incendo.cloud.processors.cooldown.CooldownConfiguration; +import org.incendo.cloud.processors.cooldown.CooldownGroup; +import org.incendo.cloud.processors.cooldown.CooldownManager; +import org.incendo.cloud.processors.cooldown.CooldownRepository; +import org.incendo.cloud.processors.cooldown.DurationFunction; +import org.incendo.cloud.processors.cooldown.listener.ScheduledCleanupCreationListener; +import org.incendo.cloud.processors.cooldown.profile.CooldownProfile; +import java.time.Duration; +import java.util.HashMap; +import java.util.UUID; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; + +/** + * Example of Cooldown usage. + */ +public class CooldownExample { + + public void configurationExample() { + // --8<-- [start:configuration] + CooldownRepository repository = CooldownRepository.forMap(new HashMap<>()); + CooldownConfiguration configuration = CooldownConfiguration.builder() + // ... + .repository(repository) + .addActiveCooldownListener(((sender, command, cooldown, remainingTime) -> { /* ... */})) + .build(); + // --8<-- [end:configuration] + } + + public void cleanupExample() { + // --8<-- [start:cleanup] + CooldownRepository repository = CooldownRepository.forMap(new HashMap<>()); + ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); + CooldownConfiguration configuration = CooldownConfiguration.builder() + // ... + .repository(repository) + .addCreationListener(new ScheduledCleanupCreationListener<>(executorService, repository)) + .build(); + // --8<-- [end:cleanup] + } + + public void registrationExample( + final @NonNull CommandManager commandManager, + final @NonNull CooldownManager cooldownManager + ) { + // --8<-- [start:registration] + commandManager.registerCommandPostProcessor( + cooldownManager.createPostprocessor() + ); + // --8<-- [end:registration] + } + + public void creationExample(final @NonNull CooldownConfiguration configuration) { + // --8<-- [start:creation] + CooldownManager cooldownManager = CooldownManager.cooldownManager( + configuration + ); + // --8<-- [end:creation] + } + + public void mappingExample() { + // --8<-- [start:mapping] + CooldownRepository repository = CooldownRepository.mapping( + YourSenderType::uuid, + CooldownRepository.forMap(new HashMap<>()) + ); + // --8<-- [end:mapping] + } + + public void cooldownExample() { + // --8<-- [start:cooldown] + Cooldown cooldown = Cooldown.of( + DurationFunction.constant(Duration.ofMinutes(5L)) + ); + Cooldown grouped = Cooldown.of( + DurationFunction.constant(Duration.ofMinutes(5L)), + CooldownGroup.named("group-name") + ); + // --8<-- [end:cooldown] + } + + private record YourSenderType(@NonNull UUID uuid) { + + } +} diff --git a/docs/annotations/index.md b/docs/annotations/index.md index 887c585..fc6d693 100644 --- a/docs/annotations/index.md +++ b/docs/annotations/index.md @@ -8,7 +8,7 @@ The module can also function as an [annotation processor](#annotation-processing There are extensions to `cloud-annotations` for Kotlin, more information [here](../kotlin/annotations.md). Examples can be found on -[GitHub](https://github.com/Incendo/cloud-minecraft/tree/master/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/annotations) +[GitHub](https://github.com/Incendo/cloud-minecraft/tree/master/examples/example-bukkit/src/main/java/org/incendo/cloud/examples/bukkit/annotations) ## Links diff --git a/docs/minecraft/paper.md b/docs/minecraft/paper.md index 079e2b7..32edf6c 100644 --- a/docs/minecraft/paper.md +++ b/docs/minecraft/paper.md @@ -24,28 +24,30 @@ Cloud for Paper is available through [Maven Central](https://central.sonatype.co ## Usage -`cloud-paper` has a command manager implementation called -{{ javadoc("https://javadoc.io/doc/org.incendo/cloud-paper/latest/org/incendo/cloud/paper/PaperCommandManager.html", "PaperCommandManager") }} -that can be created in two ways. +`cloud-paper` has two different command manager implementations: + +- {{ javadoc("https://javadoc.io/doc/org.incendo/cloud-paper/latest/org/incendo/cloud/paper/PaperCommandManager.html", "PaperCommandManager") }}: Paper command API +- {{ javadoc("https://javadoc.io/doc/org.incendo/cloud-paper/latest/org/incendo/cloud/paper/LegacyPaperCommandManager.html", "LegacyPaperCommandManager") }}: Legacy command API + +{{ javadoc("https://javadoc.io/doc/org.incendo/cloud-paper/latest/org/incendo/cloud/paper/PaperCommandManager.html", "PaperCommandManager") }} should be preferred +when targeting Paper 1.20.6+ exclusively. The new manager allows registering commands at bootstrapping time in addition to `onEnable`, +which allows for using those commands in datapack functions. + +If the plugin is targeting older Paper versions or non-paper servers, then +{{ javadoc("https://javadoc.io/doc/org.incendo/cloud-paper/latest/org/incendo/cloud/paper/LegacyPaperCommandManager.html", "LegacyPaperCommandManager") }} +should be used. + +### Legacy + +The legacy command manager can be instantiated in two different ways. With a custom sender type: -```java -PaperCommandManager commandManager = new PaperCommandManager<>( - yourPlugin, /* 1 */ - executionCoordinator, /* 2 */ - senderMapper /* 3 */ -); -``` +{{ snippet("minecraft/PaperExample.java", section = "legacy_custom", title = "") }} Or, using Bukkit's {{ javadoc("https://jd.papermc.io/paper/1.20/org/bukkit/command/CommandSender.html", "CommandSender") }}: -```java -PaperCommandManager commandManager = PaperCommandManager.createNative( - yourPlugin, /* 1 */ - executionCoordinator /* 2 */ -); -``` +{{ snippet("minecraft/PaperExample.java", section = "legacy_native", title = "") }} 1. You need to pass an instance of the plugin that is constructing the command manager. This is used to register the commands and the different event listeners. @@ -55,16 +57,26 @@ PaperCommandManager commandManager = PaperCommandManager.createNa 3. The sender mapper is a two-way mapping between Bukkit's {{ javadoc("https://jd.papermc.io/paper/1.20/org/bukkit/command/CommandSender.html", "CommandSender") }} and your custom sender type. Using {{ javadoc("", "SenderMapper.identity()") }} - is equivalent to the {{ javadoc("", "createNative") }} + is equivalent to the {{ javadoc("", "createNative") }} static factory method. +### Modern + +The modern command manager is created using a builder. You may either use the native +{{ javadoc("https://jd.papermc.io/paper/1.20.6/io/papermc/paper/command/brigadier/CommandSourceStack.html", "CommandSourceStack") }}: +{{ snippet("minecraft/PaperExample.java", section = "modern_native", title = "") }} + +or a custom type: +{{ snippet("minecraft/PaperExample.java", section = "modern_custom", title = "") }} + ## Brigadier Paper exposes [Brigadier](https://github.com/mojang/brigadier), which means that you may use the features -from [cloud-brigadier](brigadier.md) on Paper servers. +from [cloud-brigadier](brigadier.md) on Paper servers. When using the modern Paper manager, you do not need to explicitly +enable Brigadier. -You may enable Brigadier mappings using -{{ javadoc("", "PaperCommandManager#registerBrigadier()") }}. +When using the legacy command manager you may enable Brigadier mappings using +{{ javadoc("", "LegacyPaperCommandManager#registerBrigadier()") }}. You should make use of the capability system to make sure that Brigadier is available on the server your plugin is running on: @@ -85,7 +97,7 @@ Paper allows for non-blocking suggestions. You are highly recommended to make us the argument parsers during suggestion generation which ideally should not take place on the main server thread. You may enable asynchronous completions using -{{ javadoc("", "PaperCommandManager#registerAsynchronousCompletions()") }}. +{{ javadoc("", "LegacyPaperCommandManager#registerAsynchronousCompletions()") }}. You should make use of the capability system to make sure that this is available on the server your plugin is running on: ```java diff --git a/docs/processors/cooldown.md b/docs/processors/cooldown.md index 2b077c5..8e29f8b 100644 --- a/docs/processors/cooldown.md +++ b/docs/processors/cooldown.md @@ -10,23 +10,12 @@ Postprocessor that adds command cooldowns. The cooldown system is managed by a `CooldownManager`, so the first step is to create an instance of that: -```java -CooldownManager cooldownManager = CooldownManager.of( - configuration -); -``` +{{ snippet("processors/CooldownExample.java", section = "creation", title = "") }} The configuration is an instance of `CooldownConfiguration`. Refer to the JavaDocs for information about specific options, but an example would be: -```java -CooldownConfiguration.builder() - .repository(CooldownRepository.forMap(new HashMap<>())) - .addActiveCooldownListener(...) - .addCooldownCreationListener(...) - .cooldownNotifier(notifier) - .build(); -``` +{{ snippet("processors/CooldownExample.java", section = "configuration", title = "") }} The listeners are invoked when different events take place. The active cooldown listener in particular may be used to inform the command sender that their command execution got blocked due to an active cooldown. @@ -40,49 +29,24 @@ You may create a repository from a map, `CloudCache` or even implement your own. across multiple temporary sessions then you may use a mapping repository to store the cooldown profiles for a persistent key, rather than the potentially temporary command sender objects: -```java -CooldownRepository repository = CooldownRepository.mapping( - sender -> sender.uuid(), - CooldownRepository.forMap(new HashMap()) -); -``` +{{ snippet("processors/CooldownExample.java", section = "mapping", title = "") }} You may also customize how the cooldown profiles are created by passing a `CooldownProfileFactory` to the `CooldownConfiguration`. If you want to have the cooldowns automatically removed from the repository to prevent unused profiles from taking up memory you -may register a `ScheduledCleanupCreationListener`: +may register a `ScheduledCleanupCreationListener` to the configuration, using -```java -CooldownRepository repository = CooldownRepository.forMap(new HashMap<>()); -ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); -CooldownConfiguration configuration = CooldownConfiguration.builder() - // ... - .repository(repository) - .addCreationListener(new ScheduledCleanupCreationListener(executorService, repository)) - .build(); -``` +{{ snippet("processors/CooldownExample.java", section = "cleanup", title = "") }} You then need to register the postprocessor: -```java -commandManager.registerCommandPostProcessor( - cooldownManager.createPostprocessor() -); -``` +{{ snippet("processors/CooldownExample.java", section = "registration", title = "") }} ### Builders The cooldowns are configured using a `Cooldown` instance: -```java -Cooldown cooldown = Cooldown.of( - DurationFunction.constant(Duration.ofMinutes(5L)) -); -Cooldown cooldown = Cooldown.of( - DurationFunction.constant(Duration.ofMinutes(5L)), - CooldownGroup.named("group-name") -); -``` +{{ snippet("processors/CooldownExample.java", section = "cooldown", title = "") }} which can then be applied to the command by either manually setting the meta value: diff --git a/docs/processors/requirements.md b/docs/processors/requirements.md index e30e286..490e78a 100644 --- a/docs/processors/requirements.md +++ b/docs/processors/requirements.md @@ -52,7 +52,7 @@ the requirements in the command meta and for the processor to access the stored public static final CloudKey> REQUIREMENT_KEY = CloudKey.of( "requirements", - new TypeToken>>() {} + new TypeToken>() {} ); ```