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/src/main/java/net/minecraft/server/CommandDispatcher.java b/src/main/java/net/minecraft/server/CommandDispatcher.java
index 4b1ea9bc39191e01f83577c7bad128cf1ab9612f..22d748008d24fd6ed7cd8c4914e2ceb378f32c95 100644
--- a/src/main/java/net/minecraft/server/CommandDispatcher.java
+++ b/src/main/java/net/minecraft/server/CommandDispatcher.java
@@ -270,6 +270,7 @@ public class CommandDispatcher {
             bukkit.add(node.getName());
         }
         // Paper start - Async command map building
+        new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent<CommandListenerWrapper>(entityplayer.getBukkitEntity(), (RootCommandNode) rootcommandnode, false).callEvent(); // Paper
         MinecraftServer.getServer().execute(() -> {
            runSync(entityplayer, bukkit, rootcommandnode);
         });
@@ -277,6 +278,7 @@ public class CommandDispatcher {
 
     private void runSync(EntityPlayer entityplayer, Collection<String> bukkit, RootCommandNode<ICompletionProvider> rootcommandnode) {
         // Paper end - Async command map building
+        new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent<CommandListenerWrapper>(entityplayer.getBukkitEntity(), (RootCommandNode) rootcommandnode, false).callEvent(); // Paper
         PlayerCommandSendEvent event = new PlayerCommandSendEvent(entityplayer.getBukkitEntity(), new LinkedHashSet<>(bukkit));
         event.getPlayer().getServer().getPluginManager().callEvent(event);
 
diff --git a/src/main/java/net/minecraft/server/CommandListenerWrapper.java b/src/main/java/net/minecraft/server/CommandListenerWrapper.java
index 54a1988341a4a6e80ab40624280b7c92532d5db6..7073d697a5d35b9b72ea05d5608438ac3e54c20d 100644
--- a/src/main/java/net/minecraft/server/CommandListenerWrapper.java
+++ b/src/main/java/net/minecraft/server/CommandListenerWrapper.java
@@ -16,7 +16,7 @@ import java.util.function.BinaryOperator;
 import java.util.stream.Stream;
 import javax.annotation.Nullable;
 
-public class CommandListenerWrapper implements ICompletionProvider {
+public class CommandListenerWrapper implements ICompletionProvider, com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource { // Paper
 
     public static final SimpleCommandExceptionType a = new SimpleCommandExceptionType(new ChatMessage("permissions.requires.player"));
     public static final SimpleCommandExceptionType b = new SimpleCommandExceptionType(new ChatMessage("permissions.requires.entity"));
@@ -128,6 +128,25 @@ public class CommandListenerWrapper implements ICompletionProvider {
         return this.g;
     }
 
+    // Paper start
+    @Override
+    public org.bukkit.entity.Entity getBukkitEntity() {
+        return getEntity() != null ? getEntity().getBukkitEntity() : null;
+    }
+
+    @Override
+    public org.bukkit.World getBukkitWorld() {
+        return getWorld() != null ? getWorld().getWorld() : null;
+    }
+
+    @Override
+    public org.bukkit.Location getBukkitLocation() {
+        Vec3D pos = getPosition();
+        org.bukkit.World world = getBukkitWorld();
+        return world != null && pos != null ? new org.bukkit.Location(world, pos.x, pos.y, pos.z) : null;
+    }
+    // Paper end
+
     @Override
     public boolean hasPermission(int i) {
         // CraftBukkit start
diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java
index 507c844e7aa43b6bdf5d2fff08c271ab025ba926..48d72cd722e1d8207c9eaa2d4d7970af299b8d0d 100644
--- a/src/main/java/net/minecraft/server/PlayerConnection.java
+++ b/src/main/java/net/minecraft/server/PlayerConnection.java
@@ -579,8 +579,12 @@ public class PlayerConnection implements PacketListenerPlayIn {
                     ParseResults<CommandListenerWrapper> parseresults = this.minecraftServer.getCommandDispatcher().a().parse(stringreader, this.player.getCommandListener());
 
                     this.minecraftServer.getCommandDispatcher().a().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.networkManager.sendPacket(new PacketPlayOutTabComplete(packetplayintabcomplete.b(), suggestions));
+                        // Paper start
+                        com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent suggestEvent = new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent(this.getPlayer(), suggestions, buffer);
+                        suggestEvent.setCancelled(suggestions.isEmpty());
+                        if (!suggestEvent.callEvent()) return;
+                        this.networkManager.sendPacket(new PacketPlayOutTabComplete(packetplayintabcomplete.b(), (com.mojang.brigadier.suggestion.Suggestions) suggestEvent.getSuggestions())); // CraftBukkit - decompile error // Paper
+                        // Paper end
                     });
                 });
             }
@@ -589,7 +593,11 @@ public class PlayerConnection implements PacketListenerPlayIn {
 
             builder = builder.createOffset(builder.getInput().lastIndexOf(' ') + 1);
             completions.forEach(builder::suggest);
-            player.playerConnection.sendPacket(new PacketPlayOutTabComplete(packetplayintabcomplete.b(), builder.buildFuture().join()));
+            com.mojang.brigadier.suggestion.Suggestions suggestions = builder.buildFuture().join();
+            com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent suggestEvent = new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent(this.getPlayer(), suggestions, buffer);
+            suggestEvent.setCancelled(suggestions.isEmpty());
+            if (!suggestEvent.callEvent()) return;
+            this.networkManager.sendPacket(new PacketPlayOutTabComplete(packetplayintabcomplete.b(), suggestEvent.getSuggestions()));
         }
         // 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 5f33c9e52ac51486d4b22a6dcbfac7f46e0412bb..e16ecdea7d27424053b3f21378af054b2f808eca 100644
--- a/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java
+++ b/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java
@@ -17,7 +17,7 @@ import net.minecraft.server.CommandListenerWrapper;
 import org.bukkit.command.Command;
 import org.bukkit.craftbukkit.CraftServer;
 
-public class BukkitCommandWrapper implements com.mojang.brigadier.Command<CommandListenerWrapper>, Predicate<CommandListenerWrapper>, SuggestionProvider<CommandListenerWrapper> {
+public class BukkitCommandWrapper implements com.mojang.brigadier.Command<CommandListenerWrapper>, Predicate<CommandListenerWrapper>, SuggestionProvider<CommandListenerWrapper>, com.destroystokyo.paper.brigadier.BukkitBrigadierCommand<CommandListenerWrapper> { // Paper
 
     private final CraftServer server;
     private final Command command;
@@ -28,10 +28,19 @@ public class BukkitCommandWrapper implements com.mojang.brigadier.Command<Comman
     }
 
     public LiteralCommandNode<CommandListenerWrapper> register(CommandDispatcher<CommandListenerWrapper> dispatcher, String label) {
-        return dispatcher.register(
-                LiteralArgumentBuilder.<CommandListenerWrapper>literal(label).requires(this).executes(this)
-                .then(RequiredArgumentBuilder.<CommandListenerWrapper, String>argument("args", StringArgumentType.greedyString()).suggests(this).executes(this))
-        );
+        // Paper start - Expose Brigadier to Paper-MojangAPI
+        com.mojang.brigadier.tree.RootCommandNode<CommandListenerWrapper> root = dispatcher.getRoot();
+        LiteralCommandNode<CommandListenerWrapper> literal = LiteralArgumentBuilder.<CommandListenerWrapper>literal(label).requires(this).executes(this).build();
+        com.mojang.brigadier.tree.ArgumentCommandNode<CommandListenerWrapper, String> defaultArgs = RequiredArgumentBuilder.<CommandListenerWrapper, String>argument("args", StringArgumentType.greedyString()).suggests(this).executes(this).build();
+        literal.addChild(defaultArgs);
+        com.destroystokyo.paper.event.brigadier.CommandRegisteredEvent<CommandListenerWrapper> event = new com.destroystokyo.paper.event.brigadier.CommandRegisteredEvent<>(label, this, this.command, root, literal, defaultArgs);
+        if (!event.callEvent()) {
+            return null;
+        }
+        literal = event.getLiteral();
+        root.addChild(literal);
+        return literal;
+        // Paper end
     }
 
     @Override