Initial command redirection improvement

This commit is contained in:
themode 2021-03-15 09:01:29 +01:00
parent 509f8f7851
commit 3c7a96dc4a
7 changed files with 135 additions and 57 deletions

View File

@ -1,5 +1,6 @@
package net.minestom.server.command;
import it.unimi.dsi.fastutil.Pair;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
@ -9,6 +10,9 @@ import net.minestom.server.command.builder.*;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.arguments.minecraft.SuggestionType;
import net.minestom.server.command.builder.condition.CommandCondition;
import net.minestom.server.command.builder.parser.ArgumentQueryResult;
import net.minestom.server.command.builder.parser.CommandParser;
import net.minestom.server.command.builder.parser.CommandQueryResult;
import net.minestom.server.entity.Player;
import net.minestom.server.event.player.PlayerCommandEvent;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
@ -310,9 +314,46 @@ public final class CommandManager {
rootNode.flags = 0;
nodes.add(rootNode);
Map<Command, Integer> commandIdentityMap = new IdentityHashMap<>();
Map<Argument<?>, DeclareCommandsPacket.Node[]> argumentIdentityMap = new IdentityHashMap<>();
List<Pair<String, NodeMaker.Request>> nodeRequests = new ArrayList<>();
// Brigadier-like commands
for (Command command : dispatcher.getCommands()) {
serializeCommand(player, command, nodes, rootChildren);
final int commandNodeIndex = serializeCommand(player, command, nodes, rootChildren, commandIdentityMap, argumentIdentityMap, nodeRequests);
commandIdentityMap.put(command, commandNodeIndex);
}
// Answer to all node requests
for (Pair<String, NodeMaker.Request> pair : nodeRequests) {
String input = pair.left();
NodeMaker.Request request = pair.right();
final CommandQueryResult commandQueryResult = CommandParser.findCommand(input);
if (commandQueryResult == null) {
// Invalid command, return root node
request.retrieve(0);
continue;
}
final ArgumentQueryResult queryResult = CommandParser.findEligibleArgument(commandQueryResult.command,
commandQueryResult.args, input, false, argument -> true);
if (queryResult == null) {
// Invalid argument, return command node
int commandNode = commandIdentityMap.get(commandQueryResult.command);
request.retrieve(commandNode);
continue;
}
// Retrieve argument node
Argument<?> argument = queryResult.argument;
DeclareCommandsPacket.Node[] argNodes = argumentIdentityMap.get(argument);
for (DeclareCommandsPacket.Node argNode : argNodes) {
int node = argNode.children[0];
request.retrieve(node);
break;
}
}
// Pair<CommandName,EnabledTracking>
@ -371,7 +412,10 @@ public final class CommandManager {
private int serializeCommand(CommandSender sender, Command command,
List<DeclareCommandsPacket.Node> nodes,
IntList rootChildren) {
IntList rootChildren,
Map<Command, Integer> commandIdentityMap,
Map<Argument<?>, DeclareCommandsPacket.Node[]> argumentIdentityMap,
List<Pair<String, NodeMaker.Request>> nodeRequests) {
// Check if player should see this command
final CommandCondition commandCondition = command.getCondition();
if (commandCondition != null) {
@ -386,15 +430,16 @@ public final class CommandManager {
final Collection<CommandSyntax> syntaxes = command.getSyntaxes();
// Create command for main name
final DeclareCommandsPacket.Node mainNode = createCommand(sender, nodes, cmdChildren,
command.getName(), syntaxes, rootChildren);
final DeclareCommandsPacket.Node mainNode = createCommandNodes(sender, nodes, cmdChildren,
command.getName(), syntaxes, rootChildren, argumentIdentityMap, nodeRequests);
final int mainNodeIndex = nodes.indexOf(mainNode);
// Serialize all the subcommands
for (Command subcommand : command.getSubcommands()) {
final int subNodeIndex = serializeCommand(sender, subcommand, nodes, cmdChildren);
final int subNodeIndex = serializeCommand(sender, subcommand, nodes, cmdChildren, commandIdentityMap, argumentIdentityMap, nodeRequests);
if (subNodeIndex != -1) {
mainNode.children = ArrayUtils.concatenateIntArrays(mainNode.children, new int[]{subNodeIndex});
commandIdentityMap.put(subcommand, subNodeIndex);
}
}
@ -426,12 +471,14 @@ public final class CommandManager {
* @param rootChildren the children of the main node (all commands name)
* @return The index of the main node for alias redirection
*/
private DeclareCommandsPacket.Node createCommand(@NotNull CommandSender sender,
@NotNull List<DeclareCommandsPacket.Node> nodes,
@NotNull IntList cmdChildren,
@NotNull String name,
@NotNull Collection<CommandSyntax> syntaxes,
@NotNull IntList rootChildren) {
private DeclareCommandsPacket.Node createCommandNodes(@NotNull CommandSender sender,
@NotNull List<DeclareCommandsPacket.Node> nodes,
@NotNull IntList cmdChildren,
@NotNull String name,
@NotNull Collection<CommandSyntax> syntaxes,
@NotNull IntList rootChildren,
@NotNull Map<Argument<?>, DeclareCommandsPacket.Node[]> argumentIdentityMap,
@NotNull List<Pair<String, NodeMaker.Request>> nodeRequests) {
DeclareCommandsPacket.Node literalNode = createMainNode(name, syntaxes.isEmpty());
@ -465,7 +512,7 @@ public final class CommandManager {
final Argument<?> argument = arguments[i];
final boolean isLast = i == arguments.length - 1;
// Search previously parsed syntaxes to find identical part in order to create a node between those
// Search previously parsed syntaxes to find identical part in order to create a link between those
{
// Find shared part
boolean foundSharedPart = false;
@ -536,9 +583,14 @@ public final class CommandManager {
lastArgumentNodeIndex = nodesLayer.size();
}
}
nodeRequests.addAll(nodeMaker.getNodeRequests());
syntaxesArguments.add(arguments);
}
storedArgumentsNodes.forEach((argument, nodes1) -> argumentIdentityMap.put(argument, nodes1.get(0)));
literalNode.children = ArrayUtils.toArray(cmdChildren);
return literalNode;

View File

@ -118,21 +118,17 @@ public class CommandDispatcher {
final String[] parts = commandString.split(StringUtils.SPACE);
final String commandName = parts[0];
String[] args = new String[parts.length - 1];
System.arraycopy(parts, 1, args, 0, args.length);
final CommandQueryResult commandQueryResult = CommandParser.findCommand(commandName, args);
final CommandQueryResult commandQueryResult = CommandParser.findCommand(commandString);
// Check if the command exists
if (commandQueryResult == null) {
return CommandResult.of(CommandResult.Type.UNKNOWN, commandName);
}
final Command command = commandQueryResult.command;
args = commandQueryResult.args;
CommandResult result = new CommandResult();
result.input = commandString;
// Find the used syntax and fill CommandResult#type and CommandResult#parsedCommand
findParsedCommand(command, commandName, args, commandString, result);
findParsedCommand(command, commandName, commandQueryResult.args, commandString, result);
// Cache result
{

View File

@ -1,10 +1,10 @@
package net.minestom.server.command.builder;
import it.unimi.dsi.fastutil.Pair;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
@ -15,8 +15,7 @@ public class NodeMaker {
private final List<DeclareCommandsPacket.Node[]> nodes = new ArrayList<>(2);
private final Object2IntMap<DeclareCommandsPacket.Node> nodeIdsMap = new Object2IntOpenHashMap<>();
private Rule rule;
private int ruleCount;
private final List<Pair<String, Request>> nodeRequests = new ArrayList<>();
public NodeMaker(@NotNull DeclareCommandsPacket.Node[] commandNodes, int id) {
addNodes(commandNodes);
@ -41,26 +40,11 @@ public class NodeMaker {
}
public void addNodes(@NotNull DeclareCommandsPacket.Node[] nodes) {
Options options = null;
if (rule != null) {
options = rule.listen(nodes, ruleCount++);
}
if (options == null) {
options = new Options();
}
Options options = new Options();
this.configuredNodes.add(ConfiguredNodes.of(nodes, options));
this.nodes.add(nodes);
}
public void setRule(@NotNull Rule rule) {
this.rule = rule;
}
public void resetRule() {
this.rule = null;
this.ruleCount = 0;
}
@NotNull
public List<ConfiguredNodes> getConfiguredNodes() {
return configuredNodes;
@ -75,6 +59,14 @@ public class NodeMaker {
return nodeIdsMap;
}
public void request(String input, Request request) {
this.nodeRequests.add(Pair.of(input, request));
}
public List<Pair<String, Request>> getNodeRequests() {
return nodeRequests;
}
public static class ConfiguredNodes {
private DeclareCommandsPacket.Node[] nodes;
private Options options;
@ -95,11 +87,6 @@ public class NodeMaker {
}
}
public interface Rule {
@Nullable
Options listen(DeclareCommandsPacket.Node[] nodes, int count);
}
public static class Options {
private boolean updateLastNode = true;
@ -128,4 +115,9 @@ public class NodeMaker {
}
}
@FunctionalInterface
public interface Request {
void retrieve(int id);
}
}

View File

@ -6,12 +6,15 @@ import net.minestom.server.command.builder.CommandResult;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
public class ArgumentCommand extends Argument<CommandResult> {
public static final int INVALID_COMMAND_ERROR = 1;
private String shortcut = "";
public ArgumentCommand(@NotNull String id) {
super(id, true, true);
}
@ -19,8 +22,11 @@ public class ArgumentCommand extends Argument<CommandResult> {
@NotNull
@Override
public CommandResult parse(@NotNull String input) throws ArgumentSyntaxException {
final String commandString = !shortcut.isEmpty() ?
shortcut + StringUtils.SPACE + input
: input;
CommandDispatcher dispatcher = MinecraftServer.getCommandManager().getDispatcher();
CommandResult result = dispatcher.parse(input);
CommandResult result = dispatcher.parse(commandString);
if (result.getType() != CommandResult.Type.SUCCESS)
throw new ArgumentSyntaxException("Invalid command", input, INVALID_COMMAND_ERROR);
@ -32,10 +38,28 @@ public class ArgumentCommand extends Argument<CommandResult> {
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
final DeclareCommandsPacket.Node[] lastNodes = nodeMaker.getLatestNodes();
// FIXME check if lastNodes is null
for (DeclareCommandsPacket.Node node : lastNodes) {
node.flags |= 0x08; // Redirection mask
node.redirectedNode = 0; // Redirect to root
if (!shortcut.isEmpty()) {
nodeMaker.request(shortcut, (id) -> {
for (DeclareCommandsPacket.Node node : lastNodes) {
node.flags |= 0x08; // Redirection mask
node.redirectedNode = id;
}
});
} else {
for (DeclareCommandsPacket.Node node : lastNodes) {
node.flags |= 0x08; // Redirection mask
node.redirectedNode = 0; // Redirect to root
}
}
}
@NotNull
public String getShortcut() {
return shortcut;
}
public ArgumentCommand setShortcut(@NotNull String shortcut) {
this.shortcut = shortcut;
return this;
}
}

View File

@ -13,6 +13,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.Function;
public class CommandParser {
@ -49,6 +50,16 @@ public class CommandParser {
return commandQueryResult;
}
@Nullable
public static CommandQueryResult findCommand(@NotNull String input) {
final String[] parts = input.split(StringUtils.SPACE);
final String commandName = parts[0];
String[] args = new String[parts.length - 1];
System.arraycopy(parts, 1, args, 0, args.length);
return CommandParser.findCommand(commandName, args);
}
public static void parse(@Nullable CommandSyntax syntax, @NotNull Argument<?>[] commandArguments, @NotNull String[] inputArguments,
@NotNull String commandString,
@Nullable List<ValidSyntaxHolder> validSyntaxes,
@ -153,8 +164,8 @@ public class CommandParser {
}
@Nullable
public static ArgumentQueryResult findSuggestibleArgument(@NotNull Command command, String[] args, String commandString,
boolean trailingSpace) {
public static ArgumentQueryResult findEligibleArgument(@NotNull Command command, String[] args, String commandString,
boolean trailingSpace, Function<Argument<?>, Boolean> eligibilityFunction) {
final Collection<CommandSyntax> syntaxes = command.getSyntaxes();
Int2ObjectRBTreeMap<ArgumentQueryResult> suggestions = new Int2ObjectRBTreeMap<>(Collections.reverseOrder());
@ -190,7 +201,8 @@ public class CommandParser {
context.setArg(argument.getId(), argumentResult.parsedValue, argumentResult.rawArg);
}
if (argument.hasSuggestion()) {
// Save result
if (eligibilityFunction.apply(argument)) {
ArgumentQueryResult queryResult = new ArgumentQueryResult();
queryResult.syntax = syntax;
queryResult.argument = argument;

View File

@ -24,17 +24,15 @@ public class TabCompleteListener {
String commandName = split[0];
String args = commandString.replaceFirst(commandName, "");
String[] argsSplit = new String[split.length - 1];
System.arraycopy(split, 1, argsSplit, 0, argsSplit.length);
final CommandQueryResult commandQueryResult = CommandParser.findCommand(commandName, argsSplit);
final CommandQueryResult commandQueryResult = CommandParser.findCommand(commandString);
if (commandQueryResult == null) {
// Command not found
return;
}
final ArgumentQueryResult queryResult = CommandParser.findSuggestibleArgument(commandQueryResult.command,
commandQueryResult.args, commandString, text.endsWith(StringUtils.SPACE));
final ArgumentQueryResult queryResult = CommandParser.findEligibleArgument(commandQueryResult.command,
commandQueryResult.args, commandString, text.endsWith(StringUtils.SPACE), Argument::hasSuggestion);
if (queryResult == null) {
// Suggestible argument not found
return;

View File

@ -6,7 +6,7 @@ import net.minestom.server.command.builder.CommandContext;
import net.minestom.server.command.builder.suggestion.SuggestionEntry;
import static net.minestom.server.command.builder.arguments.ArgumentType.Integer;
import static net.minestom.server.command.builder.arguments.ArgumentType.Word;
import static net.minestom.server.command.builder.arguments.ArgumentType.*;
public class TestCommand extends Command {
@ -24,7 +24,11 @@ public class TestCommand extends Command {
addSyntax((sender, context) -> {
System.out.println("executed");
}, test1, test2);
}, Literal("test"), test1, test2);
addSyntax((sender, context) -> {
System.out.println("cmd syntax");
}, Literal("debug"), Command("cmd"));
}