diff --git a/SpongeAPI b/SpongeAPI index d10c3bb6281..3a1ed9aa190 160000 --- a/SpongeAPI +++ b/SpongeAPI @@ -1 +1 @@ -Subproject commit d10c3bb62814ef36039293154ebb14917879eb85 +Subproject commit 3a1ed9aa1909bdc2de59f99544768e5f50644bb4 diff --git a/src/main/java/org/spongepowered/common/command/SpongeParameterizedCommand.java b/src/main/java/org/spongepowered/common/command/SpongeParameterizedCommand.java index 5e5b50ebce8..0fbe21fc47f 100644 --- a/src/main/java/org/spongepowered/common/command/SpongeParameterizedCommand.java +++ b/src/main/java/org/spongepowered/common/command/SpongeParameterizedCommand.java @@ -168,13 +168,7 @@ public LiteralCommandNode buildWithAlias(final String primaryAlia if (this.executor == null) { return (LiteralCommandNode) SpongeParameterTranslator.createCommandTreeWithSubcommandsOnly(primary, this.subcommands); } else { - return (LiteralCommandNode) SpongeParameterTranslator.createCommandTree( - primary, - this.flags, - this.parameters, - this.subcommands, - this - ); + return (LiteralCommandNode) SpongeParameterTranslator.createCommandTree(primary, this); } } @@ -187,7 +181,7 @@ public Collection> buildWithAliases(final Iter final LiteralArgumentBuilder secondary = LiteralArgumentBuilder.literal(iterable.next()); secondary.executes(built.getCommand()); secondary.requires(built.getRequirement()); - nodes.add(new SpongeLiteralCommandNode(secondary.redirect(built))); + nodes.add(new SpongeLiteralCommandNode(secondary.redirect(built), this)); } return nodes; diff --git a/src/main/java/org/spongepowered/common/command/brigadier/SpongeParameterTranslator.java b/src/main/java/org/spongepowered/common/command/brigadier/SpongeParameterTranslator.java index 56f352f2c95..a28cb372c7d 100644 --- a/src/main/java/org/spongepowered/common/command/brigadier/SpongeParameterTranslator.java +++ b/src/main/java/org/spongepowered/common/command/brigadier/SpongeParameterTranslator.java @@ -32,7 +32,6 @@ import net.minecraft.command.CommandSource; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.api.command.CommandExecutor; import org.spongepowered.api.command.parameter.Parameter; import org.spongepowered.api.command.parameter.managed.Flag; import org.spongepowered.api.command.parameter.managed.ValueCompleter; @@ -72,13 +71,10 @@ public static CommandNode createCommandTreeWithSubcommandsOnly( @SuppressWarnings({"unchecked"}) public static CommandNode createCommandTree( @NonNull final ArgumentBuilder rootNode, - @NonNull final List flags, - @NonNull final List parameters, - @NonNull final List subcommands, - @NonNull final CommandExecutor executor) { + @NonNull final SpongeParameterizedCommand command) { - final SpongeCommandExecutorWrapper executorWrapper = new SpongeCommandExecutorWrapper(executor); - final ListIterator parameterListIterator = parameters.listIterator(); + final SpongeCommandExecutorWrapper executorWrapper = new SpongeCommandExecutorWrapper(command); + final ListIterator parameterListIterator = command.parameters().listIterator(); // If we have no parameters, or they are all optional, all literals will get an executor. final boolean isTerminal = SpongeParameterTranslator.createNode( @@ -86,14 +82,14 @@ public static CommandNode createCommandTree( if (isTerminal) { rootNode.executes(executorWrapper); } - SpongeParameterTranslator.createSubcommands(rootNode, subcommands); + SpongeParameterTranslator.createSubcommands(rootNode, command.subcommands()); final CommandNode builtNode; if (rootNode instanceof LiteralArgumentBuilder) { - builtNode = new SpongeLiteralCommandNode((LiteralArgumentBuilder) rootNode); + builtNode = new SpongeLiteralCommandNode((LiteralArgumentBuilder) rootNode, command); } else { builtNode = rootNode.build(); } - SpongeParameterTranslator.createFlags(flags, builtNode, isTerminal ? executorWrapper : null); + SpongeParameterTranslator.createFlags(command.flags(), builtNode, isTerminal ? executorWrapper : null); return builtNode; } diff --git a/src/main/java/org/spongepowered/common/command/brigadier/context/SpongeCommandContext.java b/src/main/java/org/spongepowered/common/command/brigadier/context/SpongeCommandContext.java index 70eb769c8ae..119b4cb2e46 100644 --- a/src/main/java/org/spongepowered/common/command/brigadier/context/SpongeCommandContext.java +++ b/src/main/java/org/spongepowered/common/command/brigadier/context/SpongeCommandContext.java @@ -37,6 +37,7 @@ import net.kyori.adventure.text.Component; import net.minecraft.command.CommandSource; import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.api.command.CommandCause; import org.spongepowered.api.command.parameter.Parameter; import org.spongepowered.api.command.parameter.managed.Flag; @@ -48,13 +49,12 @@ import java.util.NoSuchElementException; import java.util.Optional; -import javax.annotation.Nullable; - public final class SpongeCommandContext extends CommandContext implements org.spongepowered.api.command.parameter.CommandContext { private final Map, Collection> argumentMap; private final Object2IntOpenHashMap flagMap; private final Map> brigArguments; + private final org.spongepowered.api.command.Command.@Nullable Parameterized targetCommand; public SpongeCommandContext( final CommandSource source, @@ -68,7 +68,8 @@ public SpongeCommandContext( final StringRange range, @Nullable final CommandContext child, @Nullable final RedirectModifier modifier, - final boolean forks) { + final boolean forks, + final org.spongepowered.api.command.Command.@Nullable Parameterized currentTargetCommand) { super(source, input, brigArguments, @@ -82,6 +83,13 @@ public SpongeCommandContext( this.brigArguments = brigArguments; this.argumentMap = arguments; this.flagMap = flags.clone(); + this.targetCommand = currentTargetCommand; + } + + @Override + @NonNull + public Optional getExecutedCommand() { + return Optional.ofNullable(this.targetCommand); } @Override @@ -169,7 +177,8 @@ public CommandContext copyFor(final CommandSource source) { this.getRange(), this.getChild(), this.getRedirectModifier(), - this.isForked()); + this.isForked(), + this.targetCommand); } @Nullable diff --git a/src/main/java/org/spongepowered/common/command/brigadier/context/SpongeCommandContextBuilder.java b/src/main/java/org/spongepowered/common/command/brigadier/context/SpongeCommandContextBuilder.java index 856b90328dd..cf5f648b1ea 100644 --- a/src/main/java/org/spongepowered/common/command/brigadier/context/SpongeCommandContextBuilder.java +++ b/src/main/java/org/spongepowered/common/command/brigadier/context/SpongeCommandContextBuilder.java @@ -70,6 +70,7 @@ public final class SpongeCommandContextBuilder extends CommandContextBuilder modifier; private boolean forks; private Deque transaction = null; + private org.spongepowered.api.command.Command.Parameterized currentTargetCommand = null; public SpongeCommandContextBuilder( final CommandDispatcher dispatcher, @@ -89,6 +90,7 @@ public SpongeCommandContextBuilder(final SpongeCommandContextBuilder original, f this.modifier = original.modifier; this.forks = original.forks; + this.currentTargetCommand = original.currentTargetCommand; this.withChild(original.getChild()); this.withCommand(original.getCommand()); original.getArguments().forEach(this::withArgument); @@ -107,6 +109,7 @@ public void applySpongeElementsTo(final SpongeCommandContextBuilder builder, fin } this.flagMap.object2IntEntrySet().fastForEach(x -> builder.flagMap.put(x.getKey(), x.getIntValue())); this.arguments.forEach((key, values) -> builder.arguments.computeIfAbsent(key, k -> new ArrayList<>()).addAll((Collection) values)); + this.currentTargetCommand = builder.currentTargetCommand; } @Override @@ -264,6 +267,7 @@ public CommandContextBuilder withCommand(final Command getExecutedCommand() { + if (this.transaction != null && !this.transaction.isEmpty()) { + return this.transaction.peek().getCopyBuilder().getExecutedCommand(); + } + return Optional.ofNullable(this.currentTargetCommand); + } + @Override @NonNull public CommandCause getCause() { @@ -344,7 +357,7 @@ public Collection getAll(final Parameter.@NonNull Key key) { } @Override - public void sendMessage(final Component message) { + public void sendMessage(@NonNull final Component message) { this.getCause().sendMessage(message); } @@ -382,6 +395,14 @@ public void putEntry(final Parameter.@NonNull Key key, @NonNull final T o } } + public void setCurrentTargetCommand(final org.spongepowered.api.command.Command.Parameterized command) { + if (this.transaction != null && !this.transaction.isEmpty()) { + this.transaction.peek().setCurrentTargetCommand(command); + } else { + this.currentTargetCommand = command; + } + } + @Override @NonNull public Transaction startTransaction() { @@ -417,7 +438,8 @@ public SpongeCommandContext build(@NonNull final String input) { this.getRange(), child == null ? null : child.build(input), this.modifier, - this.forks); + this.forks, + this.currentTargetCommand); } @Override diff --git a/src/main/java/org/spongepowered/common/command/brigadier/context/SpongeCommandContextBuilderTransaction.java b/src/main/java/org/spongepowered/common/command/brigadier/context/SpongeCommandContextBuilderTransaction.java index e3538ece455..ee957031bd9 100644 --- a/src/main/java/org/spongepowered/common/command/brigadier/context/SpongeCommandContextBuilderTransaction.java +++ b/src/main/java/org/spongepowered/common/command/brigadier/context/SpongeCommandContextBuilderTransaction.java @@ -44,6 +44,7 @@ public final class SpongeCommandContextBuilderTransaction implements CommandContext.Builder.Transaction { private static final LinkedList TRANSACTION_POOL = new LinkedList<>(); + public static SpongeCommandContextBuilderTransaction getTransactionFromPool(final SpongeCommandContextBuilder builder) { SpongeCommandContextBuilderTransaction chosenTransaction = null; for (final SpongeCommandContextBuilderTransaction transaction : TRANSACTION_POOL) { @@ -73,6 +74,7 @@ public static SpongeCommandContextBuilderTransaction getTransactionFromPool(fina private final LinkedList, ?>> putEntryCapture = new LinkedList<>(); private final LinkedList> withChildCapture = new LinkedList<>(); private final LinkedList> withCommandCapture = new LinkedList<>(); + private org.spongepowered.api.command.Command.Parameterized currentTargetCommandCapture = null; private boolean isActive = false; @@ -158,6 +160,10 @@ public SpongeCommandContextBuilder getCopyBuilder() { return this.copyBuilder; } + public void setCurrentTargetCommand(final org.spongepowered.api.command.Command.Parameterized command) { + this.currentTargetCommandCapture = command; + } + public boolean isActive() { if (this.isActive) { if (this.builder.get() != null) { @@ -178,6 +184,9 @@ public void commit() { this.withCommandCapture.forEach(builderRef::withCommand); this.putEntryCapture.forEach(x -> this.putEntryAbusingGenerics(builderRef, x.getFirst(), x.getSecond())); this.flagCapture.forEach(builderRef::addFlagInvocation); + if (this.currentTargetCommandCapture != null) { + builderRef.setCurrentTargetCommand(this.currentTargetCommandCapture); + } } // we're clearing anyway! @@ -192,6 +201,7 @@ public void rollback() { this.withChildCapture.clear(); this.putEntryCapture.clear(); this.flagCapture.clear(); + this.currentTargetCommandCapture = null; this.copyBuilder = null; this.builder = null; } diff --git a/src/main/java/org/spongepowered/common/command/brigadier/tree/SpongeArgumentCommandNode.java b/src/main/java/org/spongepowered/common/command/brigadier/tree/SpongeArgumentCommandNode.java index eec8007bcff..9c463d35ca4 100644 --- a/src/main/java/org/spongepowered/common/command/brigadier/tree/SpongeArgumentCommandNode.java +++ b/src/main/java/org/spongepowered/common/command/brigadier/tree/SpongeArgumentCommandNode.java @@ -40,8 +40,6 @@ import com.mojang.brigadier.suggestion.SuggestionsBuilder; import com.mojang.brigadier.tree.ArgumentCommandNode; import com.mojang.brigadier.tree.CommandNode; -import com.mojang.brigadier.tree.LiteralCommandNode; -import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; import net.minecraft.command.CommandSource; import net.minecraft.command.ISuggestionProvider; import org.checkerframework.checker.nullness.qual.Nullable; diff --git a/src/main/java/org/spongepowered/common/command/brigadier/tree/SpongeLiteralCommandNode.java b/src/main/java/org/spongepowered/common/command/brigadier/tree/SpongeLiteralCommandNode.java index c8c475c5a8d..ae16d694dc6 100644 --- a/src/main/java/org/spongepowered/common/command/brigadier/tree/SpongeLiteralCommandNode.java +++ b/src/main/java/org/spongepowered/common/command/brigadier/tree/SpongeLiteralCommandNode.java @@ -24,10 +24,16 @@ */ package org.spongepowered.common.command.brigadier.tree; +import com.mojang.brigadier.StringReader; import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContextBuilder; +import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.tree.CommandNode; import com.mojang.brigadier.tree.LiteralCommandNode; import net.minecraft.command.CommandSource; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.api.command.Command; +import org.spongepowered.common.command.brigadier.context.SpongeCommandContextBuilder; import java.util.Collection; @@ -35,8 +41,15 @@ public class SpongeLiteralCommandNode extends LiteralCommandNode // used so we can have insertion order. private final UnsortedNodeHolder nodeHolder = new UnsortedNodeHolder(); + private final Command.@Nullable Parameterized subcommandIfApplicable; public SpongeLiteralCommandNode(final LiteralArgumentBuilder argumentBuilder) { + this(argumentBuilder, null); + } + + public SpongeLiteralCommandNode( + final LiteralArgumentBuilder argumentBuilder, + final Command.@Nullable Parameterized associatedSubcommand) { super(argumentBuilder.getLiteral(), argumentBuilder.getCommand(), argumentBuilder.getRequirement(), @@ -44,6 +57,7 @@ public SpongeLiteralCommandNode(final LiteralArgumentBuilder argu argumentBuilder.getRedirectModifier(), argumentBuilder.isFork()); argumentBuilder.getArguments().forEach(this::addChild); + this.subcommandIfApplicable = associatedSubcommand; } @Override @@ -57,4 +71,11 @@ public final Collection> getChildrenForSuggestions() return this.nodeHolder.getChildrenForSuggestions(); } + @Override + public void parse(final StringReader reader, final CommandContextBuilder contextBuilder) throws CommandSyntaxException { + super.parse(reader, contextBuilder); + if (this.subcommandIfApplicable != null && contextBuilder instanceof SpongeCommandContextBuilder) { + ((SpongeCommandContextBuilder) contextBuilder).setCurrentTargetCommand(this.subcommandIfApplicable); + } + } } diff --git a/src/main/java/org/spongepowered/common/command/sponge/SpongeCommand.java b/src/main/java/org/spongepowered/common/command/sponge/SpongeCommand.java index 2377ac51c7c..e199fc1595d 100644 --- a/src/main/java/org/spongepowered/common/command/sponge/SpongeCommand.java +++ b/src/main/java/org/spongepowered/common/command/sponge/SpongeCommand.java @@ -180,7 +180,7 @@ private CommandResult rootCommand(final CommandContext context) { final PluginContainer apiPlugin = Launcher.getInstance().getApiPlugin(); final PluginContainer minecraftPlugin = Launcher.getInstance().getMinecraftPlugin(); - context.getCause().sendMessage(TextComponent.builder().append( + context.sendMessage(TextComponent.builder().append( TextComponent.of("SpongePowered", NamedTextColor.YELLOW, TextDecoration.BOLD).append(TextComponent.space()), TextComponent.of("Plugin Platform (running on Minecraft " + minecraftPlugin.getMetadata().getVersion() + ")"), TextComponent.newline(), @@ -189,6 +189,25 @@ private CommandResult rootCommand(final CommandContext context) { TextComponent.of(platformPlugin.getMetadata().getName().get() + ": " + platformPlugin.getMetadata().getVersion()) ).build() ); + + final Optional parameterized = context.getExecutedCommand(); + if (parameterized.isPresent()) { + final String subcommands = parameterized.get() + .subcommands() + .stream() + .filter(x -> x.getCommand().canExecute(context.getCause())) + .flatMap(x -> x.getAliases().stream()) + .collect(Collectors.joining(", ")); + if (!subcommands.isEmpty()) { + context.sendMessage(TextComponent.builder().append( + TextComponent.newline(), + TextComponent.of("Available subcommands:"), + TextComponent.newline(), + TextComponent.of(subcommands)).build() + ); + } + } + return CommandResult.success(); }