From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Callahan <mr.callahhh@gmail.com>
Date: Wed, 8 Apr 2020 02:42:14 -0500
Subject: [PATCH] Async command map building

This adds a custom pool inorder to make sure that they are closed
without much though, as it doesn't matter if the client is not sent
commands if the server is restarting. Using the default async pool caused issues to arise
due to the shutdown logic generally being much later.

diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java
index 72756ef14b8ec8afd80313b9f6aaf76722cb18cf..a05aea8561ac102476ee1b3068942b095950a86a 100644
--- a/src/main/java/net/minecraft/commands/Commands.java
+++ b/src/main/java/net/minecraft/commands/Commands.java
@@ -450,6 +450,24 @@ public class Commands {
         if ( org.spigotmc.SpigotConfig.tabComplete < 0 ) return; // Spigot
         // CraftBukkit start
         // Register Vanilla commands into builtRoot as before
+        // Paper start - Perf: Async command map building
+        COMMAND_SENDING_POOL.execute(() -> {
+                this.sendAsync(player);
+        });
+    }
+
+    public static final java.util.concurrent.ThreadPoolExecutor COMMAND_SENDING_POOL = new java.util.concurrent.ThreadPoolExecutor(
+        0, 2, 60L, java.util.concurrent.TimeUnit.SECONDS,
+        new java.util.concurrent.LinkedBlockingQueue<>(),
+        new com.google.common.util.concurrent.ThreadFactoryBuilder()
+            .setNameFormat("Paper Async Command Builder Thread Pool - %1$d")
+            .setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(net.minecraft.server.MinecraftServer.LOGGER))
+            .build(),
+        new java.util.concurrent.ThreadPoolExecutor.DiscardPolicy()
+    );
+
+    private void sendAsync(ServerPlayer player) {
+        // Paper end - Perf: Async command map building
         Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> map = Maps.newIdentityHashMap(); // Use identity to prevent aliasing issues
         RootCommandNode vanillaRoot = new RootCommandNode();
 
@@ -467,7 +485,14 @@ public class Commands {
         for (CommandNode node : rootcommandnode.getChildren()) {
             bukkit.add(node.getName());
         }
+        // Paper start - Perf: Async command map building
+        net.minecraft.server.MinecraftServer.getServer().execute(() -> {
+           runSync(player, bukkit, rootcommandnode);
+        });
+    }
 
+    private void runSync(ServerPlayer player, Collection<String> bukkit, RootCommandNode<SharedSuggestionProvider> rootcommandnode) {
+        // Paper end - Perf: Async command map building
         PlayerCommandSendEvent event = new PlayerCommandSendEvent(player.getBukkitEntity(), new LinkedHashSet<>(bukkit));
         event.getPlayer().getServer().getPluginManager().callEvent(event);
 
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 2e96a4c61f9053c20060e9f45a313a6135ea4f16..df5b553b8d1525f14dbb88ea86e1bf9d9c9e6950 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -931,6 +931,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
         }
 
         MinecraftServer.LOGGER.info("Stopping server");
+        Commands.COMMAND_SENDING_POOL.shutdownNow(); // Paper - Perf: Async command map building; Shutdown and don't bother finishing
         MinecraftTimings.stopServer(); // Paper
         // CraftBukkit start
         if (this.server != null) {
