mirror of https://github.com/Minestom/Minestom.git
Merge branch 'master' into command-parser-rework
# Conflicts: # src/main/java/net/minestom/server/command/CommandManager.java # src/main/java/net/minestom/server/command/GraphBuilder.java # src/main/java/net/minestom/server/command/GraphConverter.java # src/main/java/net/minestom/server/command/Node.java # src/main/java/net/minestom/server/command/NodeGraph.java # src/test/java/net/minestom/server/command/ArgumentTypeTest.java # src/test/java/net/minestom/server/command/NodeGraphTest.java
This commit is contained in:
commit
6884c11e38
|
@ -8,7 +8,7 @@ kotlin = "1.6.20"
|
|||
hydrazine = "1.7.2"
|
||||
dependencyGetter = "v1.0.1"
|
||||
minestomData = "3e211f3953"
|
||||
hephaistos = "2.4.4"
|
||||
hephaistos = "2.4.8"
|
||||
jetbrainsAnnotations = "23.0.0"
|
||||
|
||||
# Terminal / Logging
|
||||
|
@ -102,4 +102,4 @@ kotlin = ["kotlin-stdlib-jdk8", "kotlin-reflect"]
|
|||
flare = ["flare", "flare-fastutil"]
|
||||
adventure = ["adventure-api", "adventure-serializer-gson", "adventure-serializer-legacy", "adventure-serializer-plain"]
|
||||
logging = ["tinylog-api", "tinylog-impl", "tinylog-slf4j"]
|
||||
terminal = ["jline", "jline-jansi"]
|
||||
terminal = ["jline", "jline-jansi"]
|
||||
|
|
|
@ -293,7 +293,7 @@ final class BlockCollision {
|
|||
continue;
|
||||
|
||||
final boolean intersects;
|
||||
if (type == EntityType.PLAYER) {
|
||||
if (entity instanceof Player) {
|
||||
// Ignore spectators
|
||||
if (((Player) entity).getGameMode() == GameMode.SPECTATOR)
|
||||
continue;
|
||||
|
|
|
@ -157,8 +157,7 @@ public final class CommandManager {
|
|||
* @return the {@link DeclareCommandsPacket} for {@code player}
|
||||
*/
|
||||
public @NotNull DeclareCommandsPacket createDeclareCommandsPacket(@NotNull Player player) {
|
||||
final NodeGraph nodeGraph = GraphBuilder.forPlayer(this.dispatcher.getCommands(), player);
|
||||
System.out.println(nodeGraph.exportGarphvizDot()); //TODO remove before merging
|
||||
return GraphConverter.createPacket(nodeGraph);
|
||||
final Graph merged = Graph.merge(dispatcher.getCommands());
|
||||
return GraphConverter.createPacket(merged, player);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
package net.minestom.server.command;
|
||||
|
||||
import net.minestom.server.command.builder.Command;
|
||||
import net.minestom.server.command.builder.arguments.Argument;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
sealed interface Graph permits GraphImpl {
|
||||
static @NotNull Builder builder(@NotNull Argument<?> argument) {
|
||||
return new GraphImpl.BuilderImpl(argument);
|
||||
}
|
||||
|
||||
static @NotNull Graph fromCommand(@NotNull Command command) {
|
||||
return GraphImpl.fromCommand(command);
|
||||
}
|
||||
|
||||
static @NotNull Graph merge(@NotNull Collection<@NotNull Command> commands) {
|
||||
return GraphImpl.merge(commands);
|
||||
}
|
||||
|
||||
static @NotNull Graph merge(@NotNull List<@NotNull Graph> graphs) {
|
||||
return GraphImpl.merge(graphs);
|
||||
}
|
||||
|
||||
static @NotNull Graph merge(@NotNull Graph @NotNull ... graphs) {
|
||||
return merge(List.of(graphs));
|
||||
}
|
||||
|
||||
@NotNull Node root();
|
||||
|
||||
boolean compare(@NotNull Graph graph, @NotNull Comparator comparator);
|
||||
|
||||
sealed interface Node permits GraphImpl.NodeImpl {
|
||||
@NotNull Argument<?> argument();
|
||||
|
||||
@UnknownNullability Executor executor();
|
||||
|
||||
@NotNull List<@NotNull Node> next();
|
||||
}
|
||||
|
||||
sealed interface Executor extends Predicate<CommandSender> permits GraphImpl.ExecutorImpl {
|
||||
// TODO execute the node
|
||||
}
|
||||
|
||||
sealed interface Builder permits GraphImpl.BuilderImpl {
|
||||
@NotNull Builder append(@NotNull Argument<?> argument, @NotNull Consumer<Builder> consumer);
|
||||
|
||||
@NotNull Builder append(@NotNull Argument<?> argument);
|
||||
|
||||
@NotNull Graph build();
|
||||
}
|
||||
|
||||
enum Comparator {
|
||||
TREE
|
||||
}
|
||||
}
|
|
@ -1,187 +0,0 @@
|
|||
package net.minestom.server.command;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectList;
|
||||
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.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public 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 ObjectList<Node> nodes = new ObjectArrayList<>();
|
||||
private final Node root = rootNode();
|
||||
|
||||
private GraphBuilder() {
|
||||
//no instance
|
||||
}
|
||||
|
||||
private Node rootNode() {
|
||||
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 AtomicInteger redirectTo, @Nullable Argument<?> argOverride) {
|
||||
if (aliases != null) {
|
||||
final Node node = createLiteralNode(name, parent, executable, null, null, argOverride);
|
||||
for (String alias : aliases) {
|
||||
createLiteralNode(alias, parent, executable, null, new AtomicInteger(node.id()), argOverride);
|
||||
}
|
||||
return node;
|
||||
} else {
|
||||
final Node literalNode = Node.literal(idSource.getAndIncrement(), name, executable, redirectTo,
|
||||
Objects.requireNonNullElseGet(argOverride, () -> new ArgumentLiteral(name)));
|
||||
nodes.add(literalNode);
|
||||
if (parent != null) parent.addChildren(literalNode);
|
||||
return literalNode;
|
||||
}
|
||||
}
|
||||
|
||||
private Node[] createArgumentNode(Argument<?> argument, boolean executable, @Nullable AtomicInteger redirectTarget) {
|
||||
// TODO Ensure node args are overridden properly where necessary
|
||||
final Node[] nodes;
|
||||
if (argument instanceof ArgumentEnum<?> argumentEnum) {
|
||||
return argumentEnum.entries().stream().map(x -> createLiteralNode(x, null, executable, null, null, argumentEnum)).toArray(Node[]::new);
|
||||
} else if (argument instanceof ArgumentGroup argumentGroup) {
|
||||
return argumentGroup.group().stream().map(x -> createArgumentNode(x, executable, redirectTarget)).flatMap(Stream::of).toArray(Node[]::new);
|
||||
} else if (argument instanceof ArgumentLoop<?> argumentLoop) {
|
||||
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), argument)};
|
||||
}
|
||||
nodes = new Node[] {Node.argument(idSource.getAndIncrement(), argument, executable, redirectTarget)};
|
||||
}
|
||||
this.nodes.addAll(Arrays.asList(nodes));
|
||||
return nodes;
|
||||
}
|
||||
|
||||
private void finalizeStructure(boolean forParsing) {
|
||||
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())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates the nodes for the given command
|
||||
*
|
||||
* @param command the command to add
|
||||
* @param parent where to append the command's root (literal) node
|
||||
* @param player a player if we should filter commands
|
||||
*/
|
||||
private void createCommand(Command command, Node parent, @Nullable Player player) {
|
||||
if (player != null) {
|
||||
// Check if user can use the command
|
||||
final CommandCondition condition = command.getCondition();
|
||||
if (condition != null && !condition.canUse(player, null)) return;
|
||||
}
|
||||
|
||||
// Create the command's root node
|
||||
final Node cmdNode = createLiteralNode(command.getName(), parent,
|
||||
command.getDefaultExecutor() != null, command.getAliases(), null, null);
|
||||
|
||||
cmdNode.executionInfo().set(new Node.ExecutionInfo(command.getCondition(), command.getDefaultExecutor()));
|
||||
|
||||
// Add syntax to the command
|
||||
for (CommandSyntax syntax : command.getSyntaxes()) {
|
||||
final CommandCondition syntaxCondition = syntax.getCommandCondition();
|
||||
if (player != null) {
|
||||
// Check if user can use the syntax
|
||||
if (syntaxCondition != null && !syntaxCondition.canUse(player, null)) continue;
|
||||
}
|
||||
|
||||
boolean executable = false;
|
||||
Node[] lastArgNodes = new Node[] {cmdNode}; // First arg links to cmd root
|
||||
@NotNull Argument<?>[] arguments = syntax.getArguments();
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
Argument<?> argument = arguments[i];
|
||||
// Determine if command is executable here
|
||||
if (executable && argument.getDefaultValue() == null) {
|
||||
// Optional arg was followed by a non-optional
|
||||
throw new IllegalCommandStructureException("Optional argument was followed by a non-optional one.");
|
||||
}
|
||||
if (!executable && i < arguments.length-1 && arguments[i+1].getDefaultValue() != null || i+1 == arguments.length) {
|
||||
executable = true;
|
||||
}
|
||||
// Append current node to previous
|
||||
final Node[] argNodes = createArgumentNode(argument, executable, null);
|
||||
for (Node lastArgNode : lastArgNodes) {
|
||||
lastArgNode.addChildren(argNodes);
|
||||
}
|
||||
// Populate execution info
|
||||
for (Node argNode : argNodes) {
|
||||
argNode.executionInfo().set(new Node.ExecutionInfo(
|
||||
// Syntax or command condition
|
||||
syntaxCondition == null ? command.getCondition() : syntaxCondition,
|
||||
// Syntax executor or default
|
||||
executable ? syntax.getExecutor() : command.getDefaultExecutor()));
|
||||
}
|
||||
lastArgNodes = argNodes;
|
||||
}
|
||||
}
|
||||
|
||||
// Add subcommands
|
||||
for (Command subcommand : command.getSubcommands()) {
|
||||
createCommand(subcommand, cmdNode, player);
|
||||
}
|
||||
}
|
||||
|
||||
public static NodeGraph forPlayer(@NotNull Set<Command> commands, Player player) {
|
||||
final GraphBuilder builder = new GraphBuilder();
|
||||
|
||||
if (GraphBuilder.class.desiredAssertionStatus()) {
|
||||
// Detect infinite recursion
|
||||
for (Command command : commands) {
|
||||
final HashSet<Command> processed = new HashSet<>();
|
||||
final Stack<Command> stack = new Stack<>();
|
||||
stack.push(command);
|
||||
while (!stack.isEmpty()) {
|
||||
final Command pop = stack.pop();
|
||||
if (!processed.add(pop)) {
|
||||
throw new IllegalCommandStructureException("Infinite recursion detected in command: "+command.getName());
|
||||
} else {
|
||||
stack.addAll(pop.getSubcommands());
|
||||
}
|
||||
}
|
||||
|
||||
builder.createCommand(command, builder.root, player);
|
||||
}
|
||||
} else {
|
||||
for (Command command : commands) {
|
||||
builder.createCommand(command, builder.root, player);
|
||||
}
|
||||
}
|
||||
|
||||
builder.finalizeStructure(player == null);
|
||||
|
||||
return new NodeGraph(builder.nodes, builder.root);
|
||||
}
|
||||
|
||||
public static NodeGraph forServer(@NotNull Set<Command> commands) {
|
||||
return forPlayer(commands, null);
|
||||
}
|
||||
}
|
|
@ -1,41 +1,164 @@
|
|||
package net.minestom.server.command;
|
||||
|
||||
import net.minestom.server.command.builder.arguments.Argument;
|
||||
import net.minestom.server.command.builder.arguments.ArgumentLiteral;
|
||||
import net.minestom.server.command.builder.arguments.*;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
final class GraphConverter {
|
||||
private GraphConverter() {
|
||||
//no instance
|
||||
}
|
||||
|
||||
public static DeclareCommandsPacket.Node getPacketNode(Node node) {
|
||||
final DeclareCommandsPacket.Node packetNode = new DeclareCommandsPacket.Node();
|
||||
packetNode.children = node.children().toIntArray();
|
||||
final Argument<?> arg = node.arg();
|
||||
DeclareCommandsPacket.NodeType type = arg == null ? DeclareCommandsPacket.NodeType.ROOT :
|
||||
arg instanceof ArgumentLiteral ? DeclareCommandsPacket.NodeType.LITERAL :
|
||||
DeclareCommandsPacket.NodeType.ARGUMENT;
|
||||
packetNode.flags = DeclareCommandsPacket.getFlag(type, node.executable(), node.redirectTarget() != null,
|
||||
type == DeclareCommandsPacket.NodeType.ARGUMENT && arg.hasSuggestion());
|
||||
packetNode.name = arg == null ? null : arg.getId();
|
||||
if (node.redirectTarget() != null) {
|
||||
packetNode.redirectedNode = node.redirectTarget().get();
|
||||
@Contract("_, _ -> new")
|
||||
public static DeclareCommandsPacket createPacket(Graph graph, @Nullable Player player) {
|
||||
List<DeclareCommandsPacket.Node> nodes = new ArrayList<>();
|
||||
List<Consumer<Integer>> rootRedirect = new ArrayList<>();
|
||||
final AtomicInteger idSource = new AtomicInteger(0);
|
||||
final int rootId = append(graph.root(), nodes, rootRedirect, idSource, null, null, player)[0];
|
||||
for (var i : rootRedirect) {
|
||||
i.accept(rootId);
|
||||
}
|
||||
if (type == DeclareCommandsPacket.NodeType.ARGUMENT) {
|
||||
packetNode.properties = arg.nodeProperties();
|
||||
packetNode.parser = arg.parser();
|
||||
if (arg.hasSuggestion()) {
|
||||
//noinspection ConstantConditions
|
||||
packetNode.suggestionsType = arg.suggestionType().getIdentifier();
|
||||
}
|
||||
}
|
||||
return packetNode;
|
||||
return new DeclareCommandsPacket(nodes, rootId);
|
||||
}
|
||||
|
||||
@Contract("_ -> new")
|
||||
public static DeclareCommandsPacket createPacket(NodeGraph graph) {
|
||||
return new DeclareCommandsPacket(graph.nodes().stream().map(GraphConverter::getPacketNode).toList(), graph.root().id());
|
||||
private static int[] append(Graph.Node graphNode, List<DeclareCommandsPacket.Node> to,
|
||||
List<Consumer<Integer>> rootRedirect, AtomicInteger id, @Nullable AtomicInteger redirect,
|
||||
List<Runnable> redirectSetters, @Nullable Player player) {
|
||||
final Graph.Executor executor = graphNode.executor();
|
||||
if (player != null && executor != null) {
|
||||
if (!executor.test(player)) return new int[0];
|
||||
}
|
||||
|
||||
final Argument<?> argument = graphNode.argument();
|
||||
final List<Graph.Node> children = graphNode.next();
|
||||
|
||||
final DeclareCommandsPacket.Node node = new DeclareCommandsPacket.Node();
|
||||
int[] packetNodeChildren = new int[children.size()];
|
||||
for (int i = 0; i < packetNodeChildren.length; i++) {
|
||||
final int[] append = append(children.get(i), to, rootRedirect, id, redirect, redirectSetters, player);
|
||||
if (append.length == 1) {
|
||||
packetNodeChildren[i] = append[0];
|
||||
} else {
|
||||
packetNodeChildren = Arrays.copyOf(packetNodeChildren, packetNodeChildren.length + append.length - 1);
|
||||
System.arraycopy(append, 0, packetNodeChildren, i, append.length);
|
||||
i += append.length;
|
||||
}
|
||||
}
|
||||
node.children = packetNodeChildren;
|
||||
if (argument instanceof ArgumentLiteral literal) {
|
||||
if (literal.getId().isEmpty()) {
|
||||
node.flags = 0; //root
|
||||
} else {
|
||||
node.flags = literal(false, false);
|
||||
node.name = argument.getId();
|
||||
if (redirect != null) {
|
||||
node.flags |= 0x8;
|
||||
redirectSetters.add(() -> node.redirectedNode = redirect.get());
|
||||
}
|
||||
}
|
||||
to.add(node);
|
||||
return new int[]{id.getAndIncrement()};
|
||||
} else {
|
||||
if (argument instanceof ArgumentCommand) {
|
||||
node.flags = literal(false, true);
|
||||
node.name = argument.getId();
|
||||
rootRedirect.add(i -> node.redirectedNode = i);
|
||||
to.add(node);
|
||||
|
||||
return new int[]{id.getAndIncrement()};
|
||||
} else if (argument instanceof ArgumentEnum<?> || (argument instanceof ArgumentWord word && word.hasRestrictions())) {
|
||||
List<String> entries = argument instanceof ArgumentEnum<?> ? ((ArgumentEnum<?>) argument).entries() : Arrays.stream(((ArgumentWord) argument).getRestrictions()).toList();
|
||||
final int[] res = new int[entries.size()];
|
||||
for (int i = 0; i < res.length; i++) {
|
||||
String entry = entries.get(i);
|
||||
final DeclareCommandsPacket.Node subNode = new DeclareCommandsPacket.Node();
|
||||
subNode.children = node.children;
|
||||
subNode.flags = literal(false, false);
|
||||
subNode.name = entry;
|
||||
if (redirect != null) {
|
||||
subNode.flags |= 0x8;
|
||||
redirectSetters.add(() -> subNode.redirectedNode = redirect.get());
|
||||
}
|
||||
to.add(subNode);
|
||||
res[i] = id.getAndIncrement();
|
||||
}
|
||||
return res;
|
||||
} else if (argument instanceof ArgumentGroup special) {
|
||||
List<Argument<?>> entries = special.group();
|
||||
int[] res = null;
|
||||
int[] last = new int[0];
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
Argument<?> entry = entries.get(i);
|
||||
if (i == entries.size() - 1) {
|
||||
// Last will be the parent of next args
|
||||
final int[] l = append(new GraphImpl.NodeImpl(entry, null, List.of()), to, rootRedirect, id, redirect, redirectSetters, player);
|
||||
for (int n : l) {
|
||||
to.get(n).children = node.children;
|
||||
}
|
||||
for (int n : last) {
|
||||
to.get(n).children = l;
|
||||
}
|
||||
return res == null ? l : res;
|
||||
} else if (i == 0) {
|
||||
// First will be the children & parent of following
|
||||
res = append(new GraphImpl.NodeImpl(entry, null, List.of()), to, rootRedirect, id, null, redirectSetters, player);
|
||||
last = res;
|
||||
} else {
|
||||
final int[] l = append(new GraphImpl.NodeImpl(entry, null, List.of()), to, rootRedirect, id, null, redirectSetters, player);
|
||||
for (int n : last) {
|
||||
to.get(n).children = l;
|
||||
}
|
||||
last = l;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Arg group must have child args.");
|
||||
} else if (argument instanceof ArgumentLoop special) {
|
||||
AtomicInteger r = new AtomicInteger();
|
||||
List<Runnable> setters = new ArrayList<>();
|
||||
int[] res = new int[special.arguments().size()];
|
||||
List<?> arguments = special.arguments();
|
||||
for (int i = 0; i < arguments.size(); i++) {
|
||||
Object arg = arguments.get(i);
|
||||
final int[] append = append(new GraphImpl.NodeImpl((Argument<?>) arg, null, List.of()), to, rootRedirect, id, r, setters, player);
|
||||
if (append.length == 1) {
|
||||
res[i] = append[0];
|
||||
} else {
|
||||
res = Arrays.copyOf(res, res.length + append.length - 1);
|
||||
System.arraycopy(append, 0, res, i, append.length);
|
||||
i += append.length;
|
||||
}
|
||||
}
|
||||
r.set(id.get());
|
||||
setters.forEach(Runnable::run);
|
||||
return res;
|
||||
} else {
|
||||
node.flags = arg(false, argument.hasSuggestion());
|
||||
node.name = argument.getId();
|
||||
node.parser = argument.parser();
|
||||
node.properties = argument.nodeProperties();
|
||||
if (redirect != null) {
|
||||
node.flags |= 0x8;
|
||||
redirectSetters.add(() -> node.redirectedNode = redirect.get());
|
||||
}
|
||||
to.add(node);
|
||||
return new int[]{id.getAndIncrement()};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static byte literal(boolean executable, boolean hasRedirect) {
|
||||
return DeclareCommandsPacket.getFlag(DeclareCommandsPacket.NodeType.LITERAL, executable, hasRedirect, false);
|
||||
}
|
||||
|
||||
private static byte arg(boolean executable, boolean hasSuggestion) {
|
||||
return DeclareCommandsPacket.getFlag(DeclareCommandsPacket.NodeType.ARGUMENT, executable, false, hasSuggestion);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
package net.minestom.server.command;
|
||||
|
||||
import net.minestom.server.command.builder.Command;
|
||||
import net.minestom.server.command.builder.CommandSyntax;
|
||||
import net.minestom.server.command.builder.arguments.Argument;
|
||||
import net.minestom.server.command.builder.condition.CommandCondition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static net.minestom.server.command.builder.arguments.ArgumentType.Literal;
|
||||
|
||||
record GraphImpl(NodeImpl root) implements Graph {
|
||||
static GraphImpl fromCommand(Command command) {
|
||||
return new GraphImpl(NodeImpl.command(command));
|
||||
}
|
||||
|
||||
static Graph merge(Collection<Command> commands) {
|
||||
return new GraphImpl(NodeImpl.rootCommands(commands));
|
||||
}
|
||||
|
||||
static GraphImpl merge(List<Graph> graphs) {
|
||||
final List<Node> children = graphs.stream().map(Graph::root).toList();
|
||||
final NodeImpl root = new NodeImpl(Literal(""), null, children);
|
||||
return new GraphImpl(root);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean compare(@NotNull Graph graph, @NotNull Comparator comparator) {
|
||||
return compare(root, graph.root(), comparator);
|
||||
}
|
||||
|
||||
record BuilderImpl(Argument<?> argument, List<BuilderImpl> children) implements Graph.Builder {
|
||||
public BuilderImpl(Argument<?> argument) {
|
||||
this(argument, new ArrayList<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Graph.@NotNull Builder append(@NotNull Argument<?> argument, @NotNull Consumer<Graph.Builder> consumer) {
|
||||
BuilderImpl builder = new BuilderImpl(argument);
|
||||
consumer.accept(builder);
|
||||
this.children.add(builder);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Graph.@NotNull Builder append(@NotNull Argument<?> argument) {
|
||||
this.children.add(new BuilderImpl(argument, List.of()));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull GraphImpl build() {
|
||||
return new GraphImpl(NodeImpl.fromBuilder(this));
|
||||
}
|
||||
}
|
||||
|
||||
record NodeImpl(Argument<?> argument, ExecutorImpl executor, List<Graph.Node> next) implements Graph.Node {
|
||||
static NodeImpl fromBuilder(BuilderImpl builder) {
|
||||
final List<BuilderImpl> children = builder.children;
|
||||
Node[] nodes = new NodeImpl[children.size()];
|
||||
for (int i = 0; i < children.size(); i++) nodes[i] = fromBuilder(children.get(i));
|
||||
return new NodeImpl(builder.argument, null, List.of(nodes));
|
||||
}
|
||||
|
||||
static NodeImpl command(Command command) {
|
||||
return ConversionNode.fromCommand(command).toNode();
|
||||
}
|
||||
|
||||
static NodeImpl rootCommands(Collection<Command> commands) {
|
||||
return ConversionNode.rootConv(commands).toNode();
|
||||
}
|
||||
}
|
||||
|
||||
record ExecutorImpl(Predicate<CommandSender> predicate) implements Graph.Executor {
|
||||
@Override
|
||||
public boolean test(CommandSender commandSender) {
|
||||
return predicate.test(commandSender);
|
||||
}
|
||||
|
||||
static ExecutorImpl fromCommand(Command command) {
|
||||
final CommandCondition condition = command.getCondition();
|
||||
if (condition == null) return null;
|
||||
return new ExecutorImpl(commandSender -> condition.canUse(commandSender, null));
|
||||
}
|
||||
|
||||
static ExecutorImpl fromSyntax(CommandSyntax syntax) {
|
||||
final CommandCondition condition = syntax.getCommandCondition();
|
||||
if (condition == null) return null;
|
||||
return new ExecutorImpl(commandSender -> condition.canUse(commandSender, null));
|
||||
}
|
||||
}
|
||||
|
||||
private record ConversionNode(Argument<?> argument, ExecutorImpl executor,
|
||||
Map<Argument<?>, ConversionNode> nextMap) {
|
||||
ConversionNode(Argument<?> argument, ExecutorImpl executor) {
|
||||
this(argument, executor, new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
private NodeImpl toNode() {
|
||||
Node[] nodes = new NodeImpl[nextMap.size()];
|
||||
int i = 0;
|
||||
for (var entry : nextMap.values()) nodes[i++] = entry.toNode();
|
||||
return new NodeImpl(argument, executor, List.of(nodes));
|
||||
}
|
||||
|
||||
static ConversionNode fromCommand(Command command) {
|
||||
ConversionNode root = new ConversionNode(Literal(command.getName()), ExecutorImpl.fromCommand(command));
|
||||
// Syntaxes
|
||||
for (CommandSyntax syntax : command.getSyntaxes()) {
|
||||
ConversionNode syntaxNode = root;
|
||||
for (Argument<?> arg : syntax.getArguments()) {
|
||||
boolean last = arg == syntax.getArguments()[syntax.getArguments().length - 1];
|
||||
syntaxNode = syntaxNode.nextMap.computeIfAbsent(arg, argument -> {
|
||||
var ex = last ? ExecutorImpl.fromSyntax(syntax) : null;
|
||||
return new ConversionNode(argument, ex);
|
||||
});
|
||||
}
|
||||
}
|
||||
// Subcommands
|
||||
for (Command subcommand : command.getSubcommands()) {
|
||||
root.nextMap.put(Literal(subcommand.getName()), fromCommand(subcommand));
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
static ConversionNode rootConv(Collection<Command> commands) {
|
||||
Map<Argument<?>, ConversionNode> next = new LinkedHashMap<>(commands.size());
|
||||
for (Command command : commands) {
|
||||
final ConversionNode conv = fromCommand(command);
|
||||
next.put(conv.argument, conv);
|
||||
}
|
||||
return new ConversionNode(Literal(""), null, next);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean compare(@NotNull Node first, Node second, @NotNull Comparator comparator) {
|
||||
return switch (comparator) {
|
||||
case TREE -> {
|
||||
if (!first.argument().equals(second.argument())) yield false;
|
||||
if (first.next().size() != second.next().size()) yield false;
|
||||
for (int i = 0; i < first.next().size(); i++) {
|
||||
if (!compare(first.next().get(i), second.next().get(i), comparator)) {
|
||||
yield false;
|
||||
}
|
||||
}
|
||||
yield true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
package net.minestom.server.command;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import net.minestom.server.command.builder.CommandExecutor;
|
||||
import net.minestom.server.command.builder.arguments.Argument;
|
||||
import net.minestom.server.command.builder.arguments.ArgumentLiteral;
|
||||
import net.minestom.server.command.builder.condition.CommandCondition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
record Node(int id, IntList children, boolean executable, Argument<?> arg, Argument<?> realArg, AtomicInteger redirectTarget,
|
||||
AtomicReference<ExecutionInfo> executionInfo) {
|
||||
|
||||
public static Node root(int id) {
|
||||
return new Node(id, new IntArrayList(), false, null, null, null,
|
||||
new AtomicReference<>());
|
||||
}
|
||||
|
||||
public static Node literal(int id, String name, boolean executable, @Nullable AtomicInteger redirectTarget) {
|
||||
final ArgumentLiteral literal = new ArgumentLiteral(name);
|
||||
return new Node(id, new IntArrayList(), executable, literal, literal, redirectTarget, new AtomicReference<>());
|
||||
}
|
||||
|
||||
public static Node literal(int id, String name, boolean executable, @Nullable AtomicInteger redirectTarget, @NotNull Argument<?> backingArg) {
|
||||
return new Node(id, new IntArrayList(), executable, new ArgumentLiteral(name), backingArg, redirectTarget, new AtomicReference<>());
|
||||
}
|
||||
|
||||
public static Node argument(int id, Argument<?> argument, boolean executable, @Nullable AtomicInteger redirectTarget) {
|
||||
return new Node(id, new IntArrayList(), executable, argument, argument, redirectTarget, new AtomicReference<>());
|
||||
}
|
||||
|
||||
public void addChildren(Node ...nodes) {
|
||||
for (Node node : nodes) {
|
||||
children.add(node.id);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isParentOf(Node node) {
|
||||
return children.contains(node.id());
|
||||
}
|
||||
|
||||
public boolean isRoot() {
|
||||
return arg == null;
|
||||
}
|
||||
|
||||
public record ExecutionInfo(@Nullable CommandCondition condition, @Nullable CommandExecutor executor) {}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
package net.minestom.server.command;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public record NodeGraph(List<Node> nodes, Node root) {
|
||||
|
||||
public Node resolveId(int id) {
|
||||
return nodes.get(id);
|
||||
}
|
||||
|
||||
public List<Node> getChildren(Node node) {
|
||||
return node.children().intStream().mapToObj(this::resolveId).toList();
|
||||
}
|
||||
|
||||
public @Nullable Node getRedirectTarget(Node node) {
|
||||
if (node.redirectTarget() == null) return null;
|
||||
return resolveId(node.redirectTarget().get());
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
if (node.executable()) {
|
||||
builder.append(",bgcolor=gray,style=filled");
|
||||
}
|
||||
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.arg().getId()) + '"';
|
||||
}
|
||||
}
|
|
@ -203,7 +203,7 @@ public class LivingEntity extends Entity implements EquipmentHandler {
|
|||
// Items picking
|
||||
if (canPickupItem() && itemPickupCooldown.isReady(time)) {
|
||||
itemPickupCooldown.refreshLastUpdate(time);
|
||||
final Point loweredPosition = position.sub(0, -.5, 0);
|
||||
final Point loweredPosition = position.sub(0, .5, 0);
|
||||
this.instance.getEntityTracker().nearbyEntities(position, expandedBoundingBox.width(),
|
||||
EntityTracker.Target.ITEMS, itemEntity -> {
|
||||
if (this instanceof Player player && !itemEntity.isViewer(player)) return;
|
||||
|
|
|
@ -333,7 +333,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
|||
// Experience orb pickup
|
||||
if (experiencePickupCooldown.isReady(time)) {
|
||||
experiencePickupCooldown.refreshLastUpdate(time);
|
||||
final Point loweredPosition = position.sub(0, -.5, 0);
|
||||
final Point loweredPosition = position.sub(0, .5, 0);
|
||||
this.instance.getEntityTracker().nearbyEntities(position, expandedBoundingBox.width(),
|
||||
EntityTracker.Target.EXPERIENCE_ORBS, experienceOrb -> {
|
||||
if (expandedBoundingBox.intersectEntity(loweredPosition, experienceOrb)) {
|
||||
|
@ -1446,17 +1446,19 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
|||
}
|
||||
}
|
||||
|
||||
CloseWindowPacket closeWindowPacket;
|
||||
if (openInventory == null) {
|
||||
closeWindowPacket = new CloseWindowPacket((byte) 0);
|
||||
} else {
|
||||
closeWindowPacket = new CloseWindowPacket(openInventory.getWindowId());
|
||||
openInventory.removeViewer(this); // Clear cache
|
||||
this.openInventory = null;
|
||||
if (openInventory == getOpenInventory()) {
|
||||
CloseWindowPacket closeWindowPacket;
|
||||
if (openInventory == null) {
|
||||
closeWindowPacket = new CloseWindowPacket((byte) 0);
|
||||
} else {
|
||||
closeWindowPacket = new CloseWindowPacket(openInventory.getWindowId());
|
||||
openInventory.removeViewer(this); // Clear cache
|
||||
this.openInventory = null;
|
||||
}
|
||||
sendPacket(closeWindowPacket);
|
||||
inventory.update();
|
||||
this.didCloseInventory = true;
|
||||
}
|
||||
sendPacket(closeWindowPacket);
|
||||
inventory.update();
|
||||
this.didCloseInventory = true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package net.minestom.server.event.player;
|
||||
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.trait.CancellableEvent;
|
||||
import net.minestom.server.event.trait.PlayerEvent;
|
||||
import net.minestom.server.network.packet.server.ServerPacket;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
@ -12,10 +13,10 @@ import org.jetbrains.annotations.NotNull;
|
|||
* Currently, do not support viewable packets.
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
public class PlayerPacketOutEvent implements PlayerEvent {
|
||||
|
||||
public class PlayerPacketOutEvent implements PlayerEvent, CancellableEvent {
|
||||
private final Player player;
|
||||
private final ServerPacket packet;
|
||||
private boolean cancelled;
|
||||
|
||||
public PlayerPacketOutEvent(Player player, ServerPacket packet) {
|
||||
this.player = player;
|
||||
|
@ -30,4 +31,14 @@ public class PlayerPacketOutEvent implements PlayerEvent {
|
|||
public @NotNull ServerPacket getPacket() {
|
||||
return packet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancel) {
|
||||
this.cancelled = cancel;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -349,7 +349,9 @@ public class PlayerSocketConnection extends PlayerConnection {
|
|||
// Outgoing event
|
||||
if (player != null && outgoing.hasListener()) {
|
||||
final ServerPacket serverPacket = SendablePacket.extractServerPacket(packet);
|
||||
outgoing.call(new PlayerPacketOutEvent(player, serverPacket));
|
||||
PlayerPacketOutEvent event = new PlayerPacketOutEvent(player, serverPacket);
|
||||
outgoing.call(event);
|
||||
if (event.isCancelled()) return;
|
||||
}
|
||||
// Write packet
|
||||
if (packet instanceof ServerPacket serverPacket) {
|
||||
|
|
|
@ -97,7 +97,10 @@ public final class Server {
|
|||
public void stop() {
|
||||
this.stop = true;
|
||||
try {
|
||||
this.serverSocket.close();
|
||||
if(serverSocket != null) {
|
||||
this.serverSocket.close();
|
||||
}
|
||||
|
||||
if (socketAddress instanceof UnixDomainSocketAddress unixDomainSocketAddress) {
|
||||
Files.deleteIfExists(unixDomainSocketAddress.getPath());
|
||||
}
|
||||
|
|
|
@ -172,6 +172,9 @@ public final class PacketUtils {
|
|||
if (compressed) {
|
||||
final int dataLength = readBuffer.readVarInt();
|
||||
final int payloadLength = packetLength - (readBuffer.readerOffset() - readerStart);
|
||||
if (payloadLength < 0) {
|
||||
throw new DataFormatException("Negative payload length " + payloadLength);
|
||||
}
|
||||
if (dataLength == 0) {
|
||||
// Data is too small to be compressed, payload is following
|
||||
decompressedSize = payloadLength;
|
||||
|
|
|
@ -207,11 +207,8 @@ public class ArgumentTypeTest {
|
|||
@Test
|
||||
public void testArgumentResourceLocation() {
|
||||
var arg = ArgumentType.ResourceLocation("resource_location");
|
||||
|
||||
assertArg(arg, "minecraft:resource_location_example", "minecraft:resource_location_example");
|
||||
assertInvalidArg(arg, "minecraft:");
|
||||
|
||||
assertEquals("ResourceLocation<resource_location>", arg.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -423,11 +420,11 @@ public class ArgumentTypeTest {
|
|||
@Test
|
||||
public void testArgumentStringArray() {
|
||||
var arg = ArgumentType.StringArray("string_array");
|
||||
assertArrayEquals(new String[]{"example", "text"}, arg.parse(new CommandReader("example text")).value());
|
||||
assertArrayEquals(new String[]{"some", "more", "placeholder", "text"}, arg.parse(new CommandReader("some more placeholder text")).value());
|
||||
// assertArrayEquals(new String[]{""}, arg.parse(new CommandReader("")).value()); Is it parser responsibility?
|
||||
assertArrayEquals(new String[0], arg.parse(new CommandReader(" ")).value());
|
||||
assertArrayEquals(new String[0], arg.parse(new CommandReader(" ")).value());
|
||||
assertArrayArg(arg, new String[]{"example", "text"}, "example text");
|
||||
assertArrayArg(arg, new String[]{"some", "more", "placeholder", "text"}, "some more placeholder text");
|
||||
assertArrayArg(arg, new String[]{""}, "");
|
||||
assertArrayArg(arg, new String[0], " ");
|
||||
assertArrayArg(arg, new String[0], " ");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -446,6 +443,14 @@ public class ArgumentTypeTest {
|
|||
assertEquals(expected, arg.parse(new CommandReader(input)).value());
|
||||
}
|
||||
|
||||
private static <T> void assertArrayArg(Argument<T[]> arg, T[] expected, String input) {
|
||||
assertArrayEquals(expected, arg.parse(input));
|
||||
}
|
||||
|
||||
private static <T> void assertValidArg(Argument<T> arg, String input) {
|
||||
assertDoesNotThrow(() -> arg.parse(input));
|
||||
}
|
||||
|
||||
private static <T> void assertInvalidArg(Argument<T> arg, String input) {
|
||||
assertNull(arg.parse(new CommandReader(input)).value());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
package net.minestom.server.command;
|
||||
|
||||
import net.minestom.server.command.builder.Command;
|
||||
import net.minestom.server.command.builder.arguments.ArgumentType;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public class CommandPacketFilteringTest {
|
||||
private static final Player PLAYER = new Player(UUID.randomUUID(), "", null);
|
||||
|
||||
@Test
|
||||
public void singleCommandFilteredFalse() {
|
||||
final Command foo = new Command("foo");
|
||||
foo.setCondition(((sender, commandString) -> false));
|
||||
assertFiltering(foo, "");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleCommandFilteredTrue() {
|
||||
final Command foo = new Command("foo");
|
||||
foo.setCondition(((sender, commandString) -> true));
|
||||
assertFiltering(foo, """
|
||||
foo=%
|
||||
0->foo
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleCommandUnfiltered() {
|
||||
final Command foo = new Command("foo");
|
||||
assertFiltering(foo, """
|
||||
foo=%
|
||||
0->foo
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleCommandFilteredTrueWithFilteredSubcommandTrueWithFilteredSyntaxFalse() {
|
||||
final Command foo = new Command("foo");
|
||||
foo.setCondition((sender, commandString) -> true);
|
||||
final Command bar = new Command("bar");
|
||||
bar.setCondition((sender, commandString) -> true);
|
||||
foo.addSubcommand(bar);
|
||||
bar.addConditionalSyntax((sender, commandString) -> false, null, ArgumentType.Literal("baz"));
|
||||
assertFiltering(foo, """
|
||||
foo bar=%
|
||||
0->foo
|
||||
foo->bar
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleCommandFilteredTrueWithFilteredSubcommandFalse() {
|
||||
final Command foo = new Command("foo");
|
||||
foo.setCondition((sender, commandString) -> true);
|
||||
final Command bar = new Command("bar");
|
||||
bar.setCondition((sender, commandString) -> false);
|
||||
foo.addSubcommand(bar);
|
||||
assertFiltering(foo, """
|
||||
foo=%
|
||||
0->foo
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleCommandFilteredTrueWithFilteredSubcommandTrue() {
|
||||
final Command foo = new Command("foo");
|
||||
foo.setCondition((sender, commandString) -> true);
|
||||
final Command bar = new Command("bar");
|
||||
bar.setCondition((sender, commandString) -> true);
|
||||
foo.addSubcommand(bar);
|
||||
assertFiltering(foo, """
|
||||
foo bar=%
|
||||
0->foo
|
||||
foo->bar
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleCommandFilteredTrueWithFilteredSubcommandTrueWithFilteredSyntaxBoth() {
|
||||
final Command foo = new Command("foo");
|
||||
foo.setCondition((sender, commandString) -> true);
|
||||
final Command bar = new Command("bar");
|
||||
bar.setCondition((sender, commandString) -> true);
|
||||
foo.addSubcommand(bar);
|
||||
bar.addConditionalSyntax((sender, commandString) -> true, null, ArgumentType.Literal("true"));
|
||||
bar.addConditionalSyntax((sender, commandString) -> false, null, ArgumentType.Literal("false"));
|
||||
assertFiltering(foo, """
|
||||
foo bar true=%
|
||||
0->foo
|
||||
foo->bar
|
||||
bar->true
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleCommandConditionalArgGroupTrue() {
|
||||
final Command foo = new Command("foo");
|
||||
foo.addConditionalSyntax((sender, commandString) -> true, null, ArgumentType.Group("test", ArgumentType.Literal("bar")));
|
||||
assertFiltering(foo, """
|
||||
foo bar=%
|
||||
0->foo
|
||||
foo->bar
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleCommandConditionalArgGroupFalse() {
|
||||
final Command foo = new Command("foo");
|
||||
foo.addConditionalSyntax((sender, commandString) -> false, null, ArgumentType.Group("test", ArgumentType.Literal("foo")));
|
||||
assertFiltering(foo, """
|
||||
foo=%
|
||||
0->foo
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleCommandUnconditionalArgGroup() {
|
||||
final Command foo = new Command("foo");
|
||||
foo.addSyntax(null, ArgumentType.Group("test", ArgumentType.Literal("bar")));
|
||||
assertFiltering(foo, """
|
||||
foo bar=%
|
||||
0->foo
|
||||
foo->bar
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleCommandConditionalArgGroupTrue2() {
|
||||
final Command foo = new Command("foo");
|
||||
foo.addConditionalSyntax((sender, commandString) -> true, null, ArgumentType.Group("test", ArgumentType.Literal("bar"), ArgumentType.Literal("baz")));
|
||||
assertFiltering(foo, """
|
||||
foo bar baz=%
|
||||
0->foo
|
||||
foo->bar
|
||||
bar->baz
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleCommandConditionalArgGroupFalse2() {
|
||||
final Command foo = new Command("foo");
|
||||
foo.addConditionalSyntax((sender, commandString) -> false, null, ArgumentType.Group("test", ArgumentType.Literal("foo"), ArgumentType.Literal("baz")));
|
||||
assertFiltering(foo, """
|
||||
foo=%
|
||||
0->foo
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleCommandUnconditionalArgGroup2() {
|
||||
final Command foo = new Command("foo");
|
||||
foo.addSyntax(null, ArgumentType.Group("test", ArgumentType.Literal("bar"), ArgumentType.Literal("baz")));
|
||||
assertFiltering(foo, """
|
||||
foo bar baz=%
|
||||
0->foo
|
||||
foo->bar
|
||||
bar->baz
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleCommandUnconditionalArgLoop() {
|
||||
final Command foo = new Command("foo");
|
||||
foo.addSyntax(null, ArgumentType.Loop("test", ArgumentType.Literal("bar"), ArgumentType.Literal("baz")));
|
||||
assertFiltering(foo, """
|
||||
foo bar baz=%
|
||||
0->foo
|
||||
foo->bar baz
|
||||
bar baz+>foo
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleCommandConditionalArgLoopTrue() {
|
||||
final Command foo = new Command("foo");
|
||||
foo.addConditionalSyntax((sender, commandString) -> true, null, ArgumentType.Loop("test", ArgumentType.Literal("bar"), ArgumentType.Literal("baz")));
|
||||
assertFiltering(foo, """
|
||||
foo bar baz=%
|
||||
0->foo
|
||||
foo->bar baz
|
||||
bar baz+>foo
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleCommandConditionalArgLoopFalse() {
|
||||
final Command foo = new Command("foo");
|
||||
foo.addConditionalSyntax((sender, commandString) -> false, null, ArgumentType.Loop("test", ArgumentType.Literal("bar"), ArgumentType.Literal("baz")));
|
||||
assertFiltering(foo, """
|
||||
foo=%
|
||||
0->foo
|
||||
""");
|
||||
}
|
||||
|
||||
private void assertFiltering(Command command, String expectedStructure) {
|
||||
final DeclareCommandsPacket packet = GraphConverter.createPacket(Graph.merge(Set.of(command)), PLAYER);
|
||||
CommandTestUtils.assertPacket(packet, expectedStructure);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
package net.minestom.server.command;
|
||||
|
||||
import net.minestom.server.command.builder.Command;
|
||||
import net.minestom.server.command.builder.CommandContext;
|
||||
import net.minestom.server.command.builder.arguments.ArgumentType;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
public class CommandPacketTest {
|
||||
@Test
|
||||
public void singleCommandWithOneSyntax() {
|
||||
final Command foo = new Command("foo");
|
||||
foo.addSyntax(CommandPacketTest::dummyExecutor, ArgumentType.Integer("bar"));
|
||||
|
||||
final DeclareCommandsPacket packet = GraphConverter.createPacket(Graph.merge(Graph.fromCommand(foo)), null);
|
||||
assertEquals(3, packet.nodes().size());
|
||||
final DeclareCommandsPacket.Node root = packet.nodes().get(packet.rootIndex());
|
||||
assertNotNull(root);
|
||||
assertNodeType(DeclareCommandsPacket.NodeType.ROOT, root.flags);
|
||||
assertEquals(1, root.children.length);
|
||||
final DeclareCommandsPacket.Node cmd = packet.nodes().get(root.children[0]);
|
||||
assertNotNull(cmd);
|
||||
assertNodeType(DeclareCommandsPacket.NodeType.LITERAL, cmd.flags);
|
||||
assertEquals(1, cmd.children.length);
|
||||
assertEquals("foo", cmd.name);
|
||||
final DeclareCommandsPacket.Node arg = packet.nodes().get(cmd.children[0]);
|
||||
assertNotNull(arg);
|
||||
assertNodeType(DeclareCommandsPacket.NodeType.ARGUMENT, arg.flags);
|
||||
assertEquals(0, arg.children.length);
|
||||
assertEquals("bar", arg.name);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void executeLike() {
|
||||
enum Dimension {OVERWORLD, THE_NETHER, THE_END}
|
||||
final Command execute = new Command("execute");
|
||||
execute.addSyntax(CommandPacketTest::dummyExecutor, ArgumentType.Loop("params",
|
||||
ArgumentType.Group("facing", ArgumentType.Literal("facing"), ArgumentType.RelativeVec3("pos")),
|
||||
ArgumentType.Group("at", ArgumentType.Literal("at"), ArgumentType.Entity("targets")),
|
||||
ArgumentType.Group("as", ArgumentType.Literal("as"), ArgumentType.Entity("targets")),
|
||||
ArgumentType.Group("in", ArgumentType.Literal("in"), ArgumentType.Enum("dimension", Dimension.class)),
|
||||
ArgumentType.Group("run", ArgumentType.Command("run"))
|
||||
));
|
||||
var graph = Graph.fromCommand(execute);
|
||||
assertPacketGraph("""
|
||||
execute facing at as in run=%
|
||||
overworld the_nether the_end=§
|
||||
0->execute
|
||||
atEnt asEnt=targets minecraft:entity 0
|
||||
execute->facing at as in run
|
||||
at->atEnt
|
||||
as->asEnt
|
||||
in->overworld the_nether the_end
|
||||
pos=! minecraft:vec3
|
||||
facing->pos
|
||||
pos atEnt asEnt overworld the_nether the_end+>execute
|
||||
run+>0
|
||||
""", graph);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleCommandTwoEnum() {
|
||||
var graph = Graph.builder(ArgumentType.Literal("foo"))
|
||||
.append(ArgumentType.Enum("bar", A.class), b -> b.append(ArgumentType.Enum("baz", B.class)))
|
||||
.build();
|
||||
assertPacketGraph("""
|
||||
foo=%
|
||||
a b c d e f=§
|
||||
0->foo
|
||||
foo->a b c
|
||||
a b c->d e f
|
||||
""", graph);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleCommandRestrictedWord() {
|
||||
var graph = Graph.builder(ArgumentType.Literal("foo"))
|
||||
.append(ArgumentType.Word("bar").from("A", "B", "C"))
|
||||
.build();
|
||||
assertPacketGraph("""
|
||||
foo=%
|
||||
a b c=§
|
||||
0->foo
|
||||
foo->a b c
|
||||
""", graph);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleCommandWord() {
|
||||
var graph = Graph.builder(ArgumentType.Literal("foo"))
|
||||
.append(ArgumentType.Word("bar"))
|
||||
.build();
|
||||
assertPacketGraph("""
|
||||
foo=%
|
||||
bar=! brigadier:string 0
|
||||
0->foo
|
||||
foo->bar
|
||||
""", graph);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleCommandCommandAfterEnum() {
|
||||
var graph = Graph.builder(ArgumentType.Literal("foo"))
|
||||
.append(ArgumentType.Enum("bar", A.class), b -> b.append(ArgumentType.Command("baz")))
|
||||
.build();
|
||||
assertPacketGraph("""
|
||||
foo baz=%
|
||||
a b c=§
|
||||
0->foo
|
||||
foo->a b c
|
||||
a b c->baz
|
||||
baz+>0
|
||||
""", graph);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void twoCommandIntEnumInt() {
|
||||
var graph = Graph.builder(ArgumentType.Literal("foo"))
|
||||
.append(ArgumentType.Integer("int1"), b -> b.append(ArgumentType.Enum("test", A.class), c -> c.append(ArgumentType.Integer("int2"))))
|
||||
.build();
|
||||
var graph2 = Graph.builder(ArgumentType.Literal("bar"))
|
||||
.append(ArgumentType.Integer("int3"), b -> b.append(ArgumentType.Enum("test", B.class), c -> c.append(ArgumentType.Integer("int4"))))
|
||||
.build();
|
||||
assertPacketGraph("""
|
||||
foo bar=%
|
||||
0->foo bar
|
||||
a b c d e f=§
|
||||
int1 int2 int3 int4=! brigadier:integer 0
|
||||
foo->int1
|
||||
bar->int3
|
||||
int1->a b c
|
||||
int3->d e f
|
||||
a b c->int2
|
||||
d e f->int4
|
||||
""", graph, graph2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleCommandTwoGroupOfIntInt() {
|
||||
var graph = Graph.builder(ArgumentType.Literal("foo"))
|
||||
.append(ArgumentType.Group("1", ArgumentType.Integer("int1"), ArgumentType.Integer("int2")),
|
||||
b -> b.append(ArgumentType.Group("2", ArgumentType.Integer("int3"), ArgumentType.Integer("int4"))))
|
||||
.build();
|
||||
assertPacketGraph("""
|
||||
foo=%
|
||||
int1 int2 int3 int4=! brigadier:integer 0
|
||||
0->foo
|
||||
foo->int1
|
||||
int1->int2
|
||||
int2->int3
|
||||
int3->int4
|
||||
""", graph);
|
||||
}
|
||||
|
||||
static void assertPacketGraph(String expected, Graph... graphs) {
|
||||
var packet = GraphConverter.createPacket(Graph.merge(graphs), null);
|
||||
CommandTestUtils.assertPacket(packet, expected);
|
||||
}
|
||||
|
||||
enum A {A, B, C}
|
||||
|
||||
enum B {D, E, F}
|
||||
|
||||
enum C {G, H, I, J, K}
|
||||
|
||||
private static void assertNodeType(DeclareCommandsPacket.NodeType expected, byte flags) {
|
||||
assertEquals(expected, DeclareCommandsPacket.NodeType.values()[flags & 0x03]);
|
||||
}
|
||||
|
||||
private static void dummyExecutor(CommandSender sender, CommandContext context) {
|
||||
}
|
||||
}
|
|
@ -3,6 +3,8 @@ package net.minestom.server.command;
|
|||
import net.minestom.server.command.builder.Command;
|
||||
import net.minestom.server.command.builder.CommandContext;
|
||||
import net.minestom.server.command.builder.arguments.Argument;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.item.Enchantment;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.String;
|
||||
|
@ -109,6 +111,43 @@ public class CommandSyntaxSingleTest {
|
|||
assertSyntax(groupLoop, "1 2 3 4 5", ExpectedExecution.DEFAULT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleLoopDoubleGroup() {
|
||||
List<Argument<?>> groupLoop = List.of(
|
||||
Loop("loop",
|
||||
Group("group", BlockState("block"), Enchantment("enchant")),
|
||||
Group("group2", Enchantment("enchant"), BlockState("block"))
|
||||
)
|
||||
);
|
||||
// block enchant
|
||||
{
|
||||
var input = "minecraft:stone minecraft:sharpness";
|
||||
var context = new CommandContext(input);
|
||||
context.setArg("block", Block.STONE, "minecraft:stone");
|
||||
context.setArg("enchant", Enchantment.SHARPNESS, "minecraft:sharpness");
|
||||
assertSyntax(groupLoop, input, ExpectedExecution.SYNTAX, Map.of("loop", List.of(context)));
|
||||
}
|
||||
// enchant block block enchant
|
||||
{
|
||||
var context1 = new CommandContext("minecraft:sharpness minecraft:stone");
|
||||
var context2 = new CommandContext("minecraft:grass minecraft:efficiency");
|
||||
|
||||
context1.setArg("enchant", Enchantment.SHARPNESS, "minecraft:sharpness");
|
||||
context1.setArg("block", Block.STONE, "minecraft:stone");
|
||||
|
||||
context2.setArg("block", Block.GRASS, "minecraft:grass");
|
||||
context2.setArg("enchant", Enchantment.EFFICIENCY, "minecraft:efficiency");
|
||||
|
||||
var input = context1.getInput() + " " + context2.getInput();
|
||||
assertSyntax(groupLoop, input, ExpectedExecution.SYNTAX, Map.of("loop", List.of(context1, context2)));
|
||||
}
|
||||
// Incomplete loop
|
||||
assertSyntax(groupLoop, "minecraft:sharpness", ExpectedExecution.DEFAULT);
|
||||
assertSyntax(groupLoop, "minecraft:sharpness minecraft:sharpness", ExpectedExecution.DEFAULT);
|
||||
assertSyntax(groupLoop, "minecraft:stone", ExpectedExecution.DEFAULT);
|
||||
assertSyntax(groupLoop, "minecraft:stone minecraft:stone", ExpectedExecution.DEFAULT);
|
||||
}
|
||||
|
||||
private static void assertSyntax(List<Argument<?>> args, String input, ExpectedExecution expectedExecution, Map<String, Object> expectedValues) {
|
||||
final String commandName = "name";
|
||||
|
||||
|
|
|
@ -0,0 +1,277 @@
|
|||
package net.minestom.server.command;
|
||||
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.opentest4j.AssertionFailedError;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class CommandTestUtils {
|
||||
|
||||
public static void assertPacket(DeclareCommandsPacket packet, String expectedStructure) {
|
||||
final List<NodeStructure.TestNode> expectedList = NodeStructure.fromString("0\n0=$root$\n" + expectedStructure);
|
||||
final List<NodeStructure.TestNode> actualList = NodeStructure.fromString(NodeStructure.packetToString(packet));
|
||||
try {
|
||||
assertEquals(expectedList.size(), actualList.size(), "Different node counts");
|
||||
assertTrue(actualList.containsAll(expectedList), "Packet doesn't contain all expected nodes.");
|
||||
} catch (AssertionFailedError error) {
|
||||
fail("Graphs didn't match. Actual graph from packet: " + CommandTestUtils.exportGarphvizDot(packet, false));
|
||||
}
|
||||
}
|
||||
|
||||
static class NodeStructure {
|
||||
private static final Map<Character, Function<String, Collection<String>>> functions = Map.of(
|
||||
'!', s -> {
|
||||
final String[] strings = splitDeclaration(s);
|
||||
final ArrayList<String> result = new ArrayList<>();
|
||||
for (String s1 : strings[0].split(" ")) {
|
||||
result.add(s1+"="+(strings[1].replaceAll("!", s1)));
|
||||
}
|
||||
return result;
|
||||
},
|
||||
'%', s -> {
|
||||
final String[] strings = splitDeclaration(s);
|
||||
final ArrayList<String> result = new ArrayList<>();
|
||||
for (String s1 : strings[0].split(" ")) {
|
||||
result.add(s1+"="+(strings[1].replaceAll("%", "'"+s1+"'")));
|
||||
}
|
||||
return result;
|
||||
},
|
||||
'§', s -> {
|
||||
final String[] strings = splitDeclaration(s);
|
||||
final ArrayList<String> result = new ArrayList<>();
|
||||
for (String s1 : strings[0].split(" ")) {
|
||||
result.add(s1+"="+(strings[1].replaceAll("§", "'"+(s1.toUpperCase(Locale.ROOT))+"'")));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
);
|
||||
private static final Set<Character> placeholders = functions.keySet();
|
||||
|
||||
static String packetToString(DeclareCommandsPacket packet) {
|
||||
final char lineSeparator = '\n';
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append(packet.rootIndex());
|
||||
builder.append(lineSeparator);
|
||||
@NotNull List<DeclareCommandsPacket.Node> nodes = packet.nodes();
|
||||
for (int i = 0; i < nodes.size(); i++) {
|
||||
DeclareCommandsPacket.Node node = nodes.get(i);
|
||||
builder.append(i);
|
||||
builder.append('=');
|
||||
// Meta
|
||||
if ((node.flags & 0x3) == 0) {
|
||||
builder.append("$root$");
|
||||
} else {
|
||||
if ((node.flags & 0x3) == 1) {
|
||||
builder.append("'");
|
||||
builder.append(node.name);
|
||||
builder.append("'");
|
||||
} else {
|
||||
builder.append(node.name);
|
||||
builder.append(' ');
|
||||
builder.append(node.parser);
|
||||
|
||||
if (node.properties != null) {
|
||||
builder.append(' ');
|
||||
builder.append(new BigInteger(node.properties).toString(16));
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((node.flags & 0x4) == 0x4) {
|
||||
builder.append(" executable");
|
||||
}
|
||||
if ((node.flags & 0x10) == 0x10) {
|
||||
builder.append(' ');
|
||||
builder.append(node.suggestionsType);
|
||||
}
|
||||
builder.append(lineSeparator);
|
||||
if (node.children.length > 0) {
|
||||
builder.append(i);
|
||||
builder.append("->");
|
||||
builder.append(Arrays.stream(node.children).mapToObj(String::valueOf).collect(Collectors.joining(" ")));
|
||||
builder.append(lineSeparator);
|
||||
}
|
||||
if ((node.flags & 0x8) == 0x8) {
|
||||
builder.append(i);
|
||||
builder.append("+>");
|
||||
builder.append(node.redirectedNode);
|
||||
builder.append(lineSeparator);
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static String[] splitDeclaration(String input) {
|
||||
return input.split("=", 2);
|
||||
}
|
||||
|
||||
private static List<String> preProcessString(String string) {
|
||||
final List<String> strings = Arrays.stream(string.split("\n")).toList();
|
||||
final ArrayList<String> result = new ArrayList<>();
|
||||
for (String s : strings) {
|
||||
if (s.indexOf('=') > -1) {
|
||||
boolean match = false;
|
||||
for (Character placeholder : placeholders) {
|
||||
if (s.indexOf(placeholder) > -1) {
|
||||
result.addAll(functions.get(placeholder).apply(s));
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match) {
|
||||
final int spaceIndex = s.indexOf(" ");
|
||||
if (spaceIndex > -1 && spaceIndex < s.indexOf('=')) {
|
||||
final String[] split = s.split("=", 2);
|
||||
for (String s1 : split[0].split(" ")) {
|
||||
result.add(s1+"="+split[1]);
|
||||
}
|
||||
} else {
|
||||
result.add(s);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
final int spaceIndex = s.indexOf(" ");
|
||||
if (spaceIndex > -1 && spaceIndex < s.indexOf('-')) {
|
||||
final String[] split = s.split("-", 2);
|
||||
for (String s1 : split[0].split(" ")) {
|
||||
result.add(s1+"-"+split[1]);
|
||||
}
|
||||
} else if (spaceIndex > -1 && spaceIndex < s.indexOf('+')) {
|
||||
final String[] split = s.split("\\+", 2);
|
||||
for (String s1 : split[0].split(" ")) {
|
||||
result.add(s1+"+"+split[1]);
|
||||
}
|
||||
} else {
|
||||
result.add(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static List<TestNode> fromString(String input) {
|
||||
Map<String, String[]> references = new HashMap<>();
|
||||
Map<String, TestNode> nodes = new HashMap<>();
|
||||
final List<String> strings = preProcessString(input);
|
||||
String rootId = strings.get(0);
|
||||
|
||||
for (String s : strings.stream().skip(0).toList()) {
|
||||
if (s.length() < 3) continue; //invalid line
|
||||
final int declareSeparator = s.indexOf('=');
|
||||
if (declareSeparator > -1) {
|
||||
final String id = s.substring(0, declareSeparator);
|
||||
final String meta = s.substring(declareSeparator + 1);
|
||||
nodes.put(id, new TestNode(new ArrayList<>(), meta, new AtomicReference<>()));
|
||||
} else {
|
||||
final int childSeparator = s.indexOf('-');
|
||||
if (childSeparator > -1) {
|
||||
references.put(s.substring(0, childSeparator), s.substring(childSeparator + 2).split(" "));
|
||||
} else {
|
||||
final int redirectSeparator = s.indexOf('+');
|
||||
references.put(s.substring(0, redirectSeparator), new String[]{null, s.substring(redirectSeparator + 2)});
|
||||
}
|
||||
}
|
||||
}
|
||||
final ArrayList<TestNode> result = new ArrayList<>();
|
||||
List<Runnable> redirectSetters = new ArrayList<>();
|
||||
resolveNode(rootId, references, nodes, result, new HashMap<>(), redirectSetters, "");
|
||||
redirectSetters.forEach(Runnable::run);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String resolveNode(String id, Map<String, String[]> references,
|
||||
Map<String, TestNode> nodes, ArrayList<TestNode> result,
|
||||
Map<String, String> nameToMetaPath,
|
||||
List<Runnable> redirectSetters, String metaPath) {
|
||||
final TestNode node = nodes.get(id);
|
||||
final String[] refs = references.get(id);
|
||||
final String path = metaPath + "#" + node.meta;
|
||||
if (refs == null) {
|
||||
result.add(node);
|
||||
nameToMetaPath.put(id, path);
|
||||
return path;
|
||||
} else if (refs[0] == null) {
|
||||
redirectSetters.add(() -> node.redirect.set(nameToMetaPath.get(refs[1])));
|
||||
} else {
|
||||
for (String ref : refs) {
|
||||
node.children.add(resolveNode(ref, references, nodes, result, nameToMetaPath, redirectSetters, path));
|
||||
}
|
||||
}
|
||||
result.add(node);
|
||||
nameToMetaPath.put(id, path);
|
||||
return path;
|
||||
}
|
||||
|
||||
record TestNode(List<String> children, String meta, AtomicReference<String> redirect) {
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof TestNode that) {
|
||||
return this.meta.equals(that.meta) && Objects.equals(this.redirect.get(), that.redirect.get()) &&
|
||||
this.children.containsAll(that.children) && this.children.size() == that.children.size();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static String exportGarphvizDot(DeclareCommandsPacket packet, boolean prettyPrint) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
final char statementSeparator = ';';
|
||||
builder.append("digraph G {");
|
||||
builder.append("rankdir=LR");
|
||||
builder.append(statementSeparator);
|
||||
builder.append(packet.rootIndex());
|
||||
builder.append(" [label=\"root\",shape=rectangle]");
|
||||
builder.append(statementSeparator);
|
||||
@NotNull List<DeclareCommandsPacket.Node> nodes = packet.nodes();
|
||||
for (int i = 0; i < nodes.size(); i++) {
|
||||
DeclareCommandsPacket.Node node = nodes.get(i);
|
||||
if ((node.flags & 0x3) != 0) {
|
||||
builder.append(i);
|
||||
builder.append(" [label=");
|
||||
builder.append('"');
|
||||
if ((node.flags & 0x3) == 1) {
|
||||
builder.append("'");
|
||||
builder.append(node.name);
|
||||
builder.append("'");
|
||||
} else {
|
||||
builder.append(node.name);
|
||||
}
|
||||
builder.append('"');
|
||||
if ((node.flags & 0x4) == 0x4) {
|
||||
builder.append(",bgcolor=gray,style=filled");
|
||||
}
|
||||
builder.append("]");
|
||||
builder.append(statementSeparator);
|
||||
}
|
||||
if (node.children.length == 0 && (node.flags & 0x8) == 0) continue;
|
||||
builder.append(i);
|
||||
builder.append(" -> { ");
|
||||
if ((node.flags & 0x8) == 0) {
|
||||
builder.append(Arrays.stream(node.children).mapToObj(Integer::toString).collect(Collectors.joining(" ")));
|
||||
builder.append(" }");
|
||||
builder.append(statementSeparator);
|
||||
} else {
|
||||
builder.append(node.redirectedNode);
|
||||
builder.append(" } [style = dotted]");
|
||||
builder.append(statementSeparator);
|
||||
}
|
||||
}
|
||||
builder.append("}");
|
||||
if (prettyPrint)
|
||||
return builder.toString()
|
||||
.replaceFirst("\\{r", "{\n r")
|
||||
.replaceAll(";", "\n ")
|
||||
.replaceFirst(" {2}}$", "}\n");
|
||||
else
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package net.minestom.server.command;
|
||||
|
||||
import net.minestom.server.command.builder.Command;
|
||||
import net.minestom.server.command.builder.CommandContext;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static net.minestom.server.command.builder.arguments.ArgumentType.Literal;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class GraphConversionExecutorTest {
|
||||
@Test
|
||||
public void empty() {
|
||||
final Command foo = new Command("foo");
|
||||
var graph = Graph.fromCommand(foo);
|
||||
assertNull(graph.root().executor());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultCondition() {
|
||||
final Command foo = new Command("foo");
|
||||
// Constant true
|
||||
{
|
||||
foo.setCondition((sender, commandString) -> true);
|
||||
var graph = Graph.fromCommand(foo);
|
||||
var executor = graph.root().executor();
|
||||
assertNotNull(executor);
|
||||
assertTrue(executor.test(null));
|
||||
}
|
||||
// Constant false
|
||||
{
|
||||
foo.setCondition((sender, commandString) -> false);
|
||||
var graph = Graph.fromCommand(foo);
|
||||
var executor = graph.root().executor();
|
||||
assertNotNull(executor);
|
||||
assertFalse(executor.test(null));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptySyntaxCondition() {
|
||||
final Command foo = new Command("foo");
|
||||
foo.addSyntax(GraphConversionExecutorTest::dummyExecutor, Literal("first"));
|
||||
|
||||
var graph = Graph.fromCommand(foo);
|
||||
assertEquals(1, graph.root().next().size());
|
||||
assertNull(graph.root().next().get(0).executor());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void syntaxConditionTrue() {
|
||||
final Command foo = new Command("foo");
|
||||
foo.addConditionalSyntax((sender, context) -> true,
|
||||
GraphConversionExecutorTest::dummyExecutor, Literal("first"));
|
||||
|
||||
var graph = Graph.fromCommand(foo);
|
||||
assertEquals(1, graph.root().next().size());
|
||||
var executor = graph.root().next().get(0).executor();
|
||||
assertNotNull(executor);
|
||||
assertTrue(executor.test(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void syntaxConditionFalse() {
|
||||
final Command foo = new Command("foo");
|
||||
foo.addConditionalSyntax((sender, context) -> false,
|
||||
GraphConversionExecutorTest::dummyExecutor, Literal("first"));
|
||||
|
||||
var graph = Graph.fromCommand(foo);
|
||||
assertEquals(1, graph.root().next().size());
|
||||
var executor = graph.root().next().get(0).executor();
|
||||
assertNotNull(executor);
|
||||
assertFalse(executor.test(null));
|
||||
}
|
||||
|
||||
private static void dummyExecutor(CommandSender sender, CommandContext context) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
package net.minestom.server.command;
|
||||
|
||||
import net.minestom.server.command.builder.Command;
|
||||
import net.minestom.server.command.builder.CommandContext;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static net.minestom.server.command.builder.arguments.ArgumentType.Enum;
|
||||
import static net.minestom.server.command.builder.arguments.ArgumentType.Integer;
|
||||
import static net.minestom.server.command.builder.arguments.ArgumentType.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class GraphConversionTest {
|
||||
@Test
|
||||
public void empty() {
|
||||
final Command foo = new Command("foo");
|
||||
var graph = Graph.builder(Literal("foo")).build();
|
||||
assertEqualsGraph(graph, foo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleLiteral() {
|
||||
final Command foo = new Command("foo");
|
||||
var first = Literal("first");
|
||||
foo.addSyntax(GraphConversionTest::dummyExecutor, first);
|
||||
var graph = Graph.builder(Literal("foo"))
|
||||
.append(first).build();
|
||||
assertEqualsGraph(graph, foo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void literalsPath() {
|
||||
final Command foo = new Command("foo");
|
||||
var first = Literal("first");
|
||||
var second = Literal("second");
|
||||
|
||||
foo.addSyntax(GraphConversionTest::dummyExecutor, first);
|
||||
foo.addSyntax(GraphConversionTest::dummyExecutor, second);
|
||||
|
||||
var graph = Graph.builder(Literal("foo"))
|
||||
.append(first).append(second)
|
||||
.build();
|
||||
assertEqualsGraph(graph, foo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doubleSyntax() {
|
||||
enum A {A, B, C, D, E}
|
||||
final Command foo = new Command("foo");
|
||||
|
||||
var bar = Literal("bar");
|
||||
|
||||
var baz = Literal("baz");
|
||||
var a = Enum("a", A.class);
|
||||
|
||||
foo.addSyntax(GraphConversionTest::dummyExecutor, bar);
|
||||
foo.addSyntax(GraphConversionTest::dummyExecutor, baz, a);
|
||||
|
||||
var graph = Graph.builder(Literal("foo"))
|
||||
.append(bar)
|
||||
.append(baz, builder ->
|
||||
builder.append(a))
|
||||
.build();
|
||||
assertEqualsGraph(graph, foo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doubleSyntaxMerge() {
|
||||
final Command foo = new Command("foo");
|
||||
|
||||
var bar = Literal("bar");
|
||||
var number = Integer("number");
|
||||
|
||||
foo.addSyntax(GraphConversionTest::dummyExecutor, bar);
|
||||
foo.addSyntax(GraphConversionTest::dummyExecutor, bar, number);
|
||||
|
||||
// The two syntax shall start from the same node
|
||||
var graph = Graph.builder(Literal("foo"))
|
||||
.append(bar, builder -> builder.append(number))
|
||||
.build();
|
||||
assertEqualsGraph(graph, foo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subcommand() {
|
||||
final Command main = new Command("main");
|
||||
final Command sub = new Command("sub");
|
||||
|
||||
var bar = Literal("bar");
|
||||
var number = Integer("number");
|
||||
|
||||
sub.addSyntax(GraphConversionTest::dummyExecutor, bar);
|
||||
sub.addSyntax(GraphConversionTest::dummyExecutor, bar, number);
|
||||
|
||||
main.addSubcommand(sub);
|
||||
|
||||
// The two syntax shall start from the same node
|
||||
var graph = Graph.builder(Literal("main"))
|
||||
.append(Literal("sub"), builder ->
|
||||
builder.append(bar, builder1 -> builder1.append(number)))
|
||||
.build();
|
||||
assertEqualsGraph(graph, main);
|
||||
}
|
||||
|
||||
private static void assertEqualsGraph(Graph expected, Command command) {
|
||||
final Graph actual = Graph.fromCommand(command);
|
||||
assertTrue(expected.compare(actual, Graph.Comparator.TREE), () -> {
|
||||
System.out.println("Expected: " + expected);
|
||||
System.out.println("Actual: " + actual);
|
||||
return "";
|
||||
});
|
||||
}
|
||||
|
||||
private static void dummyExecutor(CommandSender sender, CommandContext context) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package net.minestom.server.command;
|
||||
|
||||
import net.minestom.server.command.builder.Command;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static net.minestom.server.command.builder.arguments.ArgumentType.Literal;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class GraphMergeTest {
|
||||
|
||||
@Test
|
||||
public void commands() {
|
||||
var foo = new Command("foo");
|
||||
var bar = new Command("bar");
|
||||
var result = Graph.builder(Literal(""))
|
||||
.append(Literal("foo"))
|
||||
.append(Literal("bar"))
|
||||
.build();
|
||||
assertEqualsGraph(result, Graph.merge(List.of(foo, bar)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void empty() {
|
||||
var graph1 = Graph.builder(Literal("foo")).build();
|
||||
var graph2 = Graph.builder(Literal("bar")).build();
|
||||
var result = Graph.builder(Literal(""))
|
||||
.append(Literal("foo"))
|
||||
.append(Literal("bar"))
|
||||
.build();
|
||||
assertEqualsGraph(result, Graph.merge(graph1, graph2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void literals() {
|
||||
var graph1 = Graph.builder(Literal("foo")).append(Literal("1")).build();
|
||||
var graph2 = Graph.builder(Literal("bar")).append(Literal("2")).build();
|
||||
var result = Graph.builder(Literal(""))
|
||||
.append(Literal("foo"), builder -> builder.append(Literal("1")))
|
||||
.append(Literal("bar"), builder -> builder.append(Literal("2")))
|
||||
.build();
|
||||
assertEqualsGraph(result, Graph.merge(graph1, graph2));
|
||||
}
|
||||
|
||||
private static void assertEqualsGraph(Graph expected, Graph actual) {
|
||||
assertTrue(expected.compare(actual, Graph.Comparator.TREE), () -> {
|
||||
System.out.println("Expected: " + expected);
|
||||
System.out.println("Actual: " + actual);
|
||||
return "";
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package net.minestom.server.command;
|
||||
|
||||
import net.minestom.server.command.builder.Command;
|
||||
import net.minestom.server.command.builder.CommandContext;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static net.minestom.server.command.builder.arguments.ArgumentType.Literal;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class GraphTest {
|
||||
@Test
|
||||
public void empty() {
|
||||
var result = Graph.builder(Literal(""))
|
||||
.build();
|
||||
var node = result.root();
|
||||
assertEquals(Literal(""), node.argument());
|
||||
assertTrue(node.next().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void next() {
|
||||
var result = Graph.builder(Literal(""))
|
||||
.append(Literal("foo"))
|
||||
.build();
|
||||
var node = result.root();
|
||||
assertEquals(Literal(""), node.argument());
|
||||
assertEquals(1, node.next().size());
|
||||
assertEquals(Literal("foo"), node.next().get(0).argument());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void immutableNextBuilder() {
|
||||
var result = Graph.builder(Literal(""))
|
||||
.append(Literal("foo"))
|
||||
.append(Literal("bar"))
|
||||
.build();
|
||||
var node = result.root();
|
||||
assertThrows(Exception.class, () -> result.root().next().add(node));
|
||||
assertThrows(Exception.class, () -> result.root().next().get(0).next().add(node));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void immutableNextCommand() {
|
||||
final Command foo = new Command("foo");
|
||||
var first = Literal("first");
|
||||
foo.addSyntax(GraphTest::dummyExecutor, first);
|
||||
var result = Graph.fromCommand(foo);
|
||||
|
||||
var node = result.root();
|
||||
assertThrows(Exception.class, () -> result.root().next().add(node));
|
||||
assertThrows(Exception.class, () -> result.root().next().get(0).next().add(node));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void immutableNextCommands() {
|
||||
final Command foo, bar;
|
||||
|
||||
{
|
||||
var first = Literal("first");
|
||||
|
||||
foo = new Command("foo");
|
||||
foo.addSyntax(GraphTest::dummyExecutor, first);
|
||||
|
||||
bar = new Command("foo");
|
||||
bar.addSyntax(GraphTest::dummyExecutor, first);
|
||||
}
|
||||
|
||||
var result = Graph.merge(List.of(foo, bar));
|
||||
|
||||
var node = result.root();
|
||||
assertThrows(Exception.class, () -> result.root().next().add(node));
|
||||
assertThrows(Exception.class, () -> result.root().next().get(0).next().add(node));
|
||||
}
|
||||
|
||||
private static void dummyExecutor(CommandSender sender, CommandContext context) {
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
package net.minestom.server.command;
|
||||
|
||||
import net.minestom.server.command.builder.Command;
|
||||
import net.minestom.server.command.builder.CommandContext;
|
||||
import net.minestom.server.command.builder.arguments.ArgumentType;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket.NodeType;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
public class NodeGraphTest {
|
||||
|
||||
@Test
|
||||
public void singleCommandWithOneSyntax() {
|
||||
final Command foo = new Command("foo");
|
||||
foo.addSyntax(NodeGraphTest::dummyExecutor, ArgumentType.Integer("bar"));
|
||||
|
||||
final DeclareCommandsPacket packet = GraphConverter.createPacket(GraphBuilder.forServer(Set.of(foo)));
|
||||
assertEquals(3, packet.nodes().size());
|
||||
final DeclareCommandsPacket.Node root = packet.nodes().get(packet.rootIndex());
|
||||
assertNotNull(root);
|
||||
assertNodeType(NodeType.ROOT, root.flags);
|
||||
assertEquals(1, root.children.length);
|
||||
final DeclareCommandsPacket.Node cmd = packet.nodes().get(root.children[0]);
|
||||
assertNotNull(cmd);
|
||||
assertNodeType(NodeType.LITERAL, cmd.flags);
|
||||
assertEquals(1, cmd.children.length);
|
||||
assertEquals("foo", cmd.name);
|
||||
final DeclareCommandsPacket.Node arg = packet.nodes().get(cmd.children[0]);
|
||||
assertNotNull(arg);
|
||||
assertNodeType(NodeType.ARGUMENT, arg.flags);
|
||||
assertEquals(0, arg.children.length);
|
||||
assertEquals("bar", arg.name);
|
||||
}
|
||||
|
||||
private static void assertNodeType(NodeType expected, byte flags) {
|
||||
assertEquals(expected, NodeType.values()[flags & 0x03]);
|
||||
}
|
||||
|
||||
private static void dummyExecutor(CommandSender sender, CommandContext context) {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,14 +1,12 @@
|
|||
package net.minestom.server.command;
|
||||
|
||||
import net.minestom.server.command.builder.Command;
|
||||
import net.minestom.server.command.builder.exception.IllegalCommandStructureException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class SubcommandTest {
|
||||
|
||||
|
@ -67,17 +65,4 @@ public class SubcommandTest {
|
|||
assertFalse(parentExecuted.get());
|
||||
assertFalse(childExecuted.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecursionDetection() {
|
||||
final Command foo = new Command("foo");
|
||||
final Command bar = new Command("bar");
|
||||
bar.addSubcommand(foo);
|
||||
assertDoesNotThrow(() -> GraphBuilder.forServer(Set.of(foo, bar)));
|
||||
foo.addSubcommand(bar);
|
||||
assertTimeout(Duration.ofSeconds(5), () -> assertThrows(IllegalCommandStructureException.class,
|
||||
() -> GraphBuilder.forServer(Set.of(foo, bar)), "Builder didn't detect infinite recursion."),
|
||||
"Is your stack fine?!");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,10 @@ import net.minestom.server.api.Env;
|
|||
import net.minestom.server.api.EnvTest;
|
||||
import net.minestom.server.collision.BoundingBox;
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.event.item.PickupItemEvent;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.item.Material;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
@ -48,4 +52,31 @@ public class EntityBoundingBoxIntegrationTest {
|
|||
player.setPose(Entity.Pose.FALL_FLYING);
|
||||
assertEquals(0.4, player.getEyeHeight());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pickupItem(Env env) {
|
||||
final var instance = env.createFlatInstance();
|
||||
final var listener = env.listen(PickupItemEvent.class);
|
||||
final var spawnPos = new Pos(0, 42, 0);
|
||||
final var entity = new LivingEntity(EntityType.ZOMBIE);
|
||||
entity.setCanPickupItem(true);
|
||||
entity.setInstance(instance, spawnPos).join();
|
||||
|
||||
var time = System.currentTimeMillis();
|
||||
|
||||
dropItem(instance, spawnPos);
|
||||
listener.followup();
|
||||
entity.update(time += 1_000L);
|
||||
|
||||
dropItem(instance, spawnPos.sub(.5));
|
||||
listener.followup();
|
||||
entity.update(time += 1_000L);
|
||||
}
|
||||
|
||||
private void dropItem(final Instance instance, final Pos position) {
|
||||
final var entity = new ItemEntity(ItemStack.of(Material.STONE));
|
||||
entity.hasPhysics = false;
|
||||
entity.setNoGravity(true);
|
||||
entity.setInstance(instance, position).join();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import net.kyori.adventure.text.Component;
|
|||
import net.minestom.server.api.Env;
|
||||
import net.minestom.server.api.EnvTest;
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.entity.EquipmentSlot;
|
||||
import net.minestom.server.event.item.ItemDropEvent;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.item.Material;
|
||||
import net.minestom.server.network.packet.server.play.EntityEquipmentPacket;
|
||||
|
@ -12,9 +12,7 @@ import net.minestom.server.network.packet.server.play.SetSlotPacket;
|
|||
import net.minestom.server.network.packet.server.play.WindowItemsPacket;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@EnvTest
|
||||
public class InventoryIntegrationTest {
|
||||
|
@ -75,7 +73,7 @@ public class InventoryIntegrationTest {
|
|||
var connection = env.createConnection();
|
||||
var player = connection.connect(instance, new Pos(0, 42, 0)).join();
|
||||
assertEquals(instance, player.getInstance());
|
||||
|
||||
|
||||
Inventory inventory = new Inventory(InventoryType.CHEST_6_ROW, Component.empty());
|
||||
player.openInventory(inventory);
|
||||
assertEquals(inventory, player.getOpenInventory());
|
||||
|
@ -112,4 +110,39 @@ public class InventoryIntegrationTest {
|
|||
equipmentTracker.assertEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void closeInventoryTest(Env env) {
|
||||
var instance = env.createFlatInstance();
|
||||
var connection = env.createConnection();
|
||||
var player = connection.connect(instance, new Pos(0, 42, 0)).join();
|
||||
final var inventory = new Inventory(InventoryType.CHEST_1_ROW, "title");
|
||||
player.openInventory(inventory);
|
||||
assertSame(inventory, player.getOpenInventory());
|
||||
player.closeInventory();
|
||||
assertNull(player.getOpenInventory());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void openInventoryOnItemDropFromInventoryClosingTest(Env env) {
|
||||
var instance = env.createFlatInstance();
|
||||
var connection = env.createConnection();
|
||||
var player = connection.connect(instance, new Pos(0, 42, 0)).join();
|
||||
var listener = env.listen(ItemDropEvent.class);
|
||||
final var firstInventory = new Inventory(InventoryType.CHEST_1_ROW, "title");
|
||||
player.openInventory(firstInventory);
|
||||
assertSame(firstInventory, player.getOpenInventory());
|
||||
firstInventory.setCursorItem(player, ItemStack.of(Material.STONE));
|
||||
|
||||
listener.followup();
|
||||
player.closeInventory();
|
||||
assertNull(player.getOpenInventory());
|
||||
|
||||
player.openInventory(firstInventory);
|
||||
firstInventory.setCursorItem(player, ItemStack.of(Material.STONE));
|
||||
final var secondInventory = new Inventory(InventoryType.CHEST_1_ROW, "title");
|
||||
listener.followup(event -> event.getPlayer().openInventory(secondInventory));
|
||||
player.closeInventory();
|
||||
assertSame(secondInventory, player.getOpenInventory());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ public class ServerAddressTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void unixAddressTest() throws IOException, InterruptedException {
|
||||
public void unixAddressTest() throws IOException {
|
||||
UnixDomainSocketAddress address = UnixDomainSocketAddress.of("minestom.sock");
|
||||
var server = new Server(new PacketProcessor());
|
||||
server.init(address);
|
||||
|
@ -39,4 +39,10 @@ public class ServerAddressTest {
|
|||
assertDoesNotThrow(server::stop);
|
||||
assertFalse(Files.exists(address.getPath()), "The socket file should be deleted");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noAddressTest() throws IOException {
|
||||
var server = new Server(new PacketProcessor());
|
||||
assertDoesNotThrow(server::stop);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue