Skip to content

Commit 84609dc

Browse files
Don't auto-create any brig redirects (#11954)
1 parent 88cdd22 commit 84609dc

File tree

9 files changed

+95
-166
lines changed

9 files changed

+95
-166
lines changed

paper-api/src/main/java/io/papermc/paper/command/brigadier/CommandRegistrationFlag.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,10 @@
1010
*/
1111
@ApiStatus.Internal
1212
public enum CommandRegistrationFlag {
13+
14+
/**
15+
* @deprecated This is the default behavior now.
16+
*/
17+
@Deprecated(since = "1.21.4")
1318
FLATTEN_ALIASES
1419
}

paper-api/src/main/java/io/papermc/paper/command/brigadier/Commands.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ static <T> RequiredArgumentBuilder<CommandSourceStack, T> argument(final String
113113
* <p>Commands have certain overriding behavior:
114114
* <ul>
115115
* <li>Aliases will not override already existing commands (excluding namespaced ones)</li>
116+
* <li>Aliases are <b>not</b> Brigadier redirects, they just copy the command to a different label</li>
116117
* <li>The main command/namespaced label will override already existing commands</li>
117118
* </ul>
118119
*
@@ -129,6 +130,7 @@ static <T> RequiredArgumentBuilder<CommandSourceStack, T> argument(final String
129130
* <p>Commands have certain overriding behavior:
130131
* <ul>
131132
* <li>Aliases will not override already existing commands (excluding namespaced ones)</li>
133+
* <li>Aliases are <b>not</b> Brigadier redirects, they just copy the command to a different label</li>
132134
* <li>The main command/namespaced label will override already existing commands</li>
133135
* </ul>
134136
*
@@ -146,6 +148,7 @@ static <T> RequiredArgumentBuilder<CommandSourceStack, T> argument(final String
146148
* <p>Commands have certain overriding behavior:
147149
* <ul>
148150
* <li>Aliases will not override already existing commands (excluding namespaced ones)</li>
151+
* <li>Aliases are <b>not</b> Brigadier redirects, they just copy the command to a different label</li>
149152
* <li>The main command/namespaced label will override already existing commands</li>
150153
* </ul>
151154
*
@@ -163,6 +166,7 @@ static <T> RequiredArgumentBuilder<CommandSourceStack, T> argument(final String
163166
* <p>Commands have certain overriding behavior:
164167
* <ul>
165168
* <li>Aliases will not override already existing commands (excluding namespaced ones)</li>
169+
* <li>Aliases are <b>not</b> Brigadier redirects, they just copy the command to a different label</li>
166170
* <li>The main command/namespaced label will override already existing commands</li>
167171
* </ul>
168172
*
@@ -179,6 +183,7 @@ static <T> RequiredArgumentBuilder<CommandSourceStack, T> argument(final String
179183
* <p>Commands have certain overriding behavior:
180184
* <ul>
181185
* <li>Aliases will not override already existing commands (excluding namespaced ones)</li>
186+
* <li>Aliases are <b>not</b> Brigadier redirects, they just copy the command to a different label</li>
182187
* <li>The main command/namespaced label will override already existing commands</li>
183188
* </ul>
184189
*

paper-server/patches/sources/com/mojang/brigadier/tree/CommandNode.java.patch

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
--- a/com/mojang/brigadier/tree/CommandNode.java
22
+++ b/com/mojang/brigadier/tree/CommandNode.java
3-
@@ -27,11 +_,21 @@
3+
@@ -27,11 +_,22 @@
44
private final Map<String, CommandNode<S>> children = new LinkedHashMap<>();
55
private final Map<String, LiteralCommandNode<S>> literals = new LinkedHashMap<>();
66
private final Map<String, ArgumentCommandNode<S, ?>> arguments = new LinkedHashMap<>();
@@ -13,6 +13,7 @@
1313
+ public CommandNode<net.minecraft.commands.CommandSourceStack> clientNode; // Paper - Brigadier API
1414
+ public CommandNode<io.papermc.paper.command.brigadier.CommandSourceStack> unwrappedCached = null; // Paper - Brigadier Command API
1515
+ public CommandNode<io.papermc.paper.command.brigadier.CommandSourceStack> wrappedCached = null; // Paper - Brigadier Command API
16+
+ public io.papermc.paper.command.brigadier.PluginCommandMeta pluginCommandMeta; // Paper - Brigadier Command API
1617
+ // CraftBukkit start
1718
+ public void removeCommand(String name) {
1819
+ this.children.remove(name);

paper-server/patches/sources/net/minecraft/commands/Commands.java.patch

Lines changed: 14 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
--- a/net/minecraft/commands/Commands.java
22
+++ b/net/minecraft/commands/Commands.java
3-
@@ -251,6 +_,30 @@
3+
@@ -251,6 +_,24 @@
44
PublishCommand.register(this.dispatcher);
55
}
66

@@ -14,17 +14,11 @@
1414
+ // Paper start - Brigadier Command API
1515
+ // Create legacy minecraft namespace commands
1616
+ for (final CommandNode<CommandSourceStack> node : new java.util.ArrayList<>(this.dispatcher.getRoot().getChildren())) {
17-
+ // The brigadier dispatcher is not able to resolve nested redirects.
18-
+ // E.g. registering the alias minecraft:tp cannot redirect to tp, as tp itself redirects to teleport.
19-
+ // Instead, target the first none redirecting node.
20-
+ CommandNode<CommandSourceStack> flattenedAliasTarget = node;
21-
+ while (flattenedAliasTarget.getRedirect() != null) flattenedAliasTarget = flattenedAliasTarget.getRedirect();
22-
+
23-
+ this.dispatcher.register(
24-
+ com.mojang.brigadier.builder.LiteralArgumentBuilder.<CommandSourceStack>literal("minecraft:" + node.getName())
25-
+ .executes(flattenedAliasTarget.getCommand())
26-
+ .requires(flattenedAliasTarget.getRequirement())
27-
+ .redirect(flattenedAliasTarget)
17+
+ this.dispatcher.getRoot().addChild(
18+
+ io.papermc.paper.command.brigadier.PaperBrigadier.copyLiteral(
19+
+ "minecraft:" + node.getName(),
20+
+ (com.mojang.brigadier.tree.LiteralCommandNode<CommandSourceStack>) node
21+
+ )
2822
+ );
2923
+ }
3024
+ // Paper end - Brigadier Command API
@@ -150,11 +144,10 @@
150144
}
151145

152146
return null;
153-
@@ -360,25 +_,130 @@
147+
@@ -360,26 +_,85 @@
154148
}
155149

156150
public void sendCommands(ServerPlayer player) {
157-
- Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> map = Maps.newHashMap();
158151
+ // Paper start - Send empty commands if tab completion is disabled
159152
+ if (org.spigotmc.SpigotConfig.tabComplete < 0) {
160153
+ player.connection.send(new ClientboundCommandsPacket(new RootCommandNode<>()));
@@ -182,7 +175,7 @@
182175
+
183176
+ private void sendAsync(ServerPlayer player, java.util.Collection<CommandNode<CommandSourceStack>> dispatcherRootChildren) {
184177
+ // Paper end - Perf: Async command map building
185-
+ Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> map = Maps.newIdentityHashMap(); // Use identity to prevent aliasing issues
178+
Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> map = Maps.newHashMap();
186179
RootCommandNode<SharedSuggestionProvider> rootCommandNode = new RootCommandNode<>();
187180
map.put(this.dispatcher.getRoot(), rootCommandNode);
188181
- this.fillUsableCommands(this.dispatcher.getRoot(), rootCommandNode, player.createCommandSourceStack(), map);
@@ -224,7 +217,6 @@
224217
Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> commandNodeToSuggestionNode
225218
) {
226219
- for (CommandNode<CommandSourceStack> commandNode : rootCommandSource.getChildren()) {
227-
+ commandNodeToSuggestionNode.keySet().removeIf((node) -> !org.spigotmc.SpigotConfig.sendNamespaced && node.getName().contains(":")); // Paper - Remove namedspaced from result nodes to prevent redirect trimming ~ see comment below
228220
+ for (CommandNode<CommandSourceStack> commandNode : children) { // Paper - Perf: Async command map building; pass copy of children
229221
+ // Paper start - Brigadier API
230222
+ if (commandNode.clientNode != null) {
@@ -234,58 +226,16 @@
234226
+ if (!org.spigotmc.SpigotConfig.sendNamespaced && commandNode.getName().contains(":")) continue; // Spigot
235227
if (commandNode.canUse(source)) {
236228
ArgumentBuilder<SharedSuggestionProvider, ?> argumentBuilder = (ArgumentBuilder) commandNode.createBuilder();
237-
+ // Paper start
238-
+ /*
239-
+ Because of how commands can be yeeted right left and center due to bad bukkit practices
240-
+ we need to be able to ensure that ALL commands are registered (even redirects).
241-
+
242-
+ What this will do is IF the redirect seems to be "dead" it will create a builder and essentially populate (flatten)
243-
+ all the children from the dead redirect to the node.
244-
+
245-
+ 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.
246-
+
247-
+ The only way to fix this is to either:
248-
+ - Send EVERYTHING flattened, don't use redirects
249-
+ - Don't allow command nodes to be deleted
250-
+ - Do this :)
251-
+ */
252-
+
253-
+ // Is there an invalid command redirect?
254-
+ if (argumentBuilder.getRedirect() != null && commandNodeToSuggestionNode.get(argumentBuilder.getRedirect()) == null) {
255-
+ // Create the argument builder with the same values as the specified node, but with a different literal and populated children
256-
+
257-
+ CommandNode<SharedSuggestionProvider> redirect = argumentBuilder.getRedirect();
258-
+ // Diff copied from LiteralCommand#createBuilder
259-
+ final com.mojang.brigadier.builder.LiteralArgumentBuilder<SharedSuggestionProvider> builder = com.mojang.brigadier.builder.LiteralArgumentBuilder.literal(commandNode.getName());
260-
+ builder.requires(redirect.getRequirement());
261-
+ // builder.forward(redirect.getRedirect(), redirect.getRedirectModifier(), redirect.isFork()); We don't want to migrate the forward, since it's invalid.
262-
+ if (redirect.getCommand() != null) {
263-
+ builder.executes(redirect.getCommand());
264-
+ }
265-
+ // Diff copied from LiteralCommand#createBuilder
266-
+ for (CommandNode<SharedSuggestionProvider> child : redirect.getChildren()) {
267-
+ builder.then(child);
268-
+ }
269-
+
270-
+ argumentBuilder = builder;
271-
+ }
272-
+ // Paper end
273229
argumentBuilder.requires(suggestions -> true);
274-
if (argumentBuilder.getCommand() != null) {
230+
- if (argumentBuilder.getCommand() != null) {
275231
- argumentBuilder.executes(commandContext -> 0);
276-
+ // Paper start - fix suggestions due to falsely equal nodes
277-
+ // Always create a new instance
278-
+ //noinspection Convert2Lambda
279-
+ argumentBuilder.executes(new com.mojang.brigadier.Command<>() {
280-
+ @Override
281-
+ public int run(com.mojang.brigadier.context.CommandContext<SharedSuggestionProvider> commandContext) {
282-
+ return 0;
283-
+ }
284-
+ });
285-
+ // Paper end - fix suggestions due to falsely equal nodes
286-
}
232+
- }
233+
+ // Paper - don't replace Command instance on suggestion node
234+
+ // we want the exact command instance to be used for equality checks
235+
+ // when assigning serialization ids to each command node
287236

288237
if (argumentBuilder instanceof RequiredArgumentBuilder) {
238+
RequiredArgumentBuilder<SharedSuggestionProvider, ?> requiredArgumentBuilder = (RequiredArgumentBuilder<SharedSuggestionProvider, ?>)argumentBuilder;
289239
@@ -396,7 +_,7 @@
290240
commandNodeToSuggestionNode.put(commandNode, commandNode1);
291241
rootSuggestion.addChild(commandNode1);

paper-server/src/main/java/io/papermc/paper/command/brigadier/ApiMirrorRootNode.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ public static void validatePrimitiveType(ArgumentType<?> type) {
124124
}
125125

126126
converted = this.unwrapArgumentWrapper(pureArgumentNode, customArgumentType, customArgumentType.getNativeType(), suggestionProvider);
127-
} else if (pureArgumentType instanceof final VanillaArgumentProviderImpl.NativeWrapperArgumentType<?,?> nativeWrapperArgumentType) {
127+
} else if (pureArgumentType instanceof final VanillaArgumentProviderImpl.NativeWrapperArgumentType<?, ?> nativeWrapperArgumentType) {
128128
converted = this.unwrapArgumentWrapper(pureArgumentNode, nativeWrapperArgumentType, nativeWrapperArgumentType, null); // "null" for suggestion provider so it uses the argument type's suggestion provider
129129

130130
/*
@@ -140,6 +140,8 @@ public static void validatePrimitiveType(ArgumentType<?> type) {
140140
// Unknown argument type was passed
141141
throw new IllegalArgumentException("Custom unknown argument type was passed, should be wrapped inside an CustomArgumentType.");
142142
}
143+
} else if (pureNode == this) {
144+
return (CommandNode) this.getDispatcher().getRoot();
143145
} else {
144146
throw new IllegalArgumentException("Unknown command node passed. Don't know how to unwrap this.");
145147
}

paper-server/src/main/java/io/papermc/paper/command/brigadier/PaperBrigadier.java

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,19 @@
11
package io.papermc.paper.command.brigadier;
22

33
import com.mojang.brigadier.CommandDispatcher;
4+
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
45
import com.mojang.brigadier.tree.CommandNode;
56
import com.mojang.brigadier.tree.LiteralCommandNode;
6-
import io.papermc.paper.command.brigadier.bukkit.BukkitBrigForwardingMap;
77
import io.papermc.paper.command.brigadier.bukkit.BukkitCommandNode;
8+
import java.util.Map;
89
import net.minecraft.commands.CommandSource;
910
import net.minecraft.commands.Commands;
1011
import net.minecraft.network.chat.CommonComponents;
11-
import net.minecraft.server.MinecraftServer;
1212
import net.minecraft.world.phys.Vec2;
1313
import net.minecraft.world.phys.Vec3;
14-
import org.bukkit.Bukkit;
15-
import org.bukkit.Server;
1614
import org.bukkit.command.Command;
17-
import org.bukkit.command.CommandMap;
1815
import org.bukkit.craftbukkit.command.VanillaCommandWrapper;
1916

20-
import java.util.Map;
21-
2217
public final class PaperBrigadier {
2318

2419
@SuppressWarnings("DataFlowIssue")
@@ -40,7 +35,8 @@ public static Command wrapNode(CommandNode node) {
4035
throw new IllegalArgumentException("Unsure how to wrap a " + node);
4136
}
4237

43-
if (!(node instanceof PluginCommandNode pluginCommandNode)) {
38+
final PluginCommandMeta meta;
39+
if ((meta = node.pluginCommandMeta) == null) {
4440
return new VanillaCommandWrapper(null, node);
4541
}
4642
CommandNode<CommandSourceStack> argumentCommandNode = node;
@@ -49,8 +45,8 @@ public static Command wrapNode(CommandNode node) {
4945
}
5046

5147
Map<CommandNode<CommandSourceStack>, String> map = PaperCommands.INSTANCE.getDispatcherInternal().getSmartUsage(argumentCommandNode, DUMMY);
52-
String usage = map.isEmpty() ? pluginCommandNode.getUsageText() : pluginCommandNode.getUsageText() + " " + String.join("\n" + pluginCommandNode.getUsageText() + " ", map.values());
53-
return new PluginVanillaCommandWrapper(pluginCommandNode.getName(), pluginCommandNode.getDescription(), usage, pluginCommandNode.getAliases(), node, pluginCommandNode.getPlugin());
48+
String usage = map.isEmpty() ? node.getUsageText() : node.getUsageText() + " " + String.join("\n" + node.getUsageText() + " ", map.values());
49+
return new PluginVanillaCommandWrapper(node.getName(), meta.description(), usage, meta.aliases(), node, meta.plugin());
5450
}
5551

5652
/*
@@ -70,4 +66,19 @@ public static void moveBukkitCommands(Commands before, Commands after) {
7066
}
7167
}
7268
}
69+
70+
public static <S> LiteralCommandNode<S> copyLiteral(final String newLiteral, final LiteralCommandNode<S> source) {
71+
// logic copied from LiteralCommandNode#createBuilder
72+
final LiteralArgumentBuilder<S> copyBuilder = LiteralArgumentBuilder.<S>literal(newLiteral)
73+
.requires(source.getRequirement())
74+
.forward(source.getRedirect(), source.getRedirectModifier(), source.isFork());
75+
if (source.getCommand() != null) {
76+
copyBuilder.executes(source.getCommand());
77+
}
78+
79+
for (final CommandNode<S> child : source.getChildren()) {
80+
copyBuilder.then(child);
81+
}
82+
return copyBuilder.build();
83+
}
7384
}

0 commit comments

Comments
 (0)