All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.spongepowered.api.command.Command Maven / Gradle / Ivy

/*
 * This file is part of SpongeAPI, licensed under the MIT License (MIT).
 *
 * Copyright (c) SpongePowered 
 * Copyright (c) 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 org.spongepowered.api.command;

import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.spongepowered.api.Sponge;
import org.spongepowered.api.command.exception.ArgumentParseException;
import org.spongepowered.api.command.exception.CommandException;
import org.spongepowered.api.command.manager.CommandMapping;
import org.spongepowered.api.command.parameter.ArgumentReader;
import org.spongepowered.api.command.parameter.CommandContext;
import org.spongepowered.api.command.parameter.Parameter;
import org.spongepowered.api.command.parameter.managed.Flag;
import org.spongepowered.api.command.registrar.CommandRegistrar;
import org.spongepowered.api.command.registrar.tree.CommandTreeNode;
import org.spongepowered.api.command.registrar.tree.CommandTreeNodeTypes;
import org.spongepowered.api.event.Cause;
import org.spongepowered.api.event.EventContext;
import org.spongepowered.api.event.lifecycle.RegisterCommandEvent;
import org.spongepowered.api.service.permission.PermissionDescription;
import org.spongepowered.api.service.permission.Subject;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * The Command interface is the low-level interface that all commands in the
 * Sponge ecosystem inherit.
 *
 * 

Most plugins are highly recommended (but not obligated) * to use {@link Command#builder()} to create commands. The * {@link Builder} allows plugins to take advantage of a higher level * of abstraction, such as argument parsers and simple child command handling, * removing the need for boilerplate code. Such {@link Parameterized} commands * should register themselves during the {@link RegisterCommandEvent * RegisterCommandEvent<Command.Parameterized>} event.

* *

Plugins that do not want to use the {@link Builder} or any third-party * command system should implement the {@link Raw} sub-interface instead. Such * {@link Raw} commands should register themselves during the * {@link RegisterCommandEvent RegisterCommandEvent<Command.Raw>} * event.

* *

Plugins must not implement the {@link Parameterized} * sub-interface themselves.

* *

Upon execution, commands are provided with a {@link CommandCause}, * providing the {@link Cause} and {@link EventContext} that invoked the * command. See the {@link CommandCause} documentation for more information * of the structure of this cause.

*/ public interface Command { /** * Gets a builder for building a {@link Command}. * * @return The {@link Builder} */ static Builder builder() { return Sponge.game().builderProvider().provide(Builder.class); } /** * Execute the command based on input arguments. * *

The implementing class must perform the necessary permission * checks.

* * @param cause The {@link CommandCause} of the invocation of command * @param arguments The raw arguments for this command * @return The result of a command being processed * @throws CommandException Thrown on a command error */ CommandResult process(CommandCause cause, ArgumentReader.Mutable arguments) throws CommandException; /** * Gets a list of completions based on input. * *

If a completion is chosen by the user, it will replace the last * word.

* * @param cause The {@link CommandCause} of the command * @param arguments The arguments entered up to this point * @return A list of suggestions * @throws CommandException Thrown if there was a parsing error */ List complete(CommandCause cause, ArgumentReader.Mutable arguments) throws CommandException; /** * Test whether this command can probably be executed given this * {@link Cause}. * *

If implementations are unsure if the command can be executed by * the source, {@code true} should be returned. Return values of this method * may be used to determine whether this command is listed in command * listings.

* * @param cause The {@link Cause} to check * @return Whether this command will execute */ boolean canExecute(CommandCause cause); /** * Gets a short one-line description of this command. * *

The help system may display the description in the command list, or * in any other function that displays a command list. It is therefore * recommended that any description here is kept very short.

* * @see CommandRegistrar#shortDescription(CommandCause, CommandMapping) * * @param cause The {@link CommandCause} of the help request * @return A description */ Optional shortDescription(CommandCause cause); /** * Gets a longer formatted help message about this command. * *

The help system may display this description when displaying details * about this specific command. It should not contain the description * provided by {@link #shortDescription(CommandCause)}.

* * @param cause The {@link Cause} of the help request * @return A description */ Optional extendedDescription(CommandCause cause); /** * Gets a longer formatted help message about this command. This will * typically comprise of {@link #shortDescription(CommandCause)} * and {@link #extendedDescription(CommandCause)} together. * *

The help system may display this message when a source requests * detailed information about a command.

* * @see CommandRegistrar#help(CommandCause, CommandMapping) * * @param cause The {@link Cause} of the help request * @return A help text */ default Optional help(@NonNull final CommandCause cause) { final Optional shortDesc = this.shortDescription(cause); final Optional extended = this.extendedDescription(cause); if (extended.isPresent()) { if (shortDesc.isPresent()) { return Optional.of(Component.text().append(shortDesc.get(), Component.newline(), Component.newline(), extended.get()).build()); } else { return extended; } } return shortDesc; } /** * Gets the usage string of this command. * *

A usage string may look like * {@code [] }.

* *

The string must not contain the command alias.

* * @param cause The {@link Cause} of the help request * @return A usage string */ Component usage(CommandCause cause); /** * A raw command that also contains a {@link CommandTreeNode} to provide * hints to the client for command completion. */ interface Raw extends Command { /** * Gets the {@link CommandTreeNode} that represents the argument * pattern for this command. * *

Defaults to a command with an optional string argument string that * is greedy.

* * @return The tree. */ default CommandTreeNode.Root commandTree() { return CommandTreeNode.root().executable().child("arguments", CommandTreeNodeTypes.STRING.get().createNode().greedy().executable().customCompletions()); } } /** * A {@link Command} that has distinct steps for parsing arguments and * executing a command based on those arguments. * *

Plugins must not attempt to create their own instance of this * interface, and instead must use {@link Command#builder()} to do * so.

*/ interface Parameterized extends Command { /** * The {@link List} of {@link Flag}s that this {@link Command} contains. * * @return A copy of the collection of {@link Flag}s. */ List flags(); /** * The {@link List} of {@link Parameter}s that this {@link Command} * contains. * * @return A copy of the collection of {@link Parameter}s. */ List parameters(); /** * A {@link List} of direct {@link Parameter.Subcommand subcommands} * that this command contains. * *

A direct subcommand is one that is specified directly after the * literal the invokes this command, e.g. on the command {@code /foo}, * {@code bar} is a direct subcommand if it was specified in * {@link Builder#addChild(Parameterized, String...)} * or {@link Builder#addChildren(Map)} with the alias * {@code bar}. This will not contain any subcommands that were * registered via * {@link Builder#addParameter(Parameter)}

* * @return A copy of the collection of subcommands. */ List subcommands(); /** * Gets whether * {@link Builder#terminal(boolean)} was * explicitly set to {@code true}, such that this command will * execute without any arguments, regardless of the command's * {@link #parameters() parameters} or * {@link #subcommands() subcommands}. * * @return if this {@link Command.Parameterized} is terminal. */ boolean isTerminal(); /** * Gets a {@link Predicate} that backs {@link #canExecute(CommandCause)}. * * @return The predicate. */ Predicate executionRequirements(); /** * Parses the parameters based on the provided {@link #parameters()} * * @param cause The {@link Cause} of this parse * @param arguments The argument {@link String} * @return The {@link CommandContext} * @throws ArgumentParseException if a parameter could not be parsed */ CommandContext parseArguments(CommandCause cause, ArgumentReader.Mutable arguments) throws ArgumentParseException; /** * Gets the {@link CommandExecutor} for this command, if one exists. * * @return The {@link CommandExecutor}, if it exists. */ Optional executor(); /** * Processes the command by parsing the arguments, then * executing command based on these arguments. * *

By default, this will call {@link #parseArguments(CommandCause, ArgumentReader.Mutable)} * and pass the resulting {@link CommandContext} to * {@link CommandExecutor#execute(CommandContext)}, if this command has * an executor. If it does not, this will throw a * {@link CommandException}.

* * @param cause The {@link Cause} of the command * @param arguments The raw arguments for this command * @return The result of a command being processed * @throws CommandException Thrown on a command error */ @Override default CommandResult process(final CommandCause cause, final ArgumentReader.Mutable arguments) throws CommandException { if (this.executor().isPresent()) { return this.executor().get().execute(this.parseArguments(cause, arguments)); } throw new CommandException(Component.text("This command does not have an executor!")); } } /** * A high level {@link Builder} for creating a * {@link Command.Parameterized}. * *

When creating a command, ensure that a {@link CommandExecutor} * and/or a child command is specified.

*/ interface Builder extends org.spongepowered.api.util.Builder { /** * Adds a {@link Command.Parameterized} as a top-level child to this * command, using the supplied keys. The keys are case insensitive. * *

Children added here will be added to the beginning of the * {@link Parameter}s. Note that if you wish to add a subcommand * in the middle of the parameters, you can do so by creating a * {@link org.spongepowered.api.command.parameter.Parameter.Subcommand} * and adding that as a {@link #addParameter(Parameter)} at the * appropriate time.

* * @param child The {@link Command} that is a child. * @param keys The keys to register as a sub command. * @return This builder, for chaining * @throws IllegalArgumentException thrown if a child key is already * in the builder, or there are no keys * supplied */ default Builder addChild(final Command.Parameterized child, final String... keys) { return this.addChild(child, Arrays.asList(keys)); } /** * Adds a {@link Command.Parameterized} as a child to this command, * under the supplied keys. The keys are case insensitive. * *

Children added here will be added to the beginning of the * {@link Parameter}s. Note that if you wish to add a subcommand * in the middle of the parameters, you can do so by creating a * {@link org.spongepowered.api.command.parameter.Parameter.Subcommand} * and adding that as a {@link #addParameter(Parameter)} at the * appropriate time.

* * @param child The {@link Command} that is a child. * @param keys The keys to register as a sub command. * @return This builder, for chaining * @throws IllegalArgumentException thrown if a child key is already * in the builder */ Builder addChild(Command.Parameterized child, Iterable keys); /** * Adds multiple {@link Command.Parameterized} as children to this * command, under the supplied keys. The keys are case insensitive. * *

Children added here will be added to the beginning of the * {@link Parameter}s. Note that if you wish to add a subcommand * in the middle of the parameters, you can do so by creating a * {@link Parameter.Subcommand} and adding that as a * {@link #addParameter(Parameter)} at the appropriate time.

* * @param children The {@link Map} that contains a mapping of keys to * their respective {@link Command} children. * @return This builder, for chaining * @throws IllegalArgumentException thrown if a child key is already * in the builder */ default Builder addChildren(final Map, ? extends Command.Parameterized> children) { for (final Map.Entry, ? extends Command.Parameterized> child : children.entrySet()) { this.addChild(child.getValue(), child.getKey()); } return this; } /** * Adds a flag to this command. Flags are always the first arguments of * a command. There are no set rules for the order of execution of * flags (unlike {@link #addParameter(Parameter)}), and all flags are * considered optional. * *

Duplicate keys and/or duplicate aliases may not be provided.

* * @param flag The {@link Flag} to support * @return Ths builder, for chaining */ Builder addFlag(Flag flag); /** * Adds multiple {@link Flag flags} to this command. * * @see #addFlag(Flag) for more information about flags. * * @param flags The flags * @return This builder, for chaining */ default Builder addFlags(final Flag... flags) { for (final Flag flag : flags) { this.addFlag(flag); } return this; } /** * Adds multiple {@link Flag flags} to this command. * * @see #addFlag(Flag) for more information about flags. * * @param flags The flags * @return This builder, for chaining */ default Builder addFlags(final Iterable flags) { for (final Flag flag : flags) { this.addFlag(flag); } return this; } /** * Adds a parameter for use when parsing arguments. When executing a * command, they will be executed in the order they are added to this * builder. * * @param parameter The parameter to add to the parameter list * @return This builder, for chaining */ Builder addParameter(Parameter parameter); /** * Adds parameters to use when parsing arguments. Parameters will be * used in the order provided here. * * @param parameters The {@link Parameter}s to use * @return This builder, for chaining */ default Builder addParameters(final Parameter... parameters) { return this.addParameters(Arrays.asList(parameters)); } /** * Adds parameters to use when parsing arguments. Parameters will be * used in the order provided here. * * @param parameters The {@link Parameter}s to use * @return This builder, for chaining */ default Builder addParameters(final Iterable parameters) { for (final Parameter parameter : parameters) { this.addParameter(parameter); } return this; } /** * Provides the logic of the command. * *

This is only optional if child commands are specified. This will * replace any previous executor that has been set.

* * @param executor The {@link CommandExecutor} that will run the command * @return This builder, for chaining */ Builder executor(CommandExecutor executor); /** * Provides the description for this command, which is dependent on the * {@link Cause} that requests it. * *

A one line summary should be provided to * {@link #shortDescription(Component)}

* *

It is recommended to use the default text color and style. Sections * with text actions (e.g. hyperlinks) should be underlined.

* * @param extendedDescriptionFunction A function that provides a * relevant description based on the supplied {@link Cause} * @return This builder, for chaining */ Builder extendedDescription(Function> extendedDescriptionFunction); /** * Provides the description for this command. * *

A one line summary should be provided to * {@link #shortDescription(Component)}

* * @param extendedDescription The description to use, or {@code null} * for no description. * @return This builder, for chaining */ default Builder extendedDescription(final @Nullable Component extendedDescription) { // Done outside the lambda so that we don't have to recreate the object each time. final Optional text = Optional.ofNullable(extendedDescription); return this.extendedDescription((cause) -> text); } /** * Provides a simple description for this command, typically no more * than one line, which is dependent on the {@link Cause} that requests * it. * *

Fuller descriptions should be provided through * {@link #extendedDescription(Function)}

* * @param descriptionFunction A function that provides a relevant * description based on the supplied {@link Cause} * @return This builder, for chaining */ Builder shortDescription(Function> descriptionFunction); /** * Provides a simple description for this command, typically no more * than one line. * *

Fuller descriptions should be provided through * {@link #extendedDescription(Component)}

* *

It is recommended to use the default text color and style. Sections * with text actions (e.g. hyperlinks) should be underlined.

* * @param description The description to use, or {@code null} for no * description * @return This builder, for chaining */ default Builder shortDescription(final @Nullable Component description) { // Done outside the lambda so that we don't have to recreate the object each time. final Optional text = Optional.ofNullable(description); return this.shortDescription((cause) -> text); } /** * The permission that the responsible {@link Subject} in the given * {@link Cause} requires to run this command, or {@code null} if no * permission is required. Calling this method will overwrite anything * set via {@link #executionRequirements(Predicate)}, or will replace * the previous permission set by this method. * *

Any permission checks set here will be performed during the * {@link Command#canExecute(CommandCause)}.

* * @param permission The permission that is required, or {@code null} * for no permission * @return This builder, for chaining */ Builder permission(@Nullable String permission); /** * The permission that the responsible {@link Subject} in the given * {@link Cause} requires to run this command. * *

For more control over whether a command can be executed, use * {@link #executionRequirements(Predicate)}. However, note that * setting a permission here will not override anything set in * {@link #executionRequirements(Predicate)}, both will be checked * during execution.

* *

Any permission checks set here will be performed during the * {@link Command#canExecute(CommandCause)}.

* *

Calling this repeatedly will not add additional permission * checks, instead replacing the permission check. If multiple * permission checks are required, use * {@link #executionRequirements(Predicate)}.

* * @param permission The description for the required permission. * @return This builder, for chaining */ default Builder permission(PermissionDescription permission) { return this.permission(Objects.requireNonNull(permission, "permission").id()); } /** * Sets a function that determines what is required of the provided * {@link Cause} before this command executes. Calling this method * will overwrite anything set via {@link #permission(String)} or * anything previously set via this method. * *

Any requirements set here will be performed as part of * {@link Command#canExecute(CommandCause)}.

* * @param executionRequirements A function that sets the * @return This builder, for chaining */ Builder executionRequirements(@Nullable Predicate executionRequirements); /** * Sets whether this command is considered "terminal" in its own right, * that is, can be executed on its own if no arguments are supplied when * invoked, regardless of whether any supplied {@link Parameter}s are * mandatory. * * @see Parameter#isTerminal() * @see Parameter.Value.Builder#terminal() * * @param terminal Whether to mark this command as terminal * @return This builder, for chaining */ Builder terminal(boolean terminal); /** * Builds this command, creating a {@link Command.Parameterized} * object. * *

To build the command, one of the following is * required:

* *
    *
  • A {@link CommandExecutor} is provided using * {@link #executor(CommandExecutor)}
  • *
  • At least one {@link Command} is set to be a child command * using {@link #addChild(Parameterized, Iterable)} or * {@link #addChildren(Map)} *
  • *
* *

If these conditions are not fulfilled, an * {@link IllegalStateException} will be thrown.

* * @return The command, ready for registration * @throws IllegalStateException if the builder is not complete */ @Override Command.Parameterized build(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy