Search in sources :

Example 1 with CommandNode

use of com.mojang.brigadier.tree.CommandNode in project SpongeCommon by SpongePowered.

the class SpongeCommandDispatcher method getCompletionSuggestions.

@Override
public CompletableFuture<Suggestions> getCompletionSuggestions(final ParseResults<CommandSourceStack> parse, final int cursor) {
    final CommandContextBuilder<CommandSourceStack> context = parse.getContext();
    // Sponge Start - redirect if this actually represents a non-Brig command
    final CommandContextBuilder<CommandSourceStack> child = context.getLastChild();
    if (child instanceof SpongeCommandContextBuilder) {
        final SpongeCommandContextBuilder spongeChild = (SpongeCommandContextBuilder) child;
        if (((SpongeCommandContextBuilder) child).representsNonBrigCommand()) {
            // special handling!
            final String rawCommand = parse.getReader().getString();
            final String[] command = spongeChild.nonBrigCommand();
            // we know this will exist.
            final CommandMapping mapping = this.commandManager.commandMapping(command[0]).get();
            return CommandUtil.createSuggestionsForRawCommand(rawCommand, spongeChild.nonBrigCommand(), spongeChild.cause(), mapping).buildFuture();
        }
    }
    // Sponge End
    final SuggestionContext<CommandSourceStack> nodeBeforeCursor = context.findSuggestionContext(cursor);
    final CommandNode<CommandSourceStack> parent = nodeBeforeCursor.parent;
    final int start = Math.min(nodeBeforeCursor.startPos, cursor);
    final String fullInput = parse.getReader().getString();
    final String truncatedInput = fullInput.substring(0, cursor);
    // Sponge Start: the collection might be different.
    final Collection<CommandNode<CommandSourceStack>> children;
    if (parent instanceof SpongeNode) {
        children = ((SpongeNode) parent).getChildrenForSuggestions();
    } else {
        children = parent.getChildren();
    }
    // @SuppressWarnings("unchecked") final CompletableFuture<Suggestions>[] futures = new CompletableFuture[parent.getChildren().size()];
    @SuppressWarnings("unchecked") final CompletableFuture<Suggestions>[] futures = new CompletableFuture[children.size()];
    // Sponge End
    int i = 0;
    for (final CommandNode<CommandSourceStack> node : children) {
        // Sponge: parent.getChildren() -> children
        CompletableFuture<Suggestions> future = Suggestions.empty();
        try {
            future = node.listSuggestions(context.build(truncatedInput), new SuggestionsBuilder(truncatedInput, start));
        } catch (final CommandSyntaxException ignored) {
        }
        futures[i++] = future;
    }
    // See https://github.com/Mojang/brigadier/pull/81
    return CompletableFuture.allOf(futures).handle((voidResult, exception) -> {
        final List<Suggestions> suggestions = new ArrayList<>();
        for (final CompletableFuture<Suggestions> future : futures) {
            if (!future.isCompletedExceptionally()) {
                suggestions.add(future.join());
            }
        }
        return Suggestions.merge(fullInput, suggestions);
    });
// Sponge End
}
Also used : LiteralCommandNode(com.mojang.brigadier.tree.LiteralCommandNode) DummyCommandNode(org.spongepowered.common.command.brigadier.tree.DummyCommandNode) SpongeRootCommandNode(org.spongepowered.common.command.brigadier.tree.SpongeRootCommandNode) RootCommandNode(com.mojang.brigadier.tree.RootCommandNode) SpongeArgumentCommandNode(org.spongepowered.common.command.brigadier.tree.SpongeArgumentCommandNode) CommandNode(com.mojang.brigadier.tree.CommandNode) ArrayList(java.util.ArrayList) CommandSourceStack(net.minecraft.commands.CommandSourceStack) SpongeCommandContextBuilder(org.spongepowered.common.command.brigadier.context.SpongeCommandContextBuilder) Suggestions(com.mojang.brigadier.suggestion.Suggestions) CompletableFuture(java.util.concurrent.CompletableFuture) CommandMapping(org.spongepowered.api.command.manager.CommandMapping) SpongeNode(org.spongepowered.common.command.brigadier.tree.SpongeNode) SuggestionsBuilder(com.mojang.brigadier.suggestion.SuggestionsBuilder) SpongeCommandSyntaxException(org.spongepowered.common.command.exception.SpongeCommandSyntaxException) CommandSyntaxException(com.mojang.brigadier.exceptions.CommandSyntaxException)

