✨ fix-commodore (#27)
This commit is contained in:
parent
8f8f98b189
commit
c3469706ab
14 changed files with 435 additions and 11 deletions
10
build.gradle
10
build.gradle
|
|
@ -21,6 +21,16 @@ checkstyle {
|
||||||
configFile file('config/checkstyle/checkstyle.xml')
|
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 {
|
allprojects {
|
||||||
apply plugin: 'idea'
|
apply plugin: 'idea'
|
||||||
apply plugin: 'checkstyle'
|
apply plugin: 'checkstyle'
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
}
|
||||||
|
|
@ -55,6 +55,10 @@ public final class StandardParameters {
|
||||||
* The command should be hidden from help menus, etc
|
* The command should be hidden from help menus, etc
|
||||||
*/
|
*/
|
||||||
public static final ParserParameter<Boolean> HIDDEN = create("hidden", TypeToken.get(Boolean.class));
|
public static final ParserParameter<Boolean> HIDDEN = create("hidden", TypeToken.get(Boolean.class));
|
||||||
|
/**
|
||||||
|
* Indicates that a string argument should be greedy
|
||||||
|
*/
|
||||||
|
public static final ParserParameter<Boolean> GREEDY = create("greedy", TypeToken.get(Boolean.class));
|
||||||
|
|
||||||
private StandardParameters() {
|
private StandardParameters() {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
//
|
//
|
||||||
package cloud.commandframework.arguments.parser;
|
package cloud.commandframework.arguments.parser;
|
||||||
|
|
||||||
|
import cloud.commandframework.annotations.specifier.Greedy;
|
||||||
import cloud.commandframework.arguments.standard.UUIDArgument;
|
import cloud.commandframework.arguments.standard.UUIDArgument;
|
||||||
import cloud.commandframework.annotations.specifier.Completions;
|
import cloud.commandframework.annotations.specifier.Completions;
|
||||||
import cloud.commandframework.annotations.specifier.Range;
|
import cloud.commandframework.annotations.specifier.Range;
|
||||||
|
|
@ -101,9 +102,15 @@ public final class StandardParserRegistry<C> implements ParserRegistry<C> {
|
||||||
(double) options.get(StandardParameters.RANGE_MAX, Double.MAX_VALUE)));
|
(double) options.get(StandardParameters.RANGE_MAX, Double.MAX_VALUE)));
|
||||||
this.registerParserSupplier(TypeToken.get(Character.class), options -> new CharArgument.CharacterParser<C>());
|
this.registerParserSupplier(TypeToken.get(Character.class), options -> new CharArgument.CharacterParser<C>());
|
||||||
/* Make this one less awful */
|
/* Make this one less awful */
|
||||||
this.registerParserSupplier(TypeToken.get(String.class), options -> new StringArgument.StringParser<C>(
|
this.registerParserSupplier(TypeToken.get(String.class), options -> {
|
||||||
StringArgument.StringMode.SINGLE, (context, s) ->
|
final boolean greedy = options.get(StandardParameters.GREEDY, false);
|
||||||
Arrays.asList(options.get(StandardParameters.COMPLETIONS, new String[0]))));
|
final StringArgument.StringMode stringMode = greedy
|
||||||
|
? StringArgument.StringMode.GREEDY
|
||||||
|
: StringArgument.StringMode.SINGLE;
|
||||||
|
return new StringArgument.StringParser<C>(
|
||||||
|
stringMode,
|
||||||
|
(context, s) -> Arrays.asList(options.get(StandardParameters.COMPLETIONS, new String[0])));
|
||||||
|
});
|
||||||
/* Add options to this */
|
/* Add options to this */
|
||||||
this.registerParserSupplier(TypeToken.get(Boolean.class), options -> new BooleanArgument.BooleanParser<>(false));
|
this.registerParserSupplier(TypeToken.get(Boolean.class), options -> new BooleanArgument.BooleanParser<>(false));
|
||||||
this.registerParserSupplier(TypeToken.get(UUID.class), options -> new UUIDArgument.UUIDParser<>());
|
this.registerParserSupplier(TypeToken.get(UUID.class), options -> new UUIDArgument.UUIDParser<>());
|
||||||
|
|
@ -276,4 +283,15 @@ public final class StandardParserRegistry<C> implements ParserRegistry<C> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,8 @@ You can check whether or not the running server supports Brigadier, by using `bu
|
||||||
|
|
||||||
## cloud-paper
|
## 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.
|
`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
|
### dependency
|
||||||
|
|
|
||||||
|
|
@ -256,6 +256,7 @@ public final class CloudBrigadierManager<C, S> {
|
||||||
* @param label Command label
|
* @param label Command label
|
||||||
* @param cloudCommand Cloud command instance
|
* @param cloudCommand Cloud command instance
|
||||||
* @param permissionChecker Permission checker
|
* @param permissionChecker Permission checker
|
||||||
|
* @param forceRegister Whether or not to force register an executor at every node
|
||||||
* @param executor Command executor
|
* @param executor Command executor
|
||||||
* @return Literal command node
|
* @return Literal command node
|
||||||
*/
|
*/
|
||||||
|
|
@ -263,6 +264,7 @@ public final class CloudBrigadierManager<C, S> {
|
||||||
final @NonNull Command<C> cloudCommand,
|
final @NonNull Command<C> cloudCommand,
|
||||||
final @NonNull BiPredicate<@NonNull S,
|
final @NonNull BiPredicate<@NonNull S,
|
||||||
@NonNull CommandPermission> permissionChecker,
|
@NonNull CommandPermission> permissionChecker,
|
||||||
|
final boolean forceRegister,
|
||||||
final com.mojang.brigadier.@NonNull Command<S> executor) {
|
final com.mojang.brigadier.@NonNull Command<S> executor) {
|
||||||
final CommandTree.Node<CommandArgument<C, ?>> node = this.commandManager
|
final CommandTree.Node<CommandArgument<C, ?>> node = this.commandManager
|
||||||
.getCommandTree().getNamedNode(cloudCommand.getArguments().get(0).getName());
|
.getCommandTree().getNamedNode(cloudCommand.getArguments().get(0).getName());
|
||||||
|
|
@ -272,10 +274,13 @@ public final class CloudBrigadierManager<C, S> {
|
||||||
.requires(sender -> permissionChecker.test(sender, (CommandPermission) node.getNodeMeta()
|
.requires(sender -> permissionChecker.test(sender, (CommandPermission) node.getNodeMeta()
|
||||||
.getOrDefault("permission",
|
.getOrDefault("permission",
|
||||||
Permission.empty())));
|
Permission.empty())));
|
||||||
|
if (forceRegister || (node.getValue() != null && node.getValue().getOwningCommand() != null)) {
|
||||||
|
literalArgumentBuilder.executes(executor);
|
||||||
|
}
|
||||||
literalArgumentBuilder.executes(executor);
|
literalArgumentBuilder.executes(executor);
|
||||||
final LiteralCommandNode<S> constructedRoot = literalArgumentBuilder.build();
|
final LiteralCommandNode<S> constructedRoot = literalArgumentBuilder.build();
|
||||||
for (final CommandTree.Node<CommandArgument<C, ?>> child : node.getChildren()) {
|
for (final CommandTree.Node<CommandArgument<C, ?>> child : node.getChildren()) {
|
||||||
constructedRoot.addChild(this.constructCommandNode(true, child,
|
constructedRoot.addChild(this.constructCommandNode(forceRegister, child,
|
||||||
permissionChecker, executor, provider).build());
|
permissionChecker, executor, provider).build());
|
||||||
}
|
}
|
||||||
return constructedRoot;
|
return constructedRoot;
|
||||||
|
|
|
||||||
|
|
@ -72,14 +72,16 @@ public class BukkitPluginRegistrationHandler<C> implements CommandRegistrationHa
|
||||||
public final boolean registerCommand(final @NonNull Command<?> command) {
|
public final boolean registerCommand(final @NonNull Command<?> command) {
|
||||||
/* We only care about the root command argument */
|
/* We only care about the root command argument */
|
||||||
final CommandArgument<?, ?> commandArgument = command.getArguments().get(0);
|
final CommandArgument<?, ?> commandArgument = command.getArguments().get(0);
|
||||||
if (this.registeredCommands.containsKey(commandArgument)) {
|
if (!(this.bukkitCommandManager.getCommandRegistrationHandler() instanceof CloudCommodoreManager)
|
||||||
|
&& this.registeredCommands.containsKey(commandArgument)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String label;
|
final String label;
|
||||||
final String prefixedLabel = String.format("%s:%s", this.bukkitCommandManager.getOwningPlugin().getName(),
|
final String prefixedLabel = String.format("%s:%s", this.bukkitCommandManager.getOwningPlugin().getName(),
|
||||||
commandArgument.getName()).toLowerCase();
|
commandArgument.getName()).toLowerCase();
|
||||||
if (bukkitCommands.containsKey(commandArgument.getName())) {
|
if (!(this.bukkitCommandManager.getCommandRegistrationHandler() instanceof CloudCommodoreManager)
|
||||||
|
&& bukkitCommands.containsKey(commandArgument.getName())) {
|
||||||
label = prefixedLabel;
|
label = prefixedLabel;
|
||||||
} else {
|
} else {
|
||||||
label = commandArgument.getName();
|
label = commandArgument.getName();
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,17 @@ package cloud.commandframework.bukkit;
|
||||||
import cloud.commandframework.Command;
|
import cloud.commandframework.Command;
|
||||||
import cloud.commandframework.brigadier.CloudBrigadierManager;
|
import cloud.commandframework.brigadier.CloudBrigadierManager;
|
||||||
import cloud.commandframework.context.CommandContext;
|
import cloud.commandframework.context.CommandContext;
|
||||||
|
import cloud.commandframework.permission.CommandPermission;
|
||||||
|
import com.mojang.brigadier.tree.CommandNode;
|
||||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||||
import me.lucko.commodore.Commodore;
|
import me.lucko.commodore.Commodore;
|
||||||
import me.lucko.commodore.CommodoreProvider;
|
import me.lucko.commodore.CommodoreProvider;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
@SuppressWarnings("ALL")
|
@SuppressWarnings("ALL")
|
||||||
class CloudCommodoreManager<C> extends BukkitPluginRegistrationHandler<C> {
|
class CloudCommodoreManager<C> extends BukkitPluginRegistrationHandler<C> {
|
||||||
|
|
@ -57,9 +63,28 @@ class CloudCommodoreManager<C> extends BukkitPluginRegistrationHandler<C> {
|
||||||
final @NonNull BukkitCommand<C> bukkitCommand) {
|
final @NonNull BukkitCommand<C> bukkitCommand) {
|
||||||
final com.mojang.brigadier.Command<?> cmd = o -> 1;
|
final com.mojang.brigadier.Command<?> cmd = o -> 1;
|
||||||
final LiteralCommandNode<?> literalCommandNode = this.brigadierManager
|
final LiteralCommandNode<?> literalCommandNode = this.brigadierManager
|
||||||
.createLiteralCommandNode(label, command, (o, p) -> true, cmd);
|
.<Object>createLiteralCommandNode(label, command, (o, p) -> {
|
||||||
this.commodore.register(bukkitCommand, literalCommandNode, p ->
|
final CommandSender sender = this.commodore.getBukkitSender(o);
|
||||||
this.commandManager.hasPermission(commandManager.getCommandSenderMapper().apply(p),
|
return this.commandManager.hasPermission(this.commandManager.getCommandSenderMapper().apply(sender),
|
||||||
command.getCommandPermission()));
|
(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,4 +66,14 @@ public abstract class EntitySelector {
|
||||||
public @NonNull String getSelector() {
|
public @NonNull String getSelector() {
|
||||||
return this.selector;
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ final class VelocityPluginRegistrationHandler<C> implements CommandRegistrationH
|
||||||
this.brigadierManager.createLiteralCommandNode(command.getArguments().get(0).getName(), (Command<C>) command,
|
this.brigadierManager.createLiteralCommandNode(command.getArguments().get(0).getName(), (Command<C>) command,
|
||||||
(c, p) -> this.manager.hasPermission(
|
(c, p) -> this.manager.hasPermission(
|
||||||
this.manager.getCommandSenderMapper()
|
this.manager.getCommandSenderMapper()
|
||||||
.apply(c), p),
|
.apply(c), p), true,
|
||||||
commandContext -> {
|
commandContext -> {
|
||||||
final CommandSource source = commandContext.getSource();
|
final CommandSource source = commandContext.getSource();
|
||||||
final String input = commandContext.getInput();
|
final String input = commandContext.getInput();
|
||||||
|
|
|
||||||
|
|
@ -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<CommandSender> manager;
|
||||||
|
private BukkitAudiences bukkitAudiences;
|
||||||
|
private MinecraftHelp<CommandSender> minecraftHelp;
|
||||||
|
private CommandConfirmationManager<CommandSender> confirmationManager;
|
||||||
|
private AnnotationParser<CommandSender> annotationParser;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnable() {
|
||||||
|
//
|
||||||
|
// This is a function that will provide a command execution coordinator that parses and executes commands
|
||||||
|
// asynchronously
|
||||||
|
//
|
||||||
|
// final Function<CommandTree<CommandSender>, CommandExecutionCoordinator<CommandSender>> executionCoordinatorFunction =
|
||||||
|
// AsynchronousCommandExecutionCoordinator.<CommandSender>newBuilder().build();
|
||||||
|
//
|
||||||
|
// However, in this example it is fine for us to run everything synchronously
|
||||||
|
//
|
||||||
|
final Function<CommandTree<CommandSender>, CommandExecutionCoordinator<CommandSender>> 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<CommandSender, CommandSender> 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<CommandSender>) 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<ParserParameters, CommandMeta> 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<CommandSender> 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<CommandSender, World> 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 <material> <amount>")
|
||||||
|
@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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
4
examples/example-bukkit/src/main/resources/plugin.yml
Normal file
4
examples/example-bukkit/src/main/resources/plugin.yml
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
name: ExamplePlugin
|
||||||
|
version: 1.0.0
|
||||||
|
api-version: 1.13
|
||||||
|
main: cloud.commandframework.examples.bukkit.ExamplePlugin
|
||||||
|
|
@ -12,6 +12,7 @@ include(':cloud-minecraft-extras')
|
||||||
include(':cloud-cloudburst')
|
include(':cloud-cloudburst')
|
||||||
include(':cloud-javacord')
|
include(':cloud-javacord')
|
||||||
include(':cloud-jda')
|
include(':cloud-jda')
|
||||||
|
include(':example-bukkit')
|
||||||
project(':cloud-bukkit').projectDir = file('cloud-minecraft/cloud-bukkit')
|
project(':cloud-bukkit').projectDir = file('cloud-minecraft/cloud-bukkit')
|
||||||
project(':cloud-paper').projectDir = file('cloud-minecraft/cloud-paper')
|
project(':cloud-paper').projectDir = file('cloud-minecraft/cloud-paper')
|
||||||
project(':cloud-brigadier').projectDir = file('cloud-minecraft/cloud-brigadier')
|
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-cloudburst').projectDir = file('cloud-minecraft/cloud-cloudburst')
|
||||||
project(':cloud-javacord').projectDir = file('cloud-discord/cloud-javacord')
|
project(':cloud-javacord').projectDir = file('cloud-discord/cloud-javacord')
|
||||||
project(':cloud-jda').projectDir = file('cloud-discord/cloud-jda')
|
project(':cloud-jda').projectDir = file('cloud-discord/cloud-jda')
|
||||||
|
project(':example-bukkit').projectDir = file('examples/example-bukkit')
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue