Brigadier based command API

Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com>
This commit is contained in:
Owen1212055
2022-08-01 22:50:29 -04:00
parent fd8df6aeed
commit 69edd6d91f
35 changed files with 1774 additions and 5 deletions

View File

@@ -0,0 +1,16 @@
package com.destroystokyo.paper.brigadier;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import java.util.function.Predicate;
/**
* Brigadier {@link Command}, {@link SuggestionProvider}, and permission checker for Bukkit {@link Command}s.
*
* @param <S> command source type
* @deprecated For removal, see {@link io.papermc.paper.command.brigadier.Commands} on how to use the new Brigadier API.
*/
@Deprecated(forRemoval = true, since = "1.20.6")
public interface BukkitBrigadierCommand <S extends BukkitBrigadierCommandSource> extends Command<S>, Predicate<S>, SuggestionProvider<S> {
}

View File

@@ -0,0 +1,25 @@
package com.destroystokyo.paper.brigadier;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.jetbrains.annotations.Nullable;
/**
* @deprecated For removal, see {@link io.papermc.paper.command.brigadier.Commands} on how to use the new Brigadier API.
*/
@Deprecated(forRemoval = true)
public interface BukkitBrigadierCommandSource {
@Nullable
Entity getBukkitEntity();
@Nullable
World getBukkitWorld();
@Nullable
Location getBukkitLocation();
CommandSender getBukkitSender();
}

View File

