From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sun, 19 Apr 2020 18:15:29 -0400
Subject: [PATCH] Implement 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/build.gradle.kts b/build.gradle.kts
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -0,0 +0,0 @@ val alsoShade: Configuration by configurations.creating
 
 dependencies {
     implementation(project(":paper-api"))
+    implementation(project(":paper-mojangapi"))
     // Paper start
     implementation("org.jline:jline-terminal-jansi:3.21.0")
     implementation("net.minecrell:terminalconsoleappender:1.3.0")
diff --git a/src/main/java/com/mojang/brigadier/exceptions/CommandSyntaxException.java b/src/main/java/com/mojang/brigadier/exceptions/CommandSyntaxException.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/com/mojang/brigadier/exceptions/CommandSyntaxException.java
+++ b/src/main/java/com/mojang/brigadier/exceptions/CommandSyntaxException.java
@@ -0,0 +0,0 @@ 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
     public static final int CONTEXT_AMOUNT = 10;
     public static boolean ENABLE_COMMAND_STACK_TRACES = true;
     public static BuiltInExceptionProvider BUILT_IN_EXCEPTIONS = new BuiltInExceptions();
@@ -0,0 +0,0 @@ public class CommandSyntaxException extends Exception {
     public int getCursor() {
         return cursor;
     }
+
+    // Paper start
+    @Override
+    public @org.jetbrains.annotations.Nullable net.kyori.adventure.text.Component componentMessage() {
+        return io.papermc.paper.brigadier.PaperBrigadier.componentFromMessage(this.message);
+    }
+    // Paper end
 }
diff --git a/src/main/java/com/mojang/brigadier/tree/CommandNode.java b/src/main/java/com/mojang/brigadier/tree/CommandNode.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/com/mojang/brigadier/tree/CommandNode.java
+++ b/src/main/java/com/mojang/brigadier/tree/CommandNode.java
@@ -0,0 +0,0 @@ public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
     private final RedirectModifier<S> modifier;
     private final boolean forks;
     private Command<S> command;
+    public LiteralCommandNode<CommandSourceStack> clientNode = null; // Paper
     // 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/commands/CommandSourceStack.java
+++ b/src/main/java/net/minecraft/commands/CommandSourceStack.java
@@ -0,0 +0,0 @@ import net.minecraft.world.phys.Vec2;
 import net.minecraft.world.phys.Vec3;
 import com.mojang.brigadier.tree.CommandNode; // CraftBukkit
 
-public class CommandSourceStack implements SharedSuggestionProvider {
+public class CommandSourceStack implements SharedSuggestionProvider, com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource { // Paper
 
     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"));
@@ -0,0 +0,0 @@ public class CommandSourceStack implements SharedSuggestionProvider {
         return this.textName;
     }
 
+    // Paper start
+    @Override
+    public org.bukkit.entity.Entity getBukkitEntity() {
+        return getEntity() != null ? getEntity().getBukkitEntity() : null;
+    }
+
+    @Override
+    public org.bukkit.World getBukkitWorld() {
+        return getLevel() != null ? getLevel().getWorld() : null;
+    }
+
+    @Override
+    public org.bukkit.Location getBukkitLocation() {
+        Vec3 pos = getPosition();
+        org.bukkit.World world = getBukkitWorld();
+        Vec2 rot = getRotation();
+        return world != null && pos != null ? new org.bukkit.Location(world, pos.x, pos.y, pos.z, rot != null ? rot.y : 0, rot != null ? rot.x : 0) : null;
+    }
+    // Paper end
+
     @Override
     public boolean hasPermission(int level) {
         // CraftBukkit start
diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/commands/Commands.java
+++ b/src/main/java/net/minecraft/commands/Commands.java
@@ -0,0 +0,0 @@ public class Commands {
             bukkit.add(node.getName());
         }
         // Paper start - Async command map building
+        new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent<CommandSourceStack>(player.getBukkitEntity(), (RootCommandNode) rootcommandnode, false).callEvent(); // Paper
         net.minecraft.server.MinecraftServer.getServer().execute(() -> {
            runSync(player, bukkit, rootcommandnode);
         });
@@ -0,0 +0,0 @@ public class Commands {
 
     private void runSync(ServerPlayer player, Collection<String> bukkit, RootCommandNode<SharedSuggestionProvider> rootcommandnode) {
         // Paper end - Async command map building
+        new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent<CommandSourceStack>(player.getBukkitEntity(), (RootCommandNode) rootcommandnode, false).callEvent(); // Paper
         PlayerCommandSendEvent event = new PlayerCommandSendEvent(player.getBukkitEntity(), new LinkedHashSet<>(bukkit));
         event.getPlayer().getServer().getPluginManager().callEvent(event);
 
@@ -0,0 +0,0 @@ public class Commands {
 
         while (iterator.hasNext()) {
             CommandNode<CommandSourceStack> commandnode2 = (CommandNode) iterator.next();
+            // Paper start
+            if (commandnode2.clientNode != null) {
+                commandnode2 = commandnode2.clientNode;
+            }
+            // Paper end
             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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
                         ParseResults<CommandSourceStack> 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 [<args>] from showing for plugins with nothing more to offer
-                            this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestions));
+                            // Paper start - Brigadier API
+                            com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent suggestEvent = new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent(this.getCraftPlayer(), suggestions, command);
+                            suggestEvent.setCancelled(suggestions.isEmpty());
+                            if (!suggestEvent.callEvent()) return;
+                            this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestEvent.getSuggestions()));
+                            // Paper end - Brigadier API
                         });
                     });
                 }
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
                         builder.suggest(completion.suggestion(), PaperAdventure.asVanilla(completion.tooltip()));
                     }
                 });