Example 2 with CommandNode

use of com.mojang.brigadier.tree.CommandNode in project SpongeCommon by SpongePowered.

the class SpongeParameterTranslator method createFlags.

@SuppressWarnings({ "unchecked", "rawtypes" })
private void createFlags(final LiteralCommandNode<CommandSourceStack> node, final List<Flag> flags, @Nullable final SpongeCommandExecutorWrapper wrapper) {
    final Collection<CommandNode<CommandSourceStack>> nodesToAddChildrenTo = new ArrayList<>();
    for (final Flag flag : flags) {
        // first create the literal.
        final Iterator<String> aliasIterator = flag.aliases().iterator();
        final LiteralArgumentBuilder<CommandSourceStack> flagLiteral = LiteralArgumentBuilder.literal(aliasIterator.next());
        flagLiteral.requires((Predicate) flag.requirement());
        final Collection<? extends CommandNode<CommandSourceStack>> toBeRedirected;
        final SpongeFlagLiteralCommandNode flagNode = new SpongeFlagLiteralCommandNode(flagLiteral, flag);
        if (flag.associatedParameter().isPresent()) {
            toBeRedirected = this.createAndAttachNode(Collections.singleton(flagNode), Collections.singletonList(flag.associatedParameter().get()), wrapper, node.getCommand() != null, false);
        } else {
            toBeRedirected = Collections.singletonList(flagNode);
        }
        for (final CommandNode<CommandSourceStack> candidate : toBeRedirected) {
            if (candidate instanceof SpongeNode && ((SpongeNode) candidate).canForceRedirect()) {
                ((SpongeNode) candidate).forceRedirect(node);
            } else {
                nodesToAddChildrenTo.add(candidate);
            }
        }
        node.addChild(flagNode);
        while (aliasIterator.hasNext()) {
            final LiteralArgumentBuilder<CommandSourceStack> nextFlag = LiteralArgumentBuilder.<CommandSourceStack>literal(aliasIterator.next()).executes(flagNode.getCommand());
            if (flagNode.getRedirect() != null) {
                nextFlag.redirect(flagNode.getRedirect());
            } else {
                nextFlag.redirect(flagNode);
            }
            node.addChild(new SpongeFlagLiteralCommandNode(nextFlag, flag));
        }
    }
    if (!nodesToAddChildrenTo.isEmpty()) {
        for (final CommandNode<CommandSourceStack> target : node.getChildren()) {
            if (!target.getChildren().isEmpty() && target instanceof SpongeFlagLiteralCommandNode) {
                nodesToAddChildrenTo.forEach(x -> x.addChild(((SpongeFlagLiteralCommandNode) target).cloneWithRedirectToThis()));
            } else {
                nodesToAddChildrenTo.forEach(x -> x.addChild(target));
            }
        }
    }
}
Also used : LiteralCommandNode(com.mojang.brigadier.tree.LiteralCommandNode) SpongeFlagLiteralCommandNode(org.spongepowered.common.command.brigadier.tree.SpongeFlagLiteralCommandNode) SpongeLiteralCommandNode(org.spongepowered.common.command.brigadier.tree.SpongeLiteralCommandNode) CommandNode(com.mojang.brigadier.tree.CommandNode) SpongeFlagLiteralCommandNode(org.spongepowered.common.command.brigadier.tree.SpongeFlagLiteralCommandNode) ArrayList(java.util.ArrayList) SpongeNode(org.spongepowered.common.command.brigadier.tree.SpongeNode) Flag(org.spongepowered.api.command.parameter.managed.Flag) CommandSourceStack(net.minecraft.commands.CommandSourceStack)

Example 3 with CommandNode

use of com.mojang.brigadier.tree.CommandNode in project SpongeCommon by SpongePowered.

the class CommandsMixin method impl$preventPutIntoMapIfNodeIsComplex.

