Fix building and add graphviz exporter

This commit is contained in:
Noel Németh 2022-07-07 09:33:32 +02:00
parent eebb9b832f
commit 3d5721e8f2
5 changed files with 63 additions and 59 deletions

View File

@ -55,6 +55,7 @@ public class Main {
commandManager.register(new ExecuteCommand());
commandManager.register(new RedirectTestCommand());
MinecraftServer.getCommandManager().createDeclareCommandsPacket(null);
commandManager.setUnknownCommandCallback((sender, command) -> sender.sendMessage(Component.text("Unknown command", NamedTextColor.RED)));

View File

@ -157,6 +157,8 @@ public final class CommandManager {
* @return the {@link DeclareCommandsPacket} for {@code player}
*/
public @NotNull DeclareCommandsPacket createDeclareCommandsPacket(@NotNull Player player) {
return GraphBuilder.forPlayer(this.dispatcher.getCommands(), player).createPacket();
final NodeGraph nodeGraph = GraphBuilder.forPlayer(this.dispatcher.getCommands(), player);
System.out.println(nodeGraph.exportGarphvizDot()); //TODO remove before merging
return nodeGraph.createPacket();
}
}

View File

@ -2,8 +2,6 @@ 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.*;
@ -19,7 +17,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import java.util.stream.Stream;
final class GraphBuilder {
@ -28,7 +25,6 @@ final class GraphBuilder {
);
private final AtomicInteger idSource = new AtomicInteger();
private final ObjectList<Node> nodes = new ObjectArrayList<>();
private final ObjectSet<Supplier<Boolean>> redirectWaitList = new ObjectOpenHashSet<>();
private final Node root = rootNode();
private GraphBuilder() {
@ -51,72 +47,31 @@ final class GraphBuilder {
} else {
final Node literalNode = Node.literal(idSource.getAndIncrement(), name, executable, redirectTo);
nodes.add(literalNode);
if (parent != null) parent.addChild(literalNode);
if (parent != null) parent.addChildren(literalNode);
return literalNode;
}
}
private Node[] createArgumentNode(Argument<?> argument, boolean executable) {
private Node[] createArgumentNode(Argument<?> argument, boolean executable, @Nullable AtomicInteger redirectTarget) {
final Node[] nodes;
Integer overrideRedirectTarget = null;
if (argument instanceof ArgumentEnum<?> argumentEnum) {
nodes = argumentEnum.entries().stream().map(x -> createLiteralNode(x, null, executable, null, null)).toArray(Node[]::new);
return argumentEnum.entries().stream().map(x -> createLiteralNode(x, null, 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);
return argumentGroup.group().stream().map(x -> createArgumentNode(x, executable, redirectTarget)).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);
final AtomicInteger target = new AtomicInteger(idSource.get() - 1);
return argumentLoop.arguments().stream().map(x -> createArgumentNode(x, executable, target)).flatMap(Stream::of).toArray(Node[]::new);
} else {
if (argument instanceof ArgumentCommand) {
return new Node[]{createLiteralNode(argument.getId(), null, false, null, new AtomicInteger(0))};
}
final int id = idSource.getAndIncrement();
nodes = new Node[] {argument instanceof ArgumentLiteral ?
Node.literal(id, argument.getId(), executable, null) :
Node.argument(id, argument, executable, null)};
}
for (Node node : nodes) {
this.nodes.add(node);
Integer finalOverrideRedirectTarget = overrideRedirectTarget;
if (finalOverrideRedirectTarget != null) {
redirectWaitList.add(() -> {
int target = finalOverrideRedirectTarget;
if (target != -1) {
node.redirectTarget().set(target);
return true;
}
return false;
});
}
nodes = new Node[] {Node.argument(idSource.getAndIncrement(), argument, executable, redirectTarget)};
}
this.nodes.addAll(Arrays.asList(nodes));
return nodes;
}
private int tryResolveId(String[] path) {
if (path.length == 0) {
return root.id();
} 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.id();
}
}
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) {
@ -168,9 +123,9 @@ final class GraphBuilder {
executable = true;
}
// Append current node to previous
final Node[] argNodes = createArgumentNode(argument, executable);
final Node[] argNodes = createArgumentNode(argument, executable, null);
for (Node lastArgNode : lastArgNodes) {
lastArgNode.addChild(argNodes);
lastArgNode.addChildren(argNodes);
}
lastArgNodes = argNodes;
}

View File

@ -6,6 +6,7 @@ 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.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.atomic.AtomicInteger;
@ -16,15 +17,21 @@ record Node(int id, IntList children, NodeType type, String name, boolean execut
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);
return literal(id, name, executable, new ArgumentLiteral(name), redirectTarget);
}
public static Node literal(int id, String name, boolean executable, @NotNull Argument<?> backingArg, @Nullable AtomicInteger redirectTarget) {
return new Node(id, new IntArrayList(), NodeType.LITERAL, name, executable, backingArg, 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);
return new Node(id, new IntArrayList(), argument instanceof ArgumentLiteral ? NodeType.LITERAL :
NodeType.ARGUMENT, argument.getId(), executable, argument, redirectTarget);
}
public void addChild(Node ...nodes) {
public void addChildren(Node ...nodes) {
for (Node node : nodes) {
children.add(node.id);
}

View File

@ -5,6 +5,8 @@ import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
record NodeGraph(List<Node> nodes, Node root) {
@ -25,4 +27,41 @@ record NodeGraph(List<Node> nodes, Node root) {
public DeclareCommandsPacket createPacket() {
return new DeclareCommandsPacket(nodes.stream().map(Node::getPacketNode).toList(), root.id());
}
public String exportGarphvizDot() {
final StringBuilder builder = new StringBuilder();
final char statementSeparator = ';';
builder.append("digraph G {");
builder.append("rankdir=LR");
builder.append(statementSeparator);
for (Node node : nodes) {
final AtomicInteger redirectTarget = node.redirectTarget();
builder.append(node.id());
builder.append(" [label=");
builder.append(graphvizName(node));
if (node.isRoot()) {
builder.append(",shape=rectangle");
}
builder.append("]");
builder.append(statementSeparator);
if (node.children().isEmpty() && redirectTarget == null) continue;
builder.append(node.id());
builder.append(" -> { ");
if (!node.children().isEmpty()) {
builder.append(node.children().intStream().mapToObj(String::valueOf).collect(Collectors.joining(" ")));
builder.append(" }");
builder.append(statementSeparator);
} else {
builder.append(redirectTarget.get());
builder.append(" } [style = dotted]");
builder.append(statementSeparator);
}
}
builder.append("}");
return builder.toString();
}
private static String graphvizName(Node node) {
return '"' + (node.isRoot() ? "root" : node.name()) + '"';
}
}