@@ -0,0 +1,73 @@
package com.destroystokyo.paper.event.brigadier;
import com.mojang.brigadier.tree.RootCommandNode;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* Fired any time a Brigadier RootCommandNode is generated for a player to inform the client of commands.
* You may manipulate this CommandNode to change what the client sees.
*
* <p>This event may fire on login, world change, and permission rebuilds, by plugin request, and potentially future means.</p>
*
* <p>This event will fire before {@link org.bukkit.event.player.PlayerCommandSendEvent}, so no filtering has been done by
* other plugins yet.</p>
*
* <p>WARNING: This event will potentially (and most likely) fire twice! Once for Async, and once again for Sync.
* It is important that you check event.isAsynchronous() and event.hasFiredAsync() to ensure you only act once.
* If for some reason we are unable to send this asynchronously in the future, only the sync method will fire.</p>
*
* <p>Your logic should look like this:
* {@code if (event.isAsynchronous() || !event.hasFiredAsync()) { // do stuff }}</p>
*
* <p>If your logic is not safe to run asynchronously, only react to the synchronous version.</p>
*
* <p>This is a draft/experimental API and is subject to change.</p>
*/
@ApiStatus.Experimental
@NullMarked
public class AsyncPlayerSendCommandsEvent<S extends CommandSourceStack> extends PlayerEvent {
private static final HandlerList HANDLER_LIST = new HandlerList();
private final RootCommandNode<S> node;
private final boolean hasFiredAsync;
@ApiStatus.Internal
public AsyncPlayerSendCommandsEvent(final Player player, final RootCommandNode<S> node, final boolean hasFiredAsync) {
super(player, !Bukkit.isPrimaryThread());
this.node = node;
this.hasFiredAsync = hasFiredAsync;
}
/**
* Gets the full Root Command Node being sent to the client, which is mutable.
*
* @return the root command node
*/
public RootCommandNode<S> getCommandNode() {
return this.node;
}
/**
* Gets if this event has already fired asynchronously.
*
* @return whether this event has already fired asynchronously
*/
public boolean hasFiredAsync() {
return this.hasFiredAsync;
}
@Override
public HandlerList getHandlers() {
return HANDLER_LIST;
}
public static HandlerList getHandlerList() {
return HANDLER_LIST;
}
}

View File

@@ -0,0 +1,85 @@
package com.destroystokyo.paper.event.brigadier;
import com.mojang.brigadier.suggestion.Suggestions;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* Called when sending {@link Suggestions} to the client. Will be called asynchronously if a plugin
* marks the {@link com.destroystokyo.paper.event.server.AsyncTabCompleteEvent} event handled asynchronously,
* otherwise called synchronously.
*/
@NullMarked
public class AsyncPlayerSendSuggestionsEvent extends PlayerEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList();
private boolean cancelled = false;
private Suggestions suggestions;
private final String buffer;
@ApiStatus.Internal
public AsyncPlayerSendSuggestionsEvent(final Player player, final Suggestions suggestions, final String buffer) {
super(player, !Bukkit.isPrimaryThread());
this.suggestions = suggestions;
this.buffer = buffer;
}
/**
* Gets the input buffer sent to request these suggestions.
*
* @return the input buffer
*/
public String getBuffer() {
return this.buffer;
}
/**
* Gets the suggestions to be sent to client.
*
* @return the suggestions
*/
public Suggestions getSuggestions() {
return this.suggestions;
}
/**
* Sets the suggestions to be sent to client.
*
* @param suggestions suggestions
*/
public void setSuggestions(final Suggestions suggestions) {
this.suggestions = suggestions;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isCancelled() {
return this.cancelled;
}
/**
* Cancels sending suggestions to the client.
* {@inheritDoc}
*/
@Override
public void setCancelled(final boolean cancel) {
this.cancelled = cancel;
}
@Override
public HandlerList getHandlers() {
return HANDLER_LIST;
}
public static HandlerList getHandlerList() {
return HANDLER_LIST;
}
}

View File

@@ -0,0 +1,171 @@
package com.destroystokyo.paper.event.brigadier;
import com.destroystokyo.paper.brigadier.BukkitBrigadierCommand;
import com.mojang.brigadier.tree.ArgumentCommandNode;
import com.mojang.brigadier.tree.LiteralCommandNode;
import com.mojang.brigadier.tree.RootCommandNode;
import org.bukkit.Warning;
import org.bukkit.command.Command;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.server.ServerEvent;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
* Fired anytime the server synchronizes Bukkit commands to Brigadier.
*
* <p>Allows a plugin to control the command node structure for its commands.
* This is done at Plugin Enable time after commands have been registered, but may also
* run at a later point in the server lifetime due to plugins, a server reload, etc.</p>
*
* <p>This is a draft/experimental API and is subject to change.</p>
* @deprecated For removal, use the new brigadier api.
*/
@ApiStatus.Experimental
@Deprecated(since = "1.20.6")
@Warning(reason = "This event has been superseded by the Commands API and will be removed in a future release. Listen to LifecycleEvents.COMMANDS instead.", value = true)
public class CommandRegisteredEvent<S extends com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource> extends ServerEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private final String commandLabel;
private final Command command;
private final com.destroystokyo.paper.brigadier.BukkitBrigadierCommand<S> brigadierCommand;
private final RootCommandNode<S> root;
private final ArgumentCommandNode<S, String> defaultArgs;
private LiteralCommandNode<S> literal;
private boolean rawCommand = false;
private boolean cancelled = false;
public CommandRegisteredEvent(String commandLabel, com.destroystokyo.paper.brigadier.BukkitBrigadierCommand<S> brigadierCommand, Command command, RootCommandNode<S> root, LiteralCommandNode<S> literal, ArgumentCommandNode<S, String> defaultArgs) {
this.commandLabel = commandLabel;
this.brigadierCommand = brigadierCommand;
this.command = command;
this.root = root;
this.literal = literal;
this.defaultArgs = defaultArgs;
}
/**
* Gets the command label of the {@link Command} being registered.
*
* @return the command label
*/
public String getCommandLabel() {
return this.commandLabel;
}
/**
* Gets the {@link BukkitBrigadierCommand} for the {@link Command} being registered. This can be used
* as the {@link com.mojang.brigadier.Command command executor} or
* {@link com.mojang.brigadier.suggestion.SuggestionProvider} of a {@link com.mojang.brigadier.tree.CommandNode}
* to delegate to the {@link Command} being registered.
*
* @return the {@link BukkitBrigadierCommand}
*/
public BukkitBrigadierCommand<S> getBrigadierCommand() {
return this.brigadierCommand;
}
/**
* Gets the {@link Command} being registered.
*
* @return the {@link Command}
*/
public Command getCommand() {
return this.command;
}
/**
* Gets the {@link RootCommandNode} which is being registered to.
*
* @return the {@link RootCommandNode}
*/
public RootCommandNode<S> getRoot() {
return this.root;
}
/**
* Gets the Bukkit APIs default arguments node (greedy string), for if
* you wish to reuse it.
*
* @return default arguments node
*/
public ArgumentCommandNode<S, String> getDefaultArgs() {
return this.defaultArgs;
}
/**
* Gets the {@link LiteralCommandNode} to be registered for the {@link Command}.
*
* @return the {@link LiteralCommandNode}
*/
public LiteralCommandNode<S> getLiteral() {
return this.literal;
}
/**
* Sets the {@link LiteralCommandNode} used to register this command. The default literal is mutable, so
* this is primarily if you want to completely replace the object.
*
* @param literal new node
*/
public void setLiteral(LiteralCommandNode<S> literal) {
this.literal = literal;
}
/**
* Gets whether this command should is treated as "raw".
*
* @see #setRawCommand(boolean)
* @return whether this command is treated as "raw"
*/
public boolean isRawCommand() {
return this.rawCommand;
}
/**
* Sets whether this command should be treated as "raw".
*
* <p>A "raw" command will only use the node provided by this event for
* sending the command tree to the client. For execution purposes, the default
* greedy string execution of a standard Bukkit {@link Command} is used.</p>
*
* <p>On older versions of Paper, this was the default and only behavior of this
* event.</p>
*
* @param rawCommand whether this command should be treated as "raw"
*/
public void setRawCommand(final boolean rawCommand) {
this.rawCommand = rawCommand;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isCancelled() {
return this.cancelled;
}
/**
* Cancels registering this command to Brigadier, but will remain in Bukkit Command Map. Can be used to hide a
* command from all players.
*
* {@inheritDoc}
*/
@Override
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
@NotNull
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
}