@SuppressWarnings("unchecked")
@Redirect(method = "fillUsableCommands", at = @At(value = "INVOKE", target = "Ljava/util/Map;put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", remap = false))
private <K, V> V impl$preventPutIntoMapIfNodeIsComplex(final Map<K, V> map, final K key, final V value, final CommandNode<CommandSourceStack> rootCommandSource, final CommandNode<SharedSuggestionProvider> rootSuggestion, final CommandSourceStack source, final Map<CommandNode<CommandSourceStack>, CommandNode<SharedSuggestionProvider>> commandNodeToSuggestionNode) {
    if (!map.containsKey(key)) {
        // done here because this check is applicable
        final ServerPlayer e = (ServerPlayer) source.getEntity();
        final Map<CommandNode<CommandSourceStack>, List<CommandNode<SharedSuggestionProvider>>> playerNodes = this.impl$playerNodeCache.get(e);
        if (!playerNodes.containsKey(key)) {
            final List<CommandNode<SharedSuggestionProvider>> children = new ArrayList<>();
            children.add((CommandNode<SharedSuggestionProvider>) value);
            playerNodes.put((CommandNode<CommandSourceStack>) key, children);
        }
        // we need to swap it out.
        if (value instanceof ArgumentCommandNode && CommandUtil.checkForCustomSuggestions(rootSuggestion)) {
            rootSuggestion.addChild(this.impl$cloneArgumentCommandNodeWithoutSuggestions((ArgumentCommandNode<SharedSuggestionProvider, ?>) value));
        } else {
            rootSuggestion.addChild((CommandNode<SharedSuggestionProvider>) value);
        }
        return map.put(key, value);
    }
    // it's ignored anyway.
    return null;
}
Also used : ArgumentCommandNode(com.mojang.brigadier.tree.ArgumentCommandNode) SpongeArgumentCommandNode(org.spongepowered.common.command.brigadier.tree.SpongeArgumentCommandNode) ArgumentCommandNode(com.mojang.brigadier.tree.ArgumentCommandNode) RootCommandNode(com.mojang.brigadier.tree.RootCommandNode) SpongeArgumentCommandNode(org.spongepowered.common.command.brigadier.tree.SpongeArgumentCommandNode) CommandNode(com.mojang.brigadier.tree.CommandNode) ServerPlayer(net.minecraft.server.level.ServerPlayer) ArrayList(java.util.ArrayList) ArrayList(java.util.ArrayList) List(java.util.List) SharedSuggestionProvider(net.minecraft.commands.SharedSuggestionProvider) CommandSourceStack(net.minecraft.commands.CommandSourceStack) Redirect(org.spongepowered.asm.mixin.injection.Redirect)

Example 4 with CommandNode

use of com.mojang.brigadier.tree.CommandNode in project SpongeCommon by SpongePowered.

the class SpongeCommandDispatcher method parseNodes.

