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 AutoViewCommand());
commandManager.register(new SaveCommand()); commandManager.register(new SaveCommand());
commandManager.register(new GamemodeCommand()); commandManager.register(new GamemodeCommand());
commandManager.register(new ExecuteCommand());
commandManager.register(new RedirectTestCommand());
commandManager.setUnknownCommandCallback((sender, command) -> sender.sendMessage(Component.text("Unknown command", NamedTextColor.RED))); 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; package net.minestom.server.command;
import it.unimi.dsi.fastutil.Pair; import net.minestom.server.command.builder.Command;
import it.unimi.dsi.fastutil.ints.IntArrayList; import net.minestom.server.command.builder.CommandDispatcher;
import it.unimi.dsi.fastutil.ints.IntList; import net.minestom.server.command.builder.CommandResult;
import net.minestom.server.command.builder.*; 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.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.EventDispatcher; import net.minestom.server.event.EventDispatcher;
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;
import net.minestom.server.utils.ArrayUtils;
import net.minestom.server.utils.callback.CommandCallback; import net.minestom.server.utils.callback.CommandCallback;
import net.minestom.server.utils.validate.Check; import net.minestom.server.utils.validate.Check;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.*;
/** /**
* Manager used to register {@link Command commands}. * Manager used to register {@link Command commands}.
* <p> * <p>
@ -167,282 +160,43 @@ public final class CommandManager {
* @return the {@link DeclareCommandsPacket} for {@code player} * @return the {@link DeclareCommandsPacket} for {@code player}
*/ */
public @NotNull DeclareCommandsPacket createDeclareCommandsPacket(@NotNull Player player) { public @NotNull DeclareCommandsPacket createDeclareCommandsPacket(@NotNull Player player) {
return buildPacket(player); final GraphBuilder factory = new GraphBuilder();
}
/** for (Command command : this.dispatcher.getCommands()) {
* Builds the {@link DeclareCommandsPacket} for a {@link Player}. // Check if user can use the command
* final CommandCondition condition = command.getCondition();
* @param player the player to build the packet for if (condition != null && !condition.canUse(player, null)) continue;
* @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();
// Root node // Add command to the graph
DeclareCommandsPacket.Node rootNode = new DeclareCommandsPacket.Node(); // Create the command's root node
rootNode.flags = 0; final Node cmdNode = factory.createLiteralNode(command.getName(), true,
nodes.add(rootNode); command.getDefaultExecutor() != null, command.getAliases(), null);
Map<Command, Integer> commandIdentityMap = new IdentityHashMap<>(); // Add syntax to the command
Map<Argument<?>, Integer> argumentIdentityMap = new IdentityHashMap<>(); for (CommandSyntax syntax : command.getSyntaxes()) {
boolean executable = false;
List<Pair<String, NodeMaker.Request>> nodeRequests = new ArrayList<>(); Node[] lastArgNodes = new Node[] {cmdNode}; // First arg links to cmd root
@NotNull Argument<?>[] arguments = syntax.getArguments();
// Brigadier-like commands for (int i = 0; i < arguments.length; i++) {
for (Command command : dispatcher.getCommands()) { Argument<?> argument = arguments[i];
final int commandNodeIndex = serializeCommand(player, command, nodes, rootChildren, commandIdentityMap, argumentIdentityMap, nodeRequests); // Determine if command is executable here
commandIdentityMap.put(command, commandNodeIndex); if (executable && argument.getDefaultValue() == null) {
} // Optional arg was followed by a non-optional
throw new RuntimeException("");//todo exception
// 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;
}
} }
if (foundSharedPart) { if (!executable && i < arguments.length-1 && arguments[i+1].getDefaultValue() != null || i+1 == arguments.length) {
continue; executable = true;
} }
} // Append current node to previous
final Node[] argNodes = factory.createArgumentNode(argument, executable);
// Process the nodes for the argument for (Node lastArgNode : lastArgNodes) {
{ lastArgNode.addChild(argNodes);
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();
}
} }
lastArgNodes = argNodes;
// Used to do not re-compute the previous arguments
lastArgumentNodeIndex = nodesLayer.size();
} }
} }
nodeRequests.addAll(nodeMaker.getNodeRequests());
syntaxesArguments.put(syntax, arguments);
} }
storedArgumentsNodes.forEach((indexedArgument, argNodes) -> { return factory.createCommandPacket();
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;
} }
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.ArgumentCallback;
import net.minestom.server.command.builder.Command; import net.minestom.server.command.builder.Command;
import net.minestom.server.command.builder.CommandExecutor; 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.exception.ArgumentSyntaxException;
import net.minestom.server.command.builder.suggestion.SuggestionCallback; 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.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -35,6 +34,7 @@ public abstract class Argument<T> {
private Supplier<T> defaultValue; private Supplier<T> defaultValue;
private SuggestionCallback suggestionCallback; private SuggestionCallback suggestionCallback;
protected SuggestionType suggestionType;
/** /**
* Creates a new argument. * Creates a new argument.
@ -91,30 +91,14 @@ public abstract class Argument<T> {
*/ */
public abstract @NotNull T parse(@NotNull String input) throws ArgumentSyntaxException; public abstract @NotNull T parse(@NotNull String input) throws ArgumentSyntaxException;
/** public abstract String parser();
* 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 byte @Nullable [] nodeProperties() {
* Builds an argument node. return null;
* }
* @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();
argumentNode.flags = DeclareCommandsPacket.getFlag(DeclareCommandsPacket.NodeType.ARGUMENT, executable, redirect, suggestion); public @Nullable SuggestionType suggestionType() {
argumentNode.name = argument.getId(); return suggestionType;
return argumentNode;
} }
/** /**
@ -242,6 +226,7 @@ public abstract class Argument<T> {
*/ */
public Argument<T> setSuggestionCallback(@NotNull SuggestionCallback suggestionCallback) { public Argument<T> setSuggestionCallback(@NotNull SuggestionCallback suggestionCallback) {
this.suggestionCallback = suggestionCallback; this.suggestionCallback = suggestionCallback;
this.suggestionType = SuggestionType.ASK_SERVER;
return this; return this;
} }
@ -251,7 +236,7 @@ public abstract class Argument<T> {
* @return If this argument has a suggestion. * @return If this argument has a suggestion.
*/ */
public boolean hasSuggestion() { public boolean hasSuggestion() {
return suggestionCallback != null; return suggestionType != null;
} }
/** /**
@ -317,8 +302,8 @@ public abstract class Argument<T> {
} }
@Override @Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) { public String parser() {
argument.processNodes(nodeMaker, executable); return argument.parser();
} }
} }
@ -346,8 +331,8 @@ public abstract class Argument<T> {
} }
@Override @Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) { public String parser() {
argument.processNodes(nodeMaker, executable); return argument.parser();
} }
} }
} }

View File

@ -1,8 +1,6 @@
package net.minestom.server.command.builder.arguments; 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.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
/** /**
@ -30,13 +28,9 @@ public class ArgumentBoolean extends Argument<Boolean> {
} }
@Override @Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) { public String parser() {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false); return "brigadier:bool";
argumentNode.parser = "brigadier:bool";
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
} }
@Override @Override
public String toString() { public String toString() {
return String.format("Boolean<%s>", getId()); 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.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.exception.ArgumentSyntaxException; 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.StringUtils;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -37,22 +35,8 @@ public class ArgumentCommand extends Argument<CommandResult> {
} }
@Override @Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) { public String parser() {
final DeclareCommandsPacket.Node[] lastNodes = nodeMaker.getLatestNodes(); return 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) {
node.flags |= 0x08; // Redirection mask
node.redirectedNode = 0; // Redirect to root
}
}
} }
public boolean isOnlyCorrect() { public boolean isOnlyCorrect() {

View File

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

View File

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

View File

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

View File

@ -1,8 +1,6 @@
package net.minestom.server.command.builder.arguments; 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.command.builder.exception.ArgumentSyntaxException;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import net.minestom.server.utils.StringUtils; import net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -58,24 +56,12 @@ public class ArgumentLoop<T> extends Argument<List<T>> {
return result; return result;
} }
public List<Argument<T>> arguments() {
return arguments;
}
@Override @Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) { public String parser() {
DeclareCommandsPacket.Node[] latestNodes = nodeMaker.getLatestNodes(); return null;
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;
}
}
}
} }
} }

View File

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

View File

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

View File

@ -1,7 +1,10 @@
package net.minestom.server.command.builder.arguments; package net.minestom.server.command.builder.arguments;
import net.minestom.server.command.builder.arguments.minecraft.*; 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.ArgumentDouble;
import net.minestom.server.command.builder.arguments.number.ArgumentFloat; import net.minestom.server.command.builder.arguments.number.ArgumentFloat;
import net.minestom.server.command.builder.arguments.number.ArgumentInteger; import net.minestom.server.command.builder.arguments.number.ArgumentInteger;

View File

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

View File

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

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.NamedTextColor;
import net.kyori.adventure.text.format.Style; 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.arguments.Argument;
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 org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
/** /**
@ -41,11 +39,8 @@ public class ArgumentColor extends Argument<Style> {
} }
@Override @Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) { public String parser() {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false); return "minecraft:color";
argumentNode.parser = "minecraft:color";
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
} }
@Override @Override

View File

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

View File

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

View File

@ -10,7 +10,12 @@ import net.minestom.server.utils.math.IntRange;
public class ArgumentIntRange extends ArgumentRange<IntRange, Integer> { public class ArgumentIntRange extends ArgumentRange<IntRange, Integer> {
public ArgumentIntRange(String id) { 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 @Override

View File

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

View File

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

View File

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

View File

@ -1,9 +1,7 @@
package net.minestom.server.command.builder.arguments.minecraft; 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.arguments.Argument;
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.utils.math.Range; import net.minestom.server.utils.math.Range;
import org.jetbrains.annotations.NotNull; 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 min;
private final N max; private final N max;
private final Function<String, N> parser; private final Function<String, N> parser;
private final String parserName;
private final BiFunction<N, N, T> rangeConstructor; 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); super(id);
this.min = min; this.min = min;
this.max = max; this.max = max;
this.parser = parser; this.parser = parser;
this.parserName = parserName;
this.rangeConstructor = rangeConstructor; 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); 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; 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.arguments.Argument;
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.utils.StringUtils; import net.minestom.server.utils.StringUtils;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -25,11 +23,8 @@ public class ArgumentResourceLocation extends Argument<String> {
} }
@Override @Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) { public String parser() {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false); return "minecraft:resource_location";
argumentNode.parser = "minecraft:resource_location";
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
} }
@Override @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.CharArrayList;
import it.unimi.dsi.fastutil.chars.CharList; 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.arguments.Argument;
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.utils.time.TimeUnit; import net.minestom.server.utils.time.TimeUnit;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -62,11 +60,8 @@ public class ArgumentTime extends Argument<Duration> {
} }
@Override @Override
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) { public String parser() {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false); return "minecraft:time";
argumentNode.parser = "minecraft:time";
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
} }
@Override @Override

View File

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

View File

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

View File

@ -1,9 +1,7 @@
package net.minestom.server.command.builder.arguments.minecraft.registry; 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.command.builder.arguments.minecraft.SuggestionType;
import net.minestom.server.entity.EntityType; import net.minestom.server.entity.EntityType;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
/** /**
@ -13,6 +11,12 @@ public class ArgumentEntityType extends ArgumentRegistry<EntityType> {
public ArgumentEntityType(String id) { public ArgumentEntityType(String id) {
super(id); super(id);
suggestionType = SuggestionType.SUMMONABLE_ENTITIES;
}
@Override
public String parser() {
return "minecraft:resource_location";
} }
@Override @Override
@ -20,15 +24,6 @@ public class ArgumentEntityType extends ArgumentRegistry<EntityType> {
return EntityType.fromNamespaceId(value); 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 @Override
public String toString() { public String toString() {
return String.format("EntityType<%s>", getId()); return String.format("EntityType<%s>", getId());

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,11 +1,9 @@
package net.minestom.server.command; package net.minestom.server.command;
import net.minestom.server.command.builder.Command; import net.minestom.server.command.builder.Command;
import net.minestom.server.entity.Player;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket; import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
@ -47,40 +45,15 @@ public class CommandManagerTest {
assertTrue(check.get()); 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, private static void assertNodeEquals(DeclareCommandsPacket.Node node, byte flags, int[] children, int redirectedNode,
String name, String parser, byte[] properties, String suggestionsType) { String name, String parser, byte[] properties, String suggestionsType) {
assertEquals(node.flags, flags); assertEquals(flags, node.flags);
assertArrayEquals(node.children, children); assertArrayEquals(children, node.children);
assertEquals(node.redirectedNode, redirectedNode); assertEquals(redirectedNode, node.redirectedNode);
assertEquals(node.name, name); assertEquals(name, node.name);
assertEquals(node.parser, parser); assertEquals(parser, node.parser);
assertArrayEquals(node.properties, properties); assertArrayEquals(properties, node.properties);
assertEquals(node.suggestionsType, suggestionsType); assertEquals(suggestionsType, node.suggestionsType);
} }
} }