Brigadier based command API
Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com>
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
package io.papermc.paper.brigadier;
|
||||
|
||||
import com.mojang.brigadier.Message;
|
||||
import io.papermc.paper.command.brigadier.MessageComponentSerializer;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
/**
|
||||
* Helper methods to bridge the gaps between Brigadier and Paper-MojangAPI.
|
||||
* @deprecated for removal. See {@link MessageComponentSerializer} for a direct replacement of functionality found in
|
||||
* this class.
|
||||
* As a general entrypoint to brigadier on paper, see {@link io.papermc.paper.command.brigadier.Commands}.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.20.6")
|
||||
public final class PaperBrigadier {
|
||||
private PaperBrigadier() {
|
||||
throw new RuntimeException("PaperBrigadier is not to be instantiated!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Brigadier {@link Message} from a {@link ComponentLike}.
|
||||
*
|
||||
* <p>Mostly useful for creating rich suggestion tooltips in combination with other Paper-MojangAPI APIs.</p>
|
||||
*
|
||||
* @param componentLike The {@link ComponentLike} to use for the {@link Message} contents
|
||||
* @return A new Brigadier {@link Message}
|
||||
*/
|
||||
public static @NonNull Message message(final @NonNull ComponentLike componentLike) {
|
||||
return MessageComponentSerializer.message().serialize(componentLike.asComponent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Component} from a Brigadier {@link Message}.
|
||||
*
|
||||
* <p>If the {@link Message} was created from a {@link Component}, it will simply be
|
||||
* converted back, otherwise a new {@link TextComponent} will be created with the
|
||||
* content of {@link Message#getString()}</p>
|
||||
*
|
||||
* @param message The {@link Message} to create a {@link Component} from
|
||||
* @return The created {@link Component}
|
||||
*/
|
||||
public static @NonNull Component componentFromMessage(final @NonNull Message message) {
|
||||
return MessageComponentSerializer.message().deserialize(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package io.papermc.paper.command.brigadier;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Implementing this interface allows for easily creating "Bukkit-style" {@code String[] args} commands.
|
||||
* The implementation handles converting the command to a representation compatible with Brigadier on registration, usually in the form of {@literal /commandlabel <greedy_string>}.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@NullMarked
|
||||
@FunctionalInterface
|
||||
public interface BasicCommand {
|
||||
|
||||
/**
|
||||
* Executes the command with the given {@link CommandSourceStack} and arguments.
|
||||
*
|
||||
* @param commandSourceStack the commandSourceStack of the command
|
||||
* @param args the arguments of the command ignoring repeated spaces
|
||||
*/
|
||||
@ApiStatus.OverrideOnly
|
||||
void execute(CommandSourceStack commandSourceStack, String[] args);
|
||||
|
||||
/**
|
||||
* Suggests possible completions for the given command {@link CommandSourceStack} and arguments.
|
||||
*
|
||||
* @param commandSourceStack the commandSourceStack of the command
|
||||
* @param args the arguments of the command including repeated spaces
|
||||
* @return a collection of suggestions
|
||||
*/
|
||||
@ApiStatus.OverrideOnly
|
||||
default Collection<String> suggest(final CommandSourceStack commandSourceStack, final String[] args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a command sender can receive and run the root command.
|
||||
*
|
||||
* @param sender the command sender trying to execute the command
|
||||
* @return whether the command sender fulfills the root command requirement
|
||||
* @see #permission()
|
||||
*/
|
||||
@ApiStatus.OverrideOnly
|
||||
default boolean canUse(final CommandSender sender) {
|
||||
final String permission = this.permission();
|
||||
return permission == null || sender.hasPermission(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the permission for the root command used in {@link #canUse(CommandSender)} by default.
|
||||
*
|
||||
* @return the permission for the root command used in {@link #canUse(CommandSender)}
|
||||
*/
|
||||
@ApiStatus.OverrideOnly
|
||||
default @Nullable String permission() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package io.papermc.paper.command.brigadier;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* A {@link CommandRegistrationFlag} is used in {@link Commands} registration for internal purposes.
|
||||
* <p>
|
||||
* A command library may use this to achieve more specific customization on how their commands are registered.
|
||||
* @apiNote Stability of these flags is not promised! This api is not intended for public use.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public enum CommandRegistrationFlag {
|
||||
FLATTEN_ALIASES
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package io.papermc.paper.command.brigadier;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* The command source type for Brigadier commands registered using Paper API.
|
||||
* <p>
|
||||
* While the general use case for CommandSourceStack is similar to that of {@link CommandSender}, it provides access to
|
||||
* important additional context for the command execution.
|
||||
* Specifically, commands such as {@literal /execute} may alter the location or executor of the source stack before
|
||||
* passing it to another command.
|
||||
* <p>The {@link CommandSender} returned by {@link #getSender()} may be a "no-op"
|
||||
* instance of {@link CommandSender} in cases where the server either doesn't
|
||||
* exist yet, or no specific sender is available. Methods on such a {@link CommandSender}
|
||||
* will either have no effect or throw an {@link UnsupportedOperationException}.</p>
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@NullMarked
|
||||
@ApiStatus.NonExtendable
|
||||
public interface CommandSourceStack {
|
||||
|
||||
/**
|
||||
* Gets the location that this command is being executed at.
|
||||
*
|
||||
* @return a cloned location instance.
|
||||
*/
|
||||
Location getLocation();
|
||||
|
||||
/**
|
||||
* Gets the command sender that executed this command.
|
||||
* The sender of a command source stack is the one that initiated/triggered the execution of a command.
|
||||
* It differs to {@link #getExecutor()} as the executor can be changed by a command, e.g. {@literal /execute}.
|
||||
*
|
||||
* @return the command sender instance
|
||||
*/
|
||||
CommandSender getSender();
|
||||
|
||||
/**
|
||||
* Gets the entity that executes this command.
|
||||
* May not always be {@link #getSender()} as the executor of a command can be changed to a different entity
|
||||
* than the one that triggered the command.
|
||||
*
|
||||
* @return entity that executes this command
|
||||
*/
|
||||
@Nullable Entity getExecutor();
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
package io.papermc.paper.command.brigadier;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
import io.papermc.paper.plugin.bootstrap.BootstrapContext;
|
||||
import io.papermc.paper.plugin.bootstrap.PluginBootstrap;
|
||||
import io.papermc.paper.plugin.configuration.PluginMeta;
|
||||
import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager;
|
||||
import io.papermc.paper.plugin.lifecycle.event.registrar.Registrar;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* The registrar for custom commands. Supports Brigadier commands and {@link BasicCommand}.
|
||||
* <p>
|
||||
* An example of a command being registered is below
|
||||
* <pre>{@code
|
||||
* class YourPluginClass extends JavaPlugin {
|
||||
*
|
||||
* @Override
|
||||
* public void onEnable() {
|
||||
* LifecycleEventManager<Plugin> manager = this.getLifecycleManager();
|
||||
* manager.registerEventHandler(LifecycleEvents.COMMANDS, event -> {
|
||||
* final Commands commands = event.registrar();
|
||||
* commands.register(
|
||||
* Commands.literal("new-command")
|
||||
* .executes(ctx -> {
|
||||
* ctx.getSource().getSender().sendPlainMessage("some message");
|
||||
* return Command.SINGLE_SUCCESS;
|
||||
* })
|
||||
* .build(),
|
||||
* "some bukkit help description string",
|
||||
* List.of("an-alias")
|
||||
* );
|
||||
* });
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
* <p>
|
||||
* You can also register commands in {@link PluginBootstrap} by getting the {@link LifecycleEventManager} from
|
||||
* {@link BootstrapContext}.
|
||||
* Commands registered in the {@link PluginBootstrap} will be available for datapack's
|
||||
* command function parsing.
|
||||
* Note that commands registered via {@link PluginBootstrap} with the same literals as a vanilla command will override
|
||||
* that command within all loaded datapacks.
|
||||
* </p>
|
||||
* <p>The {@code register} methods that <b>do not</b> have {@link PluginMeta} as a parameter will
|
||||
* implicitly use the {@link PluginMeta} for the plugin that the {@link io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler}
|
||||
* was registered with.</p>
|
||||
*
|
||||
* @see io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents#COMMANDS
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@NullMarked
|
||||
@ApiStatus.NonExtendable
|
||||
public interface Commands extends Registrar {
|
||||
|
||||
/**
|
||||
* Utility to create a literal command node builder with the correct generic.
|
||||
*
|
||||
* @param literal literal name
|
||||
* @return a new builder instance
|
||||
*/
|
||||
static LiteralArgumentBuilder<CommandSourceStack> literal(final String literal) {
|
||||
return LiteralArgumentBuilder.literal(literal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility to create a required argument builder with the correct generic.
|
||||
*
|
||||
* @param name the name of the argument
|
||||
* @param argumentType the type of the argument
|
||||
* @param <T> the generic type of the argument value
|
||||
* @return a new required argument builder
|
||||
*/
|
||||
static <T> RequiredArgumentBuilder<CommandSourceStack, T> argument(final String name, final ArgumentType<T> argumentType) {
|
||||
return RequiredArgumentBuilder.argument(name, argumentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the underlying {@link CommandDispatcher}.
|
||||
*
|
||||
* <p><b>Note:</b> This is a delicate API that must be used with care to ensure a consistent user experience.</p>
|
||||
*
|
||||
* <p>When registering commands, it should be preferred to use the {@link #register(PluginMeta, LiteralCommandNode, String, Collection) register methods}
|
||||
* over directly registering to the dispatcher wherever possible.
|
||||
* {@link #register(PluginMeta, LiteralCommandNode, String, Collection) Register methods} automatically handle
|
||||
* command namespacing, command help, plugin association with commands, and more.</p>
|
||||
*
|
||||
* <p>Example use cases for this method <b>may</b> include:
|
||||
* <ul>
|
||||
* <li>Implementing integration between an external command framework and Paper (although {@link #register(PluginMeta, LiteralCommandNode, String, Collection) register methods} should still be preferred where possible)</li>
|
||||
* <li>Registering new child nodes to an existing plugin command (for example an "addon" plugin to another plugin may want to do this)</li>
|
||||
* <li>Retrieving existing command nodes to build redirects</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return the dispatcher instance
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
CommandDispatcher<CommandSourceStack> getDispatcher();
|
||||
|
||||
/**
|
||||
* Registers a command for the current plugin context.
|
||||
*
|
||||
* <p>Commands have certain overriding behavior:
|
||||
* <ul>
|
||||
* <li>Aliases will not override already existing commands (excluding namespaced ones)</li>
|
||||
* <li>The main command/namespaced label will override already existing commands</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param node the built literal command node
|
||||
* @return successfully registered root command labels (including aliases and namespaced variants)
|
||||
*/
|
||||
default @Unmodifiable Set<String> register(final LiteralCommandNode<CommandSourceStack> node) {
|
||||
return this.register(node, null, Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a command for the current plugin context.
|
||||
*
|
||||
* <p>Commands have certain overriding behavior:
|
||||
* <ul>
|
||||
* <li>Aliases will not override already existing commands (excluding namespaced ones)</li>
|
||||
* <li>The main command/namespaced label will override already existing commands</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param node the built literal command node
|
||||
* @param description the help description for the root literal node
|
||||
* @return successfully registered root command labels (including aliases and namespaced variants)
|
||||
*/
|
||||
default @Unmodifiable Set<String> register(final LiteralCommandNode<CommandSourceStack> node, final @Nullable String description) {
|
||||
return this.register(node, description, Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a command for the current plugin context.
|
||||
*
|
||||
* <p>Commands have certain overriding behavior:
|
||||
* <ul>
|
||||
* <li>Aliases will not override already existing commands (excluding namespaced ones)</li>
|
||||
* <li>The main command/namespaced label will override already existing commands</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param node the built literal command node
|
||||
* @param aliases a collection of aliases to register the literal node's command to
|
||||
* @return successfully registered root command labels (including aliases and namespaced variants)
|
||||
*/
|
||||
default @Unmodifiable Set<String> register(final LiteralCommandNode<CommandSourceStack> node, final Collection<String> aliases) {
|
||||
return this.register(node, null, aliases);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a command for the current plugin context.
|
||||
*
|
||||
* <p>Commands have certain overriding behavior:
|
||||
* <ul>
|
||||
* <li>Aliases will not override already existing commands (excluding namespaced ones)</li>
|
||||
* <li>The main command/namespaced label will override already existing commands</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param node the built literal command node
|
||||
* @param description the help description for the root literal node
|
||||
* @param aliases a collection of aliases to register the literal node's command to
|
||||
* @return successfully registered root command labels (including aliases and namespaced variants)
|
||||
*/
|
||||
@Unmodifiable Set<String> register(LiteralCommandNode<CommandSourceStack> node, @Nullable String description, Collection<String> aliases);
|
||||
|
||||
/**
|
||||
* Registers a command for a plugin.
|
||||
*
|
||||
* <p>Commands have certain overriding behavior:
|
||||
* <ul>
|
||||
* <li>Aliases will not override already existing commands (excluding namespaced ones)</li>
|
||||
* <li>The main command/namespaced label will override already existing commands</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param pluginMeta the owning plugin's meta
|
||||
* @param node the built literal command node
|
||||
* @param description the help description for the root literal node
|
||||
* @param aliases a collection of aliases to register the literal node's command to
|
||||
* @return successfully registered root command labels (including aliases and namespaced variants)
|
||||
*/
|
||||
@Unmodifiable Set<String> register(PluginMeta pluginMeta, LiteralCommandNode<CommandSourceStack> node, @Nullable String description, Collection<String> aliases);
|
||||
|
||||
/**
|
||||
* This allows configuring the registration of your command, which is not intended for public use.
|
||||
* See {@link Commands#register(PluginMeta, LiteralCommandNode, String, Collection)} for more information.
|
||||
*
|
||||
* @param pluginMeta the owning plugin's meta
|
||||
* @param node the built literal command node
|
||||
* @param description the help description for the root literal node
|
||||
* @param aliases a collection of aliases to register the literal node's command to
|
||||
* @param flags a collection of registration flags that control registration behaviour.
|
||||
* @return successfully registered root command labels (including aliases and namespaced variants)
|
||||
*
|
||||
* @apiNote This method is not guaranteed to be stable as it is not intended for public use.
|
||||
* See {@link CommandRegistrationFlag} for a more indepth explanation of this method's use-case.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
@Unmodifiable Set<String> registerWithFlags(PluginMeta pluginMeta, LiteralCommandNode<CommandSourceStack> node, @Nullable String description, Collection<String> aliases, Set<CommandRegistrationFlag> flags);
|
||||
|
||||
/**
|
||||
* Registers a command under the same logic as {@link Commands#register(LiteralCommandNode, String, Collection)}.
|
||||
*
|
||||
* @param label the label of the to-be-registered command
|
||||
* @param basicCommand the basic command instance to register
|
||||
* @return successfully registered root command labels (including aliases and namespaced variants)
|
||||
*/
|
||||
default @Unmodifiable Set<String> register(final String label, final BasicCommand basicCommand) {
|
||||
return this.register(label, null, Collections.emptyList(), basicCommand);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a command under the same logic as {@link Commands#register(LiteralCommandNode, String, Collection)}.
|
||||
*
|
||||
* @param label the label of the to-be-registered command
|
||||
* @param description the help description for the root literal node
|
||||
* @param basicCommand the basic command instance to register
|
||||
* @return successfully registered root command labels (including aliases and namespaced variants)
|
||||
*/
|
||||
default @Unmodifiable Set<String> register(final String label, final @Nullable String description, final BasicCommand basicCommand) {
|
||||
return this.register(label, description, Collections.emptyList(), basicCommand);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a command under the same logic as {@link Commands#register(LiteralCommandNode, String, Collection)}.
|
||||
*
|
||||
* @param label the label of the to-be-registered command
|
||||
* @param aliases a collection of aliases to register the basic command under.
|
||||
* @param basicCommand the basic command instance to register
|
||||
* @return successfully registered root command labels (including aliases and namespaced variants)
|
||||
*/
|
||||
default @Unmodifiable Set<String> register(final String label, final Collection<String> aliases, final BasicCommand basicCommand) {
|
||||
return this.register(label, null, aliases, basicCommand);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a command under the same logic as {@link Commands#register(LiteralCommandNode, String, Collection)}.
|
||||
*
|
||||
* @param label the label of the to-be-registered command
|
||||
* @param description the help description for the root literal node
|
||||
* @param aliases a collection of aliases to register the basic command under.
|
||||
* @param basicCommand the basic command instance to register
|
||||
* @return successfully registered root command labels (including aliases and namespaced variants)
|
||||
*/
|
||||
@Unmodifiable Set<String> register(String label, @Nullable String description, Collection<String> aliases, BasicCommand basicCommand);
|
||||
|
||||
/**
|
||||
* Registers a command under the same logic as {@link Commands#register(PluginMeta, LiteralCommandNode, String, Collection)}.
|
||||
*
|
||||
* @param pluginMeta the owning plugin's meta
|
||||
* @param label the label of the to-be-registered command
|
||||
* @param description the help description for the root literal node
|
||||
* @param aliases a collection of aliases to register the basic command under.
|
||||
* @param basicCommand the basic command instance to register
|
||||
* @return successfully registered root command labels (including aliases and namespaced variants)
|
||||
*/
|
||||
@Unmodifiable Set<String> register(PluginMeta pluginMeta, String label, @Nullable String description, Collection<String> aliases, BasicCommand basicCommand);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package io.papermc.paper.command.brigadier;
|
||||
|
||||
import com.mojang.brigadier.Message;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.ComponentSerializer;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* A component serializer for converting between {@link Message} and {@link Component}.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@NullMarked
|
||||
@ApiStatus.NonExtendable
|
||||
public interface MessageComponentSerializer extends ComponentSerializer<Component, Component, Message> {
|
||||
|
||||
/**
|
||||
* A component serializer for converting between {@link Message} and {@link Component}.
|
||||
*
|
||||
* @return serializer instance
|
||||
*/
|
||||
static MessageComponentSerializer message() {
|
||||
return MessageComponentSerializerHolder.PROVIDER.orElseThrow();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package io.papermc.paper.command.brigadier;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.ServiceLoader;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
@ApiStatus.Internal
|
||||
final class MessageComponentSerializerHolder {
|
||||
|
||||
static final Optional<MessageComponentSerializer> PROVIDER = ServiceLoader.load(MessageComponentSerializer.class)
|
||||
.findFirst();
|
||||
}
|
||||
@@ -0,0 +1,371 @@
|
||||
package io.papermc.paper.command.brigadier.argument;
|
||||
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import io.papermc.paper.command.brigadier.argument.predicate.ItemStackPredicate;
|
||||
import io.papermc.paper.command.brigadier.argument.range.DoubleRangeProvider;
|
||||
import io.papermc.paper.command.brigadier.argument.range.IntegerRangeProvider;
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.BlockPositionResolver;
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.FinePositionResolver;
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.PlayerProfileListResolver;
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.selector.EntitySelectorArgumentResolver;
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver;
|
||||
import io.papermc.paper.entity.LookAnchor;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import io.papermc.paper.registry.TypedKey;
|
||||
import java.util.UUID;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.Style;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.HeightMap;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.structure.Mirror;
|
||||
import org.bukkit.block.structure.StructureRotation;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.scoreboard.Criteria;
|
||||
import org.bukkit.scoreboard.DisplaySlot;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import static io.papermc.paper.command.brigadier.argument.VanillaArgumentProvider.provider;
|
||||
|
||||
/**
|
||||
* Vanilla Minecraft includes several custom {@link ArgumentType}s that are recognized by the client.
|
||||
* Many of these argument types include client-side completions and validation, and some include command signing context.
|
||||
*
|
||||
* <p>This class allows creating instances of these types for use in plugin commands, with friendly API result types.</p>
|
||||
*
|
||||
* <p>{@link CustomArgumentType} is provided for customizing parsing or result types server-side, while sending the vanilla argument type to the client.</p>
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@NullMarked
|
||||
public final class ArgumentTypes {
|
||||
|
||||
/**
|
||||
* Represents a selector that can capture any
|
||||
* single entity.
|
||||
*
|
||||
* @return argument that takes one entity
|
||||
*/
|
||||
public static ArgumentType<EntitySelectorArgumentResolver> entity() {
|
||||
return provider().entity();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a selector that can capture multiple
|
||||
* entities.
|
||||
*
|
||||
* @return argument that takes multiple entities
|
||||
*/
|
||||
public static ArgumentType<EntitySelectorArgumentResolver> entities() {
|
||||
return provider().entities();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a selector that can capture a
|
||||
* singular player entity.
|
||||
*
|
||||
* @return argument that takes one player
|
||||
*/
|
||||
public static ArgumentType<PlayerSelectorArgumentResolver> player() {
|
||||
return provider().player();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a selector that can capture multiple
|
||||
* player entities.
|
||||
*
|
||||
* @return argument that takes multiple players
|
||||
*/
|
||||
public static ArgumentType<PlayerSelectorArgumentResolver> players() {
|
||||
return provider().players();
|
||||
}
|
||||
|
||||
/**
|
||||
* A selector argument that provides a list
|
||||
* of player profiles.
|
||||
*
|
||||
* @return player profile argument
|
||||
*/
|
||||
public static ArgumentType<PlayerProfileListResolver> playerProfiles() {
|
||||
return provider().playerProfiles();
|
||||
}
|
||||
|
||||
/**
|
||||
* A block position argument.
|
||||
*
|
||||
* @return block position argument
|
||||
*/
|
||||
public static ArgumentType<BlockPositionResolver> blockPosition() {
|
||||
return provider().blockPosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* A fine position argument.
|
||||
*
|
||||
* @return fine position argument
|
||||
* @see #finePosition(boolean) to center whole numbers
|
||||
*/
|
||||
public static ArgumentType<FinePositionResolver> finePosition() {
|
||||
return finePosition(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* A fine position argument.
|
||||
*
|
||||
* @param centerIntegers if whole numbers should be centered (+0.5)
|
||||
* @return fine position argument
|
||||
*/
|
||||
public static ArgumentType<FinePositionResolver> finePosition(final boolean centerIntegers) {
|
||||
return provider().finePosition(centerIntegers);
|
||||
}
|
||||
|
||||
/**
|
||||
* A blockstate argument which will provide rich parsing for specifying
|
||||
* the specific block variant and then the block entity NBT if applicable.
|
||||
*
|
||||
* @return argument
|
||||
*/
|
||||
public static ArgumentType<BlockState> blockState() {
|
||||
return provider().blockState();
|
||||
}
|
||||
|
||||
/**
|
||||
* An ItemStack argument which provides rich parsing for
|
||||
* specifying item material and item NBT information.
|
||||
*
|
||||
* @return argument
|
||||
*/
|
||||
public static ArgumentType<ItemStack> itemStack() {
|
||||
return provider().itemStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* An item predicate argument.
|
||||
*
|
||||
* @return argument
|
||||
*/
|
||||
public static ArgumentType<ItemStackPredicate> itemPredicate() {
|
||||
return provider().itemStackPredicate();
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument for parsing {@link NamedTextColor}s.
|
||||
*
|
||||
* @return argument
|
||||
*/
|
||||
public static ArgumentType<NamedTextColor> namedColor() {
|
||||
return provider().namedColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* A component argument.
|
||||
*
|
||||
* @return argument
|
||||
*/
|
||||
public static ArgumentType<Component> component() {
|
||||
return provider().component();
|
||||
}
|
||||
|
||||
/**
|
||||
* A style argument.
|
||||
*
|
||||
* @return argument
|
||||
*/
|
||||
public static ArgumentType<Style> style() {
|
||||
return provider().style();
|
||||
}
|
||||
|
||||
/**
|
||||
* A signed message argument.
|
||||
* This argument can be resolved to retrieve the underlying
|
||||
* signed message.
|
||||
*
|
||||
* @return argument
|
||||
*/
|
||||
public static ArgumentType<SignedMessageResolver> signedMessage() {
|
||||
return provider().signedMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* A scoreboard display slot argument.
|
||||
*
|
||||
* @return argument
|
||||
*/
|
||||
public static ArgumentType<DisplaySlot> scoreboardDisplaySlot() {
|
||||
return provider().scoreboardDisplaySlot();
|
||||
}
|
||||
|
||||
/**
|
||||
* A namespaced key argument.
|
||||
*
|
||||
* @return argument
|
||||
*/
|
||||
public static ArgumentType<NamespacedKey> namespacedKey() {
|
||||
return provider().namespacedKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* A key argument.
|
||||
*
|
||||
* @return argument
|
||||
*/
|
||||
// include both key types as we are slowly moving to use adventure's key
|
||||
public static ArgumentType<Key> key() {
|
||||
return provider().key();
|
||||
}
|
||||
|
||||
/**
|
||||
* An inclusive range of integers that may be unbounded on either end.
|
||||
*
|
||||
* @return argument
|
||||
*/
|
||||
public static ArgumentType<IntegerRangeProvider> integerRange() {
|
||||
return provider().integerRange();
|
||||
}
|
||||
|
||||
/**
|
||||
* An inclusive range of doubles that may be unbounded on either end.
|
||||
*
|
||||
* @return argument
|
||||
*/
|
||||
public static ArgumentType<DoubleRangeProvider> doubleRange() {
|
||||
return provider().doubleRange();
|
||||
}
|
||||
|
||||
/**
|
||||
* A world argument.
|
||||
*
|
||||
* @return argument
|
||||
*/
|
||||
public static ArgumentType<World> world() {
|
||||
return provider().world();
|
||||
}
|
||||
|
||||
/**
|
||||
* A game mode argument.
|
||||
*
|
||||
* @return argument
|
||||
*/
|
||||
public static ArgumentType<GameMode> gameMode() {
|
||||
return provider().gameMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument for getting a heightmap type.
|
||||
*
|
||||
* @return argument
|
||||
*/
|
||||
public static ArgumentType<HeightMap> heightMap() {
|
||||
return provider().heightMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* A uuid argument.
|
||||
*
|
||||
* @return argument
|
||||
*/
|
||||
public static ArgumentType<UUID> uuid() {
|
||||
return provider().uuid();
|
||||
}
|
||||
|
||||
/**
|
||||
* An objective criteria argument
|
||||
*
|
||||
* @return argument
|
||||
*/
|
||||
public static ArgumentType<Criteria> objectiveCriteria() {
|
||||
return provider().objectiveCriteria();
|
||||
}
|
||||
|
||||
/**
|
||||
* An entity anchor argument.
|
||||
*
|
||||
* @return argument
|
||||
*/
|
||||
public static ArgumentType<LookAnchor> entityAnchor() {
|
||||
return provider().entityAnchor();
|
||||
}
|
||||
|
||||
/**
|
||||
* A time argument, returning the number of ticks.
|
||||
* <p>Examples:
|
||||
* <ul>
|
||||
* <li> "1d"
|
||||
* <li> "5s"
|
||||
* <li> "2"
|
||||
* <li> "6t"
|
||||
* </ul>
|
||||
*
|
||||
* @return argument
|
||||
*/
|
||||
public static ArgumentType<Integer> time() {
|
||||
return time(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* A time argument, returning the number of ticks.
|
||||
* <p>Examples:
|
||||
* <ul>
|
||||
* <li> "1d"
|
||||
* <li> "5s"
|
||||
* <li> "2"
|
||||
* <li> "6t"
|
||||
* </ul>
|
||||
*
|
||||
* @param mintime The minimum time required for this argument.
|
||||
* @return argument
|
||||
*/
|
||||
public static ArgumentType<Integer> time(final int mintime) {
|
||||
return provider().time(mintime);
|
||||
}
|
||||
|
||||
/**
|
||||
* A template mirror argument
|
||||
*
|
||||
* @return argument
|
||||
* @see Mirror
|
||||
*/
|
||||
public static ArgumentType<Mirror> templateMirror() {
|
||||
return provider().templateMirror();
|
||||
}
|
||||
|
||||
/**
|
||||
* A template rotation argument.
|
||||
*
|
||||
* @return argument
|
||||
* @see StructureRotation
|
||||
*/
|
||||
public static ArgumentType<StructureRotation> templateRotation() {
|
||||
return provider().templateRotation();
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument for a resource in a {@link org.bukkit.Registry}.
|
||||
*
|
||||
* @param registryKey the registry's key
|
||||
* @return argument
|
||||
* @param <T> the registry value type
|
||||
*/
|
||||
public static <T> ArgumentType<T> resource(final RegistryKey<T> registryKey) {
|
||||
return provider().resource(registryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument for a typed key for a {@link org.bukkit.Registry}.
|
||||
*
|
||||
* @param registryKey the registry's key
|
||||
* @return argument
|
||||
* @param <T> the registry value type
|
||||
* @see RegistryArgumentExtractor#getTypedKey(com.mojang.brigadier.context.CommandContext, RegistryKey, String)
|
||||
*/
|
||||
public static <T> ArgumentType<TypedKey<T>> resourceKey(final RegistryKey<T> registryKey) {
|
||||
return provider().resourceKey(registryKey);
|
||||
}
|
||||
|
||||
private ArgumentTypes() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package io.papermc.paper.command.brigadier.argument;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* An argument type that wraps around a native-to-vanilla argument type.
|
||||
* This argument receives special handling in that the native argument type will
|
||||
* be sent to the client for possible client-side completions and syntax validation.
|
||||
* <p>
|
||||
* When implementing this class, you have to create your own parsing logic from a
|
||||
* {@link StringReader}. If only want to convert from the native type ({@code N}) to the custom
|
||||
* type ({@code T}), implement {@link Converted} instead.
|
||||
*
|
||||
* @param <T> custom type
|
||||
* @param <N> type with an argument native to vanilla Minecraft (from {@link ArgumentTypes})
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@NullMarked
|
||||
public interface CustomArgumentType<T, N> extends ArgumentType<T> {
|
||||
|
||||
/**
|
||||
* Parses the argument into the custom type ({@code T}). Keep in mind
|
||||
* that this parsing will be done on the server. This means that if
|
||||
* you throw a {@link CommandSyntaxException} during parsing, this
|
||||
* will only show up to the user after the user has executed the command
|
||||
* not while they are still entering it.
|
||||
*
|
||||
* @param reader string reader input
|
||||
* @return parsed value
|
||||
* @throws CommandSyntaxException if an error occurs while parsing
|
||||
*/
|
||||
@Override
|
||||
T parse(final StringReader reader) throws CommandSyntaxException;
|
||||
|
||||
/**
|
||||
* Gets the native type that this argument uses,
|
||||
* the type that is sent to the client.
|
||||
*
|
||||
* @return native argument type
|
||||
*/
|
||||
ArgumentType<N> getNativeType();
|
||||
|
||||
/**
|
||||
* Cannot be controlled by the server.
|
||||
* Returned in cases where there are multiple arguments in the same node.
|
||||
* This helps differentiate and tell the player what the possible inputs are.
|
||||
*
|
||||
* @return client set examples
|
||||
*/
|
||||
@Override
|
||||
@ApiStatus.NonExtendable
|
||||
default Collection<String> getExamples() {
|
||||
return this.getNativeType().getExamples();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a list of suggestions to show to the client.
|
||||
*
|
||||
* @param context command context
|
||||
* @param builder suggestion builder
|
||||
* @return suggestions
|
||||
* @param <S> context type
|
||||
*/
|
||||
@Override
|
||||
default <S> CompletableFuture<Suggestions> listSuggestions(final CommandContext<S> context, final SuggestionsBuilder builder) {
|
||||
return ArgumentType.super.listSuggestions(context, builder);
|
||||
}
|
||||
|
||||
/**
|
||||
* An argument type that wraps around a native-to-vanilla argument type.
|
||||
* This argument receives special handling in that the native argument type will
|
||||
* be sent to the client for possible client-side completions and syntax validation.
|
||||
* <p>
|
||||
* The parsed native type will be converted via {@link #convert(Object)}.
|
||||
* Implement {@link CustomArgumentType} if you want to handle parsing the type manually.
|
||||
*
|
||||
* @param <T> custom type
|
||||
* @param <N> type with an argument native to vanilla Minecraft (from {@link ArgumentTypes})
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
interface Converted<T, N> extends CustomArgumentType<T, N> {
|
||||
|
||||
@ApiStatus.NonExtendable
|
||||
@Override
|
||||
default T parse(final StringReader reader) throws CommandSyntaxException {
|
||||
return this.convert(this.getNativeType().parse(reader));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the value from the native type to the custom argument type.
|
||||
*
|
||||
* @param nativeType native argument provided value
|
||||
* @return converted value
|
||||
* @throws CommandSyntaxException if an exception occurs while parsing
|
||||
*/
|
||||
T convert(N nativeType) throws CommandSyntaxException;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package io.papermc.paper.command.brigadier.argument;
|
||||
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import io.papermc.paper.registry.TypedKey;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* Utilities for extracting registry-related arguments from a {@link CommandContext}.
|
||||
*/
|
||||
@NullMarked
|
||||
public final class RegistryArgumentExtractor {
|
||||
|
||||
/**
|
||||
* Gets a typed key argument from a command context.
|
||||
*
|
||||
* @param context the command context
|
||||
* @param registryKey the registry key for the typed key
|
||||
* @param name the argument name
|
||||
* @return the typed key argument
|
||||
* @param <T> the value type
|
||||
* @param <S> the sender type
|
||||
* @throws IllegalArgumentException if the registry key doesn't match the typed key
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T, S> TypedKey<T> getTypedKey(final CommandContext<S> context, final RegistryKey<T> registryKey, final String name) {
|
||||
final TypedKey<T> typedKey = context.getArgument(name, TypedKey.class);
|
||||
if (typedKey.registryKey().equals(registryKey)) {
|
||||
return typedKey;
|
||||
}
|
||||
throw new IllegalArgumentException(registryKey + " is not the correct registry for " + typedKey);
|
||||
}
|
||||
|
||||
private RegistryArgumentExtractor() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package io.papermc.paper.command.brigadier.argument;
|
||||
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import io.papermc.paper.command.brigadier.CommandSourceStack;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import net.kyori.adventure.chat.SignedMessage;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* A resolver for a {@link SignedMessage}
|
||||
*
|
||||
* @see ArgumentTypes#signedMessage()
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@NullMarked
|
||||
@ApiStatus.NonExtendable
|
||||
public interface SignedMessageResolver {
|
||||
|
||||
/**
|
||||
* Gets the string content of the message
|
||||
*
|
||||
* @return string content
|
||||
*/
|
||||
String content();
|
||||
|
||||
/**
|
||||
* Resolves this signed message. This will the {@link CommandContext}
|
||||
* and signed arguments sent by the client.
|
||||
* <p>
|
||||
* In the case that signed message information isn't provided, a "system"
|
||||
* signed message will be returned instead.
|
||||
*
|
||||
* @param argumentName argument name
|
||||
* @param context the command context
|
||||
* @return a completable future for the {@link SignedMessage}
|
||||
* @throws CommandSyntaxException syntax exception
|
||||
*/
|
||||
CompletableFuture<SignedMessage> resolveSignedMessage(String argumentName, CommandContext<CommandSourceStack> context) throws CommandSyntaxException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package io.papermc.paper.command.brigadier.argument;
|
||||
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import io.papermc.paper.command.brigadier.argument.predicate.ItemStackPredicate;
|
||||
import io.papermc.paper.command.brigadier.argument.range.DoubleRangeProvider;
|
||||
import io.papermc.paper.command.brigadier.argument.range.IntegerRangeProvider;
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.BlockPositionResolver;
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.FinePositionResolver;
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.PlayerProfileListResolver;
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.selector.EntitySelectorArgumentResolver;
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver;
|
||||
import io.papermc.paper.entity.LookAnchor;
|
||||
import io.papermc.paper.registry.RegistryKey;
|
||||
import io.papermc.paper.registry.TypedKey;
|
||||
import java.util.Optional;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.UUID;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.Style;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.HeightMap;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.structure.Mirror;
|
||||
import org.bukkit.block.structure.StructureRotation;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.scoreboard.Criteria;
|
||||
import org.bukkit.scoreboard.DisplaySlot;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@ApiStatus.Internal
|
||||
@NullMarked
|
||||
interface VanillaArgumentProvider {
|
||||
|
||||
Optional<VanillaArgumentProvider> PROVIDER = ServiceLoader.load(VanillaArgumentProvider.class)
|
||||
.findFirst();
|
||||
|
||||
static VanillaArgumentProvider provider() {
|
||||
return PROVIDER.orElseThrow();
|
||||
}
|
||||
|
||||
ArgumentType<EntitySelectorArgumentResolver> entity();
|
||||
|
||||
ArgumentType<PlayerSelectorArgumentResolver> player();
|
||||
|
||||
ArgumentType<EntitySelectorArgumentResolver> entities();
|
||||
|
||||
ArgumentType<PlayerSelectorArgumentResolver> players();
|
||||
|
||||
ArgumentType<PlayerProfileListResolver> playerProfiles();
|
||||
|
||||
ArgumentType<BlockPositionResolver> blockPosition();
|
||||
|
||||
ArgumentType<FinePositionResolver> finePosition(boolean centerIntegers);
|
||||
|
||||
ArgumentType<BlockState> blockState();
|
||||
|
||||
ArgumentType<ItemStack> itemStack();
|
||||
|
||||
ArgumentType<ItemStackPredicate> itemStackPredicate();
|
||||
|
||||
ArgumentType<NamedTextColor> namedColor();
|
||||
|
||||
ArgumentType<Component> component();
|
||||
|
||||
ArgumentType<Style> style();
|
||||
|
||||
ArgumentType<SignedMessageResolver> signedMessage();
|
||||
|
||||
ArgumentType<DisplaySlot> scoreboardDisplaySlot();
|
||||
|
||||
ArgumentType<NamespacedKey> namespacedKey();
|
||||
|
||||
// include both key types as we are slowly moving to use adventure's key
|
||||
ArgumentType<Key> key();
|
||||
|
||||
ArgumentType<IntegerRangeProvider> integerRange();
|
||||
|
||||
ArgumentType<DoubleRangeProvider> doubleRange();
|
||||
|
||||
ArgumentType<World> world();
|
||||
|
||||
ArgumentType<GameMode> gameMode();
|
||||
|
||||
ArgumentType<HeightMap> heightMap();
|
||||
|
||||
ArgumentType<UUID> uuid();
|
||||
|
||||
ArgumentType<Criteria> objectiveCriteria();
|
||||
|
||||
ArgumentType<LookAnchor> entityAnchor();
|
||||
|
||||
ArgumentType<Integer> time(int minTicks);
|
||||
|
||||
ArgumentType<Mirror> templateMirror();
|
||||
|
||||
ArgumentType<StructureRotation> templateRotation();
|
||||
|
||||
<T> ArgumentType<TypedKey<T>> resourceKey(RegistryKey<T> registryKey);
|
||||
|
||||
<T> ArgumentType<T> resource(RegistryKey<T> registryKey);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package io.papermc.paper.command.brigadier.argument.predicate;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* A predicate for ItemStack.
|
||||
*
|
||||
* @see io.papermc.paper.command.brigadier.argument.ArgumentTypes#itemPredicate()
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@ApiStatus.NonExtendable
|
||||
public interface ItemStackPredicate extends Predicate<ItemStack> {
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package io.papermc.paper.command.brigadier.argument.range;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* A provider for a {@link com.google.common.collect.Range} of doubles.
|
||||
*
|
||||
* @see io.papermc.paper.command.brigadier.argument.ArgumentTypes#doubleRange()
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@ApiStatus.NonExtendable
|
||||
public non-sealed interface DoubleRangeProvider extends RangeProvider<Double> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package io.papermc.paper.command.brigadier.argument.range;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* A provider for a {@link com.google.common.collect.Range} of integers.
|
||||
*
|
||||
* @see io.papermc.paper.command.brigadier.argument.ArgumentTypes#integerRange()
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@ApiStatus.NonExtendable
|
||||
public non-sealed interface IntegerRangeProvider extends RangeProvider<Integer> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package io.papermc.paper.command.brigadier.argument.range;
|
||||
|
||||
import com.google.common.collect.Range;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* A provider for a range of numbers
|
||||
*
|
||||
* @param <T>
|
||||
* @see io.papermc.paper.command.brigadier.argument.ArgumentTypes
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@NullMarked
|
||||
public sealed interface RangeProvider<T extends Comparable<?>> permits DoubleRangeProvider, IntegerRangeProvider {
|
||||
|
||||
/**
|
||||
* Provides the given range.
|
||||
* @return range
|
||||
*/
|
||||
Range<T> range();
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.papermc.paper.command.brigadier.argument.resolvers;
|
||||
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import io.papermc.paper.command.brigadier.CommandSourceStack;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
/**
|
||||
* An {@link ArgumentResolver} is capable of resolving
|
||||
* an argument value using a {@link CommandSourceStack}.
|
||||
*
|
||||
* @param <T> resolved type
|
||||
* @see io.papermc.paper.command.brigadier.argument.ArgumentTypes
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@NullMarked
|
||||
@ApiStatus.NonExtendable
|
||||
public interface ArgumentResolver<T> {
|
||||
|
||||
/**
|
||||
* Resolves the argument with the given
|
||||
* command source stack.
|
||||
* @param sourceStack source stack
|
||||
* @return resolved
|
||||
*/
|
||||
T resolve(CommandSourceStack sourceStack) throws CommandSyntaxException;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package io.papermc.paper.command.brigadier.argument.resolvers;
|
||||
|
||||
import io.papermc.paper.command.brigadier.CommandSourceStack;
|
||||
import io.papermc.paper.math.BlockPosition;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* An {@link ArgumentResolver} that's capable of resolving
|
||||
* a block position argument value using a {@link CommandSourceStack}.
|
||||
*
|
||||
* @see io.papermc.paper.command.brigadier.argument.ArgumentTypes#blockPosition()
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@ApiStatus.NonExtendable
|
||||
public interface BlockPositionResolver extends ArgumentResolver<BlockPosition> {
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.papermc.paper.command.brigadier.argument.resolvers;
|
||||
|
||||
import io.papermc.paper.command.brigadier.CommandSourceStack;
|
||||
import io.papermc.paper.math.FinePosition;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* An {@link ArgumentResolver} that's capable of resolving
|
||||
* a fine position argument value using a {@link CommandSourceStack}.
|
||||
*
|
||||
* @see io.papermc.paper.command.brigadier.argument.ArgumentTypes#finePosition()
|
||||
* @see io.papermc.paper.command.brigadier.argument.ArgumentTypes#finePosition(boolean)
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@ApiStatus.NonExtendable
|
||||
public interface FinePositionResolver extends ArgumentResolver<FinePosition> {
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.papermc.paper.command.brigadier.argument.resolvers;
|
||||
|
||||
import com.destroystokyo.paper.profile.PlayerProfile;
|
||||
import io.papermc.paper.command.brigadier.CommandSourceStack;
|
||||
import java.util.Collection;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* An {@link ArgumentResolver} that's capable of resolving
|
||||
* argument value using a {@link CommandSourceStack}.
|
||||
*
|
||||
* @see io.papermc.paper.command.brigadier.argument.ArgumentTypes#playerProfiles()
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@ApiStatus.NonExtendable
|
||||
public interface PlayerProfileListResolver extends ArgumentResolver<Collection<PlayerProfile>> {
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package io.papermc.paper.command.brigadier.argument.resolvers.selector;
|
||||
|
||||
import io.papermc.paper.command.brigadier.CommandSourceStack;
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.ArgumentResolver;
|
||||
import java.util.List;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* An {@link ArgumentResolver} that's capable of resolving
|
||||
* an entity selector argument value using a {@link CommandSourceStack}.
|
||||
*
|
||||
* @see io.papermc.paper.command.brigadier.argument.ArgumentTypes#entity()
|
||||
* @see io.papermc.paper.command.brigadier.argument.ArgumentTypes#entities()
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@ApiStatus.NonExtendable
|
||||
public interface EntitySelectorArgumentResolver extends SelectorArgumentResolver<List<Entity>> {
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package io.papermc.paper.command.brigadier.argument.resolvers.selector;
|
||||
|
||||
import io.papermc.paper.command.brigadier.CommandSourceStack;
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.ArgumentResolver;
|
||||
import java.util.List;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* An {@link ArgumentResolver} that's capable of resolving
|
||||
* a player selector argument value using a {@link CommandSourceStack}.
|
||||
*
|
||||
* @see io.papermc.paper.command.brigadier.argument.ArgumentTypes#player()
|
||||
* @see io.papermc.paper.command.brigadier.argument.ArgumentTypes#players()
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@ApiStatus.NonExtendable
|
||||
public interface PlayerSelectorArgumentResolver extends SelectorArgumentResolver<List<Player>> {
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.papermc.paper.command.brigadier.argument.resolvers.selector;
|
||||
|
||||
import io.papermc.paper.command.brigadier.CommandSourceStack;
|
||||
import io.papermc.paper.command.brigadier.argument.resolvers.ArgumentResolver;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* An {@link ArgumentResolver} that's capable of resolving
|
||||
* a selector argument value using a {@link CommandSourceStack}.
|
||||
*
|
||||
* @param <T> resolved type
|
||||
* @see <a href="https://minecraft.wiki/w/Target_selectors">Target Selectors</a>
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
@ApiStatus.NonExtendable
|
||||
public interface SelectorArgumentResolver<T> extends ArgumentResolver<T> {
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
package io.papermc.paper.plugin.lifecycle.event.types;
|
||||
|
||||
import io.papermc.paper.command.brigadier.Commands;
|
||||
import io.papermc.paper.plugin.bootstrap.BootstrapContext;
|
||||
import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
|
||||
import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager;
|
||||
import io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner;
|
||||
import io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -17,6 +19,13 @@ import org.jspecify.annotations.NullMarked;
|
||||
@NullMarked
|
||||
public final class LifecycleEvents {
|
||||
|
||||
/**
|
||||
* This event is for registering commands to the server's brigadier command system. You can register a handler for this event in
|
||||
* {@link org.bukkit.plugin.java.JavaPlugin#onEnable()} or {@link io.papermc.paper.plugin.bootstrap.PluginBootstrap#bootstrap(BootstrapContext)}.
|
||||
* @see Commands an example of a command being registered
|
||||
*/
|
||||
public static final LifecycleEventType.Prioritizable<LifecycleEventOwner, ReloadableRegistrarEvent<Commands>> COMMANDS = prioritized("commands", LifecycleEventOwner.class);
|
||||
|
||||
//<editor-fold desc="helper methods" defaultstate="collapsed">
|
||||
@ApiStatus.Internal
|
||||
static <E extends LifecycleEvent> LifecycleEventType.Monitorable<Plugin, E> plugin(final String name) {
|
||||
|
||||
Reference in New Issue
Block a user