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
}
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));
}
}
}
}
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;
}
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);
}
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;
}
Aggregations