Simplify command graph generation (#1200)

This commit is contained in:
Noel Németh 2022-07-02 05:39:39 +02:00 committed by GitHub
parent d47e761bf7
commit 793561e0cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 404 additions and 740 deletions

View File

@ -52,6 +52,8 @@ public class Main {
commandManager.register(new AutoViewCommand());
commandManager.register(new SaveCommand());
commandManager.register(new GamemodeCommand());
commandManager.register(new ExecuteCommand());
commandManager.register(new RedirectTestCommand());
commandManager.setUnknownCommandCallback((sender, command) -> sender.sendMessage(Component.text("Unknown command", NamedTextColor.RED)));

View File

@ -0,0 +1,14 @@
package net.minestom.demo.commands;
import net.minestom.server.command.builder.Command;
import net.minestom.server.command.builder.arguments.ArgumentCommand;
public class ExecuteCommand extends Command {
public ExecuteCommand() {
super("execute");
ArgumentCommand run = new ArgumentCommand("run");
addSyntax(((sender, context) -> {}), run);
}
}

View File

@ -0,0 +1,18 @@
package net.minestom.demo.commands;
import net.minestom.server.command.builder.Command;
import net.minestom.server.command.builder.arguments.ArgumentLiteral;
import net.minestom.server.command.builder.arguments.ArgumentLoop;
public class RedirectTestCommand extends Command {
public RedirectTestCommand() {
super("redirect");
final ArgumentLiteral a = new ArgumentLiteral("a");
final ArgumentLiteral b = new ArgumentLiteral("b");
final ArgumentLiteral c = new ArgumentLiteral("c");
final ArgumentLiteral d = new ArgumentLiteral("d");
addSyntax(((sender, context) -> {}), new ArgumentLoop<>("test", a,b,c,d));
}
}

View File

@ -1,27 +1,20 @@
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 net.minestom.server.command.builder.*;
import net.minestom.server.command.builder.Command;
import net.minestom.server.command.builder.CommandDispatcher;
import net.minestom.server.command.builder.CommandResult;
import net.minestom.server.command.builder.CommandSyntax;
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.EventDispatcher;
import net.minestom.server.event.player.PlayerCommandEvent;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import net.minestom.server.utils.ArrayUtils;
import net.minestom.server.utils.callback.CommandCallback;
import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* Manager used to register {@link Command commands}.
* <p>
@ -167,282 +160,43 @@ public final class CommandManager {
* @return the {@link DeclareCommandsPacket} for {@code player}
*/
public @NotNull DeclareCommandsPacket createDeclareCommandsPacket(@NotNull Player player) {
return buildPacket(player);
}
final GraphBuilder factory = new GraphBuilder();
/**
* Builds the {@link DeclareCommandsPacket} for a {@link Player}.
*
* @param player the player to build the packet for
* @return the commands packet for the specific player
*/
private @NotNull DeclareCommandsPacket buildPacket(@NotNull Player player) {
List<DeclareCommandsPacket.Node> nodes = new ArrayList<>();
// Contains the children of the main node (all commands name)
IntList rootChildren = new IntArrayList();
for (Command command : this.dispatcher.getCommands()) {
// Check if user can use the command
final CommandCondition condition = command.getCondition();
if (condition != null && !condition.canUse(player, null)) continue;
// Root node
DeclareCommandsPacket.Node rootNode = new DeclareCommandsPacket.Node();
rootNode.flags = 0;
nodes.add(rootNode);
// Add command to the graph
// Create the command's root node
final Node cmdNode = factory.createLiteralNode(command.getName(), true,
command.getDefaultExecutor() != null, command.getAliases(), null);
Map<Command, Integer> commandIdentityMap = new IdentityHashMap<>();
Map<Argument<?>, Integer> argumentIdentityMap = new IdentityHashMap<>();
List<Pair<String, NodeMaker.Request>> nodeRequests = new ArrayList<>();
// Brigadier-like commands
for (Command command : dispatcher.getCommands()) {
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(dispatcher, 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 int argumentNode = argumentIdentityMap.getOrDefault(queryResult.argument(), 0);
request.retrieve(argumentNode);
}
// Add root node children
rootNode.children = rootChildren.toIntArray();
return new DeclareCommandsPacket(nodes, 0);
}
private int serializeCommand(CommandSender sender, Command command,
List<DeclareCommandsPacket.Node> nodes,
IntList rootChildren,
Map<Command, Integer> commandIdentityMap,
Map<Argument<?>, Integer> argumentIdentityMap,
List<Pair<String, NodeMaker.Request>> nodeRequests) {
// Check if player should see this command
final CommandCondition commandCondition = command.getCondition();
if (commandCondition != null) {
// Do not show command if return false
if (!commandCondition.canUse(sender, null)) {
return -1;
}
}
// The main root of this command
IntList cmdChildren = new IntArrayList();
final Collection<CommandSyntax> syntaxes = command.getSyntaxes();
// Create command for main name
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, commandIdentityMap, argumentIdentityMap, nodeRequests);
if (subNodeIndex != -1) {
mainNode.children = ArrayUtils.concatenateIntArrays(mainNode.children, new int[]{subNodeIndex});
commandIdentityMap.put(subcommand, subNodeIndex);
}
}
// Use redirection to hook aliases with the command
final String[] aliases = command.getAliases();
if (aliases != null) {
for (String alias : aliases) {
DeclareCommandsPacket.Node aliasNode = new DeclareCommandsPacket.Node();
aliasNode.flags = DeclareCommandsPacket.getFlag(DeclareCommandsPacket.NodeType.LITERAL,
false, true, false);
aliasNode.name = alias;
aliasNode.redirectedNode = mainNodeIndex;
addCommandNameNode(aliasNode, rootChildren, nodes);
}
}
return mainNodeIndex;
}
/**
* Adds the command's syntaxes to the nodes list.
*
* @param sender the potential sender of the command
* @param nodes the nodes of the packet
* @param cmdChildren the main root of this command
* @param name the name of the command (or the alias)
* @param syntaxes the syntaxes of the command
* @param rootChildren the children of the main node (all commands name)
* @return The index of the main node for alias redirection
*/
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<?>, Integer> argumentIdentityMap,
@NotNull List<Pair<String, NodeMaker.Request>> nodeRequests) {
DeclareCommandsPacket.Node literalNode = createMainNode(name, syntaxes.isEmpty());
final int literalNodeId = addCommandNameNode(literalNode, rootChildren, nodes);
// Contains the arguments of the already-parsed syntaxes
Map<CommandSyntax, Argument<?>[]> syntaxesArguments = new HashMap<>();
// Contains the nodes of an argument
Map<IndexedArgument, List<DeclareCommandsPacket.Node[]>> storedArgumentsNodes = new HashMap<>();
// Sort syntaxes by argument count. Brigadier requires it.
syntaxes = syntaxes.stream().sorted(Comparator.comparingInt(o -> -o.getArguments().length)).toList();
for (CommandSyntax syntax : syntaxes) {
final CommandCondition commandCondition = syntax.getCommandCondition();
if (commandCondition != null && !commandCondition.canUse(sender, null)) {
// Sender does not have the right to use this syntax, ignore it
continue;
}
// Represent the last nodes computed in the last iteration
DeclareCommandsPacket.Node[] lastNodes = new DeclareCommandsPacket.Node[]{literalNode};
// Represent the children of the last node
IntList argChildren = cmdChildren;
NodeMaker nodeMaker = new NodeMaker(lastNodes, literalNodeId);
int lastArgumentNodeIndex = nodeMaker.getNodesCount();
final Argument<?>[] arguments = syntax.getArguments();
for (int i = 0; i < arguments.length; i++) {
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 link between those
{
// Find shared part
boolean foundSharedPart = false;
for (var entry : syntaxesArguments.entrySet()) {
final var parsedArguments = entry.getValue();
final int index = i + 1;
if (Arrays.mismatch(arguments, 0, index, parsedArguments, 0, index) == -1) {
final Argument<?> sharedArgument = parsedArguments[i];
final var sharedSyntax = entry.getKey();
final var indexed = new IndexedArgument(sharedSyntax, sharedArgument, i);
final List<DeclareCommandsPacket.Node[]> storedNodes = storedArgumentsNodes.get(indexed);
if (storedNodes == null)
continue; // Retrieved argument has already been redirected
argChildren = new IntArrayList();
lastNodes = storedNodes.get(storedNodes.size() > index ? index : i);
foundSharedPart = true;
}
// Add syntax to the command
for (CommandSyntax syntax : command.getSyntaxes()) {
boolean executable = false;
Node[] lastArgNodes = new Node[] {cmdNode}; // First arg links to cmd root
@NotNull Argument<?>[] arguments = syntax.getArguments();
for (int i = 0; i < arguments.length; i++) {
Argument<?> argument = arguments[i];
// Determine if command is executable here
if (executable && argument.getDefaultValue() == null) {
// Optional arg was followed by a non-optional
throw new RuntimeException("");//todo exception
}
if (foundSharedPart) {
continue;
if (!executable && i < arguments.length-1 && arguments[i+1].getDefaultValue() != null || i+1 == arguments.length) {
executable = true;
}
}
// Process the nodes for the argument
{
argument.processNodes(nodeMaker, isLast);
// Each node array represent a layer
final List<DeclareCommandsPacket.Node[]> nodesLayer = nodeMaker.getNodes();
storedArgumentsNodes.put(new IndexedArgument(syntax, argument, i), new ArrayList<>(nodesLayer));
for (int nodeIndex = lastArgumentNodeIndex; nodeIndex < nodesLayer.size(); nodeIndex++) {
final NodeMaker.ConfiguredNodes configuredNodes = nodeMaker.getConfiguredNodes().get(nodeIndex);
final NodeMaker.Options options = configuredNodes.getOptions();
final DeclareCommandsPacket.Node[] argumentNodes = nodesLayer.get(nodeIndex);
for (DeclareCommandsPacket.Node argumentNode : argumentNodes) {
final int childId = nodes.size();
nodeMaker.getNodeIdsMap().put(argumentNode, childId);
argChildren.add(childId);
// Enable ASK_SERVER suggestion if required
{
if (argument.hasSuggestion()) {
argumentNode.flags |= 0x10; // Suggestion flag
argumentNode.suggestionsType = SuggestionType.ASK_SERVER.getIdentifier();
}
}
// Append to the last node
{
final int[] children = argChildren.toIntArray();
for (DeclareCommandsPacket.Node lastNode : lastNodes) {
lastNode.children = lastNode.children == null ?
children :
ArrayUtils.concatenateIntArrays(lastNode.children, children);
}
}
nodes.add(argumentNode);
}
if (options.shouldUpdateLastNode()) {
// 'previousNodes' used if the nodes options require to overwrite the parent
final DeclareCommandsPacket.Node[] previousNodes = options.getPreviousNodes();
lastNodes = previousNodes != null ? previousNodes : argumentNodes;
argChildren = new IntArrayList();
}
// Append current node to previous
final Node[] argNodes = factory.createArgumentNode(argument, executable);
for (Node lastArgNode : lastArgNodes) {
lastArgNode.addChild(argNodes);
}
// Used to do not re-compute the previous arguments
lastArgumentNodeIndex = nodesLayer.size();
lastArgNodes = argNodes;
}
}
nodeRequests.addAll(nodeMaker.getNodeRequests());
syntaxesArguments.put(syntax, arguments);
}
storedArgumentsNodes.forEach((indexedArgument, argNodes) -> {
int value = 0;
for (DeclareCommandsPacket.Node[] n1 : argNodes) {
for (DeclareCommandsPacket.Node n2 : n1) {
value = nodes.indexOf(n2);
}
}
// FIXME: add syntax for indexing
argumentIdentityMap.put(indexedArgument.argument, value);
});
literalNode.children = cmdChildren.toIntArray();
return literalNode;
return factory.createCommandPacket();
}
private @NotNull DeclareCommandsPacket.Node createMainNode(@NotNull String name, boolean executable) {
DeclareCommandsPacket.Node literalNode = new DeclareCommandsPacket.Node();
literalNode.flags = DeclareCommandsPacket.getFlag(DeclareCommandsPacket.NodeType.LITERAL, executable, false, false);
literalNode.name = name;
return literalNode;
}
private int addCommandNameNode(@NotNull DeclareCommandsPacket.Node commandNode,
@NotNull IntList rootChildren,
@NotNull List<DeclareCommandsPacket.Node> nodes) {
final int node = nodes.size();
rootChildren.add(node);
nodes.add(commandNode);
return node;
}
private record IndexedArgument(CommandSyntax syntax, Argument<?> argument, int index) {}
}

View File

@ -0,0 +1,108 @@
package net.minestom.server.command;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectSet;
import net.minestom.server.command.builder.arguments.*;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.jetbrains.annotations.Nullable;
import java.util.Comparator;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import java.util.stream.Stream;
final class GraphBuilder {
private final AtomicInteger idSource = new AtomicInteger();
private final ObjectSet<Node> nodes = new ObjectOpenHashSet<>();
private final ObjectSet<Supplier<Boolean>> redirectWaitList = new ObjectOpenHashSet<>();
private final Node root = rootNode();
private Node rootNode() {
final Node rootNode = new Node(idSource.getAndIncrement());
nodes.add(rootNode);
return rootNode;
}
public Node createLiteralNode(String name, boolean addToRoot, boolean executable, @Nullable String[] aliases, @Nullable Integer redirectTo) {
if (aliases != null) {
final Node node = createLiteralNode(name, addToRoot, executable, null, null);
for (String alias : aliases) {
createLiteralNode(alias, addToRoot, false, null, node.getId());
}
return node;
} else {
final Node literalNode = new Node(idSource.getAndIncrement(), name, redirectTo);
literalNode.setExecutable(executable);
nodes.add(literalNode);
if (addToRoot) root.addChild(literalNode);
return literalNode;
}
}
public Node[] createArgumentNode(Argument<?> argument, boolean executable) {
final Node[] nodes;
Integer overrideRedirectTarget = null;
if (argument instanceof ArgumentEnum<?> argumentEnum) {
nodes = argumentEnum.entries().stream().map(x -> createLiteralNode(x, false, executable, null, null)).toArray(Node[]::new);
} else if (argument instanceof ArgumentGroup argumentGroup) {
nodes = argumentGroup.group().stream().map(x -> createArgumentNode(x, executable)).flatMap(Stream::of).toArray(Node[]::new);
} else if (argument instanceof ArgumentLoop<?> argumentLoop) {
overrideRedirectTarget = idSource.get()-1;
nodes = argumentLoop.arguments().stream().map(x -> createArgumentNode(x, executable)).flatMap(Stream::of).toArray(Node[]::new);
} else {
if (argument instanceof ArgumentCommand) {
return new Node[]{createLiteralNode(argument.getId(), false, false, null, 0)};
}
final int id = idSource.getAndIncrement();
nodes = new Node[] {argument instanceof ArgumentLiteral ? new Node(id, argument.getId(), null) : new Node(id, argument)};
}
for (Node node : nodes) {
node.setExecutable(executable);
this.nodes.add(node);
Integer finalOverrideRedirectTarget = overrideRedirectTarget;
if (finalOverrideRedirectTarget != null) {
redirectWaitList.add(() -> {
int target = finalOverrideRedirectTarget;
if (target != -1) {
node.setRedirectTarget(target);
return true;
}
return false;
});
}
}
return nodes;
}
private int tryResolveId(String[] path) {
if (path.length == 0) {
return root.getId();
} else {
Node target = root;
for (String next : path) {
Node finalTarget = target;
final Optional<Node> result = nodes.stream().filter(finalTarget::isParentOf)
.filter(x -> x.name().equals(next)).findFirst();
if (result.isEmpty()) {
return -1;
} else {
target = result.get();
}
}
return target.getId();
}
}
private void finalizeStructure() {
redirectWaitList.removeIf(Supplier::get);
if (redirectWaitList.size() > 0)
throw new RuntimeException("Could not set redirects for all arguments! Did you provide a correct id path which doesn't rely on redirects?");
}
public DeclareCommandsPacket createCommandPacket() {
finalizeStructure();
return new DeclareCommandsPacket(nodes.stream().sorted(Comparator.comparingInt(Node::getId))
.map(Node::getPacketNode).toList(), root.getId());
}
}

View File

@ -0,0 +1,88 @@
package net.minestom.server.command;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
final class Node {
private final int id;
private final IntSet children;
private final DeclareCommandsPacket.NodeType type;
private String name;
private Integer redirectTarget;
private Argument<?> argument;
private boolean executable;
Node(int id, DeclareCommandsPacket.NodeType type) {
this.id = id;
this.children = new IntOpenHashSet();
this.type = type;
}
Node(int id) {
this(id, DeclareCommandsPacket.NodeType.ROOT);
}
Node(int id, String name, Integer redirectTarget) {
this(id, DeclareCommandsPacket.NodeType.LITERAL);
setName(name);
setRedirectTarget(redirectTarget);
}
Node(int id, Argument<?> argument) {
this(id, DeclareCommandsPacket.NodeType.ARGUMENT);
setName(argument.getId());
this.argument = argument;
}
public void setExecutable(boolean executable) {
this.executable = executable;
}
public String name() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setRedirectTarget(Integer redirectTarget) {
this.redirectTarget = redirectTarget;
}
public void addChild(Node ...nodes) {
for (Node node : nodes) {
children.add(node.id);
}
}
public boolean isParentOf(Node node) {
return children.contains(node.getId());
}
public int getId() {
return id;
}
public DeclareCommandsPacket.Node getPacketNode() {
final DeclareCommandsPacket.Node node = new DeclareCommandsPacket.Node();
node.children = children.toIntArray();
node.flags = DeclareCommandsPacket.getFlag(type, executable, redirectTarget != null,
type == DeclareCommandsPacket.NodeType.ARGUMENT && argument.hasSuggestion());
node.name = name;
if (redirectTarget != null) {
node.redirectedNode = redirectTarget;
}
if (type == DeclareCommandsPacket.NodeType.ARGUMENT) {
node.properties = argument.nodeProperties();
node.parser = argument.parser();
if (argument.hasSuggestion()) {
//noinspection ConstantConditions
node.suggestionsType = argument.suggestionType().getIdentifier();
}
}
return node;
}
}

View File

@ -1,123 +0,0 @@
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 java.util.ArrayList;
import java.util.List;
public class NodeMaker {
private final List<ConfiguredNodes> configuredNodes = new ArrayList<>(2);
private final List<DeclareCommandsPacket.Node[]> nodes = new ArrayList<>(2);
private final Object2IntMap<DeclareCommandsPacket.Node> nodeIdsMap = new Object2IntOpenHashMap<>();
private final List<Pair<String, Request>> nodeRequests = new ArrayList<>();
public NodeMaker(@NotNull DeclareCommandsPacket.Node[] commandNodes, int id) {
addNodes(commandNodes);
for (DeclareCommandsPacket.Node node : commandNodes) {
this.nodeIdsMap.put(node, id);
}
}
public ConfiguredNodes getLatestConfiguredNodes() {
if (configuredNodes.isEmpty())
return null;
return configuredNodes.get(configuredNodes.size() - 1);
}
public DeclareCommandsPacket.Node[] getLatestNodes() {
ConfiguredNodes configuredNodes = getLatestConfiguredNodes();
return configuredNodes != null ? configuredNodes.nodes : null;
}
public int getNodesCount() {
return nodes.size();
}
public void addNodes(@NotNull DeclareCommandsPacket.Node[] nodes) {
Options options = new Options();
this.configuredNodes.add(ConfiguredNodes.of(nodes, options));
this.nodes.add(nodes);
}
@NotNull
public List<ConfiguredNodes> getConfiguredNodes() {
return configuredNodes;
}
public List<DeclareCommandsPacket.Node[]> getNodes() {
return nodes;
}
@NotNull
public Object2IntMap<DeclareCommandsPacket.Node> getNodeIdsMap() {
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;
private static ConfiguredNodes of(DeclareCommandsPacket.Node[] nodes, Options options) {
ConfiguredNodes configuredNodes = new ConfiguredNodes();
configuredNodes.nodes = nodes;
configuredNodes.options = options;
return configuredNodes;
}
public DeclareCommandsPacket.Node[] getNodes() {
return nodes;
}
public Options getOptions() {
return options;
}
}
public static class Options {
private boolean updateLastNode = true;
private DeclareCommandsPacket.Node[] previousNodes;
public static Options init() {
return new Options();
}
public boolean shouldUpdateLastNode() {
return updateLastNode;
}
public Options updateLastNode(boolean updateLastNode) {
this.updateLastNode = updateLastNode;
return this;
}
public DeclareCommandsPacket.Node[] getPreviousNodes() {
return previousNodes;
}
public Options setPreviousNodes(DeclareCommandsPacket.Node[] previousNodes) {
this.previousNodes = previousNodes;
return this;
}
}
@FunctionalInterface
public interface Request {
void retrieve(int id);
}
}

View File

@ -3,10 +3,9 @@ package net.minestom.server.command.builder.arguments;
import net.minestom.server.command.builder.ArgumentCallback;
import net.minestom.server.command.builder.Command;
import net.minestom.server.command.builder.CommandExecutor;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.arguments.minecraft.SuggestionType;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.command.builder.suggestion.SuggestionCallback;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -35,6 +34,7 @@ public abstract class Argument<T> {
private Supplier<T> defaultValue;
private SuggestionCallback suggestionCallback;
protected SuggestionType suggestionType;
/**
* Creates a new argument.
@ -91,30 +91,14 @@ public abstract class Argument<T> {
*/
public abstract @NotNull T parse(@NotNull String input) throws ArgumentSyntaxException;
/**
* Turns the argument into a list of nodes for command dispatching. Make sure to set the Node's parser.
*
* @param nodeMaker helper object used to create and modify nodes
* @param executable true if this will be the last argument, false otherwise
*/
public abstract void processNodes(@NotNull NodeMaker nodeMaker, boolean executable);
public abstract String parser();
/**
* Builds an argument node.
*
* @param argument the argument
* @param executable true if this will be the last argument, false otherwise
* @return the created {@link DeclareCommandsPacket.Node}
*/
@NotNull
protected static DeclareCommandsPacket.Node simpleArgumentNode(@NotNull Argument<?> argument,
boolean executable, boolean redirect, boolean suggestion) {
DeclareCommandsPacket.Node argumentNode = new DeclareCommandsPacket.Node();
public byte @Nullable [] nodeProperties() {
return null;
}
argumentNode.flags = DeclareCommandsPacket.getFlag(DeclareCommandsPacket.NodeType.ARGUMENT, executable, redirect, suggestion);
argumentNode.name = argument.getId();
return argumentNode;
public @Nullable SuggestionType suggestionType() {
return suggestionType;
}
/**
@ -242,6 +226,7 @@ public abstract class Argument<T> {
*/
public Argument<T> setSuggestionCallback(@NotNull SuggestionCallback suggestionCallback) {
this.suggestionCallback = suggestionCallback;
this.suggestionType = SuggestionType.ASK_SERVER;
return this;
}
@ -251,7 +236,7 @@ public abstract class Argument<T> {
* @return If this argument has a suggestion.
*/
public boolean hasSuggestion() {
return suggestionCallback != null;
return suggestionType != null;
}
/**
@ -317,8 +302,8 @@ public abstract class Argument<T> {
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
argument.processNodes(nodeMaker, executable);
public String parser() {
return argument.parser();
}
}
@ -346,8 +331,8 @@ public abstract class Argument<T> {
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
argument.processNodes(nodeMaker, executable);
public String parser() {
return argument.parser();
}
}
}

View File

@ -1,8 +1,6 @@
package net.minestom.server.command.builder.arguments;
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.jetbrains.annotations.NotNull;
/**
@ -30,13 +28,9 @@ public class ArgumentBoolean extends Argument<Boolean> {
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
argumentNode.parser = "brigadier:bool";
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
public String parser() {
return "brigadier:bool";
}
@Override
public String toString() {
return String.format("Boolean<%s>", getId());

View File

@ -3,9 +3,7 @@ package net.minestom.server.command.builder.arguments;
import net.minestom.server.MinecraftServer;
import net.minestom.server.command.builder.CommandDispatcher;
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 net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
@ -37,22 +35,8 @@ public class ArgumentCommand extends Argument<CommandResult> {
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
final DeclareCommandsPacket.Node[] lastNodes = nodeMaker.getLatestNodes();
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
}
}
public String parser() {
return null;
}
public boolean isOnlyCorrect() {

View File

@ -1,10 +1,10 @@
package net.minestom.server.command.builder.arguments;
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.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.function.UnaryOperator;
@ -40,20 +40,12 @@ public class ArgumentEnum<E extends Enum> extends Argument<E> {
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
// Create a primitive array for mapping
DeclareCommandsPacket.Node[] nodes = new DeclareCommandsPacket.Node[this.values.length];
public String parser() {
return null;
}
// Create a node for each restrictions as literal
for (int i = 0; i < nodes.length; i++) {
DeclareCommandsPacket.Node argumentNode = new DeclareCommandsPacket.Node();
argumentNode.flags = DeclareCommandsPacket.getFlag(DeclareCommandsPacket.NodeType.LITERAL,
executable, false, false);
argumentNode.name = this.format.formatter.apply(this.values[i].name());
nodes[i] = argumentNode;
}
nodeMaker.addNodes(nodes);
public List<String> entries() {
return Arrays.stream(values).map(x -> format.formatter.apply(x.name())).toList();
}
public enum Format {

View File

@ -1,7 +1,6 @@
package net.minestom.server.command.builder.arguments;
import net.minestom.server.command.builder.CommandContext;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.command.builder.parser.CommandParser;
import net.minestom.server.command.builder.parser.ValidSyntaxHolder;
@ -38,10 +37,11 @@ public class ArgumentGroup extends Argument<CommandContext> {
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
for (int i = 0; i < group.length; i++) {
final boolean isLast = i == group.length - 1;
group[i].processNodes(nodeMaker, executable && isLast);
}
public String parser() {
return null;
}
public List<Argument<?>> group() {
return List.of(group);
}
}

View File

@ -1,8 +1,6 @@
package net.minestom.server.command.builder.arguments;
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.jetbrains.annotations.NotNull;
public class ArgumentLiteral extends Argument<String> {
@ -23,13 +21,8 @@ public class ArgumentLiteral extends Argument<String> {
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
DeclareCommandsPacket.Node literalNode = new DeclareCommandsPacket.Node();
literalNode.flags = DeclareCommandsPacket.getFlag(DeclareCommandsPacket.NodeType.LITERAL,
executable, false, false);
literalNode.name = getId();
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{literalNode});
public String parser() {
return null;
}
@Override

View File

@ -1,8 +1,6 @@
package net.minestom.server.command.builder.arguments;
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 net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.NotNull;
@ -58,24 +56,12 @@ public class ArgumentLoop<T> extends Argument<List<T>> {
return result;
}
public List<Argument<T>> arguments() {
return arguments;
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
DeclareCommandsPacket.Node[] latestNodes = nodeMaker.getLatestNodes();
for (DeclareCommandsPacket.Node latestNode : latestNodes) {
final int id = nodeMaker.getNodeIdsMap().getInt(latestNode);
for (Argument<T> argument : arguments) {
argument.processNodes(nodeMaker, executable);
NodeMaker.ConfiguredNodes configuredNodes = nodeMaker.getLatestConfiguredNodes();
// For the next loop argument to start at the same place
configuredNodes.getOptions().setPreviousNodes(latestNodes);
for (DeclareCommandsPacket.Node lastArgumentNode : configuredNodes.getNodes()) {
lastArgumentNode.flags |= 0x08;
lastArgumentNode.redirectedNode = id;
}
}
}
public String parser() {
return null;
}
}

View File

@ -1,11 +1,10 @@
package net.minestom.server.command.builder.arguments;
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 net.minestom.server.utils.StringUtils;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Argument which will take a quoted string.
@ -31,15 +30,15 @@ public class ArgumentString extends Argument<String> {
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
public String parser() {
return "brigadier:string";
}
argumentNode.parser = "brigadier:string";
argumentNode.properties = BinaryWriter.makeArray(packetWriter -> {
@Override
public byte @Nullable [] nodeProperties() {
return BinaryWriter.makeArray(packetWriter -> {
packetWriter.writeVarInt(1); // Quotable phrase
});
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
}
/**

View File

@ -1,10 +1,9 @@
package net.minestom.server.command.builder.arguments;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import net.minestom.server.utils.StringUtils;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.regex.Pattern;
@ -26,15 +25,15 @@ public class ArgumentStringArray extends Argument<String[]> {
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
public String parser() {
return "brigadier:string";
}
argumentNode.parser = "brigadier:string";
argumentNode.properties = BinaryWriter.makeArray(packetWriter -> {
@Override
public byte @Nullable [] nodeProperties() {
return BinaryWriter.makeArray(packetWriter -> {
packetWriter.writeVarInt(2); // Greedy phrase
});
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
}
@Override

View File

@ -1,7 +1,10 @@
package net.minestom.server.command.builder.arguments;
import net.minestom.server.command.builder.arguments.minecraft.*;
import net.minestom.server.command.builder.arguments.minecraft.registry.*;
import net.minestom.server.command.builder.arguments.minecraft.registry.ArgumentEnchantment;
import net.minestom.server.command.builder.arguments.minecraft.registry.ArgumentEntityType;
import net.minestom.server.command.builder.arguments.minecraft.registry.ArgumentParticle;
import net.minestom.server.command.builder.arguments.minecraft.registry.ArgumentPotionEffect;
import net.minestom.server.command.builder.arguments.number.ArgumentDouble;
import net.minestom.server.command.builder.arguments.number.ArgumentFloat;
import net.minestom.server.command.builder.arguments.number.ArgumentInteger;

View File

@ -1,11 +1,9 @@
package net.minestom.server.command.builder.arguments;
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 net.minestom.server.utils.StringUtils;
import net.minestom.server.utils.binary.BinaryWriter;
import net.minestom.server.utils.validate.Check;
import net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -70,32 +68,15 @@ public class ArgumentWord extends Argument<String> {
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
if (restrictions != null) {
public String parser() {
return "brigadier:string";
}
// Create a primitive array for mapping
DeclareCommandsPacket.Node[] nodes = new DeclareCommandsPacket.Node[this.restrictions.length];
// Create a node for each restrictions as literal
for (int i = 0; i < nodes.length; i++) {
DeclareCommandsPacket.Node argumentNode = new DeclareCommandsPacket.Node();
argumentNode.flags = DeclareCommandsPacket.getFlag(DeclareCommandsPacket.NodeType.LITERAL,
executable, false, false);
argumentNode.name = this.restrictions[i];
nodes[i] = argumentNode;
}
nodeMaker.addNodes(nodes);
} else {
// Can be any word, add only one argument node
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
argumentNode.parser = "brigadier:string";
argumentNode.properties = BinaryWriter.makeArray(packetWriter -> {
packetWriter.writeVarInt(0); // Single word
});
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
}
@Override
public byte @Nullable [] nodeProperties() {
return BinaryWriter.makeArray(packetWriter -> {
packetWriter.writeVarInt(0); // Single word
});
}
/**

View File

@ -1,10 +1,8 @@
package net.minestom.server.command.builder.arguments.minecraft;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.instance.block.Block;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import net.minestom.server.utils.block.BlockUtils;
import org.jetbrains.annotations.NotNull;
@ -25,11 +23,8 @@ public class ArgumentBlockState extends Argument<Block> {
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
argumentNode.parser = "minecraft:block_state";
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
public String parser() {
return "minecraft:block_state";
}
/**

View File

@ -2,10 +2,8 @@ package net.minestom.server.command.builder.arguments.minecraft;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.jetbrains.annotations.NotNull;
/**
@ -41,11 +39,8 @@ public class ArgumentColor extends Argument<Style> {
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
argumentNode.parser = "minecraft:color";
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
public String parser() {
return "minecraft:color";
}
@Override

View File

@ -3,10 +3,8 @@ package net.minestom.server.command.builder.arguments.minecraft;
import com.google.gson.JsonParseException;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.jetbrains.annotations.NotNull;
public class ArgumentComponent extends Argument<Component> {
@ -28,12 +26,8 @@ public class ArgumentComponent extends Argument<Component> {
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
argumentNode.parser = "minecraft:component";
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
public String parser() {
return "minecraft:component";
}
@Override

View File

@ -1,16 +1,15 @@
package net.minestom.server.command.builder.arguments.minecraft;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.entity.EntityType;
import net.minestom.server.entity.GameMode;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import net.minestom.server.utils.StringUtils;
import net.minestom.server.utils.binary.BinaryWriter;
import net.minestom.server.utils.entity.EntityFinder;
import net.minestom.server.utils.math.IntRange;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
@ -72,10 +71,13 @@ public class ArgumentEntity extends Argument<EntityFinder> {
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
argumentNode.parser = "minecraft:entity";
argumentNode.properties = BinaryWriter.makeArray(packetWriter -> {
public String parser() {
return "minecraft:entity";
}
@Override
public byte @Nullable [] nodeProperties() {
return BinaryWriter.makeArray(packetWriter -> {
byte mask = 0;
if (this.isOnlySingleEntity()) {
mask |= 0x01;
@ -85,8 +87,6 @@ public class ArgumentEntity extends Argument<EntityFinder> {
}
packetWriter.writeByte(mask);
});
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
}
/**

View File

@ -10,7 +10,12 @@ import net.minestom.server.utils.math.FloatRange;
public class ArgumentFloatRange extends ArgumentRange<FloatRange, Float> {
public ArgumentFloatRange(String id) {
super(id, "minecraft:float_range", -Float.MAX_VALUE, Float.MAX_VALUE, Float::parseFloat, FloatRange::new);
super(id, -Float.MAX_VALUE, Float.MAX_VALUE, Float::parseFloat, FloatRange::new);
}
@Override
public String parser() {
return "minecraft:float_range";
}
@Override

View File

@ -10,7 +10,12 @@ import net.minestom.server.utils.math.IntRange;
public class ArgumentIntRange extends ArgumentRange<IntRange, Integer> {
public ArgumentIntRange(String id) {
super(id, "minecraft:int_range", Integer.MIN_VALUE, Integer.MAX_VALUE, Integer::parseInt, IntRange::new);
super(id, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer::parseInt, IntRange::new);
}
@Override
public String parser() {
return "minecraft:int_range";
}
@Override

View File

@ -1,11 +1,9 @@
package net.minestom.server.command.builder.arguments.minecraft;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
import org.jglrxavpok.hephaistos.nbt.NBTException;
@ -37,11 +35,8 @@ public class ArgumentItemStack extends Argument<ItemStack> {
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
argumentNode.parser = "minecraft:item_stack";
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
public String parser() {
return "minecraft:item_stack";
}
/**

View File

@ -1,9 +1,7 @@
package net.minestom.server.command.builder.arguments.minecraft;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
@ -41,11 +39,8 @@ public class ArgumentNbtCompoundTag extends Argument<NBTCompound> {
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
argumentNode.parser = "minecraft:nbt_compound_tag";
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
public String parser() {
return "minecraft:nbt_compound_tag";
}
@Override

View File

@ -1,9 +1,7 @@
package net.minestom.server.command.builder.arguments.minecraft;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.jetbrains.annotations.NotNull;
import org.jglrxavpok.hephaistos.nbt.NBT;
import org.jglrxavpok.hephaistos.nbt.NBTException;
@ -37,11 +35,8 @@ public class ArgumentNbtTag extends Argument<NBT> {
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
argumentNode.parser = "minecraft:nbt_tag";
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
public String parser() {
return "minecraft:nbt_tag";
}
@Override

View File

@ -1,9 +1,7 @@
package net.minestom.server.command.builder.arguments.minecraft;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import net.minestom.server.utils.math.Range;
import org.jetbrains.annotations.NotNull;
@ -22,15 +20,13 @@ public abstract class ArgumentRange<T extends Range<N>, N extends Number> extend
private final N min;
private final N max;
private final Function<String, N> parser;
private final String parserName;
private final BiFunction<N, N, T> rangeConstructor;
public ArgumentRange(@NotNull String id, String parserName, N min, N max, Function<String, N> parser, BiFunction<N, N, T> rangeConstructor) {
public ArgumentRange(@NotNull String id, N min, N max, Function<String, N> parser, BiFunction<N, N, T> rangeConstructor) {
super(id);
this.min = min;
this.max = max;
this.parser = parser;
this.parserName = parserName;
this.rangeConstructor = rangeConstructor;
}
@ -69,13 +65,4 @@ public abstract class ArgumentRange<T extends Range<N>, N extends Number> extend
}
throw new ArgumentSyntaxException("Invalid range format", input, FORMAT_ERROR);
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
argumentNode.parser = parserName;
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
}
}

View File

@ -1,9 +1,7 @@
package net.minestom.server.command.builder.arguments.minecraft;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.NotNull;
@ -25,11 +23,8 @@ public class ArgumentResourceLocation extends Argument<String> {
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
argumentNode.parser = "minecraft:resource_location";
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
public String parser() {
return "minecraft:resource_location";
}
@Override

View File

@ -2,10 +2,8 @@ package net.minestom.server.command.builder.arguments.minecraft;
import it.unimi.dsi.fastutil.chars.CharArrayList;
import it.unimi.dsi.fastutil.chars.CharList;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import net.minestom.server.utils.time.TimeUnit;
import org.jetbrains.annotations.NotNull;
@ -62,11 +60,8 @@ public class ArgumentTime extends Argument<Duration> {
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
argumentNode.parser = "minecraft:time";
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
public String parser() {
return "minecraft:time";
}
@Override

View File

@ -1,9 +1,7 @@
package net.minestom.server.command.builder.arguments.minecraft;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
@ -27,11 +25,8 @@ public class ArgumentUUID extends Argument<UUID> {
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
argumentNode.parser = "minecraft:uuid";
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
public String parser() {
return "minecraft:uuid";
}
@Override

View File

@ -1,8 +1,6 @@
package net.minestom.server.command.builder.arguments.minecraft.registry;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.item.Enchantment;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.jetbrains.annotations.NotNull;
/**
@ -15,16 +13,13 @@ public class ArgumentEnchantment extends ArgumentRegistry<Enchantment> {
}
@Override
public Enchantment getRegistry(@NotNull String value) {
return Enchantment.fromNamespaceId(value);
public String parser() {
return "minecraft:item_enchantment";
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
argumentNode.parser = "minecraft:item_enchantment";
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
public Enchantment getRegistry(@NotNull String value) {
return Enchantment.fromNamespaceId(value);
}
@Override

View File

@ -1,9 +1,7 @@
package net.minestom.server.command.builder.arguments.minecraft.registry;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.arguments.minecraft.SuggestionType;
import net.minestom.server.entity.EntityType;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.jetbrains.annotations.NotNull;
/**
@ -13,6 +11,12 @@ public class ArgumentEntityType extends ArgumentRegistry<EntityType> {
public ArgumentEntityType(String id) {
super(id);
suggestionType = SuggestionType.SUMMONABLE_ENTITIES;
}
@Override
public String parser() {
return "minecraft:resource_location";
}
@Override
@ -20,15 +24,6 @@ public class ArgumentEntityType extends ArgumentRegistry<EntityType> {
return EntityType.fromNamespaceId(value);
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, true);
argumentNode.parser = "minecraft:resource_location";
argumentNode.suggestionsType = SuggestionType.SUMMONABLE_ENTITIES.getIdentifier();
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
}
@Override
public String toString() {
return String.format("EntityType<%s>", getId());

View File

@ -1,7 +1,5 @@
package net.minestom.server.command.builder.arguments.minecraft.registry;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import net.minestom.server.particle.Particle;
import org.jetbrains.annotations.NotNull;
@ -15,16 +13,13 @@ public class ArgumentParticle extends ArgumentRegistry<Particle> {
}
@Override
public Particle getRegistry(@NotNull String value) {
return Particle.fromNamespaceId(value);
public String parser() {
return "minecraft:particle";
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
argumentNode.parser = "minecraft:particle";
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
public Particle getRegistry(@NotNull String value) {
return Particle.fromNamespaceId(value);
}
@Override

View File

@ -1,7 +1,5 @@
package net.minestom.server.command.builder.arguments.minecraft.registry;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import net.minestom.server.potion.PotionEffect;
import org.jetbrains.annotations.NotNull;
@ -15,16 +13,13 @@ public class ArgumentPotionEffect extends ArgumentRegistry<PotionEffect> {
}
@Override
public PotionEffect getRegistry(@NotNull String value) {
return PotionEffect.fromNamespaceId(value);
public String parser() {
return "minecraft:mob_effect";
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
argumentNode.parser = "minecraft:mob_effect";
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
public PotionEffect getRegistry(@NotNull String value) {
return PotionEffect.fromNamespaceId(value);
}
@Override

View File

@ -1,9 +1,7 @@
package net.minestom.server.command.builder.arguments.number;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -67,19 +65,19 @@ public class ArgumentNumber<T extends Number> extends Argument<T> {
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
public String parser() {
return parserName;
}
argumentNode.parser = parserName;
argumentNode.properties = BinaryWriter.makeArray(packetWriter -> {
@Override
public byte @Nullable [] nodeProperties() {
return BinaryWriter.makeArray(packetWriter -> {
packetWriter.writeByte(getNumberProperties());
if (this.hasMin())
propertiesWriter.accept(packetWriter, getMin());
if (this.hasMax())
propertiesWriter.accept(packetWriter, getMax());
});
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
}
@NotNull

View File

@ -1,7 +1,5 @@
package net.minestom.server.command.builder.arguments.relative;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.jetbrains.annotations.NotNull;
import java.util.function.Function;
@ -18,11 +16,8 @@ public class ArgumentRelativeBlockPosition extends ArgumentRelativeVec {
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
argumentNode.parser = "minecraft:block_pos";
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
public String parser() {
return "minecraft:block_pos";
}
@Override

View File

@ -1,8 +1,6 @@
package net.minestom.server.command.builder.arguments.relative;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.jetbrains.annotations.NotNull;
import java.util.function.Function;
@ -19,11 +17,8 @@ public class ArgumentRelativeVec2 extends ArgumentRelativeVec {
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
argumentNode.parser = "minecraft:vec2";
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
public String parser() {
return "minecraft:vec2";
}
@Override

View File

@ -1,8 +1,6 @@
package net.minestom.server.command.builder.arguments.relative;
import net.minestom.server.command.builder.NodeMaker;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.jetbrains.annotations.NotNull;
import java.util.function.Function;
@ -19,11 +17,8 @@ public class ArgumentRelativeVec3 extends ArgumentRelativeVec {
}
@Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
argumentNode.parser = "minecraft:vec3";
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
public String parser() {
return "minecraft:vec3";
}
@Override

View File

@ -1,11 +1,9 @@
package net.minestom.server.command;
import net.minestom.server.command.builder.Command;
import net.minestom.server.entity.Player;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.junit.jupiter.api.Test;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.junit.jupiter.api.Assertions.*;
@ -47,40 +45,15 @@ public class CommandManagerTest {
assertTrue(check.get());
}
@Test
public void testDeclareCommandsPacket() {
var manager = new CommandManager();
var player = new Player(UUID.randomUUID(), "TestPlayer", null) {
@Override
protected void playerConnectionInit() {
}
@Override
public boolean isOnline() {
return false;
}
};
manager.register(new Command("name"));
var packet = manager.createDeclareCommandsPacket(player);
assertEquals(packet.rootIndex(), 0);
assertEquals(packet.nodes().size(), 2);
assertNodeEquals(packet.nodes().get(0), (byte) 0, new int[]{1}, 0, "", "", null, "");
assertNodeEquals(packet.nodes().get(1), (byte) 5, new int[0], 0, "name", "", null, "");
}
private static void assertNodeEquals(DeclareCommandsPacket.Node node, byte flags, int[] children, int redirectedNode,
String name, String parser, byte[] properties, String suggestionsType) {
assertEquals(node.flags, flags);
assertArrayEquals(node.children, children);
assertEquals(node.redirectedNode, redirectedNode);
assertEquals(node.name, name);
assertEquals(node.parser, parser);
assertArrayEquals(node.properties, properties);
assertEquals(node.suggestionsType, suggestionsType);
assertEquals(flags, node.flags);
assertArrayEquals(children, node.children);
assertEquals(redirectedNode, node.redirectedNode);
assertEquals(name, node.name);
assertEquals(parser, node.parser);
assertArrayEquals(properties, node.properties);
assertEquals(suggestionsType, node.suggestionsType);
}
}