Make Node and NodeGraph records and extend them as a preparation for parsing support

This commit is contained in:
Noel Németh 2022-07-07 02:33:44 +02:00
parent ba73c742f4
commit eebb9b832f
3 changed files with 59 additions and 102 deletions

View File

@ -1,27 +1,33 @@
package net.minestom.server.command;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectList;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectSet;
import net.minestom.server.command.builder.Command;
import net.minestom.server.command.builder.CommandSyntax;
import net.minestom.server.command.builder.arguments.*;
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;
import net.minestom.server.command.builder.arguments.number.ArgumentLong;
import net.minestom.server.command.builder.condition.CommandCondition;
import net.minestom.server.command.builder.exception.IllegalCommandStructureException;
import net.minestom.server.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.Stack;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import java.util.stream.Stream;
final class GraphBuilder {
private static final List<Class<? extends Argument<?>>> argPriorities = List.of(
ArgumentInteger.class, ArgumentLong.class, ArgumentFloat.class, ArgumentDouble.class //TODO the rest
);
private final AtomicInteger idSource = new AtomicInteger();
private final ObjectSet<Node> nodes = new ObjectOpenHashSet<>();
private final ObjectList<Node> nodes = new ObjectArrayList<>();
private final ObjectSet<Supplier<Boolean>> redirectWaitList = new ObjectOpenHashSet<>();
private final Node root = rootNode();
@ -30,21 +36,20 @@ final class GraphBuilder {
}
private Node rootNode() {
final Node rootNode = new Node(idSource.getAndIncrement());
final Node rootNode = Node.root(idSource.getAndIncrement());
nodes.add(rootNode);
return rootNode;
}
private Node createLiteralNode(String name, @Nullable Node parent, boolean executable, @Nullable String[] aliases, @Nullable Integer redirectTo) {
private Node createLiteralNode(String name, @Nullable Node parent, boolean executable, @Nullable String[] aliases, @Nullable AtomicInteger redirectTo) {
if (aliases != null) {
final Node node = createLiteralNode(name, parent, executable, null, null);
for (String alias : aliases) {
createLiteralNode(alias, parent, executable, null, node.id());
createLiteralNode(alias, parent, executable, null, new AtomicInteger(node.id()));
}
return node;
} else {
final Node literalNode = new Node(idSource.getAndIncrement(), name, redirectTo);
literalNode.setExecutable(executable);
final Node literalNode = Node.literal(idSource.getAndIncrement(), name, executable, redirectTo);
nodes.add(literalNode);
if (parent != null) parent.addChild(literalNode);
return literalNode;
@ -63,20 +68,21 @@ final class GraphBuilder {
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(), null, false, null, 0)};
return new Node[]{createLiteralNode(argument.getId(), null, false, null, new AtomicInteger(0))};
}
final int id = idSource.getAndIncrement();
nodes = new Node[] {argument instanceof ArgumentLiteral ? new Node(id, argument.getId(), null) : new Node(id, argument)};
nodes = new Node[] {argument instanceof ArgumentLiteral ?
Node.literal(id, argument.getId(), executable, null) :
Node.argument(id, argument, executable, null)};
}
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);
node.redirectTarget().set(target);
return true;
}
return false;
@ -105,11 +111,20 @@ final class GraphBuilder {
}
}
private void finalizeStructure() {
private void finalizeStructure(boolean forParsing) {
redirectWaitList.removeIf(Supplier::get);
if (redirectWaitList.size() > 0)
throw new IllegalCommandStructureException("Could not set redirects for all arguments! Did you provide a " +
"correct id path which doesn't rely on redirects?");
nodes.sort(Comparator.comparing(Node::id));
if (forParsing) {
for (Node node : nodes) {
node.children().sort((k1, k2) -> Integer.compare(argPriorities.indexOf(nodes.get(k1).arg().getClass()),
argPriorities.indexOf(nodes.get(k2).arg().getClass())));
}
}
}
@ -193,9 +208,9 @@ final class GraphBuilder {
}
}
builder.finalizeStructure();
builder.finalizeStructure(player == null);
return new NodeGraph(builder.nodes, builder.root.id());
return new NodeGraph(builder.nodes, builder.root);
}
public static NodeGraph forServer(@NotNull Set<Command> commands) {

View File

@ -1,56 +1,27 @@
package net.minestom.server.command;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.ints.IntSets;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.arguments.ArgumentLiteral;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket.NodeType;
import org.jetbrains.annotations.Nullable;
final class Node {
private final int id;
private final IntSet children = new IntOpenHashSet();
private final IntSet childrenView = IntSets.unmodifiable(children);
private final DeclareCommandsPacket.NodeType type;
private String name;
private Integer redirectTarget;
private Argument<?> argument;
private boolean executable;
import java.util.concurrent.atomic.AtomicInteger;
Node(int id, DeclareCommandsPacket.NodeType type) {
this.id = id;
this.type = type;
record Node(int id, IntList children, NodeType type, String name, boolean executable, Argument<?> arg,
AtomicInteger redirectTarget) {
public static Node root(int id) {
return new Node(id, new IntArrayList(), NodeType.ROOT, null, false, null, null);
}
public static Node literal(int id, String name, boolean executable, @Nullable AtomicInteger redirectTarget) {
return new Node(id, new IntArrayList(), NodeType.LITERAL, name, executable, new ArgumentLiteral(name), redirectTarget);
}
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 static Node argument(int id, Argument<?> argument, boolean executable, @Nullable AtomicInteger redirectTarget) {
return new Node(id, new IntArrayList(), NodeType.ARGUMENT, argument.getId(), executable, argument, redirectTarget);
}
public void addChild(Node ...nodes) {
@ -63,41 +34,25 @@ final class Node {
return children.contains(node.id());
}
public int id() {
return id;
}
public DeclareCommandsPacket.NodeType type() {
return type;
}
public IntSet children() {
return childrenView;
}
public Integer redirectTarget() {
return redirectTarget;
}
public boolean isRoot() {
return type == DeclareCommandsPacket.NodeType.ROOT;
return type == NodeType.ROOT;
}
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());
type == NodeType.ARGUMENT && arg.hasSuggestion());
node.name = name;
if (redirectTarget != null) {
node.redirectedNode = redirectTarget;
node.redirectedNode = redirectTarget.get();
}
if (type == DeclareCommandsPacket.NodeType.ARGUMENT) {
node.properties = argument.nodeProperties();
node.parser = argument.parser();
if (argument.hasSuggestion()) {
if (type == NodeType.ARGUMENT) {
node.properties = arg.nodeProperties();
node.parser = arg.parser();
if (arg.hasSuggestion()) {
//noinspection ConstantConditions
node.suggestionsType = argument.suggestionType().getIdentifier();
node.suggestionsType = arg.suggestionType().getIdentifier();
}
}
return node;

View File

@ -1,25 +1,12 @@
package net.minestom.server.command;
import it.unimi.dsi.fastutil.objects.ObjectImmutableList;
import it.unimi.dsi.fastutil.objects.ObjectList;
import it.unimi.dsi.fastutil.objects.ObjectSet;
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
import java.util.Comparator;
import java.util.List;
class NodeGraph {
private final ObjectList<Node> nodes;
private final Node root;
NodeGraph(ObjectSet<Node> nodes, int rootId) {
this.nodes = new ObjectImmutableList<>(nodes.stream().sorted(Comparator.comparing(Node::id)).toList());
this.root = this.nodes.get(rootId);
assert root.isRoot() : "rootId doesn't point to the root node";
assert this.nodes.stream().filter(Node::isRoot).count() == 1 : "Invalid root node count!";
}
record NodeGraph(List<Node> nodes, Node root) {
public Node resolveId(int id) {
return nodes.get(id);
@ -30,8 +17,8 @@ class NodeGraph {
}
public @Nullable Node getRedirectTarget(Node node) {
final Integer target = node.redirectTarget();
return target == null ? null : resolveId(target);
if (node.redirectTarget() == null) return null;
return resolveId(node.redirectTarget().get());
}
@Contract("-> new")