From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Aikar Date: Sun, 19 Apr 2020 18:15:29 -0400 Subject: [PATCH] Brigadier Mojang API Adds AsyncPlayerSendCommandsEvent - Allows modifying on a per command basis what command data they see. Adds CommandRegisteredEvent - Allows manipulating the CommandNode to add more children/metadata for the client diff --git a/src/main/java/com/mojang/brigadier/exceptions/CommandSyntaxException.java b/src/main/java/com/mojang/brigadier/exceptions/CommandSyntaxException.java index 3370731ee064d2693b972a0765c13dd4fd69f66a..09d486a05179b9d878e1c33725b4e614c3544da9 100644 --- a/src/main/java/com/mojang/brigadier/exceptions/CommandSyntaxException.java +++ b/src/main/java/com/mojang/brigadier/exceptions/CommandSyntaxException.java @@ -5,7 +5,7 @@ package com.mojang.brigadier.exceptions; import com.mojang.brigadier.Message; -public class CommandSyntaxException extends Exception { +public class CommandSyntaxException extends Exception implements net.kyori.adventure.util.ComponentMessageThrowable { // Paper - Brigadier API public static final int CONTEXT_AMOUNT = 10; public static boolean ENABLE_COMMAND_STACK_TRACES = true; public static BuiltInExceptionProvider BUILT_IN_EXCEPTIONS = new BuiltInExceptions(); @@ -73,4 +73,11 @@ public class CommandSyntaxException extends Exception { public int getCursor() { return cursor; } + + // Paper start - Brigadier API + @Override + public @org.jetbrains.annotations.Nullable net.kyori.adventure.text.Component componentMessage() { + return io.papermc.paper.brigadier.PaperBrigadier.componentFromMessage(this.message); + } + // Paper end - Brigadier API } diff --git a/src/main/java/com/mojang/brigadier/tree/CommandNode.java b/src/main/java/com/mojang/brigadier/tree/CommandNode.java index da6250df1c5f3385b683cffde47754bca4606f5e..14ccd0c8f721e9be7dca8a5dcb8ef95b5cd82731 100644 --- a/src/main/java/com/mojang/brigadier/tree/CommandNode.java +++ b/src/main/java/com/mojang/brigadier/tree/CommandNode.java @@ -34,6 +34,7 @@ public abstract class CommandNode implements Comparable> { private final RedirectModifier modifier; private final boolean forks; private Command command; + public CommandNode clientNode; // Paper - Brigadier API // CraftBukkit start public void removeCommand(String name) { this.children.remove(name); diff --git a/src/main/java/net/minecraft/commands/CommandSourceStack.java b/src/main/java/net/minecraft/commands/CommandSourceStack.java index d9fc3c25bef251df6a53ee47ec224b07240a931c..2a22827f44dd0d524c22264447959a6979e9f0de 100644 --- a/src/main/java/net/minecraft/commands/CommandSourceStack.java +++ b/src/main/java/net/minecraft/commands/CommandSourceStack.java @@ -45,7 +45,7 @@ import net.minecraft.world.phys.Vec2; import net.minecraft.world.phys.Vec3; import com.mojang.brigadier.tree.CommandNode; // CraftBukkit -public class CommandSourceStack implements ExecutionCommandSource, SharedSuggestionProvider { +public class CommandSourceStack implements ExecutionCommandSource, SharedSuggestionProvider, com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource { // Paper - Brigadier API public static final SimpleCommandExceptionType ERROR_NOT_PLAYER = new SimpleCommandExceptionType(Component.translatable("permissions.requires.player")); public static final SimpleCommandExceptionType ERROR_NOT_ENTITY = new SimpleCommandExceptionType(Component.translatable("permissions.requires.entity")); @@ -170,6 +170,26 @@ public class CommandSourceStack implements ExecutionCommandSource(player.getBukkitEntity(), (RootCommandNode) rootcommandnode, false).callEvent(); // Paper - Brigadier API net.minecraft.server.MinecraftServer.getServer().execute(() -> { runSync(player, bukkit, rootcommandnode); }); @@ -495,6 +496,7 @@ public class Commands { private void runSync(ServerPlayer player, Collection bukkit, RootCommandNode rootcommandnode) { // Paper end - Perf: Async command map building + new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent(player.getBukkitEntity(), (RootCommandNode) rootcommandnode, true).callEvent(); // Paper - Brigadier API PlayerCommandSendEvent event = new PlayerCommandSendEvent(player.getBukkitEntity(), new LinkedHashSet<>(bukkit)); event.getPlayer().getServer().getPluginManager().callEvent(event); @@ -513,6 +515,11 @@ public class Commands { while (iterator.hasNext()) { CommandNode commandnode2 = (CommandNode) iterator.next(); + // Paper start - Brigadier API + if (commandnode2.clientNode != null) { + commandnode2 = commandnode2.clientNode; + } + // Paper end - Brigadier API if ( !org.spigotmc.SpigotConfig.sendNamespaced && commandnode2.getName().contains( ":" ) ) continue; // Spigot if (commandnode2.canUse(source)) { diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java index 3b3384fcbcb66a87dd50dece6bdac558491f66f0..04a9c48b9a9895ffe6ec0721bdfafb1b524bf386 100644 --- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java @@ -766,19 +766,34 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl builder.suggest(completion.suggestion(), PaperAdventure.asVanilla(completion.tooltip())); } } - this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), builder.buildFuture().join())); + // Paper start - Brigadier API + com.mojang.brigadier.suggestion.Suggestions suggestions = builder.buildFuture().join(); + com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent suggestEvent = new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent(this.getCraftPlayer(), suggestions, packet.getCommand()); + suggestEvent.setCancelled(suggestions.isEmpty()); + if (suggestEvent.callEvent()) { + this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), limitTo(suggestEvent.getSuggestions(), ServerGamePacketListenerImpl.MAX_COMMAND_SUGGESTIONS))); + } + // Paper end - Brigadier API } } + // Paper start - brig API + private static Suggestions limitTo(final Suggestions suggestions, final int size) { + return suggestions.getList().size() <= size ? suggestions : new Suggestions(suggestions.getRange(), suggestions.getList().subList(0, size)); + } + // Paper end - brig API private void sendServerSuggestions(final ServerboundCommandSuggestionPacket packet, final StringReader stringreader) { // Paper end - AsyncTabCompleteEvent ParseResults parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack()); this.server.getCommands().getDispatcher().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> { - if (suggestions.isEmpty()) return; // CraftBukkit - don't send through empty suggestions - prevents [] from showing for plugins with nothing more to offer - Suggestions suggestions1 = suggestions.getList().size() <= 1000 ? suggestions : new Suggestions(suggestions.getRange(), suggestions.getList().subList(0, 1000)); - - this.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestions1)); + // Paper start - Brigadier API + com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent suggestEvent = new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent(this.getCraftPlayer(), suggestions, packet.getCommand()); + suggestEvent.setCancelled(suggestions.isEmpty()); + if (suggestEvent.callEvent()) { + this.send(new ClientboundCommandSuggestionsPacket(packet.getId(), limitTo(suggestEvent.getSuggestions(), ServerGamePacketListenerImpl.MAX_COMMAND_SUGGESTIONS))); + } + // Paper end - Brigadier API }); } diff --git a/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java index 83d81b9371902b0302d13e53b31c15fac4e67966..d113e54a30db16e2ad955170df6030d15de530d6 100644 --- a/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java +++ b/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java @@ -20,7 +20,7 @@ import org.bukkit.command.CommandException; import org.bukkit.command.CommandSender; import org.bukkit.craftbukkit.CraftServer; -public class BukkitCommandWrapper implements com.mojang.brigadier.Command, Predicate, SuggestionProvider { +public class BukkitCommandWrapper implements com.mojang.brigadier.Command, Predicate, SuggestionProvider, com.destroystokyo.paper.brigadier.BukkitBrigadierCommand { // Paper private final CraftServer server; private final Command command; @@ -31,10 +31,24 @@ public class BukkitCommandWrapper implements com.mojang.brigadier.Command register(CommandDispatcher dispatcher, String label) { - return dispatcher.register( - LiteralArgumentBuilder.literal(label).requires(this).executes(this) - .then(RequiredArgumentBuilder.argument("args", StringArgumentType.greedyString()).suggests(this).executes(this)) - ); + // Paper start - Expose Brigadier to Paper-MojangAPI + com.mojang.brigadier.tree.RootCommandNode root = dispatcher.getRoot(); + LiteralCommandNode literal = LiteralArgumentBuilder.literal(label).requires(this).executes(this).build(); + LiteralCommandNode defaultNode = literal; + com.mojang.brigadier.tree.ArgumentCommandNode defaultArgs = RequiredArgumentBuilder.argument("args", StringArgumentType.greedyString()).suggests(this).executes(this).build(); + literal.addChild(defaultArgs); + com.destroystokyo.paper.event.brigadier.CommandRegisteredEvent event = new com.destroystokyo.paper.event.brigadier.CommandRegisteredEvent<>(label, this, this.command, root, literal, defaultArgs); + if (!event.callEvent()) { + return null; + } + literal = event.getLiteral(); + if (event.isRawCommand()) { + defaultNode.clientNode = literal; + literal = defaultNode; + } + root.addChild(literal); + return literal; + // Paper end } @Override