private ParseResults<CommandSourceStack> parseNodes(// Sponge: used in permission checks
final boolean isRoot, // Sponge: needed for handling what we do with defaults.
final boolean isSuggestion, final CommandNode<CommandSourceStack> node, final SpongeStringReader originalReader, final SpongeCommandContextBuilder contextSoFar) {
    final CommandSourceStack source = contextSoFar.getSource();
    // Sponge Start
    Map<CommandNode<CommandSourceStack>, CommandSyntaxException> errors = null;
    List<ParseResults<CommandSourceStack>> potentials = null;
    // Sponge End
    final int cursor = originalReader.getCursor();
    // Sponge Start: get relevant nodes if we're completing
    final Collection<? extends CommandNode<CommandSourceStack>> nodes;
    if (isSuggestion && node instanceof SpongeNode) {
        nodes = ((SpongeNode) node).getRelevantNodesForSuggestions(originalReader);
    } else if (originalReader.canRead()) {
        nodes = node.getRelevantNodes(originalReader);
    } else {
        // Reader cannot read anymore so ONLY nodes with parsers that do not read can be processed
        nodes = node.getChildren().stream().filter(n -> n instanceof SpongeArgumentCommandNode && (((SpongeArgumentCommandNode<?>) n).getParser().doesNotRead())).collect(Collectors.toList());
    }
    for (final CommandNode<CommandSourceStack> child : nodes) {
        final boolean doesNotRead = child instanceof SpongeArgumentCommandNode && ((SpongeArgumentCommandNode<?>) child).getParser().doesNotRead();
        // if (!child.canUse(source)) {
        if (!SpongeNodePermissionCache.canUse(isRoot, this, child, source)) {
            // Sponge End
            continue;
        }
        // Sponge Start
        final SpongeCommandContextBuilder context = contextSoFar.copy();
        final SpongeStringReader reader = new SpongeStringReader(originalReader);
        // Sponge End
        try {
            try {
                child.parse(reader, context);
            } catch (final RuntimeException ex) {
                throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherParseException().createWithContext(reader, ex.getMessage());
            }
            // Sponge Start: if we didn't consume anything we don't want Brig to complain at us.
            if (reader.getCursor() == cursor) {
                // of suggestions.
                if (isSuggestion && !reader.canRead()) {
                    throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherExpectedArgumentSeparator().createWithContext(reader);
                }
                reader.unskipWhitespace();
            } else if (reader.canRead()) {
                // Sponge End
                if (reader.peek() != CommandDispatcher.ARGUMENT_SEPARATOR_CHAR) {
                    throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherExpectedArgumentSeparator().createWithContext(reader);
                }
            }
        } catch (final CommandSyntaxException ex) {
            if (errors == null) {
                errors = new LinkedHashMap<>();
            }
            errors.put(child, ex);
            reader.setCursor(cursor);
            continue;
        }
        context.withCommand(child.getCommand());
        // must let it do so.
        if (this.shouldContinueTraversing(reader, child)) {
            // if (reader.canRead(child.getRedirect() == null ? 2 : 1)) {
            // Sponge End
            reader.skip();
            // Sponge Start: redirect is now in a local variable as we use it a fair bit
            final CommandNode<CommandSourceStack> redirect = child.getRedirect();
            if (redirect != null) {
                // If we've redirected to the root node, we may need to head back to our command
                // manager - so we should check to see if that's going to actually be the case.
                final boolean redirectingToRoot = redirect == this.getRoot();
                final SpongeCommandContextBuilder childContext = new SpongeCommandContextBuilder(this, source, child.getRedirect(), reader.getCursor());
                // For a redirect, we want to ensure all of our currently parsed information is available.
                context.applySpongeElementsTo(childContext, false);
                ParseResults<CommandSourceStack> parse = null;
                if (redirectingToRoot) {
                    // check to see if we can get an element
                    final ArgumentReader.Immutable snapshot = reader.immutable();
                    try {
                        final String commandToAttempt = reader.peekString();
                        final int offset = reader.cursor();
                        if (redirect.getChild(commandToAttempt) == null) {
                            // The redirect fails, so we intercept here, and reroute to our command
                            // manager with the rest of the string and the new context.
                            reader.parseString();
                            final boolean hasMore = reader.canRead();
                            reader.skipWhitespace();
                            final Optional<CommandMapping> optionalMapping = this.commandManager.commandMapping(commandToAttempt);
                            if (optionalMapping.isPresent()) {
                                final CommandMapping mapping = optionalMapping.get();
                                final String remaining = reader.remaining();
                                childContext.withCommand(commandContext -> {
                                    final SpongeCommandContext spongeContext = (SpongeCommandContext) commandContext;
                                    try {
                                        return mapping.registrar().process(spongeContext.cause(), mapping, commandToAttempt, remaining).result();
                                    } catch (final CommandException e) {
                                        throw new SpongeCommandSyntaxException(e, spongeContext);
                                    }
                                });
                                childContext.withNode(new DummyCommandNode(childContext.getCommand()), StringRange.between(offset, reader.totalLength()));
                                childContext.setNonBrigCommand(hasMore ? new String[] { commandToAttempt, remaining } : new String[] { commandToAttempt });
                                reader.setCursor(reader.totalLength());
                                parse = new ParseResults<>(childContext, reader, Collections.emptyMap());
                            } else {
                                // We'll just let this parser fail as normal.
                                reader.setState(snapshot);
                            }
                        }
                    } catch (final ArgumentParseException ignored) {
                        // ignore it, it'll handle it later.
                        reader.setState(snapshot);
                    }
                }
                if (parse == null) {
                    parse = this.parseNodes(redirect instanceof RootCommandNode, isSuggestion, child.getRedirect(), reader, childContext);
                }
                // It worked out, so let's apply it back. We clear and reapply, it's simpler than comparing and conditional adding.
                childContext.applySpongeElementsTo(context, true);
                // Sponge End
                context.withChild(parse.getContext());
                final ParseResults<CommandSourceStack> parse2 = new ParseResults<>(context, parse.getReader(), parse.getExceptions());
                if (doesNotRead && potentials != null) {
                    // If this is a optional or default parameter we only add the redirect as a potential option
                    potentials.add(parse2);
                    continue;
                }
                return parse2;
            } else {
                final ParseResults<CommandSourceStack> parse = this.parseNodes(false, isSuggestion, child, reader, context);
                if (potentials == null) {
                    potentials = new ArrayList<>(1);
                }
                potentials.add(parse);
            }
        } else {
            if (potentials == null) {
                potentials = new ArrayList<>(1);
            }
            potentials.add(new ParseResults<>(context, reader, Collections.emptyMap()));
        }
    }
    if (potentials != null) {
        if (potentials.size() > 1) {
            potentials.sort((a, b) -> {
                if (!a.getReader().canRead() && b.getReader().canRead()) {
                    return -1;
                }
                if (a.getReader().canRead() && !b.getReader().canRead()) {
                    return 1;
                }
                if (a.getExceptions().isEmpty() && !b.getExceptions().isEmpty()) {
                    return -1;
                }
                if (!a.getExceptions().isEmpty() && b.getExceptions().isEmpty()) {
                    return 1;
                }
                // If we get here both potentials parsed everything and there was no exception
                // BUT if parsing stopped at a non-terminal node this will cause an error later
                // see at the end of #execute() where !foundCommand
                // Instead we attempt to sort commands before that happens
                final Command<CommandSourceStack> aCommand = SpongeCommandDispatcher.getCommand(a.getContext());
                final Command<CommandSourceStack> bCommand = SpongeCommandDispatcher.getCommand(b.getContext());
                if (aCommand == null && bCommand != null) {
                    return 1;
                } else if (aCommand != null && bCommand == null) {
                    return -1;
                }
                return 0;
            });
        }
        return potentials.get(0);
    }
    return new ParseResults<>(contextSoFar, originalReader, errors == null ? Collections.emptyMap() : errors);
}
Also used : LiteralCommandNode(com.mojang.brigadier.tree.LiteralCommandNode) CommandDispatcher(com.mojang.brigadier.CommandDispatcher) EventContextKeys(org.spongepowered.api.event.EventContextKeys) CommandSourceStack(net.minecraft.commands.CommandSourceStack) Command(com.mojang.brigadier.Command) CommandContextBuilder(com.mojang.brigadier.context.CommandContextBuilder) CompletableFuture(java.util.concurrent.CompletableFuture) SpongeAdventure(org.spongepowered.common.adventure.SpongeAdventure) RedirectModifier(com.mojang.brigadier.RedirectModifier) SuggestionContext(com.mojang.brigadier.context.SuggestionContext) SpongeCommandContextBuilder(org.spongepowered.common.command.brigadier.context.SpongeCommandContextBuilder) ArrayList(java.util.ArrayList) LinkedHashMap(java.util.LinkedHashMap) CommandSourceStackBridge(org.spongepowered.common.bridge.commands.CommandSourceStackBridge) Map(java.util.Map) DummyCommandNode(org.spongepowered.common.command.brigadier.tree.DummyCommandNode) StringReader(com.mojang.brigadier.StringReader) SpongeRootCommandNode(org.spongepowered.common.command.brigadier.tree.SpongeRootCommandNode) CauseStackManager(org.spongepowered.api.event.CauseStackManager) SuggestionsBuilder(com.mojang.brigadier.suggestion.SuggestionsBuilder) RootCommandNode(com.mojang.brigadier.tree.RootCommandNode) SpongeCommandSyntaxException(org.spongepowered.common.command.exception.SpongeCommandSyntaxException) Suggestions(com.mojang.brigadier.suggestion.Suggestions) CommandContext(com.mojang.brigadier.context.CommandContext) CommandUtil(org.spongepowered.common.util.CommandUtil) Collection(java.util.Collection) ArgumentReader(org.spongepowered.api.command.parameter.ArgumentReader) PhaseTracker(org.spongepowered.common.event.tracking.PhaseTracker) Collectors(java.util.stream.Collectors) CommandMapping(org.spongepowered.api.command.manager.CommandMapping) AmbiguityConsumer(com.mojang.brigadier.AmbiguityConsumer) List(java.util.List) SpongeStringReader(org.spongepowered.common.command.brigadier.SpongeStringReader) ArgumentParseException(org.spongepowered.api.command.exception.ArgumentParseException) SpongeCommandContext(org.spongepowered.common.command.brigadier.context.SpongeCommandContext) ParseResults(com.mojang.brigadier.ParseResults) SpongeArgumentCommandNode(org.spongepowered.common.command.brigadier.tree.SpongeArgumentCommandNode) CommandNode(com.mojang.brigadier.tree.CommandNode) Optional(java.util.Optional) ResultConsumer(com.mojang.brigadier.ResultConsumer) CommandSyntaxException(com.mojang.brigadier.exceptions.CommandSyntaxException) CommandManager(org.spongepowered.api.command.manager.CommandManager) SpongeNode(org.spongepowered.common.command.brigadier.tree.SpongeNode) Collections(java.util.Collections) StringRange(com.mojang.brigadier.context.StringRange) CommandException(org.spongepowered.api.command.exception.CommandException) SpongeCommandManager(org.spongepowered.common.command.manager.SpongeCommandManager) DummyCommandNode(org.spongepowered.common.command.brigadier.tree.DummyCommandNode) LiteralCommandNode(com.mojang.brigadier.tree.LiteralCommandNode) DummyCommandNode(org.spongepowered.common.command.brigadier.tree.DummyCommandNode) SpongeRootCommandNode(org.spongepowered.common.command.brigadier.tree.SpongeRootCommandNode) RootCommandNode(com.mojang.brigadier.tree.RootCommandNode) SpongeArgumentCommandNode(org.spongepowered.common.command.brigadier.tree.SpongeArgumentCommandNode) CommandNode(com.mojang.brigadier.tree.CommandNode) SpongeRootCommandNode(org.spongepowered.common.command.brigadier.tree.SpongeRootCommandNode) RootCommandNode(com.mojang.brigadier.tree.RootCommandNode) ArgumentParseException(org.spongepowered.api.command.exception.ArgumentParseException) CommandSourceStack(net.minecraft.commands.CommandSourceStack) SpongeCommandContextBuilder(org.spongepowered.common.command.brigadier.context.SpongeCommandContextBuilder) LinkedHashMap(java.util.LinkedHashMap) SpongeStringReader(org.spongepowered.common.command.brigadier.SpongeStringReader) CommandMapping(org.spongepowered.api.command.manager.CommandMapping) SpongeNode(org.spongepowered.common.command.brigadier.tree.SpongeNode) SpongeCommandSyntaxException(org.spongepowered.common.command.exception.SpongeCommandSyntaxException) CommandSyntaxException(com.mojang.brigadier.exceptions.CommandSyntaxException) SpongeArgumentCommandNode(org.spongepowered.common.command.brigadier.tree.SpongeArgumentCommandNode) ArgumentReader(org.spongepowered.api.command.parameter.ArgumentReader) CommandException(org.spongepowered.api.command.exception.CommandException) SpongeCommandSyntaxException(org.spongepowered.common.command.exception.SpongeCommandSyntaxException) ParseResults(com.mojang.brigadier.ParseResults) SpongeCommandContext(org.spongepowered.common.command.brigadier.context.SpongeCommandContext)

