diff --git a/build.gradle b/build.gradle index a0afdc29..8bb6fefb 100644 --- a/build.gradle +++ b/build.gradle @@ -21,6 +21,16 @@ checkstyle { configFile file('config/checkstyle/checkstyle.xml') } +gradle.taskGraph.whenReady { + gradle.taskGraph.allTasks.each { + if (it.project.name.contains('example')) { + it.onlyIf { + project.hasProperty('compile-examples') + } + } + } +} + allprojects { apply plugin: 'idea' apply plugin: 'checkstyle' diff --git a/cloud-core/src/main/java/cloud/commandframework/annotations/specifier/Greedy.java b/cloud-core/src/main/java/cloud/commandframework/annotations/specifier/Greedy.java new file mode 100644 index 00000000..58d3f11b --- /dev/null +++ b/cloud-core/src/main/java/cloud/commandframework/annotations/specifier/Greedy.java @@ -0,0 +1,37 @@ +// +// MIT License +// +// Copyright (c) 2020 Alexander Söderberg & Contributors +// +// 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 cloud.commandframework.annotations.specifier; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation used to make {@link cloud.commandframework.arguments.standard.StringArgument string arguments} greedy + */ +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface Greedy { +} diff --git a/cloud-core/src/main/java/cloud/commandframework/arguments/parser/StandardParameters.java b/cloud-core/src/main/java/cloud/commandframework/arguments/parser/StandardParameters.java index 0ae49c3a..4e365086 100644 --- a/cloud-core/src/main/java/cloud/commandframework/arguments/parser/StandardParameters.java +++ b/cloud-core/src/main/java/cloud/commandframework/arguments/parser/StandardParameters.java @@ -55,6 +55,10 @@ public final class StandardParameters { * The command should be hidden from help menus, etc */ public static final ParserParameter HIDDEN = create("hidden", TypeToken.get(Boolean.class)); + /** + * Indicates that a string argument should be greedy + */ + public static final ParserParameter GREEDY = create("greedy", TypeToken.get(Boolean.class)); private StandardParameters() { } diff --git a/cloud-core/src/main/java/cloud/commandframework/arguments/parser/StandardParserRegistry.java b/cloud-core/src/main/java/cloud/commandframework/arguments/parser/StandardParserRegistry.java index c32ceb15..f2e9fb7f 100644 --- a/cloud-core/src/main/java/cloud/commandframework/arguments/parser/StandardParserRegistry.java +++ b/cloud-core/src/main/java/cloud/commandframework/arguments/parser/StandardParserRegistry.java @@ -23,6 +23,7 @@ // package cloud.commandframework.arguments.parser; +import cloud.commandframework.annotations.specifier.Greedy; import cloud.commandframework.arguments.standard.UUIDArgument; import cloud.commandframework.annotations.specifier.Completions; import cloud.commandframework.annotations.specifier.Range; @@ -101,9 +102,15 @@ public final class StandardParserRegistry implements ParserRegistry { (double) options.get(StandardParameters.RANGE_MAX, Double.MAX_VALUE))); this.registerParserSupplier(TypeToken.get(Character.class), options -> new CharArgument.CharacterParser()); /* Make this one less awful */ - this.registerParserSupplier(TypeToken.get(String.class), options -> new StringArgument.StringParser( - StringArgument.StringMode.SINGLE, (context, s) -> - Arrays.asList(options.get(StandardParameters.COMPLETIONS, new String[0])))); + this.registerParserSupplier(TypeToken.get(String.class), options -> { + final boolean greedy = options.get(StandardParameters.GREEDY, false); + final StringArgument.StringMode stringMode = greedy + ? StringArgument.StringMode.GREEDY + : StringArgument.StringMode.SINGLE; + return new StringArgument.StringParser( + stringMode, + (context, s) -> Arrays.asList(options.get(StandardParameters.COMPLETIONS, new String[0]))); + }); /* Add options to this */ this.registerParserSupplier(TypeToken.get(Boolean.class), options -> new BooleanArgument.BooleanParser<>(false)); this.registerParserSupplier(TypeToken.get(UUID.class), options -> new UUIDArgument.UUIDParser<>()); @@ -276,4 +283,15 @@ public final class StandardParserRegistry implements ParserRegistry { } + + private static final class GreedyMapper implements BiFunction<@NonNull Greedy, @NonNull TypeToken, + @NonNull ParserParameters> { + + @Override + public @NonNull ParserParameters apply(@NonNull final Greedy greedy, @NonNull final TypeToken typeToken) { + return ParserParameters.single(StandardParameters.GREEDY, true); + } + + } + } diff --git a/cloud-minecraft/README.md b/cloud-minecraft/README.md index 9b8bb3c8..d326909d 100644 --- a/cloud-minecraft/README.md +++ b/cloud-minecraft/README.md @@ -76,6 +76,8 @@ You can check whether or not the running server supports Brigadier, by using `bu ## cloud-paper +An example plugin using the `cloud-paper` API can be found [here](https://github.com/Sauilitired/cloud/tree/master/examples/example-bukkit). + `cloud-paper`works on all Bukkit derivatives and has graceful fallbacks for cases where Paper specific features are missing. It is initialized the same way as the Bukkit manager, except `PaperCommandManager`is used instead. When using Paper 1.15+ Brigadier mappings are available even without commodore present. ### dependency diff --git a/cloud-minecraft/cloud-brigadier/src/main/java/cloud/commandframework/brigadier/CloudBrigadierManager.java b/cloud-minecraft/cloud-brigadier/src/main/java/cloud/commandframework/brigadier/CloudBrigadierManager.java index 8bb55e0b..06699d55 100644 --- a/cloud-minecraft/cloud-brigadier/src/main/java/cloud/commandframework/brigadier/CloudBrigadierManager.java +++ b/cloud-minecraft/cloud-brigadier/src/main/java/cloud/commandframework/brigadier/CloudBrigadierManager.java @@ -256,6 +256,7 @@ public final class CloudBrigadierManager { * @param label Command label * @param cloudCommand Cloud command instance * @param permissionChecker Permission checker + * @param forceRegister Whether or not to force register an executor at every node * @param executor Command executor * @return Literal command node */ @@ -263,6 +264,7 @@ public final class CloudBrigadierManager { final @NonNull Command cloudCommand, final @NonNull BiPredicate<@NonNull S, @NonNull CommandPermission> permissionChecker, + final boolean forceRegister, final com.mojang.brigadier.@NonNull Command executor) { final CommandTree.Node> node = this.commandManager .getCommandTree().getNamedNode(cloudCommand.getArguments().get(0).getName()); @@ -272,10 +274,13 @@ public final class CloudBrigadierManager { .requires(sender -> permissionChecker.test(sender, (CommandPermission) node.getNodeMeta() .getOrDefault("permission", Permission.empty()))); + if (forceRegister || (node.getValue() != null && node.getValue().getOwningCommand() != null)) { + literalArgumentBuilder.executes(executor); + } literalArgumentBuilder.executes(executor); final LiteralCommandNode constructedRoot = literalArgumentBuilder.build(); for (final CommandTree.Node> child : node.getChildren()) { - constructedRoot.addChild(this.constructCommandNode(true, child, + constructedRoot.addChild(this.constructCommandNode(forceRegister, child, permissionChecker, executor, provider).build()); } return constructedRoot; diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/BukkitPluginRegistrationHandler.java b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/BukkitPluginRegistrationHandler.java index 1eb85b81..06f8b8a9 100644 --- a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/BukkitPluginRegistrationHandler.java +++ b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/BukkitPluginRegistrationHandler.java @@ -72,14 +72,16 @@ public class BukkitPluginRegistrationHandler implements CommandRegistrationHa public final boolean registerCommand(final @NonNull Command command) { /* We only care about the root command argument */ final CommandArgument commandArgument = command.getArguments().get(0); - if (this.registeredCommands.containsKey(commandArgument)) { + if (!(this.bukkitCommandManager.getCommandRegistrationHandler() instanceof CloudCommodoreManager) + && this.registeredCommands.containsKey(commandArgument)) { return false; } final String label; final String prefixedLabel = String.format("%s:%s", this.bukkitCommandManager.getOwningPlugin().getName(), commandArgument.getName()).toLowerCase(); - if (bukkitCommands.containsKey(commandArgument.getName())) { + if (!(this.bukkitCommandManager.getCommandRegistrationHandler() instanceof CloudCommodoreManager) + && bukkitCommands.containsKey(commandArgument.getName())) { label = prefixedLabel; } else { label = commandArgument.getName(); diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/CloudCommodoreManager.java b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/CloudCommodoreManager.java index 08c56eb6..cddfc6ca 100644 --- a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/CloudCommodoreManager.java +++ b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/CloudCommodoreManager.java @@ -26,11 +26,17 @@ package cloud.commandframework.bukkit; import cloud.commandframework.Command; import cloud.commandframework.brigadier.CloudBrigadierManager; import cloud.commandframework.context.CommandContext; +import cloud.commandframework.permission.CommandPermission; +import com.mojang.brigadier.tree.CommandNode; import com.mojang.brigadier.tree.LiteralCommandNode; import me.lucko.commodore.Commodore; import me.lucko.commodore.CommodoreProvider; import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.Collections; @SuppressWarnings("ALL") class CloudCommodoreManager extends BukkitPluginRegistrationHandler { @@ -57,9 +63,28 @@ class CloudCommodoreManager extends BukkitPluginRegistrationHandler { final @NonNull BukkitCommand bukkitCommand) { final com.mojang.brigadier.Command cmd = o -> 1; final LiteralCommandNode literalCommandNode = this.brigadierManager - .createLiteralCommandNode(label, command, (o, p) -> true, cmd); - this.commodore.register(bukkitCommand, literalCommandNode, p -> - this.commandManager.hasPermission(commandManager.getCommandSenderMapper().apply(p), - command.getCommandPermission())); + .createLiteralCommandNode(label, command, (o, p) -> { + final CommandSender sender = this.commodore.getBukkitSender(o); + return this.commandManager.hasPermission(this.commandManager.getCommandSenderMapper().apply(sender), + (CommandPermission) p); + }, false, cmd); + final CommandNode existingNode = this.commodore.getDispatcher().findNode(Collections.singletonList(label)); + if (existingNode != null) { + this.mergeChildren(existingNode, literalCommandNode); + } else { + this.commodore.register(literalCommandNode); + } } + + private void mergeChildren(@Nullable final CommandNode existingNode, @Nullable final CommandNode node) { + for (final CommandNode child : node.getChildren()) { + final CommandNode existingChild = existingNode.getChild(child.getName()); + if (existingChild == null) { + existingNode.addChild(child); + } else { + this.mergeChildren(existingChild, child); + } + } + } + } diff --git a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/arguments/selector/EntitySelector.java b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/arguments/selector/EntitySelector.java index 2aadbc48..1fada9e1 100644 --- a/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/arguments/selector/EntitySelector.java +++ b/cloud-minecraft/cloud-bukkit/src/main/java/cloud/commandframework/bukkit/arguments/selector/EntitySelector.java @@ -66,4 +66,14 @@ public abstract class EntitySelector { public @NonNull String getSelector() { return this.selector; } + + /** + * Check whether the selector selected at least one entity + * + * @return {@code true} if at least one entity was selected, else {@code false} + */ + public boolean hasAny() { + return !this.entities.isEmpty(); + } + } diff --git a/cloud-minecraft/cloud-velocity/src/main/java/cloud/commandframework/velocity/VelocityPluginRegistrationHandler.java b/cloud-minecraft/cloud-velocity/src/main/java/cloud/commandframework/velocity/VelocityPluginRegistrationHandler.java index 61f71ab1..5f379612 100644 --- a/cloud-minecraft/cloud-velocity/src/main/java/cloud/commandframework/velocity/VelocityPluginRegistrationHandler.java +++ b/cloud-minecraft/cloud-velocity/src/main/java/cloud/commandframework/velocity/VelocityPluginRegistrationHandler.java @@ -72,7 +72,7 @@ final class VelocityPluginRegistrationHandler implements CommandRegistrationH this.brigadierManager.createLiteralCommandNode(command.getArguments().get(0).getName(), (Command) command, (c, p) -> this.manager.hasPermission( this.manager.getCommandSenderMapper() - .apply(c), p), + .apply(c), p), true, commandContext -> { final CommandSource source = commandContext.getSource(); final String input = commandContext.getInput(); diff --git a/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/ExamplePlugin.java b/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/ExamplePlugin.java new file mode 100644 index 00000000..c13e5fe4 --- /dev/null +++ b/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/ExamplePlugin.java @@ -0,0 +1,276 @@ +// +// MIT License +// +// Copyright (c) 2020 Alexander Söderberg & Contributors +// +// 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 cloud.commandframework.examples.bukkit; + +import cloud.commandframework.Command; +import cloud.commandframework.CommandTree; +import cloud.commandframework.Description; +import cloud.commandframework.MinecraftHelp; +import cloud.commandframework.annotations.AnnotationParser; +import cloud.commandframework.annotations.Argument; +import cloud.commandframework.annotations.CommandDescription; +import cloud.commandframework.annotations.CommandMethod; +import cloud.commandframework.annotations.CommandPermission; +import cloud.commandframework.annotations.Confirmation; +import cloud.commandframework.annotations.Flag; +import cloud.commandframework.annotations.specifier.Greedy; +import cloud.commandframework.arguments.CommandArgument; +import cloud.commandframework.arguments.parser.ParserParameters; +import cloud.commandframework.arguments.parser.StandardParameters; +import cloud.commandframework.bukkit.BukkitCommandManager; +import cloud.commandframework.bukkit.BukkitCommandMetaBuilder; +import cloud.commandframework.bukkit.CloudBukkitCapabilities; +import cloud.commandframework.bukkit.arguments.selector.SingleEntitySelector; +import cloud.commandframework.bukkit.parsers.WorldArgument; +import cloud.commandframework.bukkit.parsers.selector.SingleEntitySelectorArgument; +import cloud.commandframework.execution.CommandExecutionCoordinator; +import cloud.commandframework.extra.confirmation.CommandConfirmationManager; +import cloud.commandframework.meta.CommandMeta; +import cloud.commandframework.paper.PaperCommandManager; +import cloud.commandframework.types.tuples.Triplet; +import io.leangen.geantyref.TypeToken; +import net.kyori.adventure.platform.bukkit.BukkitAudiences; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.util.Vector; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.concurrent.TimeUnit; +import java.util.function.Function; + +/** + * Example plugin class + */ +public final class ExamplePlugin extends JavaPlugin { + + private BukkitCommandManager manager; + private BukkitAudiences bukkitAudiences; + private MinecraftHelp minecraftHelp; + private CommandConfirmationManager confirmationManager; + private AnnotationParser annotationParser; + + @Override + public void onEnable() { + // + // This is a function that will provide a command execution coordinator that parses and executes commands + // asynchronously + // + // final Function, CommandExecutionCoordinator> executionCoordinatorFunction = + // AsynchronousCommandExecutionCoordinator.newBuilder().build(); + // + // However, in this example it is fine for us to run everything synchronously + // + final Function, CommandExecutionCoordinator> executionCoordinatorFunction = + CommandExecutionCoordinator.simpleCoordinator(); + // + // This function maps the command sender type of our choice to the bukkit command sender. + // However, in this example we use the Bukkit command sender, and so we just need to map it + // to itself + // + final Function mapperFunction = Function.identity(); + try { + this.manager = new PaperCommandManager<>( + /* Owning plugin */ this, + /* Coordinator function */ executionCoordinatorFunction, + /* Command Sender -> C */ mapperFunction, + /* C -> Command Sender */ mapperFunction + ); + } catch (final Exception e) { + this.getLogger().severe("Failed to initialize the command manager"); + /* Disable the plugin */ + this.getServer().getPluginManager().disablePlugin(this); + return; + } + // + // Create a BukkitAudiences instance (adventure) in order to use the minecraft-extras + // help system + // + this.bukkitAudiences = BukkitAudiences.create(this); + // + // Create the Minecraft help menu system + // + this.minecraftHelp = new MinecraftHelp<>( + /* Help Prefix */ "/example help", + /* Audience mapper */ this.bukkitAudiences::sender, + /* Manager */ this.manager + ); + // + // Register Brigadier mappings + // + if (manager.queryCapability(CloudBukkitCapabilities.BRIGADIER)) { + manager.registerBrigadier(); + } + // + // Register asynchronous completions + // + if (manager.queryCapability(CloudBukkitCapabilities.ASYNCHRONOUS_COMPLETION)) { + ((PaperCommandManager) this.manager).registerAsynchronousCompletions(); + } + // + // Create the confirmation manager. This allows us to require certain commands to be + // confirmed before they can be executed + // + this.confirmationManager = new CommandConfirmationManager<>( + /* Timeout */ 30L, + /* Timeout unit */ TimeUnit.SECONDS, + /* Action when confirmation is required */ context -> context.getCommandContext().getSender().sendMessage( + ChatColor.RED + "Confirmation required. Confirm using /example confirm."), + /* 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 + // + this.confirmationManager.registerConfirmationProcessor(manager); + // + // Create the annotation parser. This allows you to define commands using methods annotated with + // @CommandMethod + // + final Function commandMetaFunction = p -> + BukkitCommandMetaBuilder.builder() + // This will allow you to decorate commands with descriptions + .withDescription(p.get(StandardParameters.DESCRIPTION, "No description")) + .build(); + this.annotationParser = new AnnotationParser<>( + /* Manager */ this.manager, + /* Command sender type */ CommandSender.class, + /* Mapper for command meta instances */ commandMetaFunction + ); + // + // Create the commands + // + this.constructCommands(); + } + + private void constructCommands() { + // + // Parse all @CommandMethod-annotated methods + // + this.annotationParser.parse(this); + // + // Base command builder + // + final Command.Builder builder = this.manager.commandBuilder("example"); + // + // Add a confirmation command + // + this.manager.command(builder.literal("confirm") + .meta("description", "Confirm a pending command") + .handler(this.confirmationManager.createConfirmationExecutionHandler())); + // + // Create a world argument + // + final CommandArgument worldArgument = WorldArgument.of("world"); + // + // Create a teleportation command + // + this.manager.command(builder.literal("teleport") + .literal("me") + // Require a player sender + .withSenderType(Player.class) + .argument(worldArgument, Description.of("World name")) + .argumentTriplet( + "coords", + TypeToken.get(Vector.class), + Triplet.of("x", "y", "z"), + Triplet.of(Integer.class, Integer.class, Integer.class), + triplet -> new Vector(triplet.getFirst(), triplet.getSecond(), triplet.getThird()), + Description.of("Coordinates")) + .handler(context -> { + final Player player = (Player) context.getSender(); + final World world = context.get(worldArgument); + final Vector coords = context.get("coords"); + final Location location = coords.toLocation(world); + player.teleport(location); + })) + .command(builder.literal("teleport") + .literal("entity") + .withSenderType(Player.class) + .argument(SingleEntitySelectorArgument.of("entity"), + Description.of("Entity to teleport")) + .literal("here") + .handler(commandContext -> { + final Player player = (Player) commandContext.getSender(); + final SingleEntitySelector singleEntitySelector = commandContext.get("entity"); + if (singleEntitySelector.hasAny()) { + singleEntitySelector.getEntity().teleport(player); + player.sendMessage(ChatColor.GREEN + "The entity was teleported to you!"); + } else { + player.sendMessage(ChatColor.RED + "No entity matched your query."); + } + })); + } + + @CommandMethod("example help [query]") + @CommandDescription("Help menu") + private void commandHelp(final @NonNull CommandSender sender, + final @Argument("query") @Greedy String query) { + this.minecraftHelp.queryCommands(query == null ? "" : query, sender); + } + + @Confirmation + @CommandMethod("example clear") + @CommandDescription("Clear your inventory") + @CommandPermission("example.clear") + private void commandClear(final @NonNull Player player) { + player.getInventory().clear(); + this.bukkitAudiences.player(player) + .sendMessage(Component.text("Your inventory has been cleared", NamedTextColor.GOLD)); + } + + @CommandMethod("example give ") + @CommandDescription("Give yourself an item") + private void commandGive(final @NonNull Player player, + final @NonNull @Argument("material") Material material, + final @Argument("amount") int number, + final @Nullable @Flag("color") ChatColor nameColor) { + final ItemStack itemStack = new ItemStack(material, number); + String itemName = String.format("%s's %s", + player.getName(), + material.name() + .toLowerCase() + .replace('_', ' ')); + if (nameColor != null) { + itemName = nameColor + itemName; + } + final ItemMeta meta = itemStack.getItemMeta(); + if (meta != null) { + meta.setDisplayName(itemName); + itemStack.setItemMeta(meta); + } + player.getInventory().addItem(itemStack); + player.sendMessage(ChatColor.GREEN + String.format("You have been given %d x %s", number, material)); + } + +} diff --git a/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/package-info.java b/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/package-info.java new file mode 100644 index 00000000..fc744f5a --- /dev/null +++ b/examples/example-bukkit/src/main/java/cloud/commandframework/examples/bukkit/package-info.java @@ -0,0 +1,28 @@ +// +// MIT License +// +// Copyright (c) 2020 Alexander Söderberg & Contributors +// +// 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. +// + +/** + * Bukkit example plugin + */ +package cloud.commandframework.examples.bukkit; diff --git a/examples/example-bukkit/src/main/resources/plugin.yml b/examples/example-bukkit/src/main/resources/plugin.yml new file mode 100644 index 00000000..3319bcf4 --- /dev/null +++ b/examples/example-bukkit/src/main/resources/plugin.yml @@ -0,0 +1,4 @@ +name: ExamplePlugin +version: 1.0.0 +api-version: 1.13 +main: cloud.commandframework.examples.bukkit.ExamplePlugin diff --git a/settings.gradle b/settings.gradle index d78dcb21..2377eeb4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -12,6 +12,7 @@ include(':cloud-minecraft-extras') include(':cloud-cloudburst') include(':cloud-javacord') include(':cloud-jda') +include(':example-bukkit') project(':cloud-bukkit').projectDir = file('cloud-minecraft/cloud-bukkit') project(':cloud-paper').projectDir = file('cloud-minecraft/cloud-paper') project(':cloud-brigadier').projectDir = file('cloud-minecraft/cloud-brigadier') @@ -21,3 +22,5 @@ project(':cloud-minecraft-extras').projectDir = file('cloud-minecraft/cloud-mine project(':cloud-cloudburst').projectDir = file('cloud-minecraft/cloud-cloudburst') project(':cloud-javacord').projectDir = file('cloud-discord/cloud-javacord') project(':cloud-jda').projectDir = file('cloud-discord/cloud-jda') +project(':example-bukkit').projectDir = file('examples/example-bukkit') +