diff --git a/patches/api/0390-Brigadier-based-command-API.patch b/patches/api/0390-Brigadier-based-command-API.patch index c8f9ea4395a3d..9f20fcea46146 100644 --- a/patches/api/0390-Brigadier-based-command-API.patch +++ b/patches/api/0390-Brigadier-based-command-API.patch @@ -379,139 +379,6 @@ index 0000000000000000000000000000000000000000..8be98ea599c864782198d0deddb22a68 + + +} -diff --git a/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitCommandNode.java b/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitCommandNode.java -new file mode 100644 -index 0000000000000000000000000000000000000000..5c56e986ffe037498b339714c6c1db81452ece5e ---- /dev/null -+++ b/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitCommandNode.java -@@ -0,0 +1,127 @@ -+package io.papermc.paper.command.brigadier.bukkit; -+ -+import co.aikar.timings.Timing; -+import com.mojang.brigadier.arguments.StringArgumentType; -+import com.mojang.brigadier.builder.RequiredArgumentBuilder; -+import com.mojang.brigadier.context.CommandContext; -+import com.mojang.brigadier.exceptions.CommandSyntaxException; -+import com.mojang.brigadier.suggestion.SuggestionProvider; -+import com.mojang.brigadier.suggestion.Suggestions; -+import com.mojang.brigadier.suggestion.SuggestionsBuilder; -+import com.mojang.brigadier.tree.LiteralCommandNode; -+import io.papermc.paper.command.brigadier.CommandSourceStack; -+import org.bukkit.Bukkit; -+import org.bukkit.ChatColor; -+import org.bukkit.Location; -+import org.bukkit.command.Command; -+import org.bukkit.command.CommandException; -+import org.bukkit.command.CommandSender; -+import org.jetbrains.annotations.ApiStatus; -+ -+import java.util.Arrays; -+import java.util.List; -+import java.util.concurrent.CompletableFuture; -+import java.util.logging.Level; -+ -+@ApiStatus.Internal -+public class BukkitCommandNode extends LiteralCommandNode { -+ -+ private final Command command; -+ -+ private BukkitCommandNode(String literal, Command command, BukkitBrigCommand bukkitBrigCommand) { -+ super( -+ literal, bukkitBrigCommand, s -> command.testPermissionSilent(s.getBukkitSender()), -+ null, null, false -+ ); -+ this.command = command; -+ } -+ -+ public static BukkitCommandNode of(String name, Command command) { -+ BukkitBrigCommand bukkitBrigCommand = new BukkitBrigCommand(command, name); -+ BukkitCommandNode commandNode = new BukkitCommandNode(name, command, bukkitBrigCommand); -+ commandNode.addChild( -+ RequiredArgumentBuilder.argument("args", StringArgumentType.greedyString()).suggests(BukkitBrigSuggestionProvider.INSTANCE).executes(bukkitBrigCommand).build() -+ ); -+ -+ return commandNode; -+ } -+ -+ public Command getBukkitCommand() { -+ return this.command; -+ } -+ -+ static class BukkitBrigCommand implements com.mojang.brigadier.Command { -+ -+ private final org.bukkit.command.Command command; -+ private final String literal; -+ -+ BukkitBrigCommand(org.bukkit.command.Command command, String literal) { -+ this.command = command; -+ this.literal = literal; -+ } -+ -+ @Override -+ public int run(CommandContext context) throws CommandSyntaxException { -+ CommandSender sender = context.getSource().getBukkitSender(); -+ -+ // Plugins do weird things to workaround normal registration -+ if (this.command.timings == null) { -+ this.command.timings = co.aikar.timings.TimingsManager.getCommandTiming(null, this.command); -+ } -+ -+ String[] args = org.apache.commons.lang3.StringUtils.split(context.getInput(), ' '); // fix adjacent spaces (from console/plugins) causing empty array elements -+ -+ try (Timing ignored = this.command.timings.startTiming()) { -+ // Note: we don't return the result of target.execute as thats success / failure, we return handled (true) or not handled (false) -+ this.command.execute(sender, this.literal, Arrays.copyOfRange(args, 1, args.length)); -+ } -+ -+ // return true as command was handled -+ return 1; -+ } -+ } -+ -+ static class BukkitBrigSuggestionProvider implements SuggestionProvider { -+ -+ public static final SuggestionProvider INSTANCE = new BukkitBrigSuggestionProvider(); -+ -+ @Override -+ public CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) throws CommandSyntaxException { -+ // Paper start -+ org.bukkit.command.CommandSender sender = context.getSource().getBukkitSender(); -+ if (!(sender instanceof org.bukkit.entity.Player player)) { -+ return CompletableFuture.completedFuture(builder.build()); -+ } -+ -+ String message = builder.getInput(); -+ List results = null; -+ Location pos = context.getSource().getBukkitLocation(); -+ try { -+ if (message.startsWith("/")) { -+ // Trim leading '/' if present (won't always be present in command blocks) -+ message = message.substring(1); -+ } -+ if (pos == null) { -+ results = Bukkit.getCommandMap().tabComplete(player, message); -+ } else { -+ results = Bukkit.getCommandMap().tabComplete(player, message, pos.clone()); -+ } -+ } catch (CommandException ex) { -+ player.sendMessage(ChatColor.RED + "An internal error occurred while attempting to tab-complete this command"); -+ Bukkit.getServer().getLogger().log(Level.SEVERE, "Exception when " + player.getName() + " attempted to tab complete " + message, ex); -+ } -+ -+ // Paper end -+ -+ // Defaults to sub nodes, but we have just one giant args node, so offset accordingly -+ builder = builder.createOffset(builder.getInput().lastIndexOf(' ') + 1); -+ -+ for (String s : results) { -+ builder.suggest(s); -+ } -+ -+ return builder.buildFuture(); -+ } -+ } -+ -+} diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java index 92a1462261029e804da73da2743bbd68e57841e9..6f67001ee605400969514a074faf4c703a79bab1 100644 --- a/src/main/java/org/bukkit/Bukkit.java @@ -562,6 +429,19 @@ index 864c263bbd4dd6dd7c37a74b39b1a40a884d0731..d5c10484497a091c46923e186f0a075c public interface CommandMap { /** +diff --git a/src/main/java/org/bukkit/command/FormattedCommandAlias.java b/src/main/java/org/bukkit/command/FormattedCommandAlias.java +index 9d4f553c04784cca63901a56a7aea62a5cae1d72..abe256e1e45ce28036da4aa1586715bc8a1a3414 100644 +--- a/src/main/java/org/bukkit/command/FormattedCommandAlias.java ++++ b/src/main/java/org/bukkit/command/FormattedCommandAlias.java +@@ -117,7 +117,7 @@ public class FormattedCommandAlias extends Command { + index = formatString.indexOf('$', index); + } + +- return formatString; ++ return formatString.trim(); // Paper - Causes an extra space at the end, breaks with brig commands + } + + @NotNull diff --git a/src/main/java/org/bukkit/command/SimpleCommandMap.java b/src/main/java/org/bukkit/command/SimpleCommandMap.java index b8623575b1c1b565560c2dd6438190716845a652..499b2f33747c5dd960ff869aabcccb87ca4e3fdb 100644 --- a/src/main/java/org/bukkit/command/SimpleCommandMap.java diff --git a/patches/server/0928-Brigadier-based-command-API.patch b/patches/server/0928-Brigadier-based-command-API.patch index f7b39621789e4..b30f79fb56e5e 100644 --- a/patches/server/0928-Brigadier-based-command-API.patch +++ b/patches/server/0928-Brigadier-based-command-API.patch @@ -150,206 +150,6 @@ index 0000000000000000000000000000000000000000..857b63a474a51cb541d88ea6224202e9 + } + +} -diff --git a/src/main/java/io/papermc/paper/command/brigadier/BukkitBrigForwardingMap.java b/src/main/java/io/papermc/paper/command/brigadier/BukkitBrigForwardingMap.java -new file mode 100644 -index 0000000000000000000000000000000000000000..ee30d191f360076d4f89b34697b70178903cf4fd ---- /dev/null -+++ b/src/main/java/io/papermc/paper/command/brigadier/BukkitBrigForwardingMap.java -@@ -0,0 +1,194 @@ -+package io.papermc.paper.command.brigadier; -+ -+import com.mojang.brigadier.CommandDispatcher; -+import com.mojang.brigadier.tree.CommandNode; -+import com.mojang.brigadier.tree.LiteralCommandNode; -+import io.papermc.paper.command.brigadier.CommandSourceStack; -+import io.papermc.paper.command.brigadier.bukkit.BukkitCommandNode; -+import org.bukkit.Bukkit; -+import org.bukkit.command.Command; -+import org.jetbrains.annotations.ApiStatus; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+import java.util.ArrayList; -+import java.util.Collection; -+import java.util.HashSet; -+import java.util.List; -+import java.util.Map; -+import java.util.Set; -+ -+public class BukkitBrigForwardingMap implements Map { -+ -+ private final CommandDispatcher dispatcher = Bukkit.getServer().getCommandDispatcher(); -+ -+ @Override -+ public int size() { -+ return this.dispatcher.getRoot().getChildren().size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return this.size() != 0; -+ } -+ -+ @Override -+ public boolean containsKey(Object key) { -+ if (key == null) { -+ return false; -+ } -+ -+ // Do any children match? -+ for (CommandNode child : this.dispatcher.getRoot().getChildren()) { -+ if (child instanceof LiteralCommandNode literalCommandNode && literalCommandNode.getLiteral().equals(key)) { -+ return true; -+ } -+ } -+ -+ return false; -+ } -+ -+ @Override -+ public boolean containsValue(Object value) { -+ if (value == null) { -+ return false; -+ } -+ -+ for (CommandNode child : this.dispatcher.getRoot().getChildren()) { -+ // If child is a bukkit command node, we can convert it! -+ if (child instanceof BukkitCommandNode bukkitCommandNode) { -+ return bukkitCommandNode.equals(value); -+ } -+ } -+ -+ return false; -+ } -+ -+ @Override -+ public Command get(Object key) { -+ for (CommandNode child : this.dispatcher.getRoot().getChildren()) { -+ if (child instanceof LiteralCommandNode literalCommandNode && literalCommandNode.getLiteral().equals(key)) { -+ if (literalCommandNode instanceof BukkitCommandNode bukkitCommandNode) { -+ return bukkitCommandNode.getBukkitCommand(); -+ } -+ -+ return Bukkit.getUnsafe().wrapBrigNode(child); -+ } -+ } -+ -+ return null; -+ } -+ -+ @Nullable -+ @Override -+ public Command put(String key, Command value) { -+ this.dispatcher.getRoot().addChild(BukkitCommandNode.of(key, value)); -+ return null; -+ } -+ -+ @Override -+ public Command remove(Object key) { -+ Command old = this.get(key); -+ if (old != null) { -+ this.dispatcher.getRoot().removeCommand((String) key); -+ } -+ new NullPointerException().printStackTrace(); -+ -+ return old; -+ } -+ -+ @Override -+ public void putAll(@NotNull Map m) { -+ for (Entry entry : m.entrySet()) { -+ this.put(entry.getKey(), entry.getValue()); -+ } -+ } -+ -+ @Override -+ public void clear() { -+ this.dispatcher.getRoot().clearAll(); -+ } -+ -+ // TODO: Mutability? -+ -+ @NotNull -+ @Override -+ public Set keySet() { -+ Set keys = new HashSet<>(); -+ for (CommandNode child : this.dispatcher.getRoot().getChildren()) { -+ if (child instanceof LiteralCommandNode literalCommandNode) { -+ keys.add(literalCommandNode.getLiteral()); -+ } -+ } -+ -+ return keys; -+ } -+ -+ @NotNull -+ @Override -+ public Collection values() { -+ List commands = new ArrayList<>(); -+ -+ for (CommandNode child : this.dispatcher.getRoot().getChildren()) { -+ if (child instanceof LiteralCommandNode literalCommandNode) { -+ if (literalCommandNode instanceof BukkitCommandNode bukkitCommandNode) { -+ commands.add(bukkitCommandNode.getBukkitCommand()); -+ } else { -+ commands.add(Bukkit.getUnsafe().wrapBrigNode(child)); -+ } -+ } -+ } -+ -+ return commands; -+ } -+ -+ -+ @NotNull -+ @Override -+ public Set> entrySet() { -+ Set> commands = new HashSet<>(); -+ -+ for (CommandNode child : this.dispatcher.getRoot().getChildren()) { -+ if (child instanceof LiteralCommandNode literalCommandNode) { -+ if (literalCommandNode instanceof BukkitCommandNode bukkitCommandNode) { -+ commands.add(new Entry<>() { -+ @Override -+ public String getKey() { -+ return bukkitCommandNode.getName(); -+ } -+ -+ @Override -+ public Command getValue() { -+ return bukkitCommandNode.getBukkitCommand(); -+ } -+ -+ @Override -+ public Command setValue(Command value) { -+ return bukkitCommandNode.getBukkitCommand(); -+ } -+ }); -+ } else { -+ Command wrapped = Bukkit.getUnsafe().wrapBrigNode(child); -+ commands.add(new Entry<>() { -+ @Override -+ public String getKey() { -+ return child.getName(); -+ } -+ -+ @Override -+ public Command getValue() { -+ return wrapped; -+ } -+ -+ @Override -+ public Command setValue(Command value) { -+ return wrapped; -+ } -+ }); -+ } -+ } -+ } -+ -+ return commands; -+ } -+} diff --git a/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializerImpl.java b/src/main/java/io/papermc/paper/command/brigadier/MessageComponentSerializerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..48e55b763b0c92ada24e624b2fe0eb50d187229d @@ -397,7 +197,7 @@ index 0000000000000000000000000000000000000000..b40783d0c3dcd52d53a7bc8a56eca94e +} diff --git a/src/main/java/io/papermc/paper/command/brigadier/PaperBrigadier.java b/src/main/java/io/papermc/paper/command/brigadier/PaperBrigadier.java new file mode 100644 -index 0000000000000000000000000000000000000000..2b9ce12607a5c3d2872aa1cd1ab422abed3405f6 +index 0000000000000000000000000000000000000000..7a1d5677e45aa3f8f7f91a3983c607ed83261094 --- /dev/null +++ b/src/main/java/io/papermc/paper/command/brigadier/PaperBrigadier.java @@ -0,0 +1,28 @@ @@ -420,7 +220,7 @@ index 0000000000000000000000000000000000000000..2b9ce12607a5c3d2872aa1cd1ab422ab + + if (node instanceof LiteralCommandNode) { + if (node instanceof PluginCommandNode pluginCommandNode) { -+ return new VanillaCommandWrapper(pluginCommandNode.getName(), pluginCommandNode.getDescription(), pluginCommandNode.getUsageText(), List.of(), commands, node); ++ return new PluginVanillaCommandWrapper(pluginCommandNode.getName(), pluginCommandNode.getDescription(), pluginCommandNode.getUsageText(), List.of(), commands, node, pluginCommandNode.getPlugin()); + } else { + return new VanillaCommandWrapper(commands, node); + } @@ -469,6 +269,45 @@ index 0000000000000000000000000000000000000000..6a4eaf1ac9fea3abf5b422afc4fe06a3 + return this.getHandle().getBukkitSender(); + } +} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/PluginVanillaCommandWrapper.java b/src/main/java/io/papermc/paper/command/brigadier/PluginVanillaCommandWrapper.java +new file mode 100644 +index 0000000000000000000000000000000000000000..bdb7223453be2ed971941ab57e226133e42b76ec +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/PluginVanillaCommandWrapper.java +@@ -0,0 +1,33 @@ ++package io.papermc.paper.command.brigadier; ++ ++import com.mojang.brigadier.tree.CommandNode; ++import net.minecraft.commands.CommandSourceStack; ++import net.minecraft.commands.Commands; ++import org.bukkit.command.PluginIdentifiableCommand; ++import org.bukkit.craftbukkit.command.VanillaCommandWrapper; ++import org.bukkit.plugin.Plugin; ++import org.jetbrains.annotations.NotNull; ++ ++import java.util.List; ++ ++// Exists to that /help can show the plugin ++public class PluginVanillaCommandWrapper extends VanillaCommandWrapper implements PluginIdentifiableCommand { ++ ++ private final Plugin plugin; ++ ++ public PluginVanillaCommandWrapper(String name, String description, String usageMessage, List aliases, Commands dispatcher, CommandNode vanillaCommand, Plugin plugin) { ++ super(name, description, usageMessage, aliases, dispatcher, vanillaCommand); ++ this.plugin = plugin; ++ } ++ ++ @Override ++ public @NotNull Plugin getPlugin() { ++ return this.plugin; ++ } ++ ++ // Show in help menu! ++ @Override ++ public boolean isRegistered() { ++ return true; ++ } ++} diff --git a/src/main/java/io/papermc/paper/command/brigadier/argument/MessageArgumentImpl.java b/src/main/java/io/papermc/paper/command/brigadier/argument/MessageArgumentImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..1336b8125d0db3f5b288ce2f91e8ecfac54f6d26 @@ -602,6 +441,338 @@ index 0000000000000000000000000000000000000000..7a838bab16b33025b9a2eae8325f353a + contextBuilder.withNode(this, parsed.getRange()); + } +} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitBrigForwardingMap.java b/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitBrigForwardingMap.java +new file mode 100644 +index 0000000000000000000000000000000000000000..c8889e5ae6d7283eb80f40cd59b7e3d682ade0f5 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitBrigForwardingMap.java +@@ -0,0 +1,194 @@ ++package io.papermc.paper.command.brigadier.bukkit; ++ ++import com.mojang.brigadier.CommandDispatcher; ++import com.mojang.brigadier.tree.CommandNode; ++import com.mojang.brigadier.tree.LiteralCommandNode; ++import io.papermc.paper.command.brigadier.CommandSourceStack; ++import io.papermc.paper.command.brigadier.bukkit.BukkitCommandNode; ++import org.bukkit.Bukkit; ++import org.bukkit.command.Command; ++import org.jetbrains.annotations.ApiStatus; ++import org.jetbrains.annotations.NotNull; ++import org.jetbrains.annotations.Nullable; ++ ++import java.util.ArrayList; ++import java.util.Collection; ++import java.util.HashSet; ++import java.util.List; ++import java.util.Map; ++import java.util.Set; ++ ++public class BukkitBrigForwardingMap implements Map { ++ ++ private final CommandDispatcher dispatcher = Bukkit.getServer().getCommandDispatcher(); ++ ++ @Override ++ public int size() { ++ return this.dispatcher.getRoot().getChildren().size(); ++ } ++ ++ @Override ++ public boolean isEmpty() { ++ return this.size() != 0; ++ } ++ ++ @Override ++ public boolean containsKey(Object key) { ++ if (key == null) { ++ return false; ++ } ++ ++ // Do any children match? ++ for (CommandNode child : this.dispatcher.getRoot().getChildren()) { ++ if (child instanceof LiteralCommandNode literalCommandNode && literalCommandNode.getLiteral().equals(key)) { ++ return true; ++ } ++ } ++ ++ return false; ++ } ++ ++ @Override ++ public boolean containsValue(Object value) { ++ if (value == null) { ++ return false; ++ } ++ ++ for (CommandNode child : this.dispatcher.getRoot().getChildren()) { ++ // If child is a bukkit command node, we can convert it! ++ if (child instanceof BukkitCommandNode bukkitCommandNode) { ++ return bukkitCommandNode.equals(value); ++ } ++ } ++ ++ return false; ++ } ++ ++ @Override ++ public Command get(Object key) { ++ for (CommandNode child : this.dispatcher.getRoot().getChildren()) { ++ if (child instanceof LiteralCommandNode literalCommandNode && literalCommandNode.getLiteral().equals(key)) { ++ if (literalCommandNode instanceof BukkitCommandNode bukkitCommandNode) { ++ return bukkitCommandNode.getBukkitCommand(); ++ } ++ ++ return Bukkit.getUnsafe().wrapBrigNode(child); ++ } ++ } ++ ++ return null; ++ } ++ ++ @Nullable ++ @Override ++ public Command put(String key, Command value) { ++ this.dispatcher.getRoot().addChild(BukkitCommandNode.of(key, value)); ++ return null; ++ } ++ ++ @Override ++ public Command remove(Object key) { ++ Command old = this.get(key); ++ if (old != null) { ++ this.dispatcher.getRoot().removeCommand((String) key); ++ } ++ new NullPointerException().printStackTrace(); ++ ++ return old; ++ } ++ ++ @Override ++ public void putAll(@NotNull Map m) { ++ for (Entry entry : m.entrySet()) { ++ this.put(entry.getKey(), entry.getValue()); ++ } ++ } ++ ++ @Override ++ public void clear() { ++ this.dispatcher.getRoot().clearAll(); ++ } ++ ++ // TODO: Mutability? ++ ++ @NotNull ++ @Override ++ public Set keySet() { ++ Set keys = new HashSet<>(); ++ for (CommandNode child : this.dispatcher.getRoot().getChildren()) { ++ if (child instanceof LiteralCommandNode literalCommandNode) { ++ keys.add(literalCommandNode.getLiteral()); ++ } ++ } ++ ++ return keys; ++ } ++ ++ @NotNull ++ @Override ++ public Collection values() { ++ List commands = new ArrayList<>(); ++ ++ for (CommandNode child : this.dispatcher.getRoot().getChildren()) { ++ if (child instanceof LiteralCommandNode literalCommandNode) { ++ if (literalCommandNode instanceof BukkitCommandNode bukkitCommandNode) { ++ commands.add(bukkitCommandNode.getBukkitCommand()); ++ } else { ++ commands.add(Bukkit.getUnsafe().wrapBrigNode(child)); ++ } ++ } ++ } ++ ++ return commands; ++ } ++ ++ ++ @NotNull ++ @Override ++ public Set> entrySet() { ++ Set> commands = new HashSet<>(); ++ ++ for (CommandNode child : this.dispatcher.getRoot().getChildren()) { ++ if (child instanceof LiteralCommandNode literalCommandNode) { ++ if (literalCommandNode instanceof BukkitCommandNode bukkitCommandNode) { ++ commands.add(new Entry<>() { ++ @Override ++ public String getKey() { ++ return bukkitCommandNode.getName(); ++ } ++ ++ @Override ++ public Command getValue() { ++ return bukkitCommandNode.getBukkitCommand(); ++ } ++ ++ @Override ++ public Command setValue(Command value) { ++ return bukkitCommandNode.getBukkitCommand(); ++ } ++ }); ++ } else { ++ Command wrapped = Bukkit.getUnsafe().wrapBrigNode(child); ++ commands.add(new Entry<>() { ++ @Override ++ public String getKey() { ++ return child.getName(); ++ } ++ ++ @Override ++ public Command getValue() { ++ return wrapped; ++ } ++ ++ @Override ++ public Command setValue(Command value) { ++ return wrapped; ++ } ++ }); ++ } ++ } ++ } ++ ++ return commands; ++ } ++} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitCommandNode.java b/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitCommandNode.java +new file mode 100644 +index 0000000000000000000000000000000000000000..5d12c78f0f5e6f3cc49dc87ef814927e4af66be1 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitCommandNode.java +@@ -0,0 +1,126 @@ ++package io.papermc.paper.command.brigadier.bukkit; ++ ++import co.aikar.timings.Timing; ++import com.mojang.brigadier.arguments.StringArgumentType; ++import com.mojang.brigadier.builder.RequiredArgumentBuilder; ++import com.mojang.brigadier.context.CommandContext; ++import com.mojang.brigadier.exceptions.CommandSyntaxException; ++import com.mojang.brigadier.suggestion.SuggestionProvider; ++import com.mojang.brigadier.suggestion.Suggestions; ++import com.mojang.brigadier.suggestion.SuggestionsBuilder; ++import com.mojang.brigadier.tree.LiteralCommandNode; ++import io.papermc.paper.command.brigadier.CommandSourceStack; ++import org.bukkit.Bukkit; ++import org.bukkit.ChatColor; ++import org.bukkit.Location; ++import org.bukkit.command.Command; ++import org.bukkit.command.CommandException; ++import org.bukkit.command.CommandSender; ++import org.jetbrains.annotations.ApiStatus; ++ ++import java.util.Arrays; ++import java.util.List; ++import java.util.concurrent.CompletableFuture; ++import java.util.logging.Level; ++ ++public class BukkitCommandNode extends LiteralCommandNode { ++ ++ private final Command command; ++ ++ private BukkitCommandNode(String literal, Command command, BukkitBrigCommand bukkitBrigCommand) { ++ super( ++ literal, bukkitBrigCommand, s -> command.testPermissionSilent(s.getBukkitSender()), ++ null, null, false ++ ); ++ this.command = command; ++ } ++ ++ public static BukkitCommandNode of(String name, Command command) { ++ BukkitBrigCommand bukkitBrigCommand = new BukkitBrigCommand(command, name); ++ BukkitCommandNode commandNode = new BukkitCommandNode(name, command, bukkitBrigCommand); ++ commandNode.addChild( ++ RequiredArgumentBuilder.argument("args", StringArgumentType.greedyString()).suggests(BukkitBrigSuggestionProvider.INSTANCE).executes(bukkitBrigCommand).build() ++ ); ++ ++ return commandNode; ++ } ++ ++ public Command getBukkitCommand() { ++ return this.command; ++ } ++ ++ static class BukkitBrigCommand implements com.mojang.brigadier.Command { ++ ++ private final org.bukkit.command.Command command; ++ private final String literal; ++ ++ BukkitBrigCommand(org.bukkit.command.Command command, String literal) { ++ this.command = command; ++ this.literal = literal; ++ } ++ ++ @Override ++ public int run(CommandContext context) throws CommandSyntaxException { ++ CommandSender sender = context.getSource().getBukkitSender(); ++ ++ // Plugins do weird things to workaround normal registration ++ if (this.command.timings == null) { ++ this.command.timings = co.aikar.timings.TimingsManager.getCommandTiming(null, this.command); ++ } ++ ++ String[] args = org.apache.commons.lang3.StringUtils.split(context.getInput(), ' '); // fix adjacent spaces (from console/plugins) causing empty array elements ++ ++ try (Timing ignored = this.command.timings.startTiming()) { ++ // Note: we don't return the result of target.execute as thats success / failure, we return handled (true) or not handled (false) ++ this.command.execute(sender, this.literal, Arrays.copyOfRange(args, 1, args.length)); ++ } ++ ++ // return true as command was handled ++ return 1; ++ } ++ } ++ ++ static class BukkitBrigSuggestionProvider implements SuggestionProvider { ++ ++ public static final SuggestionProvider INSTANCE = new BukkitBrigSuggestionProvider(); ++ ++ @Override ++ public CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) throws CommandSyntaxException { ++ // Paper start ++ org.bukkit.command.CommandSender sender = context.getSource().getBukkitSender(); ++ if (!(sender instanceof org.bukkit.entity.Player player)) { ++ return CompletableFuture.completedFuture(builder.build()); ++ } ++ ++ String message = builder.getInput(); ++ List results = null; ++ Location pos = context.getSource().getBukkitLocation(); ++ try { ++ if (message.startsWith("/")) { ++ // Trim leading '/' if present (won't always be present in command blocks) ++ message = message.substring(1); ++ } ++ if (pos == null) { ++ results = Bukkit.getCommandMap().tabComplete(player, message); ++ } else { ++ results = Bukkit.getCommandMap().tabComplete(player, message, pos.clone()); ++ } ++ } catch (CommandException ex) { ++ player.sendMessage(ChatColor.RED + "An internal error occurred while attempting to tab-complete this command"); ++ Bukkit.getServer().getLogger().log(Level.SEVERE, "Exception when " + player.getName() + " attempted to tab complete " + message, ex); ++ } ++ ++ // Paper end ++ ++ // Defaults to sub nodes, but we have just one giant args node, so offset accordingly ++ builder = builder.createOffset(builder.getInput().lastIndexOf(' ') + 1); ++ ++ for (String s : results) { ++ builder.suggest(s); ++ } ++ ++ return builder.buildFuture(); ++ } ++ } ++ ++} diff --git a/src/main/java/net/minecraft/commands/CommandSourceStack.java b/src/main/java/net/minecraft/commands/CommandSourceStack.java index e0dd0fc1638377f4d4226d4b2976b901d635dff0..79884a7f8cd54a0a8a1464891a5cc2dbef790626 100644 --- a/src/main/java/net/minecraft/commands/CommandSourceStack.java @@ -656,9 +827,18 @@ index e0dd0fc1638377f4d4226d4b2976b901d635dff0..79884a7f8cd54a0a8a1464891a5cc2db public org.bukkit.command.CommandSender getBukkitSender() { return this.source.getBukkitSender(this); diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java -index a0f5aa8c3cfce63af9cb286278a7fdebd7aa3642..581d8798423ffa1c055aaef3021f6f97277c3331 100644 +index a0f5aa8c3cfce63af9cb286278a7fdebd7aa3642..1a34141032a0ae601d512483d71eea0b3e3a57b9 100644 --- a/src/main/java/net/minecraft/commands/Commands.java +++ b/src/main/java/net/minecraft/commands/Commands.java +@@ -151,7 +151,7 @@ public class Commands { + GameModeCommand.register(this.dispatcher); + GameRuleCommand.register(this.dispatcher); + GiveCommand.register(this.dispatcher, commandRegistryAccess); +- HelpCommand.register(this.dispatcher); ++ //HelpCommand.register(this.dispatcher); Paper - Use bukkit /help command + ItemCommands.register(this.dispatcher, commandRegistryAccess); + KickCommand.register(this.dispatcher); + KillCommand.register(this.dispatcher); @@ -291,6 +291,11 @@ public class Commands { } @@ -891,10 +1071,16 @@ index 2476727cd60034c4df2db36b9ed808e72d7b686f..13982e650d8d0f3edc8f99cd84be64aa // Defaults to sub nodes, but we have just one giant args node, so offset accordingly builder = builder.createOffset(builder.getInput().lastIndexOf(' ') + 1); diff --git a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java -index 6035af2cf08353b3d3801220d8116d8611a0cd37..2b26c17f53d1d56a76c7493ff736e29cffcc6dce 100644 +index 6035af2cf08353b3d3801220d8116d8611a0cd37..ecb62216e411e89b21608b141e43301dc11edd0e 100644 --- a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java +++ b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java -@@ -30,6 +30,13 @@ public final class VanillaCommandWrapper extends BukkitCommand { +@@ -25,11 +25,18 @@ import org.bukkit.craftbukkit.entity.CraftPlayer; + import org.bukkit.entity.Player; + import org.bukkit.entity.minecart.CommandMinecart; + +-public final class VanillaCommandWrapper extends BukkitCommand { ++public class VanillaCommandWrapper extends BukkitCommand { // Paper + private final Commands dispatcher; public final CommandNode vanillaCommand; @@ -908,8 +1094,34 @@ index 6035af2cf08353b3d3801220d8116d8611a0cd37..2b26c17f53d1d56a76c7493ff736e29c public VanillaCommandWrapper(Commands dispatcher, CommandNode vanillaCommand) { super(vanillaCommand.getName(), "A Mojang provided command.", vanillaCommand.getUsageText(), Collections.EMPTY_LIST); this.dispatcher = dispatcher; +diff --git a/src/main/java/org/bukkit/craftbukkit/help/SimpleHelpMap.java b/src/main/java/org/bukkit/craftbukkit/help/SimpleHelpMap.java +index 40b66adcb5aac64212b1937dc506ebb60f2eed83..44287bc95009dc1b6c70443759895ee28ecc54d9 100644 +--- a/src/main/java/org/bukkit/craftbukkit/help/SimpleHelpMap.java ++++ b/src/main/java/org/bukkit/craftbukkit/help/SimpleHelpMap.java +@@ -199,15 +199,18 @@ public class SimpleHelpMap implements HelpMap { + } + + private String getCommandPluginName(Command command) { ++ // Paper start - Move up ++ if (command instanceof PluginIdentifiableCommand) { ++ return ((PluginIdentifiableCommand) command).getPlugin().getName(); ++ } ++ // Paper end + if (command instanceof VanillaCommandWrapper) { + return "Minecraft"; + } + if (command instanceof BukkitCommand) { + return "Bukkit"; + } +- if (command instanceof PluginIdentifiableCommand) { +- return ((PluginIdentifiableCommand) command).getPlugin().getName(); +- } ++ // Paper - Move PluginIdentifiableCommand instanceof check to allow brig commands + return null; + } + diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java -index 8961da579b114cbafb329c00aadf1cf75e70cf97..5b95a0f7b41d965d45864028195753b592d26b35 100644 +index 8961da579b114cbafb329c00aadf1cf75e70cf97..94ea22836660124d44dee50b34b8f49534856ca7 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java @@ -604,6 +604,15 @@ public final class CraftMagicNumbers implements UnsafeValues { @@ -923,7 +1135,7 @@ index 8961da579b114cbafb329c00aadf1cf75e70cf97..5b95a0f7b41d965d45864028195753b5 + + @Override + public java.util.Map getCommandMirrorMap() { -+ return new io.papermc.paper.command.brigadier.BukkitBrigForwardingMap(); ++ return new io.papermc.paper.command.brigadier.bukkit.BukkitBrigForwardingMap(); + } // Paper end