Don't auto-create any brig redirects (#11954)

This commit is contained in:
Jake Potrebic
2025-02-16 13:55:27 -08:00
committed by GitHub
parent 88cdd22076
commit 84609dc046
9 changed files with 95 additions and 166 deletions

View File

@@ -124,7 +124,7 @@ public abstract class ApiMirrorRootNode extends RootCommandNode<CommandSourceSta
}
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
/*
@@ -140,6 +140,8 @@ public abstract class ApiMirrorRootNode extends RootCommandNode<CommandSourceSta
// Unknown argument type was passed
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 {
throw new IllegalArgumentException("Unknown command node passed. Don't know how to unwrap this.");
}

View File

@@ -1,24 +1,19 @@
package io.papermc.paper.command.brigadier;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.LiteralCommandNode;
import io.papermc.paper.command.brigadier.bukkit.BukkitBrigForwardingMap;
import io.papermc.paper.command.brigadier.bukkit.BukkitCommandNode;
import java.util.Map;
import net.minecraft.commands.CommandSource;
import net.minecraft.commands.Commands;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.phys.Vec2;
import net.minecraft.world.phys.Vec3;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.command.Command;
import org.bukkit.command.CommandMap;
import org.bukkit.craftbukkit.command.VanillaCommandWrapper;
import java.util.Map;
public final class PaperBrigadier {
@SuppressWarnings("DataFlowIssue")
@@ -40,7 +35,8 @@ public final class PaperBrigadier {
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);
}
CommandNode<CommandSourceStack> argumentCommandNode = node;
@@ -49,8 +45,8 @@ public final class PaperBrigadier {
}
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());
return new PluginVanillaCommandWrapper(pluginCommandNode.getName(), pluginCommandNode.getDescription(), usage, pluginCommandNode.getAliases(), node, pluginCommandNode.getPlugin());
String usage = map.isEmpty() ? node.getUsageText() : node.getUsageText() + " " + String.join("\n" + node.getUsageText() + " ", map.values());
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();
}
}

View File

@@ -21,23 +21,20 @@ import java.util.Set;
import net.minecraft.commands.CommandBuildContext;
import org.apache.commons.lang3.ArrayUtils;
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.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import static java.util.Objects.requireNonNull;
@DefaultQualifier(NonNull.class)
@NullMarked
public class PaperCommands implements Commands, PaperRegistrar<LifecycleEventOwner> {
public static final PaperCommands INSTANCE = new PaperCommands();
private @Nullable LifecycleEventOwner currentContext;
private @MonotonicNonNull CommandDispatcher<CommandSourceStack> dispatcher;
private @MonotonicNonNull CommandBuildContext buildContext;
private @Nullable CommandDispatcher<CommandSourceStack> dispatcher;
private @Nullable CommandBuildContext buildContext;
private boolean invalid = false;
@Override
@@ -93,65 +90,47 @@ public class PaperCommands implements Commands, PaperRegistrar<LifecycleEventOwn
}
@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) {
final boolean hasFlattenRedirectFlag = flags.contains(CommandRegistrationFlag.FLATTEN_ALIASES);
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 PluginCommandMeta meta = new PluginCommandMeta(pluginMeta, description);
final String identifier = pluginMeta.getName().toLowerCase(Locale.ROOT);
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);
if (this.registerIntoDispatcher(pluginLiteral, true)) {
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);
}
// Add aliases
final List<String> registeredAliases = new ArrayList<>(aliases.size() * 2);
for (final String alias : aliases) {
if (this.registerRedirect(alias, pluginMeta, pluginLiteral, description, false, hasFlattenRedirectFlag)) {
if (this.registerCopy(alias, pluginLiteral, meta)) {
registeredAliases.add(alias);
}
if (this.registerRedirect(identifier + ":" + alias, pluginMeta, pluginLiteral, description, false, hasFlattenRedirectFlag)) {
if (this.registerCopy(identifier + ":" + alias, pluginLiteral, meta)) {
registeredAliases.add(identifier + ":" + alias);
}
}
if (!registeredAliases.isEmpty()) {
pluginLiteral.setAliases(registeredAliases);
}
pluginLiteral.pluginCommandMeta = new PluginCommandMeta(pluginMeta, description, registeredAliases);
registeredLabels.addAll(registeredAliases);
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) {
final LiteralCommandNode<CommandSourceStack> redirect;
if (redirectTo.getChildren().isEmpty() || hasFlattenRedirectFlag) {
redirect = Commands.literal(aliasLiteral)
.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 registerCopy(final String aliasLiteral, final LiteralCommandNode<CommandSourceStack> redirectTo, final PluginCommandMeta meta) {
final LiteralCommandNode<CommandSourceStack> node = PaperBrigadier.copyLiteral(aliasLiteral, redirectTo);
node.pluginCommandMeta = meta;
return this.registerIntoDispatcher(node, false);
}
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)) {
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)) {
override = true; // override vanilla commands
}
if (existingChild == null || override) { // Avoid merging behavior. Maybe something to look into in the future

View File

@@ -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()));
}
}

View File

@@ -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;
}
}