-                player.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, command);
+                suggestEvent.setCancelled(suggestions.isEmpty());
+                if (!suggestEvent.callEvent()) return;
+                this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestEvent.getSuggestions()));
+                // Paper end - Brigadier API
             }
         });
         // Paper end - async tab completion
diff --git a/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java
+++ b/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java
@@ -0,0 +0,0 @@ import org.bukkit.command.CommandException;
 import org.bukkit.command.CommandSender;
 import org.bukkit.craftbukkit.CraftServer;
 
-public class BukkitCommandWrapper implements com.mojang.brigadier.Command<CommandSourceStack>, Predicate<CommandSourceStack>, SuggestionProvider<CommandSourceStack> {
+public class BukkitCommandWrapper implements com.mojang.brigadier.Command<CommandSourceStack>, Predicate<CommandSourceStack>, SuggestionProvider<CommandSourceStack>, com.destroystokyo.paper.brigadier.BukkitBrigadierCommand<CommandSourceStack> { // Paper
 
     private final CraftServer server;
     private final Command command;
@@ -0,0 +0,0 @@ public class BukkitCommandWrapper implements com.mojang.brigadier.Command<Comman
     }
 
     public LiteralCommandNode<CommandSourceStack> register(CommandDispatcher<CommandSourceStack> dispatcher, String label) {
-        return dispatcher.register(
-                LiteralArgumentBuilder.<CommandSourceStack>literal(label).requires(this).executes(this)
-                .then(RequiredArgumentBuilder.<CommandSourceStack, String>argument("args", StringArgumentType.greedyString()).suggests(this).executes(this))
-        );
+        // Paper start - Expose Brigadier to Paper-MojangAPI
+        com.mojang.brigadier.tree.RootCommandNode<CommandSourceStack> root = dispatcher.getRoot();
+        LiteralCommandNode<CommandSourceStack> literal = LiteralArgumentBuilder.<CommandSourceStack>literal(label).requires(this).executes(this).build();
+        LiteralCommandNode<CommandSourceStack> defaultNode = literal;
+        com.mojang.brigadier.tree.ArgumentCommandNode<CommandSourceStack, String> defaultArgs = RequiredArgumentBuilder.<CommandSourceStack, String>argument("args", StringArgumentType.greedyString()).suggests(this).executes(this).build();
+        literal.addChild(defaultArgs);
+        com.destroystokyo.paper.event.brigadier.CommandRegisteredEvent<CommandSourceStack> 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