|
1 | 1 | --- a/net/minecraft/commands/Commands.java
|
2 | 2 | +++ b/net/minecraft/commands/Commands.java
|
3 |
| -@@ -251,6 +_,30 @@ |
| 3 | +@@ -251,6 +_,24 @@ |
4 | 4 | PublishCommand.register(this.dispatcher);
|
5 | 5 | }
|
6 | 6 |
|
|
14 | 14 | + // Paper start - Brigadier Command API
|
15 | 15 | + // Create legacy minecraft namespace commands
|
16 | 16 | + 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 | ++ ) |
28 | 22 | + );
|
29 | 23 | + }
|
30 | 24 | + // Paper end - Brigadier Command API
|
|
150 | 144 | }
|
151 | 145 |
|
152 | 146 | return null;
|
153 |
| -@@ -360,25 +_,130 @@ |
| 147 | +@@ -360,26 +_,85 @@ |
154 | 148 | }
|
155 | 149 |
|
156 | 150 | public void sendCommands(ServerPlayer player) {
|
157 |
| -- Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> map = Maps.newHashMap(); |
158 | 151 | + // Paper start - Send empty commands if tab completion is disabled
|
159 | 152 | + if (org.spigotmc.SpigotConfig.tabComplete < 0) {
|
160 | 153 | + player.connection.send(new ClientboundCommandsPacket(new RootCommandNode<>()));
|
|
182 | 175 | +
|
183 | 176 | + private void sendAsync(ServerPlayer player, java.util.Collection<CommandNode<CommandSourceStack>> dispatcherRootChildren) {
|
184 | 177 | + // 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(); |
186 | 179 | RootCommandNode<SharedSuggestionProvider> rootCommandNode = new RootCommandNode<>();
|
187 | 180 | map.put(this.dispatcher.getRoot(), rootCommandNode);
|
188 | 181 | - this.fillUsableCommands(this.dispatcher.getRoot(), rootCommandNode, player.createCommandSourceStack(), map);
|
|
224 | 217 | Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> commandNodeToSuggestionNode
|
225 | 218 | ) {
|
226 | 219 | - 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 |
228 | 220 | + for (CommandNode<CommandSourceStack> commandNode : children) { // Paper - Perf: Async command map building; pass copy of children
|
229 | 221 | + // Paper start - Brigadier API
|
230 | 222 | + if (commandNode.clientNode != null) {
|
|
234 | 226 | + if (!org.spigotmc.SpigotConfig.sendNamespaced && commandNode.getName().contains(":")) continue; // Spigot
|
235 | 227 | if (commandNode.canUse(source)) {
|
236 | 228 | 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 |
273 | 229 | argumentBuilder.requires(suggestions -> true);
|
274 |
| - if (argumentBuilder.getCommand() != null) { |
| 230 | +- if (argumentBuilder.getCommand() != null) { |
275 | 231 | - 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 |
287 | 236 |
|
288 | 237 | if (argumentBuilder instanceof RequiredArgumentBuilder) {
|
| 238 | + RequiredArgumentBuilder<SharedSuggestionProvider, ?> requiredArgumentBuilder = (RequiredArgumentBuilder<SharedSuggestionProvider, ?>)argumentBuilder; |
289 | 239 | @@ -396,7 +_,7 @@
|
290 | 240 | commandNodeToSuggestionNode.put(commandNode, commandNode1);
|
291 | 241 | rootSuggestion.addChild(commandNode1);
|
|
0 commit comments