mirror of
https://github.com/Minestom/Minestom.git
synced 2025-02-03 05:51:37 +01:00
Merge pull request #171 from Minestom/command-redirection
Command redirection improvement
This commit is contained in:
commit
98b453714e
@ -1,5 +1,6 @@
|
|||||||
package net.minestom.server.command;
|
package net.minestom.server.command;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.Pair;
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
import it.unimi.dsi.fastutil.ints.IntList;
|
import it.unimi.dsi.fastutil.ints.IntList;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
|
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.Argument;
|
||||||
import net.minestom.server.command.builder.arguments.minecraft.SuggestionType;
|
import net.minestom.server.command.builder.arguments.minecraft.SuggestionType;
|
||||||
import net.minestom.server.command.builder.condition.CommandCondition;
|
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.entity.Player;
|
||||||
import net.minestom.server.event.player.PlayerCommandEvent;
|
import net.minestom.server.event.player.PlayerCommandEvent;
|
||||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||||
@ -310,9 +314,42 @@ public final class CommandManager {
|
|||||||
rootNode.flags = 0;
|
rootNode.flags = 0;
|
||||||
nodes.add(rootNode);
|
nodes.add(rootNode);
|
||||||
|
|
||||||
|
Map<Command, Integer> commandIdentityMap = new IdentityHashMap<>();
|
||||||
|
Map<Argument<?>, Integer> argumentIdentityMap = new IdentityHashMap<>();
|
||||||
|
|
||||||
|
List<Pair<String, NodeMaker.Request>> nodeRequests = new ArrayList<>();
|
||||||
|
|
||||||
// Brigadier-like commands
|
// Brigadier-like commands
|
||||||
for (Command command : dispatcher.getCommands()) {
|
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, true, syntax -> true, argument -> true);
|
||||||
|
if (queryResult == null) {
|
||||||
|
// Invalid argument, return command node (default to root)
|
||||||
|
final int commandNode = commandIdentityMap.getOrDefault(commandQueryResult.command, 0);
|
||||||
|
request.retrieve(commandNode);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve argument node
|
||||||
|
final Argument<?> argument = queryResult.argument;
|
||||||
|
final int argumentNode = argumentIdentityMap.getOrDefault(argument, 0);
|
||||||
|
request.retrieve(argumentNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pair<CommandName,EnabledTracking>
|
// Pair<CommandName,EnabledTracking>
|
||||||
@ -371,7 +408,10 @@ public final class CommandManager {
|
|||||||
|
|
||||||
private int serializeCommand(CommandSender sender, Command command,
|
private int serializeCommand(CommandSender sender, Command command,
|
||||||
List<DeclareCommandsPacket.Node> nodes,
|
List<DeclareCommandsPacket.Node> nodes,
|
||||||
IntList rootChildren) {
|
IntList rootChildren,
|
||||||
|
Map<Command, Integer> commandIdentityMap,
|
||||||
|
Map<Argument<?>, Integer> argumentIdentityMap,
|
||||||
|
List<Pair<String, NodeMaker.Request>> nodeRequests) {
|
||||||
// Check if player should see this command
|
// Check if player should see this command
|
||||||
final CommandCondition commandCondition = command.getCondition();
|
final CommandCondition commandCondition = command.getCondition();
|
||||||
if (commandCondition != null) {
|
if (commandCondition != null) {
|
||||||
@ -386,15 +426,16 @@ public final class CommandManager {
|
|||||||
final Collection<CommandSyntax> syntaxes = command.getSyntaxes();
|
final Collection<CommandSyntax> syntaxes = command.getSyntaxes();
|
||||||
|
|
||||||
// Create command for main name
|
// Create command for main name
|
||||||
final DeclareCommandsPacket.Node mainNode = createCommand(sender, nodes, cmdChildren,
|
final DeclareCommandsPacket.Node mainNode = createCommandNodes(sender, nodes, cmdChildren,
|
||||||
command.getName(), syntaxes, rootChildren);
|
command.getName(), syntaxes, rootChildren, argumentIdentityMap, nodeRequests);
|
||||||
final int mainNodeIndex = nodes.indexOf(mainNode);
|
final int mainNodeIndex = nodes.indexOf(mainNode);
|
||||||
|
|
||||||
// Serialize all the subcommands
|
// Serialize all the subcommands
|
||||||
for (Command subcommand : command.getSubcommands()) {
|
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) {
|
if (subNodeIndex != -1) {
|
||||||
mainNode.children = ArrayUtils.concatenateIntArrays(mainNode.children, new int[]{subNodeIndex});
|
mainNode.children = ArrayUtils.concatenateIntArrays(mainNode.children, new int[]{subNodeIndex});
|
||||||
|
commandIdentityMap.put(subcommand, subNodeIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,12 +467,14 @@ public final class CommandManager {
|
|||||||
* @param rootChildren the children of the main node (all commands name)
|
* @param rootChildren the children of the main node (all commands name)
|
||||||
* @return The index of the main node for alias redirection
|
* @return The index of the main node for alias redirection
|
||||||
*/
|
*/
|
||||||
private DeclareCommandsPacket.Node createCommand(@NotNull CommandSender sender,
|
private DeclareCommandsPacket.Node createCommandNodes(@NotNull CommandSender sender,
|
||||||
@NotNull List<DeclareCommandsPacket.Node> nodes,
|
@NotNull List<DeclareCommandsPacket.Node> nodes,
|
||||||
@NotNull IntList cmdChildren,
|
@NotNull IntList cmdChildren,
|
||||||
@NotNull String name,
|
@NotNull String name,
|
||||||
@NotNull Collection<CommandSyntax> syntaxes,
|
@NotNull Collection<CommandSyntax> syntaxes,
|
||||||
@NotNull IntList rootChildren) {
|
@NotNull IntList rootChildren,
|
||||||
|
@NotNull Map<Argument<?>, Integer> argumentIdentityMap,
|
||||||
|
@NotNull List<Pair<String, NodeMaker.Request>> nodeRequests) {
|
||||||
|
|
||||||
DeclareCommandsPacket.Node literalNode = createMainNode(name, syntaxes.isEmpty());
|
DeclareCommandsPacket.Node literalNode = createMainNode(name, syntaxes.isEmpty());
|
||||||
|
|
||||||
@ -465,7 +508,7 @@ public final class CommandManager {
|
|||||||
final Argument<?> argument = arguments[i];
|
final Argument<?> argument = arguments[i];
|
||||||
final boolean isLast = i == arguments.length - 1;
|
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
|
// Find shared part
|
||||||
boolean foundSharedPart = false;
|
boolean foundSharedPart = false;
|
||||||
@ -491,7 +534,7 @@ public final class CommandManager {
|
|||||||
|
|
||||||
// Each node array represent a layer
|
// Each node array represent a layer
|
||||||
final List<DeclareCommandsPacket.Node[]> nodesLayer = nodeMaker.getNodes();
|
final List<DeclareCommandsPacket.Node[]> nodesLayer = nodeMaker.getNodes();
|
||||||
storedArgumentsNodes.put(argument, nodesLayer);
|
storedArgumentsNodes.put(argument, new ArrayList<>(nodesLayer));
|
||||||
for (int nodeIndex = lastArgumentNodeIndex; nodeIndex < nodesLayer.size(); nodeIndex++) {
|
for (int nodeIndex = lastArgumentNodeIndex; nodeIndex < nodesLayer.size(); nodeIndex++) {
|
||||||
final NodeMaker.ConfiguredNodes configuredNodes = nodeMaker.getConfiguredNodes().get(nodeIndex);
|
final NodeMaker.ConfiguredNodes configuredNodes = nodeMaker.getConfiguredNodes().get(nodeIndex);
|
||||||
final NodeMaker.Options options = configuredNodes.getOptions();
|
final NodeMaker.Options options = configuredNodes.getOptions();
|
||||||
@ -536,9 +579,22 @@ public final class CommandManager {
|
|||||||
lastArgumentNodeIndex = nodesLayer.size();
|
lastArgumentNodeIndex = nodesLayer.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nodeRequests.addAll(nodeMaker.getNodeRequests());
|
||||||
|
|
||||||
syntaxesArguments.add(arguments);
|
syntaxesArguments.add(arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
storedArgumentsNodes.forEach((argument, argNodes) -> {
|
||||||
|
int value = 0;
|
||||||
|
for (DeclareCommandsPacket.Node[] n1 : argNodes) {
|
||||||
|
for (DeclareCommandsPacket.Node n2 : n1) {
|
||||||
|
value = nodes.indexOf(n2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argumentIdentityMap.put(argument, value);
|
||||||
|
});
|
||||||
|
|
||||||
literalNode.children = ArrayUtils.toArray(cmdChildren);
|
literalNode.children = ArrayUtils.toArray(cmdChildren);
|
||||||
return literalNode;
|
return literalNode;
|
||||||
|
|
||||||
|
@ -118,21 +118,17 @@ public class CommandDispatcher {
|
|||||||
final String[] parts = commandString.split(StringUtils.SPACE);
|
final String[] parts = commandString.split(StringUtils.SPACE);
|
||||||
final String commandName = parts[0];
|
final String commandName = parts[0];
|
||||||
|
|
||||||
String[] args = new String[parts.length - 1];
|
final CommandQueryResult commandQueryResult = CommandParser.findCommand(commandString);
|
||||||
System.arraycopy(parts, 1, args, 0, args.length);
|
|
||||||
|
|
||||||
final CommandQueryResult commandQueryResult = CommandParser.findCommand(commandName, args);
|
|
||||||
// Check if the command exists
|
// Check if the command exists
|
||||||
if (commandQueryResult == null) {
|
if (commandQueryResult == null) {
|
||||||
return CommandResult.of(CommandResult.Type.UNKNOWN, commandName);
|
return CommandResult.of(CommandResult.Type.UNKNOWN, commandName);
|
||||||
}
|
}
|
||||||
final Command command = commandQueryResult.command;
|
final Command command = commandQueryResult.command;
|
||||||
args = commandQueryResult.args;
|
|
||||||
|
|
||||||
CommandResult result = new CommandResult();
|
CommandResult result = new CommandResult();
|
||||||
result.input = commandString;
|
result.input = commandString;
|
||||||
// Find the used syntax and fill CommandResult#type and CommandResult#parsedCommand
|
// 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
|
// Cache result
|
||||||
{
|
{
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package net.minestom.server.command.builder;
|
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.Object2IntMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -15,8 +15,7 @@ public class NodeMaker {
|
|||||||
private final List<DeclareCommandsPacket.Node[]> nodes = new ArrayList<>(2);
|
private final List<DeclareCommandsPacket.Node[]> nodes = new ArrayList<>(2);
|
||||||
private final Object2IntMap<DeclareCommandsPacket.Node> nodeIdsMap = new Object2IntOpenHashMap<>();
|
private final Object2IntMap<DeclareCommandsPacket.Node> nodeIdsMap = new Object2IntOpenHashMap<>();
|
||||||
|
|
||||||
private Rule rule;
|
private final List<Pair<String, Request>> nodeRequests = new ArrayList<>();
|
||||||
private int ruleCount;
|
|
||||||
|
|
||||||
public NodeMaker(@NotNull DeclareCommandsPacket.Node[] commandNodes, int id) {
|
public NodeMaker(@NotNull DeclareCommandsPacket.Node[] commandNodes, int id) {
|
||||||
addNodes(commandNodes);
|
addNodes(commandNodes);
|
||||||
@ -41,26 +40,11 @@ public class NodeMaker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void addNodes(@NotNull DeclareCommandsPacket.Node[] nodes) {
|
public void addNodes(@NotNull DeclareCommandsPacket.Node[] nodes) {
|
||||||
Options options = null;
|
Options options = new Options();
|
||||||
if (rule != null) {
|
|
||||||
options = rule.listen(nodes, ruleCount++);
|
|
||||||
}
|
|
||||||
if (options == null) {
|
|
||||||
options = new Options();
|
|
||||||
}
|
|
||||||
this.configuredNodes.add(ConfiguredNodes.of(nodes, options));
|
this.configuredNodes.add(ConfiguredNodes.of(nodes, options));
|
||||||
this.nodes.add(nodes);
|
this.nodes.add(nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRule(@NotNull Rule rule) {
|
|
||||||
this.rule = rule;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void resetRule() {
|
|
||||||
this.rule = null;
|
|
||||||
this.ruleCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public List<ConfiguredNodes> getConfiguredNodes() {
|
public List<ConfiguredNodes> getConfiguredNodes() {
|
||||||
return configuredNodes;
|
return configuredNodes;
|
||||||
@ -75,6 +59,14 @@ public class NodeMaker {
|
|||||||
return nodeIdsMap;
|
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 {
|
public static class ConfiguredNodes {
|
||||||
private DeclareCommandsPacket.Node[] nodes;
|
private DeclareCommandsPacket.Node[] nodes;
|
||||||
private Options options;
|
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 {
|
public static class Options {
|
||||||
|
|
||||||
private boolean updateLastNode = true;
|
private boolean updateLastNode = true;
|
||||||
@ -128,4 +115,9 @@ public class NodeMaker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface Request {
|
||||||
|
void retrieve(int id);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,22 @@
|
|||||||
package net.minestom.server.command.builder.arguments;
|
package net.minestom.server.command.builder.arguments;
|
||||||
|
|
||||||
|
import com.google.common.annotations.Beta;
|
||||||
import net.minestom.server.MinecraftServer;
|
import net.minestom.server.MinecraftServer;
|
||||||
import net.minestom.server.command.builder.CommandDispatcher;
|
import net.minestom.server.command.builder.CommandDispatcher;
|
||||||
import net.minestom.server.command.builder.CommandResult;
|
import net.minestom.server.command.builder.CommandResult;
|
||||||
import net.minestom.server.command.builder.NodeMaker;
|
import net.minestom.server.command.builder.NodeMaker;
|
||||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public class ArgumentCommand extends Argument<CommandResult> {
|
public class ArgumentCommand extends Argument<CommandResult> {
|
||||||
|
|
||||||
public static final int INVALID_COMMAND_ERROR = 1;
|
public static final int INVALID_COMMAND_ERROR = 1;
|
||||||
|
|
||||||
|
private boolean onlyCorrect;
|
||||||
|
private String shortcut = "";
|
||||||
|
|
||||||
public ArgumentCommand(@NotNull String id) {
|
public ArgumentCommand(@NotNull String id) {
|
||||||
super(id, true, true);
|
super(id, true, true);
|
||||||
}
|
}
|
||||||
@ -19,10 +24,13 @@ public class ArgumentCommand extends Argument<CommandResult> {
|
|||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public CommandResult parse(@NotNull String input) throws ArgumentSyntaxException {
|
public CommandResult parse(@NotNull String input) throws ArgumentSyntaxException {
|
||||||
|
final String commandString = !shortcut.isEmpty() ?
|
||||||
|
shortcut + StringUtils.SPACE + input
|
||||||
|
: input;
|
||||||
CommandDispatcher dispatcher = MinecraftServer.getCommandManager().getDispatcher();
|
CommandDispatcher dispatcher = MinecraftServer.getCommandManager().getDispatcher();
|
||||||
CommandResult result = dispatcher.parse(input);
|
CommandResult result = dispatcher.parse(commandString);
|
||||||
|
|
||||||
if (result.getType() != CommandResult.Type.SUCCESS)
|
if (onlyCorrect && result.getType() != CommandResult.Type.SUCCESS)
|
||||||
throw new ArgumentSyntaxException("Invalid command", input, INVALID_COMMAND_ERROR);
|
throw new ArgumentSyntaxException("Invalid command", input, INVALID_COMMAND_ERROR);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -32,10 +40,38 @@ public class ArgumentCommand extends Argument<CommandResult> {
|
|||||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||||
final DeclareCommandsPacket.Node[] lastNodes = nodeMaker.getLatestNodes();
|
final DeclareCommandsPacket.Node[] lastNodes = nodeMaker.getLatestNodes();
|
||||||
|
|
||||||
// FIXME check if lastNodes is null
|
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) {
|
for (DeclareCommandsPacket.Node node : lastNodes) {
|
||||||
node.flags |= 0x08; // Redirection mask
|
node.flags |= 0x08; // Redirection mask
|
||||||
node.redirectedNode = 0; // Redirect to root
|
node.redirectedNode = 0; // Redirect to root
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOnlyCorrect() {
|
||||||
|
return onlyCorrect;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArgumentCommand setOnlyCorrect(boolean onlyCorrect) {
|
||||||
|
this.onlyCorrect = onlyCorrect;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public String getShortcut() {
|
||||||
|
return shortcut;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Beta
|
||||||
|
public ArgumentCommand setShortcut(@NotNull String shortcut) {
|
||||||
|
this.shortcut = shortcut;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
public class CommandParser {
|
public class CommandParser {
|
||||||
|
|
||||||
@ -49,6 +50,16 @@ public class CommandParser {
|
|||||||
return commandQueryResult;
|
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,
|
public static void parse(@Nullable CommandSyntax syntax, @NotNull Argument<?>[] commandArguments, @NotNull String[] inputArguments,
|
||||||
@NotNull String commandString,
|
@NotNull String commandString,
|
||||||
@Nullable List<ValidSyntaxHolder> validSyntaxes,
|
@Nullable List<ValidSyntaxHolder> validSyntaxes,
|
||||||
@ -153,14 +164,16 @@ public class CommandParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static ArgumentQueryResult findSuggestibleArgument(@NotNull Command command, String[] args, String commandString,
|
public static ArgumentQueryResult findEligibleArgument(@NotNull Command command, String[] args, String commandString,
|
||||||
boolean trailingSpace) {
|
boolean trailingSpace, boolean forceCorrect,
|
||||||
|
Predicate<CommandSyntax> syntaxPredicate,
|
||||||
|
Predicate<Argument<?>> argumentPredicate) {
|
||||||
final Collection<CommandSyntax> syntaxes = command.getSyntaxes();
|
final Collection<CommandSyntax> syntaxes = command.getSyntaxes();
|
||||||
|
|
||||||
Int2ObjectRBTreeMap<ArgumentQueryResult> suggestions = new Int2ObjectRBTreeMap<>(Collections.reverseOrder());
|
Int2ObjectRBTreeMap<ArgumentQueryResult> suggestions = new Int2ObjectRBTreeMap<>(Collections.reverseOrder());
|
||||||
|
|
||||||
for (CommandSyntax syntax : syntaxes) {
|
for (CommandSyntax syntax : syntaxes) {
|
||||||
if (!syntax.hasSuggestion()) {
|
if (!syntaxPredicate.test(syntax)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +203,9 @@ public class CommandParser {
|
|||||||
context.setArg(argument.getId(), argumentResult.parsedValue, argumentResult.rawArg);
|
context.setArg(argument.getId(), argumentResult.parsedValue, argumentResult.rawArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argument.hasSuggestion()) {
|
// Save result
|
||||||
|
if ((!forceCorrect || argumentResult.correct) &&
|
||||||
|
argumentPredicate.test(argument)) {
|
||||||
ArgumentQueryResult queryResult = new ArgumentQueryResult();
|
ArgumentQueryResult queryResult = new ArgumentQueryResult();
|
||||||
queryResult.syntax = syntax;
|
queryResult.syntax = syntax;
|
||||||
queryResult.argument = argument;
|
queryResult.argument = argument;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package net.minestom.server.listener;
|
package net.minestom.server.listener;
|
||||||
|
|
||||||
import net.minestom.server.command.CommandManager;
|
import net.minestom.server.command.CommandManager;
|
||||||
|
import net.minestom.server.command.builder.CommandSyntax;
|
||||||
import net.minestom.server.command.builder.arguments.Argument;
|
import net.minestom.server.command.builder.arguments.Argument;
|
||||||
import net.minestom.server.command.builder.parser.ArgumentQueryResult;
|
import net.minestom.server.command.builder.parser.ArgumentQueryResult;
|
||||||
import net.minestom.server.command.builder.parser.CommandParser;
|
import net.minestom.server.command.builder.parser.CommandParser;
|
||||||
@ -24,17 +25,16 @@ public class TabCompleteListener {
|
|||||||
String commandName = split[0];
|
String commandName = split[0];
|
||||||
|
|
||||||
String args = commandString.replaceFirst(commandName, "");
|
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) {
|
if (commandQueryResult == null) {
|
||||||
// Command not found
|
// Command not found
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ArgumentQueryResult queryResult = CommandParser.findSuggestibleArgument(commandQueryResult.command,
|
final ArgumentQueryResult queryResult = CommandParser.findEligibleArgument(commandQueryResult.command,
|
||||||
commandQueryResult.args, commandString, text.endsWith(StringUtils.SPACE));
|
commandQueryResult.args, commandString, text.endsWith(StringUtils.SPACE), false,
|
||||||
|
CommandSyntax::hasSuggestion, Argument::hasSuggestion);
|
||||||
if (queryResult == null) {
|
if (queryResult == null) {
|
||||||
// Suggestible argument not found
|
// Suggestible argument not found
|
||||||
return;
|
return;
|
||||||
|
@ -6,7 +6,7 @@ import net.minestom.server.command.builder.CommandContext;
|
|||||||
import net.minestom.server.command.builder.suggestion.SuggestionEntry;
|
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.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 {
|
public class TestCommand extends Command {
|
||||||
|
|
||||||
@ -18,13 +18,17 @@ public class TestCommand extends Command {
|
|||||||
suggestion.addEntry(new SuggestionEntry("test"));
|
suggestion.addEntry(new SuggestionEntry("test"));
|
||||||
});
|
});
|
||||||
|
|
||||||
var test2 = Word("msg2").setSuggestionCallback((sender, context, suggestion) -> {
|
var test2 = String("msg2").setSuggestionCallback((sender, context, suggestion) -> {
|
||||||
suggestion.addEntry(new SuggestionEntry("greer"));
|
suggestion.addEntry(new SuggestionEntry("greer"));
|
||||||
});
|
});
|
||||||
|
|
||||||
addSyntax((sender, context) -> {
|
addSyntax((sender, context) -> {
|
||||||
System.out.println("executed");
|
System.out.println("executed");
|
||||||
}, test1, test2);
|
}, Literal("test"), test1, test2);
|
||||||
|
|
||||||
|
addSyntax((sender, context) -> {
|
||||||
|
System.out.println("cmd syntax");
|
||||||
|
}, Literal("debug"), Command("cmd").setShortcut("testcmd test"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user