use of org.spongepowered.api.command.manager.CommandMapping 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 org.spongepowered.api.command.manager.CommandMapping in project SpongeCommon by SpongePowered.
the class ServerGamePacketListenerImplMixin method impl$getSuggestionsFromNonBrigCommand.
@Inject(method = "handleCustomCommandSuggestions", at = @At(value = "NEW", target = "com/mojang/brigadier/StringReader", remap = false), cancellable = true)
private void impl$getSuggestionsFromNonBrigCommand(final ServerboundCommandSuggestionPacket packet, final CallbackInfo ci) {
final String rawCommand = packet.getCommand();
final String[] command = CommandUtil.extractCommandString(rawCommand);
final CommandCause cause = CommandCause.create();
final SpongeCommandManager manager = SpongeCommandManager.get(this.server);
if (!rawCommand.contains(" ")) {
final SuggestionsBuilder builder = new SuggestionsBuilder(command[0], 0);
if (command[0].isEmpty()) {
manager.getAliasesForCause(cause).forEach(builder::suggest);
} else {
manager.getAliasesThatStartWithForCause(cause, command[0]).forEach(builder::suggest);
}
this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), builder.build()));
ci.cancel();
} else {
final Optional<CommandMapping> mappingOptional = manager.commandMapping(command[0].toLowerCase(Locale.ROOT)).filter(x -> !(x.registrar() instanceof BrigadierBasedRegistrar));
if (mappingOptional.isPresent()) {
final CommandMapping mapping = mappingOptional.get();
if (mapping.registrar().canExecute(cause, mapping)) {
final SuggestionsBuilder builder = CommandUtil.createSuggestionsForRawCommand(rawCommand, command, cause, mapping);
this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), builder.build()));
} else {
this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), Suggestions.empty().join()));
}
ci.cancel();
}
}
}
use of org.spongepowered.api.command.manager.CommandMapping in project SpongeCommon by SpongePowered.
the class SpongeParameterizedCommandRegistrar method register.
@Override
@NonNull
public CommandMapping register(@NonNull final PluginContainer container, final Command.@NonNull Parameterized command, @NonNull final String primaryAlias, @NonNull final String@NonNull ... secondaryAliases) throws CommandFailedRegistrationException {
// Get the builder and the first literal.
final String namespacedCommand = container.metadata().id() + ":" + primaryAlias.toLowerCase(Locale.ROOT);
final ArrayList<String> aliases = new ArrayList<>();
aliases.add(primaryAlias);
aliases.addAll(Arrays.asList(secondaryAliases));
// This will throw an error if there is an issue.
final CommandMapping mapping = this.commandManager().registerAliasWithNamespacing(this, container, namespacedCommand, aliases, null);
this.createNode(mapping, command).forEach(this.commandManager().getDispatcher()::register);
((SpongeParameterizedCommand) command).setCommandManager(this.commandManager());
this.commandMap.put(mapping, command);
return mapping;
}
use of org.spongepowered.api.command.manager.CommandMapping 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 org.spongepowered.api.command.manager.CommandMapping in project SpongeCommon by SpongePowered.
the class SpongeCommand method whichExecutor.
@NonNull
private CommandResult whichExecutor(final CommandContext context) {
final CommandMapping mapping = context.requireOne(this.commandMappingKey);
context.sendMessage(Identity.nil(), Component.text().append(this.title("Aliases: "), Component.join(JoinConfiguration.separator(Component.text(", ")), mapping.allAliases().stream().map(x -> Component.text(x, NamedTextColor.YELLOW)).collect(Collectors.toList())), Component.newline(), this.title("Owned by: "), this.hl(mapping.plugin().map(x -> x.metadata().name().orElseGet(() -> x.metadata().id())).orElse("unknown"))).build());
return CommandResult.success();
}
Aggregations