Example 5 with CommandNode

use of com.mojang.brigadier.tree.CommandNode in project SpongeCommon by SpongePowered.

the class SpongeParameterTranslator method createAndAttachNode.

private Collection<? extends CommandNode<CommandSourceStack>> createAndAttachNode(final Collection<? extends CommandNode<CommandSourceStack>> parents, final List<Parameter> children, @Nullable final SpongeCommandExecutorWrapper executorWrapper, final boolean shouldTerminate, final boolean allowSubcommands) {
    final Set<CommandNode<CommandSourceStack>> nodesToAttachTo = new HashSet<>(parents);
    final ListIterator<Parameter> parameterIterator = children.listIterator();
    while (parameterIterator.hasNext()) {
        final Parameter parameter = parameterIterator.next();
        final boolean hasNext = parameterIterator.hasNext();
        if (parameter instanceof Parameter.Subcommand) {
            final Parameter.Subcommand subcommands = ((Parameter.Subcommand) parameter);
            if (!allowSubcommands) {
                throw new IllegalStateException("Subcommands are not allowed for this element (subcommands were " + String.join(", ", subcommands.aliases() + ")!"));
            }
            if (hasNext) {
                // this is a failure condition because there cannot be a parameter after a subcommand.
                throw new IllegalStateException("A parameter cannot be placed after a subcommand parameter!");
            }
            // If the child is a subcommand, get the subcommand and attach it. At this point,
            // we're done with the chain and we break out.
            final Collection<LiteralCommandNode<CommandSourceStack>> nodes = this.createCommandTree(subcommands.command(), subcommands.aliases());
            for (final LiteralCommandNode<CommandSourceStack> node : nodes) {
                nodesToAttachTo.forEach(x -> x.addChild(node));
            }
            // No further attaching should be done in this scenario
            return Collections.emptyList();
        } else {
            final boolean isOptional;
            final boolean isTerminal;
            final Collection<CommandNode<CommandSourceStack>> parametersToAttachTo;
            if (parameter instanceof SpongeMultiParameter) {
                isOptional = parameter.isOptional();
                isTerminal = !hasNext || parameter.isTerminal();
                // In these cases, we delegate to the parameter, which may return here.
                if (parameter instanceof SpongeFirstOfParameter) {
                    // take each parameter in turn and evaluate it, returns the terminal nodes of each block
                    parametersToAttachTo = new ArrayList<>();
                    for (final Parameter p : ((SpongeFirstOfParameter) parameter).childParameters()) {
                        final Collection<? extends CommandNode<CommandSourceStack>> branchNodesToAttachTo = this.createAndAttachNode(nodesToAttachTo, Collections.singletonList(p), executorWrapper, isTerminal, allowSubcommands);
                        parametersToAttachTo.addAll(branchNodesToAttachTo);
                    }
                } else {
                    // not so fancy stuff, it's a sequence, returns the terminal nodes of the block
                    parametersToAttachTo = new ArrayList<>(this.createAndAttachNode(nodesToAttachTo, ((SpongeMultiParameter) parameter).childParameters(), executorWrapper, isTerminal, allowSubcommands));
                }
            } else {
                // We have a Parameter.Value
                parametersToAttachTo = new ArrayList<>();
                final SpongeParameterValue<?> valueParameter = (SpongeParameterValue<?>) parameter;
                final boolean isConsumeAll = valueParameter.willConsumeAllRemaining();
                if (isConsumeAll && hasNext) {
                    // this should not happen.
                    throw new IllegalStateException("A parameter that consumes all must be at the end of a parameter chain.");
                }
                isOptional = valueParameter.isOptional();
                isTerminal = (shouldTerminate && !hasNext) || valueParameter.isTerminal();
                // Process the next element if it exists
                StringBuilder suffix = null;
                final Set<String> names = nodesToAttachTo.stream().flatMap(x -> x.getChildren().stream()).map(CommandNode::getName).collect(Collectors.toSet());
                String key = valueParameter.key().key();
                while (names.contains(key)) {
                    if (suffix == null) {
                        suffix = new StringBuilder(String.valueOf(names.size()));
                    } else {
                        suffix.append("_").append(names.size());
                    }
                    // prevents duplication
                    key = key + suffix;
                }
                final SpongeArgumentCommandNodeBuilder<?> thisNode = SpongeParameterTranslator.createArgumentNodeBuilders(valueParameter, suffix == null ? null : suffix.toString());
                if (isTerminal) {
                    thisNode.executes(executorWrapper);
                }
                // Apply the node to the parent.
                final CommandNode<CommandSourceStack> builtNode = thisNode.build();
                if (isConsumeAll) {
                    // the built child will return to the previous node, which will allow this to be called again.
                    builtNode.addChild(thisNode.redirect(builtNode).build());
                }
                parametersToAttachTo.add(builtNode);
                // Make sure the nodes we need to attach to have the nodes we need to
                nodesToAttachTo.forEach(x -> x.addChild(builtNode));
            }
            // If this is not optional, then we clear the "toAttachTo" list because we do not want to skip the parameter.
            if (!isOptional) {
                nodesToAttachTo.clear();
            }
            nodesToAttachTo.addAll(parametersToAttachTo);
        }
    }
    // If we should make any terminal parameters actually terminal, we do that now.
    if (shouldTerminate) {
        for (final CommandNode<CommandSourceStack> node : nodesToAttachTo) {
            // These are therefore terminal.
            if (node instanceof SpongeNode) {
                // they should be, but just in case
                ((SpongeNode) node).forceExecutor(executorWrapper);
            }
        }
    }
    return nodesToAttachTo;
}
Also used : LiteralCommandNode(com.mojang.brigadier.tree.LiteralCommandNode) SpongeFlagLiteralCommandNode(org.spongepowered.common.command.brigadier.tree.SpongeFlagLiteralCommandNode) SpongeLiteralCommandNode(org.spongepowered.common.command.brigadier.tree.SpongeLiteralCommandNode) CommandNode(com.mojang.brigadier.tree.CommandNode) CommandSourceStack(net.minecraft.commands.CommandSourceStack) LiteralCommandNode(com.mojang.brigadier.tree.LiteralCommandNode) SpongeFlagLiteralCommandNode(org.spongepowered.common.command.brigadier.tree.SpongeFlagLiteralCommandNode) SpongeLiteralCommandNode(org.spongepowered.common.command.brigadier.tree.SpongeLiteralCommandNode) SpongeNode(org.spongepowered.common.command.brigadier.tree.SpongeNode) HashSet(java.util.HashSet) SpongeParameterValue(org.spongepowered.common.command.parameter.SpongeParameterValue) SpongeMultiParameter(org.spongepowered.common.command.parameter.multi.SpongeMultiParameter) Parameter(org.spongepowered.api.command.parameter.Parameter) SpongeMultiParameter(org.spongepowered.common.command.parameter.multi.SpongeMultiParameter) SpongeFirstOfParameter(org.spongepowered.common.command.parameter.multi.SpongeFirstOfParameter) SpongeFirstOfParameter(org.spongepowered.common.command.parameter.multi.SpongeFirstOfParameter)

