Don't auto-create any brig redirects (#11954)
This commit is contained in:
@ -10,5 +10,10 @@ import org.jetbrains.annotations.ApiStatus;
|
|||||||
*/
|
*/
|
||||||
@ApiStatus.Internal
|
@ApiStatus.Internal
|
||||||
public enum CommandRegistrationFlag {
|
public enum CommandRegistrationFlag {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated This is the default behavior now.
|
||||||
|
*/
|
||||||
|
@Deprecated(since = "1.21.4")
|
||||||
FLATTEN_ALIASES
|
FLATTEN_ALIASES
|
||||||
}
|
}
|
||||||
|
|||||||
@ -113,6 +113,7 @@ public interface Commands extends Registrar {
|
|||||||
* <p>Commands have certain overriding behavior:
|
* <p>Commands have certain overriding behavior:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Aliases will not override already existing commands (excluding namespaced ones)</li>
|
* <li>Aliases will not override already existing commands (excluding namespaced ones)</li>
|
||||||
|
* <li>Aliases are <b>not</b> Brigadier redirects, they just copy the command to a different label</li>
|
||||||
* <li>The main command/namespaced label will override already existing commands</li>
|
* <li>The main command/namespaced label will override already existing commands</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
@ -129,6 +130,7 @@ public interface Commands extends Registrar {
|
|||||||
* <p>Commands have certain overriding behavior:
|
* <p>Commands have certain overriding behavior:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Aliases will not override already existing commands (excluding namespaced ones)</li>
|
* <li>Aliases will not override already existing commands (excluding namespaced ones)</li>
|
||||||
|
* <li>Aliases are <b>not</b> Brigadier redirects, they just copy the command to a different label</li>
|
||||||
* <li>The main command/namespaced label will override already existing commands</li>
|
* <li>The main command/namespaced label will override already existing commands</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
@ -146,6 +148,7 @@ public interface Commands extends Registrar {
|
|||||||
* <p>Commands have certain overriding behavior:
|
* <p>Commands have certain overriding behavior:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Aliases will not override already existing commands (excluding namespaced ones)</li>
|
* <li>Aliases will not override already existing commands (excluding namespaced ones)</li>
|
||||||
|
* <li>Aliases are <b>not</b> Brigadier redirects, they just copy the command to a different label</li>
|
||||||
* <li>The main command/namespaced label will override already existing commands</li>
|
* <li>The main command/namespaced label will override already existing commands</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
@ -163,6 +166,7 @@ public interface Commands extends Registrar {
|
|||||||
* <p>Commands have certain overriding behavior:
|
* <p>Commands have certain overriding behavior:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Aliases will not override already existing commands (excluding namespaced ones)</li>
|
* <li>Aliases will not override already existing commands (excluding namespaced ones)</li>
|
||||||
|
* <li>Aliases are <b>not</b> Brigadier redirects, they just copy the command to a different label</li>
|
||||||
* <li>The main command/namespaced label will override already existing commands</li>
|
* <li>The main command/namespaced label will override already existing commands</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
@ -179,6 +183,7 @@ public interface Commands extends Registrar {
|
|||||||
* <p>Commands have certain overriding behavior:
|
* <p>Commands have certain overriding behavior:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Aliases will not override already existing commands (excluding namespaced ones)</li>
|
* <li>Aliases will not override already existing commands (excluding namespaced ones)</li>
|
||||||
|
* <li>Aliases are <b>not</b> Brigadier redirects, they just copy the command to a different label</li>
|
||||||
* <li>The main command/namespaced label will override already existing commands</li>
|
* <li>The main command/namespaced label will override already existing commands</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
--- a/com/mojang/brigadier/tree/CommandNode.java
|
--- a/com/mojang/brigadier/tree/CommandNode.java
|
||||||
+++ b/com/mojang/brigadier/tree/CommandNode.java
|
+++ b/com/mojang/brigadier/tree/CommandNode.java
|
||||||
@@ -27,11 +_,21 @@
|
@@ -27,11 +_,22 @@
|
||||||
private final Map<String, CommandNode<S>> children = new LinkedHashMap<>();
|
private final Map<String, CommandNode<S>> children = new LinkedHashMap<>();
|
||||||
private final Map<String, LiteralCommandNode<S>> literals = new LinkedHashMap<>();
|
private final Map<String, LiteralCommandNode<S>> literals = new LinkedHashMap<>();
|
||||||
private final Map<String, ArgumentCommandNode<S, ?>> arguments = new LinkedHashMap<>();
|
private final Map<String, ArgumentCommandNode<S, ?>> arguments = new LinkedHashMap<>();
|
||||||
@ -13,6 +13,7 @@
|
|||||||
+ public CommandNode<net.minecraft.commands.CommandSourceStack> clientNode; // Paper - Brigadier API
|
+ public CommandNode<net.minecraft.commands.CommandSourceStack> clientNode; // Paper - Brigadier API
|
||||||
+ public CommandNode<io.papermc.paper.command.brigadier.CommandSourceStack> unwrappedCached = null; // Paper - Brigadier Command API
|
+ public CommandNode<io.papermc.paper.command.brigadier.CommandSourceStack> unwrappedCached = null; // Paper - Brigadier Command API
|
||||||
+ public CommandNode<io.papermc.paper.command.brigadier.CommandSourceStack> wrappedCached = null; // Paper - Brigadier Command API
|
+ public CommandNode<io.papermc.paper.command.brigadier.CommandSourceStack> wrappedCached = null; // Paper - Brigadier Command API
|
||||||
|
+ public io.papermc.paper.command.brigadier.PluginCommandMeta pluginCommandMeta; // Paper - Brigadier Command API
|
||||||
+ // CraftBukkit start
|
+ // CraftBukkit start
|
||||||
+ public void removeCommand(String name) {
|
+ public void removeCommand(String name) {
|
||||||
+ this.children.remove(name);
|
+ this.children.remove(name);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
--- a/net/minecraft/commands/Commands.java
|
--- a/net/minecraft/commands/Commands.java
|
||||||
+++ b/net/minecraft/commands/Commands.java
|
+++ b/net/minecraft/commands/Commands.java
|
||||||
@@ -251,6 +_,30 @@
|
@@ -251,6 +_,24 @@
|
||||||
PublishCommand.register(this.dispatcher);
|
PublishCommand.register(this.dispatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -14,17 +14,11 @@
|
|||||||
+ // Paper start - Brigadier Command API
|
+ // Paper start - Brigadier Command API
|
||||||
+ // Create legacy minecraft namespace commands
|
+ // Create legacy minecraft namespace commands
|
||||||
+ for (final CommandNode<CommandSourceStack> node : new java.util.ArrayList<>(this.dispatcher.getRoot().getChildren())) {
|
+ for (final CommandNode<CommandSourceStack> node : new java.util.ArrayList<>(this.dispatcher.getRoot().getChildren())) {
|
||||||
+ // The brigadier dispatcher is not able to resolve nested redirects.
|
+ this.dispatcher.getRoot().addChild(
|
||||||
+ // E.g. registering the alias minecraft:tp cannot redirect to tp, as tp itself redirects to teleport.
|
+ io.papermc.paper.command.brigadier.PaperBrigadier.copyLiteral(
|
||||||
+ // Instead, target the first none redirecting node.
|
+ "minecraft:" + node.getName(),
|
||||||
+ CommandNode<CommandSourceStack> flattenedAliasTarget = node;
|
+ (com.mojang.brigadier.tree.LiteralCommandNode<CommandSourceStack>) node
|
||||||
+ while (flattenedAliasTarget.getRedirect() != null) flattenedAliasTarget = flattenedAliasTarget.getRedirect();
|
+ )
|
||||||
+
|
|
||||||
+ this.dispatcher.register(
|
|
||||||
+ com.mojang.brigadier.builder.LiteralArgumentBuilder.<CommandSourceStack>literal("minecraft:" + node.getName())
|
|
||||||
+ .executes(flattenedAliasTarget.getCommand())
|
|
||||||
+ .requires(flattenedAliasTarget.getRequirement())
|
|
||||||
+ .redirect(flattenedAliasTarget)
|
|
||||||
+ );
|
+ );
|
||||||
+ }
|
+ }
|
||||||
+ // Paper end - Brigadier Command API
|
+ // Paper end - Brigadier Command API
|
||||||
@ -150,11 +144,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -360,25 +_,130 @@
|
@@ -360,26 +_,85 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendCommands(ServerPlayer player) {
|
public void sendCommands(ServerPlayer player) {
|
||||||
- Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> map = Maps.newHashMap();
|
|
||||||
+ // Paper start - Send empty commands if tab completion is disabled
|
+ // Paper start - Send empty commands if tab completion is disabled
|
||||||
+ if (org.spigotmc.SpigotConfig.tabComplete < 0) {
|
+ if (org.spigotmc.SpigotConfig.tabComplete < 0) {
|
||||||
+ player.connection.send(new ClientboundCommandsPacket(new RootCommandNode<>()));
|
+ player.connection.send(new ClientboundCommandsPacket(new RootCommandNode<>()));
|
||||||
@ -182,7 +175,7 @@
|
|||||||
+
|
+
|
||||||
+ private void sendAsync(ServerPlayer player, java.util.Collection<CommandNode<CommandSourceStack>> dispatcherRootChildren) {
|
+ private void sendAsync(ServerPlayer player, java.util.Collection<CommandNode<CommandSourceStack>> dispatcherRootChildren) {
|
||||||
+ // Paper end - Perf: Async command map building
|
+ // Paper end - Perf: Async command map building
|
||||||
+ Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> map = Maps.newIdentityHashMap(); // Use identity to prevent aliasing issues
|
Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> map = Maps.newHashMap();
|
||||||
RootCommandNode<SharedSuggestionProvider> rootCommandNode = new RootCommandNode<>();
|
RootCommandNode<SharedSuggestionProvider> rootCommandNode = new RootCommandNode<>();
|
||||||
map.put(this.dispatcher.getRoot(), rootCommandNode);
|
map.put(this.dispatcher.getRoot(), rootCommandNode);
|
||||||
- this.fillUsableCommands(this.dispatcher.getRoot(), rootCommandNode, player.createCommandSourceStack(), map);
|
- this.fillUsableCommands(this.dispatcher.getRoot(), rootCommandNode, player.createCommandSourceStack(), map);
|
||||||
@ -224,7 +217,6 @@
|
|||||||
Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> commandNodeToSuggestionNode
|
Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> commandNodeToSuggestionNode
|
||||||
) {
|
) {
|
||||||
- for (CommandNode<CommandSourceStack> commandNode : rootCommandSource.getChildren()) {
|
- for (CommandNode<CommandSourceStack> commandNode : rootCommandSource.getChildren()) {
|
||||||
+ commandNodeToSuggestionNode.keySet().removeIf((node) -> !org.spigotmc.SpigotConfig.sendNamespaced && node.getName().contains(":")); // Paper - Remove namedspaced from result nodes to prevent redirect trimming ~ see comment below
|
|
||||||
+ for (CommandNode<CommandSourceStack> commandNode : children) { // Paper - Perf: Async command map building; pass copy of children
|
+ for (CommandNode<CommandSourceStack> commandNode : children) { // Paper - Perf: Async command map building; pass copy of children
|
||||||
+ // Paper start - Brigadier API
|
+ // Paper start - Brigadier API
|
||||||
+ if (commandNode.clientNode != null) {
|
+ if (commandNode.clientNode != null) {
|
||||||
@ -234,58 +226,16 @@
|
|||||||
+ if (!org.spigotmc.SpigotConfig.sendNamespaced && commandNode.getName().contains(":")) continue; // Spigot
|
+ if (!org.spigotmc.SpigotConfig.sendNamespaced && commandNode.getName().contains(":")) continue; // Spigot
|
||||||
if (commandNode.canUse(source)) {
|
if (commandNode.canUse(source)) {
|
||||||
ArgumentBuilder<SharedSuggestionProvider, ?> argumentBuilder = (ArgumentBuilder) commandNode.createBuilder();
|
ArgumentBuilder<SharedSuggestionProvider, ?> argumentBuilder = (ArgumentBuilder) commandNode.createBuilder();
|
||||||
+ // Paper start
|
|
||||||
+ /*
|
|
||||||
+ Because of how commands can be yeeted right left and center due to bad bukkit practices
|
|
||||||
+ we need to be able to ensure that ALL commands are registered (even redirects).
|
|
||||||
+
|
|
||||||
+ What this will do is IF the redirect seems to be "dead" it will create a builder and essentially populate (flatten)
|
|
||||||
+ all the children from the dead redirect to the node.
|
|
||||||
+
|
|
||||||
+ So, if minecraft:msg redirects to msg but the original msg node has been overriden minecraft:msg will now act as msg and will explicilty inherit its children.
|
|
||||||
+
|
|
||||||
+ The only way to fix this is to either:
|
|
||||||
+ - Send EVERYTHING flattened, don't use redirects
|
|
||||||
+ - Don't allow command nodes to be deleted
|
|
||||||
+ - Do this :)
|
|
||||||
+ */
|
|
||||||
+
|
|
||||||
+ // Is there an invalid command redirect?
|
|
||||||
+ if (argumentBuilder.getRedirect() != null && commandNodeToSuggestionNode.get(argumentBuilder.getRedirect()) == null) {
|
|
||||||
+ // Create the argument builder with the same values as the specified node, but with a different literal and populated children
|
|
||||||
+
|
|
||||||
+ CommandNode<SharedSuggestionProvider> redirect = argumentBuilder.getRedirect();
|
|
||||||
+ // Diff copied from LiteralCommand#createBuilder
|
|
||||||
+ final com.mojang.brigadier.builder.LiteralArgumentBuilder<SharedSuggestionProvider> builder = com.mojang.brigadier.builder.LiteralArgumentBuilder.literal(commandNode.getName());
|
|
||||||
+ builder.requires(redirect.getRequirement());
|
|
||||||
+ // builder.forward(redirect.getRedirect(), redirect.getRedirectModifier(), redirect.isFork()); We don't want to migrate the forward, since it's invalid.
|
|
||||||
+ if (redirect.getCommand() != null) {
|
|
||||||
+ builder.executes(redirect.getCommand());
|
|
||||||
+ }
|
|
||||||
+ // Diff copied from LiteralCommand#createBuilder
|
|
||||||
+ for (CommandNode<SharedSuggestionProvider> child : redirect.getChildren()) {
|
|
||||||
+ builder.then(child);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ argumentBuilder = builder;
|
|
||||||
+ }
|
|
||||||
+ // Paper end
|
|
||||||
argumentBuilder.requires(suggestions -> true);
|
argumentBuilder.requires(suggestions -> true);
|
||||||
if (argumentBuilder.getCommand() != null) {
|
- if (argumentBuilder.getCommand() != null) {
|
||||||
- argumentBuilder.executes(commandContext -> 0);
|
- argumentBuilder.executes(commandContext -> 0);
|
||||||
+ // Paper start - fix suggestions due to falsely equal nodes
|
- }
|
||||||
+ // Always create a new instance
|
+ // Paper - don't replace Command instance on suggestion node
|
||||||
+ //noinspection Convert2Lambda
|
+ // we want the exact command instance to be used for equality checks
|
||||||
+ argumentBuilder.executes(new com.mojang.brigadier.Command<>() {
|
+ // when assigning serialization ids to each command node
|
||||||
+ @Override
|
|
||||||
+ public int run(com.mojang.brigadier.context.CommandContext<SharedSuggestionProvider> commandContext) {
|
|
||||||
+ return 0;
|
|
||||||
+ }
|
|
||||||
+ });
|
|
||||||
+ // Paper end - fix suggestions due to falsely equal nodes
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argumentBuilder instanceof RequiredArgumentBuilder) {
|
if (argumentBuilder instanceof RequiredArgumentBuilder) {
|
||||||
|
RequiredArgumentBuilder<SharedSuggestionProvider, ?> requiredArgumentBuilder = (RequiredArgumentBuilder<SharedSuggestionProvider, ?>)argumentBuilder;
|
||||||
@@ -396,7 +_,7 @@
|
@@ -396,7 +_,7 @@
|
||||||
commandNodeToSuggestionNode.put(commandNode, commandNode1);
|
commandNodeToSuggestionNode.put(commandNode, commandNode1);
|
||||||
rootSuggestion.addChild(commandNode1);
|
rootSuggestion.addChild(commandNode1);
|
||||||
|
|||||||
@ -124,7 +124,7 @@ public abstract class ApiMirrorRootNode extends RootCommandNode<CommandSourceSta
|
|||||||
}
|
}
|
||||||
|
|
||||||
converted = this.unwrapArgumentWrapper(pureArgumentNode, customArgumentType, customArgumentType.getNativeType(), suggestionProvider);
|
converted = this.unwrapArgumentWrapper(pureArgumentNode, customArgumentType, customArgumentType.getNativeType(), suggestionProvider);
|
||||||
} else if (pureArgumentType instanceof final VanillaArgumentProviderImpl.NativeWrapperArgumentType<?,?> nativeWrapperArgumentType) {
|
} else if (pureArgumentType instanceof final VanillaArgumentProviderImpl.NativeWrapperArgumentType<?, ?> nativeWrapperArgumentType) {
|
||||||
converted = this.unwrapArgumentWrapper(pureArgumentNode, nativeWrapperArgumentType, nativeWrapperArgumentType, null); // "null" for suggestion provider so it uses the argument type's suggestion provider
|
converted = this.unwrapArgumentWrapper(pureArgumentNode, nativeWrapperArgumentType, nativeWrapperArgumentType, null); // "null" for suggestion provider so it uses the argument type's suggestion provider
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -140,6 +140,8 @@ public abstract class ApiMirrorRootNode extends RootCommandNode<CommandSourceSta
|
|||||||
// Unknown argument type was passed
|
// Unknown argument type was passed
|
||||||
throw new IllegalArgumentException("Custom unknown argument type was passed, should be wrapped inside an CustomArgumentType.");
|
throw new IllegalArgumentException("Custom unknown argument type was passed, should be wrapped inside an CustomArgumentType.");
|
||||||
}
|
}
|
||||||
|
} else if (pureNode == this) {
|
||||||
|
return (CommandNode) this.getDispatcher().getRoot();
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Unknown command node passed. Don't know how to unwrap this.");
|
throw new IllegalArgumentException("Unknown command node passed. Don't know how to unwrap this.");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,24 +1,19 @@
|
|||||||
package io.papermc.paper.command.brigadier;
|
package io.papermc.paper.command.brigadier;
|
||||||
|
|
||||||
import com.mojang.brigadier.CommandDispatcher;
|
import com.mojang.brigadier.CommandDispatcher;
|
||||||
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
import com.mojang.brigadier.tree.CommandNode;
|
import com.mojang.brigadier.tree.CommandNode;
|
||||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||||
import io.papermc.paper.command.brigadier.bukkit.BukkitBrigForwardingMap;
|
|
||||||
import io.papermc.paper.command.brigadier.bukkit.BukkitCommandNode;
|
import io.papermc.paper.command.brigadier.bukkit.BukkitCommandNode;
|
||||||
|
import java.util.Map;
|
||||||
import net.minecraft.commands.CommandSource;
|
import net.minecraft.commands.CommandSource;
|
||||||
import net.minecraft.commands.Commands;
|
import net.minecraft.commands.Commands;
|
||||||
import net.minecraft.network.chat.CommonComponents;
|
import net.minecraft.network.chat.CommonComponents;
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.world.phys.Vec2;
|
import net.minecraft.world.phys.Vec2;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.Server;
|
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
import org.bukkit.command.CommandMap;
|
|
||||||
import org.bukkit.craftbukkit.command.VanillaCommandWrapper;
|
import org.bukkit.craftbukkit.command.VanillaCommandWrapper;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public final class PaperBrigadier {
|
public final class PaperBrigadier {
|
||||||
|
|
||||||
@SuppressWarnings("DataFlowIssue")
|
@SuppressWarnings("DataFlowIssue")
|
||||||
@ -40,7 +35,8 @@ public final class PaperBrigadier {
|
|||||||
throw new IllegalArgumentException("Unsure how to wrap a " + node);
|
throw new IllegalArgumentException("Unsure how to wrap a " + node);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(node instanceof PluginCommandNode pluginCommandNode)) {
|
final PluginCommandMeta meta;
|
||||||
|
if ((meta = node.pluginCommandMeta) == null) {
|
||||||
return new VanillaCommandWrapper(null, node);
|
return new VanillaCommandWrapper(null, node);
|
||||||
}
|
}
|
||||||
CommandNode<CommandSourceStack> argumentCommandNode = node;
|
CommandNode<CommandSourceStack> argumentCommandNode = node;
|
||||||
@ -49,8 +45,8 @@ public final class PaperBrigadier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Map<CommandNode<CommandSourceStack>, String> map = PaperCommands.INSTANCE.getDispatcherInternal().getSmartUsage(argumentCommandNode, DUMMY);
|
Map<CommandNode<CommandSourceStack>, String> map = PaperCommands.INSTANCE.getDispatcherInternal().getSmartUsage(argumentCommandNode, DUMMY);
|
||||||
String usage = map.isEmpty() ? pluginCommandNode.getUsageText() : pluginCommandNode.getUsageText() + " " + String.join("\n" + pluginCommandNode.getUsageText() + " ", map.values());
|
String usage = map.isEmpty() ? node.getUsageText() : node.getUsageText() + " " + String.join("\n" + node.getUsageText() + " ", map.values());
|
||||||
return new PluginVanillaCommandWrapper(pluginCommandNode.getName(), pluginCommandNode.getDescription(), usage, pluginCommandNode.getAliases(), node, pluginCommandNode.getPlugin());
|
return new PluginVanillaCommandWrapper(node.getName(), meta.description(), usage, meta.aliases(), node, meta.plugin());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -70,4 +66,19 @@ public final class PaperBrigadier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <S> LiteralCommandNode<S> copyLiteral(final String newLiteral, final LiteralCommandNode<S> source) {
|
||||||
|
// logic copied from LiteralCommandNode#createBuilder
|
||||||
|
final LiteralArgumentBuilder<S> copyBuilder = LiteralArgumentBuilder.<S>literal(newLiteral)
|
||||||
|
.requires(source.getRequirement())
|
||||||
|
.forward(source.getRedirect(), source.getRedirectModifier(), source.isFork());
|
||||||
|
if (source.getCommand() != null) {
|
||||||
|
copyBuilder.executes(source.getCommand());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final CommandNode<S> child : source.getChildren()) {
|
||||||
|
copyBuilder.then(child);
|
||||||
|
}
|
||||||
|
return copyBuilder.build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,23 +21,20 @@ import java.util.Set;
|
|||||||
import net.minecraft.commands.CommandBuildContext;
|
import net.minecraft.commands.CommandBuildContext;
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
|
||||||
import org.checkerframework.framework.qual.DefaultQualifier;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Unmodifiable;
|
import org.jetbrains.annotations.Unmodifiable;
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
@DefaultQualifier(NonNull.class)
|
@NullMarked
|
||||||
public class PaperCommands implements Commands, PaperRegistrar<LifecycleEventOwner> {
|
public class PaperCommands implements Commands, PaperRegistrar<LifecycleEventOwner> {
|
||||||
|
|
||||||
public static final PaperCommands INSTANCE = new PaperCommands();
|
public static final PaperCommands INSTANCE = new PaperCommands();
|
||||||
|
|
||||||
private @Nullable LifecycleEventOwner currentContext;
|
private @Nullable LifecycleEventOwner currentContext;
|
||||||
private @MonotonicNonNull CommandDispatcher<CommandSourceStack> dispatcher;
|
private @Nullable CommandDispatcher<CommandSourceStack> dispatcher;
|
||||||
private @MonotonicNonNull CommandBuildContext buildContext;
|
private @Nullable CommandBuildContext buildContext;
|
||||||
private boolean invalid = false;
|
private boolean invalid = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -93,65 +90,47 @@ public class PaperCommands implements Commands, PaperRegistrar<LifecycleEventOwn
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Unmodifiable Set<String> registerWithFlags(@NotNull final PluginMeta pluginMeta, @NotNull final LiteralCommandNode<CommandSourceStack> node, @org.jetbrains.annotations.Nullable final String description, @NotNull final Collection<String> aliases, @NotNull final Set<CommandRegistrationFlag> flags) {
|
public @Unmodifiable Set<String> registerWithFlags(final PluginMeta pluginMeta, final LiteralCommandNode<CommandSourceStack> node, final @Nullable String description, final Collection<String> aliases, final Set<CommandRegistrationFlag> flags) {
|
||||||
final boolean hasFlattenRedirectFlag = flags.contains(CommandRegistrationFlag.FLATTEN_ALIASES);
|
final PluginCommandMeta meta = new PluginCommandMeta(pluginMeta, description);
|
||||||
final String identifier = pluginMeta.getName().toLowerCase(Locale.ROOT);
|
final String identifier = pluginMeta.getName().toLowerCase(Locale.ROOT);
|
||||||
final String literal = node.getLiteral();
|
final String literal = node.getLiteral();
|
||||||
final PluginCommandNode pluginLiteral = new PluginCommandNode(identifier + ":" + literal, pluginMeta, node, description); // Treat the keyed version of the command as the root
|
final LiteralCommandNode<CommandSourceStack> pluginLiteral = PaperBrigadier.copyLiteral(identifier + ":" + literal, node);
|
||||||
|
|
||||||
final Set<String> registeredLabels = new HashSet<>(aliases.size() * 2 + 2);
|
final Set<String> registeredLabels = new HashSet<>(aliases.size() * 2 + 2);
|
||||||
|
|
||||||
if (this.registerIntoDispatcher(pluginLiteral, true)) {
|
if (this.registerIntoDispatcher(pluginLiteral, true)) {
|
||||||
registeredLabels.add(pluginLiteral.getLiteral());
|
registeredLabels.add(pluginLiteral.getLiteral());
|
||||||
}
|
}
|
||||||
if (this.registerRedirect(literal, pluginMeta, pluginLiteral, description, true, hasFlattenRedirectFlag)) { // Plugin commands should override vanilla commands
|
if (this.registerIntoDispatcher(node, true)) { // Plugin commands should override vanilla commands
|
||||||
registeredLabels.add(literal);
|
registeredLabels.add(literal);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add aliases
|
// Add aliases
|
||||||
final List<String> registeredAliases = new ArrayList<>(aliases.size() * 2);
|
final List<String> registeredAliases = new ArrayList<>(aliases.size() * 2);
|
||||||
for (final String alias : aliases) {
|
for (final String alias : aliases) {
|
||||||
if (this.registerRedirect(alias, pluginMeta, pluginLiteral, description, false, hasFlattenRedirectFlag)) {
|
if (this.registerCopy(alias, pluginLiteral, meta)) {
|
||||||
registeredAliases.add(alias);
|
registeredAliases.add(alias);
|
||||||
}
|
}
|
||||||
if (this.registerRedirect(identifier + ":" + alias, pluginMeta, pluginLiteral, description, false, hasFlattenRedirectFlag)) {
|
if (this.registerCopy(identifier + ":" + alias, pluginLiteral, meta)) {
|
||||||
registeredAliases.add(identifier + ":" + alias);
|
registeredAliases.add(identifier + ":" + alias);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!registeredAliases.isEmpty()) {
|
pluginLiteral.pluginCommandMeta = new PluginCommandMeta(pluginMeta, description, registeredAliases);
|
||||||
pluginLiteral.setAliases(registeredAliases);
|
|
||||||
}
|
|
||||||
|
|
||||||
registeredLabels.addAll(registeredAliases);
|
registeredLabels.addAll(registeredAliases);
|
||||||
return registeredLabels.isEmpty() ? Collections.emptySet() : Collections.unmodifiableSet(registeredLabels);
|
return registeredLabels.isEmpty() ? Collections.emptySet() : Collections.unmodifiableSet(registeredLabels);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean registerRedirect(final String aliasLiteral, final PluginMeta plugin, final PluginCommandNode redirectTo, final @Nullable String description, final boolean override, boolean hasFlattenRedirectFlag) {
|
private boolean registerCopy(final String aliasLiteral, final LiteralCommandNode<CommandSourceStack> redirectTo, final PluginCommandMeta meta) {
|
||||||
final LiteralCommandNode<CommandSourceStack> redirect;
|
final LiteralCommandNode<CommandSourceStack> node = PaperBrigadier.copyLiteral(aliasLiteral, redirectTo);
|
||||||
if (redirectTo.getChildren().isEmpty() || hasFlattenRedirectFlag) {
|
node.pluginCommandMeta = meta;
|
||||||
redirect = Commands.literal(aliasLiteral)
|
return this.registerIntoDispatcher(node, false);
|
||||||
.executes(redirectTo.getCommand())
|
|
||||||
.requires(redirectTo.getRequirement())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
for (final CommandNode<CommandSourceStack> child : redirectTo.getChildren()) {
|
|
||||||
redirect.addChild(child);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
redirect = Commands.literal(aliasLiteral)
|
|
||||||
.executes(redirectTo.getCommand())
|
|
||||||
.redirect(redirectTo)
|
|
||||||
.requires(redirectTo.getRequirement())
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.registerIntoDispatcher(new PluginCommandNode(aliasLiteral, plugin, redirect, description), override);
|
private boolean registerIntoDispatcher(final LiteralCommandNode<CommandSourceStack> node, boolean override) {
|
||||||
}
|
final CommandNode<CommandSourceStack> existingChild = this.getDispatcher().getRoot().getChild(node.getLiteral());
|
||||||
|
if (existingChild != null && existingChild.pluginCommandMeta == null && !(existingChild instanceof BukkitCommandNode)) {
|
||||||
private boolean registerIntoDispatcher(final PluginCommandNode node, boolean override) {
|
|
||||||
final @Nullable CommandNode<CommandSourceStack> existingChild = this.getDispatcher().getRoot().getChild(node.getLiteral());
|
|
||||||
if (existingChild != null && !(existingChild instanceof PluginCommandNode) && !(existingChild instanceof BukkitCommandNode)) {
|
|
||||||
override = true; // override vanilla commands
|
override = true; // override vanilla commands
|
||||||
}
|
}
|
||||||
if (existingChild == null || override) { // Avoid merging behavior. Maybe something to look into in the future
|
if (existingChild == null || override) { // Avoid merging behavior. Maybe something to look into in the future
|
||||||
|
|||||||
@ -0,0 +1,26 @@
|
|||||||
|
package io.papermc.paper.command.brigadier;
|
||||||
|
|
||||||
|
import io.papermc.paper.plugin.configuration.PluginMeta;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
@NullMarked
|
||||||
|
public record PluginCommandMeta(PluginMeta pluginMeta, @Nullable String description, List<String> aliases) {
|
||||||
|
|
||||||
|
public PluginCommandMeta(final PluginMeta pluginMeta, final @Nullable String description) {
|
||||||
|
this(pluginMeta, description, Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public PluginCommandMeta {
|
||||||
|
aliases = List.copyOf(aliases);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Plugin plugin() {
|
||||||
|
return Objects.requireNonNull(Bukkit.getPluginManager().getPlugin(this.pluginMeta.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,50 +0,0 @@
|
|||||||
package io.papermc.paper.command.brigadier;
|
|
||||||
|
|
||||||
import com.mojang.brigadier.tree.CommandNode;
|
|
||||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import io.papermc.paper.plugin.configuration.PluginMeta;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.plugin.Plugin;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public class PluginCommandNode extends LiteralCommandNode<CommandSourceStack> {
|
|
||||||
|
|
||||||
private final PluginMeta plugin;
|
|
||||||
private final String description;
|
|
||||||
private List<String> aliases = Collections.emptyList();
|
|
||||||
|
|
||||||
public PluginCommandNode(final @NotNull String literal, final @NotNull PluginMeta plugin, final @NotNull LiteralCommandNode<CommandSourceStack> rootLiteral, final @Nullable String description) {
|
|
||||||
super(
|
|
||||||
literal, rootLiteral.getCommand(), rootLiteral.getRequirement(),
|
|
||||||
rootLiteral.getRedirect(), rootLiteral.getRedirectModifier(), rootLiteral.isFork()
|
|
||||||
);
|
|
||||||
this.plugin = plugin;
|
|
||||||
this.description = description;
|
|
||||||
|
|
||||||
for (CommandNode<CommandSourceStack> argument : rootLiteral.getChildren()) {
|
|
||||||
this.addChild(argument);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public Plugin getPlugin() {
|
|
||||||
return Objects.requireNonNull(Bukkit.getPluginManager().getPlugin(this.plugin.getName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public String getDescription() {
|
|
||||||
return this.description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAliases(List<String> aliases) {
|
|
||||||
this.aliases = aliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getAliases() {
|
|
||||||
return this.aliases;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user