mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-09 18:08:37 +01:00
Initial command redirection improvement
This commit is contained in:
parent
509f8f7851
commit
3c7a96dc4a
@ -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;
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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"));
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user