Aggregations

CommandNode (com.mojang.brigadier.tree.CommandNode)8 ArrayList (java.util.ArrayList)6 CommandSourceStack (net.minecraft.commands.CommandSourceStack)6 LiteralCommandNode (com.mojang.brigadier.tree.LiteralCommandNode)5 RootCommandNode (com.mojang.brigadier.tree.RootCommandNode)5 SpongeArgumentCommandNode (org.spongepowered.common.command.brigadier.tree.SpongeArgumentCommandNode)5 SpongeNode (org.spongepowered.common.command.brigadier.tree.SpongeNode)4 ArgumentCommandNode (com.mojang.brigadier.tree.ArgumentCommandNode)3 List (java.util.List)3 SharedSuggestionProvider (net.minecraft.commands.SharedSuggestionProvider)3 Redirect (org.spongepowered.asm.mixin.injection.Redirect)3 SpongeFlagLiteralCommandNode (org.spongepowered.common.command.brigadier.tree.SpongeFlagLiteralCommandNode)3 SpongeLiteralCommandNode (org.spongepowered.common.command.brigadier.tree.SpongeLiteralCommandNode)3 CommandSyntaxException (com.mojang.brigadier.exceptions.CommandSyntaxException)2 Suggestions (com.mojang.brigadier.suggestion.Suggestions)2 SuggestionsBuilder (com.mojang.brigadier.suggestion.SuggestionsBuilder)2 CompletableFuture (java.util.concurrent.CompletableFuture)2 Parameter (org.spongepowered.api.command.parameter.Parameter)2 CauseStackManager (org.spongepowered.api.event.CauseStackManager)2 CommandSourceStackBridge (org.spongepowered.common.bridge.commands.CommandSourceStackBridge)2