mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-09 01:47:54 +01:00
commit
6c4454e9fb
3
.gitignore
vendored
3
.gitignore
vendored
@ -55,4 +55,5 @@ gradle-app.setting
|
||||
/extensions/
|
||||
|
||||
# When compiling we get a docs folder
|
||||
/docs
|
||||
/docs
|
||||
.mixin.out/
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 843fc32877802b9b86ae291a2b2fa3d633c24183
|
||||
Subproject commit 7151ce11dcef265ae87c7d279f2a237d1158a05c
|
@ -2,21 +2,11 @@ package net.minestom.server.command;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.command.builder.Command;
|
||||
import net.minestom.server.command.builder.CommandData;
|
||||
import net.minestom.server.command.builder.CommandDispatcher;
|
||||
import net.minestom.server.command.builder.CommandSyntax;
|
||||
import net.minestom.server.command.builder.arguments.*;
|
||||
import net.minestom.server.command.builder.arguments.minecraft.*;
|
||||
import net.minestom.server.command.builder.arguments.minecraft.registry.*;
|
||||
import net.minestom.server.command.builder.arguments.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.ArgumentNumber;
|
||||
import net.minestom.server.command.builder.arguments.relative.ArgumentRelativeBlockPosition;
|
||||
import net.minestom.server.command.builder.arguments.relative.ArgumentRelativeVec2;
|
||||
import net.minestom.server.command.builder.arguments.relative.ArgumentRelativeVec3;
|
||||
import net.minestom.server.command.builder.*;
|
||||
import net.minestom.server.command.builder.arguments.Argument;
|
||||
import net.minestom.server.command.builder.condition.CommandCondition;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.player.PlayerCommandEvent;
|
||||
@ -25,7 +15,6 @@ import net.minestom.server.utils.ArrayUtils;
|
||||
import net.minestom.server.utils.callback.CommandCallback;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@ -33,7 +22,6 @@ import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Manager used to register {@link Command} and {@link CommandProcessor}.
|
||||
@ -75,9 +63,11 @@ public final class CommandManager {
|
||||
public synchronized void register(@NotNull Command command) {
|
||||
Check.stateCondition(commandExists(command.getName()),
|
||||
"A command with the name " + command.getName() + " is already registered!");
|
||||
for (String alias : command.getAliases()) {
|
||||
Check.stateCondition(commandExists(alias),
|
||||
"A command with the name " + alias + " is already registered!");
|
||||
if (command.getAliases() != null) {
|
||||
for (String alias : command.getAliases()) {
|
||||
Check.stateCondition(commandExists(alias),
|
||||
"A command with the name " + alias + " is already registered!");
|
||||
}
|
||||
}
|
||||
this.dispatcher.register(command);
|
||||
}
|
||||
@ -154,10 +144,10 @@ public final class CommandManager {
|
||||
*
|
||||
* @param sender the sender of the command
|
||||
* @param command the raw command string (without the command prefix)
|
||||
* @return true if the command hadn't been cancelled and has been successful
|
||||
* @return the execution result
|
||||
*/
|
||||
@Nullable
|
||||
public CommandData execute(@NotNull CommandSender sender, @NotNull String command) {
|
||||
@NotNull
|
||||
public CommandResult execute(@NotNull CommandSender sender, @NotNull String command) {
|
||||
|
||||
// Command event
|
||||
if (sender instanceof Player) {
|
||||
@ -167,7 +157,7 @@ public final class CommandManager {
|
||||
player.callEvent(PlayerCommandEvent.class, playerCommandEvent);
|
||||
|
||||
if (playerCommandEvent.isCancelled())
|
||||
return null;
|
||||
return CommandResult.of(CommandResult.Type.CANCELLED, command);
|
||||
|
||||
command = playerCommandEvent.getCommand();
|
||||
}
|
||||
@ -176,9 +166,9 @@ public final class CommandManager {
|
||||
|
||||
{
|
||||
// Check for rich-command
|
||||
final CommandData commandData = this.dispatcher.execute(sender, command);
|
||||
if (commandData != null) {
|
||||
return commandData;
|
||||
final CommandResult result = this.dispatcher.execute(sender, command);
|
||||
if (result.getType() != CommandResult.Type.UNKNOWN) {
|
||||
return result;
|
||||
} else {
|
||||
// Check for legacy-command
|
||||
final String[] splitCommand = command.split(StringUtils.SPACE);
|
||||
@ -188,13 +178,13 @@ public final class CommandManager {
|
||||
if (unknownCommandCallback != null) {
|
||||
this.unknownCommandCallback.apply(sender, command);
|
||||
}
|
||||
return null;
|
||||
return CommandResult.of(CommandResult.Type.CANCELLED, command);
|
||||
}
|
||||
|
||||
// Execute the legacy-command
|
||||
final String[] args = command.substring(command.indexOf(StringUtils.SPACE) + 1).split(StringUtils.SPACE);
|
||||
commandProcessor.process(sender, commandName, args);
|
||||
return null;
|
||||
return CommandResult.of(CommandResult.Type.SUCCESS, command);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -205,11 +195,16 @@ public final class CommandManager {
|
||||
*
|
||||
* @see #execute(CommandSender, String)
|
||||
*/
|
||||
@Nullable
|
||||
public CommandData executeServerCommand(@NotNull String command) {
|
||||
@NotNull
|
||||
public CommandResult executeServerCommand(@NotNull String command) {
|
||||
return execute(serverSender, command);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public CommandDispatcher getDispatcher() {
|
||||
return dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the callback executed once an unknown command is run.
|
||||
*
|
||||
@ -304,6 +299,11 @@ public final class CommandManager {
|
||||
// Contains the children of the main node (all commands name)
|
||||
IntList rootChildren = new IntArrayList();
|
||||
|
||||
// Root node
|
||||
DeclareCommandsPacket.Node rootNode = new DeclareCommandsPacket.Node();
|
||||
rootNode.flags = 0;
|
||||
nodes.add(rootNode);
|
||||
|
||||
// Brigadier-like commands
|
||||
for (Command command : dispatcher.getCommands()) {
|
||||
// Check if player should see this command
|
||||
@ -319,39 +319,52 @@ public final class CommandManager {
|
||||
IntList cmdChildren = new IntArrayList();
|
||||
final Collection<CommandSyntax> syntaxes = command.getSyntaxes();
|
||||
|
||||
List<String> names = new ArrayList<>();
|
||||
names.add(command.getName());
|
||||
names.addAll(Arrays.asList(command.getAliases()));
|
||||
for (String name : names) {
|
||||
createCommand(player, nodes, cmdChildren, name, syntaxes, rootChildren);
|
||||
// Create command for main name
|
||||
final int mainNodeIndex = createCommand(player, nodes, cmdChildren,
|
||||
command.getName(), syntaxes, rootChildren);
|
||||
|
||||
// Use redirection to hook aliases with the command
|
||||
final String[] aliases = command.getAliases();
|
||||
if (aliases == null)
|
||||
continue;
|
||||
|
||||
for (String alias : aliases) {
|
||||
DeclareCommandsPacket.Node aliasNode = new DeclareCommandsPacket.Node();
|
||||
aliasNode.flags = DeclareCommandsPacket.getFlag(DeclareCommandsPacket.NodeType.LITERAL,
|
||||
false, true, false);
|
||||
aliasNode.name = alias;
|
||||
aliasNode.redirectedNode = mainNodeIndex;
|
||||
|
||||
addCommandNameNode(aliasNode, rootChildren, nodes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Pair<CommandName,EnabledTracking>
|
||||
final List<Pair<String, Boolean>> commandsPair = new ArrayList<>();
|
||||
final Object2BooleanMap<String> commandsPair = new Object2BooleanOpenHashMap<>();
|
||||
for (CommandProcessor commandProcessor : commandProcessorMap.values()) {
|
||||
final boolean enableTracking = commandProcessor.enableWritingTracking();
|
||||
// Do not show command if return false
|
||||
if (!commandProcessor.hasAccess(player))
|
||||
continue;
|
||||
|
||||
commandsPair.add(Pair.of(commandProcessor.getCommandName(), enableTracking));
|
||||
commandsPair.put(commandProcessor.getCommandName(), enableTracking);
|
||||
final String[] aliases = commandProcessor.getAliases();
|
||||
if (aliases == null || aliases.length == 0)
|
||||
continue;
|
||||
for (String alias : aliases) {
|
||||
commandsPair.add(Pair.of(alias, enableTracking));
|
||||
commandsPair.put(alias, enableTracking);
|
||||
}
|
||||
}
|
||||
|
||||
for (Pair<String, Boolean> pair : commandsPair) {
|
||||
final String name = pair.getLeft();
|
||||
final boolean tracking = pair.getRight();
|
||||
for (Object2BooleanMap.Entry<String> entry : commandsPair.object2BooleanEntrySet()) {
|
||||
final String name = entry.getKey();
|
||||
final boolean tracking = entry.getBooleanValue();
|
||||
// Server suggestion (ask_server)
|
||||
{
|
||||
DeclareCommandsPacket.Node tabNode = new DeclareCommandsPacket.Node();
|
||||
tabNode.flags = getFlag(NodeType.ARGUMENT, true, false, tracking);
|
||||
tabNode.flags = DeclareCommandsPacket.getFlag(DeclareCommandsPacket.NodeType.ARGUMENT,
|
||||
true, false, tracking);
|
||||
tabNode.name = tracking ? "tab_completion" : "args";
|
||||
tabNode.parser = "brigadier:string";
|
||||
tabNode.properties = packetWriter -> packetWriter.writeVarInt(2); // Greedy phrase
|
||||
@ -364,22 +377,19 @@ public final class CommandManager {
|
||||
}
|
||||
|
||||
DeclareCommandsPacket.Node literalNode = new DeclareCommandsPacket.Node();
|
||||
literalNode.flags = getFlag(NodeType.LITERAL, true, false, false);
|
||||
literalNode.flags = DeclareCommandsPacket.getFlag(DeclareCommandsPacket.NodeType.LITERAL,
|
||||
true, false, false);
|
||||
literalNode.name = name;
|
||||
literalNode.children = new int[]{nodes.size() - 1};
|
||||
|
||||
rootChildren.add(nodes.size());
|
||||
nodes.add(literalNode);
|
||||
addCommandNameNode(literalNode, rootChildren, nodes);
|
||||
}
|
||||
|
||||
DeclareCommandsPacket.Node rootNode = new DeclareCommandsPacket.Node();
|
||||
rootNode.flags = 0;
|
||||
// Add root node children
|
||||
rootNode.children = ArrayUtils.toArray(rootChildren);
|
||||
|
||||
nodes.add(rootNode);
|
||||
|
||||
declareCommandsPacket.nodes = nodes.toArray(new DeclareCommandsPacket.Node[0]);
|
||||
declareCommandsPacket.rootIndex = nodes.size() - 1;
|
||||
declareCommandsPacket.rootIndex = 0;
|
||||
|
||||
return declareCommandsPacket;
|
||||
}
|
||||
@ -393,23 +403,23 @@ public final class CommandManager {
|
||||
* @param name the name of the command (or the alias)
|
||||
* @param syntaxes the syntaxes of the command
|
||||
* @param rootChildren the children of the main node (all commands name)
|
||||
* @return The index of the main node for alias redirection
|
||||
*/
|
||||
private void createCommand(@NotNull CommandSender sender,
|
||||
@NotNull List<DeclareCommandsPacket.Node> nodes,
|
||||
@NotNull IntList cmdChildren,
|
||||
@NotNull String name,
|
||||
@NotNull Collection<CommandSyntax> syntaxes,
|
||||
@NotNull IntList rootChildren) {
|
||||
private int createCommand(@NotNull CommandSender sender,
|
||||
@NotNull List<DeclareCommandsPacket.Node> nodes,
|
||||
@NotNull IntList cmdChildren,
|
||||
@NotNull String name,
|
||||
@NotNull Collection<CommandSyntax> syntaxes,
|
||||
@NotNull IntList rootChildren) {
|
||||
|
||||
DeclareCommandsPacket.Node literalNode = createMainNode(name, syntaxes.isEmpty());
|
||||
|
||||
rootChildren.add(nodes.size());
|
||||
nodes.add(literalNode);
|
||||
addCommandNameNode(literalNode, rootChildren, nodes);
|
||||
|
||||
// Contains the arguments of the already-parsed syntaxes
|
||||
List<Argument<?>[]> syntaxesArguments = new ArrayList<>();
|
||||
// Contains the nodes of an argument
|
||||
Map<Argument<?>, List<DeclareCommandsPacket.Node>> storedArgumentsNodes = new HashMap<>();
|
||||
Map<Argument<?>, List<DeclareCommandsPacket.Node[]>> storedArgumentsNodes = new HashMap<>();
|
||||
|
||||
for (CommandSyntax syntax : syntaxes) {
|
||||
final CommandCondition commandCondition = syntax.getCommandCondition();
|
||||
@ -418,331 +428,107 @@ public final class CommandManager {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Represent the last nodes computed in the last iteration
|
||||
List<DeclareCommandsPacket.Node> lastNodes = null;
|
||||
DeclareCommandsPacket.Node[] lastNodes = new DeclareCommandsPacket.Node[]{literalNode};
|
||||
|
||||
// Represent the children of the last node
|
||||
IntList argChildren = null;
|
||||
IntList argChildren = cmdChildren;
|
||||
|
||||
NodeMaker nodeMaker = new NodeMaker(lastNodes);
|
||||
int lastArgumentNodeIndex = nodeMaker.getNodesCount();
|
||||
|
||||
final Argument<?>[] arguments = syntax.getArguments();
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
final Argument<?> argument = arguments[i];
|
||||
final boolean isFirst = i == 0;
|
||||
final boolean isLast = i == arguments.length - 1;
|
||||
|
||||
// Find shared part
|
||||
boolean foundSharedPart = false;
|
||||
for (Argument<?>[] parsedArguments : syntaxesArguments) {
|
||||
if (ArrayUtils.sameStart(arguments, parsedArguments, i + 1)) {
|
||||
final Argument<?> sharedArgument = parsedArguments[i];
|
||||
// Search previously parsed syntaxes to find identical part in order to create a node between those
|
||||
{
|
||||
// Find shared part
|
||||
boolean foundSharedPart = false;
|
||||
for (Argument<?>[] parsedArguments : syntaxesArguments) {
|
||||
final int index = i + 1;
|
||||
if (ArrayUtils.sameStart(arguments, parsedArguments, index)) {
|
||||
final Argument<?> sharedArgument = parsedArguments[i];
|
||||
final List<DeclareCommandsPacket.Node[]> storedNodes = storedArgumentsNodes.get(sharedArgument);
|
||||
|
||||
argChildren = new IntArrayList();
|
||||
lastNodes = storedArgumentsNodes.get(sharedArgument);
|
||||
foundSharedPart = true;
|
||||
argChildren = new IntArrayList();
|
||||
lastNodes = storedNodes.get(index);
|
||||
foundSharedPart = true;
|
||||
}
|
||||
}
|
||||
if (foundSharedPart) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (foundSharedPart) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Process the nodes for the argument
|
||||
{
|
||||
argument.processNodes(nodeMaker, isLast);
|
||||
|
||||
final List<DeclareCommandsPacket.Node> argumentNodes = toNodes(argument, isLast);
|
||||
storedArgumentsNodes.put(argument, argumentNodes);
|
||||
for (DeclareCommandsPacket.Node node : argumentNodes) {
|
||||
final int childId = nodes.size();
|
||||
// Each node array represent a layer
|
||||
final List<DeclareCommandsPacket.Node[]> nodesLayer = nodeMaker.getNodes();
|
||||
storedArgumentsNodes.put(argument, nodesLayer);
|
||||
for (int nodeIndex = lastArgumentNodeIndex; nodeIndex < nodesLayer.size(); nodeIndex++) {
|
||||
final NodeMaker.ConfiguredNodes configuredNodes = nodeMaker.getConfiguredNodes().get(nodeIndex);
|
||||
final NodeMaker.Options options = configuredNodes.getOptions();
|
||||
final DeclareCommandsPacket.Node[] argumentNodes = nodesLayer.get(nodeIndex);
|
||||
|
||||
if (isFirst) {
|
||||
// Add to main command child
|
||||
cmdChildren.add(childId);
|
||||
} else {
|
||||
// Add to previous argument children
|
||||
argChildren.add(childId);
|
||||
for (DeclareCommandsPacket.Node argumentNode : argumentNodes) {
|
||||
final int childId = nodes.size();
|
||||
nodeMaker.getNodeIdsMap().put(argumentNode, childId);
|
||||
argChildren.add(childId);
|
||||
|
||||
// Append to the last node
|
||||
{
|
||||
final int[] children = ArrayUtils.toArray(argChildren);
|
||||
for (DeclareCommandsPacket.Node lastNode : lastNodes) {
|
||||
lastNode.children = lastNode.children == null ?
|
||||
children :
|
||||
ArrayUtils.concatenateIntArrays(lastNode.children, children);
|
||||
}
|
||||
}
|
||||
|
||||
nodes.add(argumentNode);
|
||||
}
|
||||
|
||||
if (options.shouldUpdateLastNode()) {
|
||||
// 'previousNodes' used if the nodes options require to overwrite the parent
|
||||
final DeclareCommandsPacket.Node[] previousNodes = options.getPreviousNodes();
|
||||
|
||||
lastNodes = previousNodes != null ? previousNodes : argumentNodes;
|
||||
argChildren = new IntArrayList();
|
||||
}
|
||||
}
|
||||
|
||||
if (lastNodes != null) {
|
||||
final int[] children = ArrayUtils.toArray(argChildren);
|
||||
lastNodes.forEach(n -> n.children = n.children == null ?
|
||||
children :
|
||||
ArrayUtils.concatenateIntArrays(n.children, children));
|
||||
}
|
||||
|
||||
nodes.add(node);
|
||||
}
|
||||
|
||||
//System.out.println("debug: " + argument.getId() + " : " + isFirst + " : " + isLast);
|
||||
//System.out.println("debug2: " + i);
|
||||
//System.out.println("size: " + (argChildren != null ? argChildren.size() : "NULL"));
|
||||
|
||||
if (isLast) {
|
||||
// Last argument doesn't have children
|
||||
final int[] children = new int[0];
|
||||
argumentNodes.forEach(node -> node.children = children);
|
||||
} else {
|
||||
// Create children list which will be filled during next iteration
|
||||
argChildren = new IntArrayList();
|
||||
lastNodes = argumentNodes;
|
||||
// Used to do not re-compute the previous arguments
|
||||
lastArgumentNodeIndex = nodesLayer.size();
|
||||
}
|
||||
}
|
||||
|
||||
syntaxesArguments.add(arguments);
|
||||
|
||||
}
|
||||
final int[] children = ArrayUtils.toArray(cmdChildren);
|
||||
//System.out.println("test " + children.length + " : " + children[0]);
|
||||
literalNode.children = children;
|
||||
if (children.length > 0) {
|
||||
literalNode.redirectedNode = children[0];
|
||||
}
|
||||
|
||||
literalNode.children = ArrayUtils.toArray(cmdChildren);
|
||||
|
||||
return nodes.indexOf(literalNode);
|
||||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private DeclareCommandsPacket.Node createMainNode(@NotNull String name, boolean executable) {
|
||||
DeclareCommandsPacket.Node literalNode = new DeclareCommandsPacket.Node();
|
||||
literalNode.flags = getFlag(NodeType.LITERAL, executable, false, false);
|
||||
literalNode.flags = DeclareCommandsPacket.getFlag(DeclareCommandsPacket.NodeType.LITERAL, executable, false, false);
|
||||
literalNode.name = name;
|
||||
|
||||
return literalNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an argument to a node with the correct brigadier parser.
|
||||
*
|
||||
* @param argument the argument to convert
|
||||
* @param executable true if this is the last argument, false otherwise
|
||||
* @return the list of nodes that the argument require
|
||||
*/
|
||||
@NotNull
|
||||
private List<DeclareCommandsPacket.Node> toNodes(@NotNull Argument<?> argument, boolean executable) {
|
||||
List<DeclareCommandsPacket.Node> nodes = new ArrayList<>();
|
||||
|
||||
// You can uncomment this to test any brigadier parser on the client
|
||||
/*DeclareCommandsPacket.Node testNode = simpleArgumentNode(nodes, argument, executable, false);
|
||||
testNode.parser = "minecraft:block_state";
|
||||
|
||||
if (true) {
|
||||
return nodes;
|
||||
}*/
|
||||
|
||||
if (argument instanceof ArgumentBoolean) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
||||
|
||||
argumentNode.parser = "brigadier:bool";
|
||||
} else if (argument instanceof ArgumentDouble) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
||||
|
||||
ArgumentDouble argumentDouble = (ArgumentDouble) argument;
|
||||
argumentNode.parser = "brigadier:double";
|
||||
argumentNode.properties = packetWriter -> {
|
||||
packetWriter.writeByte(getNumberProperties(argumentDouble));
|
||||
if (argumentDouble.hasMin())
|
||||
packetWriter.writeDouble(argumentDouble.getMin());
|
||||
if (argumentDouble.hasMax())
|
||||
packetWriter.writeDouble(argumentDouble.getMax());
|
||||
};
|
||||
} else if (argument instanceof ArgumentFloat) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
||||
|
||||
ArgumentFloat argumentFloat = (ArgumentFloat) argument;
|
||||
argumentNode.parser = "brigadier:float";
|
||||
argumentNode.properties = packetWriter -> {
|
||||
packetWriter.writeByte(getNumberProperties(argumentFloat));
|
||||
if (argumentFloat.hasMin())
|
||||
packetWriter.writeFloat(argumentFloat.getMin());
|
||||
if (argumentFloat.hasMax())
|
||||
packetWriter.writeFloat(argumentFloat.getMax());
|
||||
};
|
||||
} else if (argument instanceof ArgumentInteger) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
||||
|
||||
ArgumentInteger argumentInteger = (ArgumentInteger) argument;
|
||||
argumentNode.parser = "brigadier:integer";
|
||||
argumentNode.properties = packetWriter -> {
|
||||
packetWriter.writeByte(getNumberProperties(argumentInteger));
|
||||
if (argumentInteger.hasMin())
|
||||
packetWriter.writeInt(argumentInteger.getMin());
|
||||
if (argumentInteger.hasMax())
|
||||
packetWriter.writeInt(argumentInteger.getMax());
|
||||
};
|
||||
} else if (argument instanceof ArgumentWord) {
|
||||
|
||||
ArgumentWord argumentWord = (ArgumentWord) argument;
|
||||
|
||||
// Add the single word properties + parser
|
||||
final Consumer<DeclareCommandsPacket.Node> wordConsumer = node -> {
|
||||
node.parser = "brigadier:string";
|
||||
node.properties = packetWriter -> {
|
||||
packetWriter.writeVarInt(0); // Single word
|
||||
};
|
||||
};
|
||||
|
||||
final boolean hasRestriction = argumentWord.hasRestrictions();
|
||||
if (hasRestriction) {
|
||||
// Create a node for each restrictions as literal
|
||||
for (String restrictionWord : argumentWord.getRestrictions()) {
|
||||
DeclareCommandsPacket.Node argumentNode = new DeclareCommandsPacket.Node();
|
||||
nodes.add(argumentNode);
|
||||
|
||||
argumentNode.flags = getFlag(NodeType.LITERAL, executable, false, false);
|
||||
argumentNode.name = restrictionWord;
|
||||
wordConsumer.accept(argumentNode);
|
||||
}
|
||||
} else {
|
||||
// Can be any word, add only one argument node
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
||||
wordConsumer.accept(argumentNode);
|
||||
}
|
||||
} else if (argument instanceof ArgumentDynamicWord) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, true);
|
||||
|
||||
final SuggestionType suggestionType = ((ArgumentDynamicWord) argument).getSuggestionType();
|
||||
|
||||
argumentNode.parser = "brigadier:string";
|
||||
argumentNode.properties = packetWriter -> {
|
||||
packetWriter.writeVarInt(0); // Single word
|
||||
};
|
||||
argumentNode.suggestionsType = suggestionType.getIdentifier();
|
||||
|
||||
} else if (argument instanceof ArgumentString) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
||||
|
||||
argumentNode.parser = "brigadier:string";
|
||||
argumentNode.properties = packetWriter -> {
|
||||
packetWriter.writeVarInt(1); // Quotable phrase
|
||||
};
|
||||
} else if (argument instanceof ArgumentStringArray) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
||||
|
||||
argumentNode.parser = "brigadier:string";
|
||||
argumentNode.properties = packetWriter -> {
|
||||
packetWriter.writeVarInt(2); // Greedy phrase
|
||||
};
|
||||
} else if (argument instanceof ArgumentDynamicStringArray) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, true);
|
||||
|
||||
argumentNode.parser = "brigadier:string";
|
||||
argumentNode.properties = packetWriter -> {
|
||||
packetWriter.writeVarInt(2); // Greedy phrase
|
||||
};
|
||||
argumentNode.suggestionsType = "minecraft:ask_server";
|
||||
} else if (argument instanceof ArgumentColor) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
||||
argumentNode.parser = "minecraft:color";
|
||||
} else if (argument instanceof ArgumentTime) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
||||
argumentNode.parser = "minecraft:time";
|
||||
} else if (argument instanceof ArgumentEnchantment) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
||||
argumentNode.parser = "minecraft:item_enchantment";
|
||||
} else if (argument instanceof ArgumentParticle) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
||||
argumentNode.parser = "minecraft:particle";
|
||||
} else if (argument instanceof ArgumentPotionEffect) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
||||
argumentNode.parser = "minecraft:mob_effect";
|
||||
} else if (argument instanceof ArgumentEntityType) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
||||
argumentNode.parser = "minecraft:entity_summon";
|
||||
} else if (argument instanceof ArgumentBlockState) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
||||
argumentNode.parser = "minecraft:block_state";
|
||||
} else if (argument instanceof ArgumentIntRange) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
||||
argumentNode.parser = "minecraft:int_range";
|
||||
} else if (argument instanceof ArgumentFloatRange) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
||||
argumentNode.parser = "minecraft:float_range";
|
||||
} else if (argument instanceof ArgumentEntity) {
|
||||
ArgumentEntity argumentEntity = (ArgumentEntity) argument;
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
||||
argumentNode.parser = "minecraft:entity";
|
||||
argumentNode.properties = packetWriter -> {
|
||||
byte mask = 0;
|
||||
if (argumentEntity.isOnlySingleEntity()) {
|
||||
mask += 1;
|
||||
}
|
||||
if (argumentEntity.isOnlyPlayers()) {
|
||||
mask += 2;
|
||||
}
|
||||
packetWriter.writeByte(mask);
|
||||
};
|
||||
} else if (argument instanceof ArgumentItemStack) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
||||
argumentNode.parser = "minecraft:item_stack";
|
||||
} else if (argument instanceof ArgumentNbtCompoundTag) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
||||
argumentNode.parser = "minecraft:nbt_compound_tag";
|
||||
} else if (argument instanceof ArgumentNbtTag) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
||||
argumentNode.parser = "minecraft:nbt_tag";
|
||||
} else if (argument instanceof ArgumentRelativeBlockPosition) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
||||
argumentNode.parser = "minecraft:block_pos";
|
||||
} else if (argument instanceof ArgumentRelativeVec3) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
||||
argumentNode.parser = "minecraft:vec3";
|
||||
} else if (argument instanceof ArgumentRelativeVec2) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
||||
argumentNode.parser = "minecraft:vec2";
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
private byte getNumberProperties(@NotNull ArgumentNumber<? extends Number> argumentNumber) {
|
||||
byte result = 0;
|
||||
if (argumentNumber.hasMin())
|
||||
result += 1;
|
||||
if (argumentNumber.hasMax())
|
||||
result += 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an argument nod and add it to the nodes list.
|
||||
*
|
||||
* @param nodes the current nodes list
|
||||
* @param argument the argument
|
||||
* @param executable true if this will be the last argument, false otherwise
|
||||
* @return the created {@link DeclareCommandsPacket.Node}
|
||||
*/
|
||||
@NotNull
|
||||
private DeclareCommandsPacket.Node simpleArgumentNode(@NotNull List<DeclareCommandsPacket.Node> nodes,
|
||||
@NotNull Argument<?> argument, boolean executable, boolean suggestion) {
|
||||
DeclareCommandsPacket.Node argumentNode = new DeclareCommandsPacket.Node();
|
||||
nodes.add(argumentNode);
|
||||
|
||||
argumentNode.flags = getFlag(NodeType.ARGUMENT, executable, false, suggestion);
|
||||
argumentNode.name = argument.getId();
|
||||
|
||||
return argumentNode;
|
||||
}
|
||||
|
||||
private byte getFlag(@NotNull NodeType type, boolean executable, boolean redirect, boolean suggestionType) {
|
||||
byte result = (byte) type.mask;
|
||||
|
||||
if (executable) {
|
||||
result |= 0x04;
|
||||
}
|
||||
|
||||
if (redirect) {
|
||||
result |= 0x08;
|
||||
}
|
||||
|
||||
if (suggestionType) {
|
||||
result |= 0x10;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private enum NodeType {
|
||||
ROOT(0), LITERAL(0b1), ARGUMENT(0b10), NONE(0x11);
|
||||
|
||||
private final int mask;
|
||||
|
||||
NodeType(int mask) {
|
||||
this.mask = mask;
|
||||
}
|
||||
|
||||
private void addCommandNameNode(@NotNull DeclareCommandsPacket.Node commandNode,
|
||||
@NotNull IntList rootChildren,
|
||||
@NotNull List<DeclareCommandsPacket.Node> nodes) {
|
||||
rootChildren.add(nodes.size());
|
||||
nodes.add(commandNode);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package net.minestom.server.command;
|
||||
|
||||
import net.minestom.server.chat.ColoredText;
|
||||
import net.minestom.server.chat.JsonMessage;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.permission.PermissionHandler;
|
||||
|
@ -2,6 +2,8 @@ package net.minestom.server.command;
|
||||
|
||||
import net.minestom.server.permission.Permission;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
@ -11,11 +13,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
*/
|
||||
public class ConsoleSender implements CommandSender {
|
||||
|
||||
private final static Logger LOGGER = LoggerFactory.getLogger(ConsoleSender.class);
|
||||
|
||||
private final Set<Permission> permissions = new CopyOnWriteArraySet<>();
|
||||
|
||||
@Override
|
||||
public void sendMessage(@NotNull String message) {
|
||||
System.out.println(message);
|
||||
LOGGER.info(message);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
@ -42,6 +42,13 @@ public final class Arguments {
|
||||
return (T) getObject(argument.getId());
|
||||
}
|
||||
|
||||
public <T> T get(@NotNull String identifier) {
|
||||
return (T) args.computeIfAbsent(identifier, s -> {
|
||||
throw new NullPointerException(
|
||||
"The argument with the id '" + identifier + "' has no value assigned, be sure to check your arguments id, your syntax, and that you do not change the argument id dynamically.");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #get(Argument)}.
|
||||
*/
|
||||
@ -244,6 +251,10 @@ public final class Arguments {
|
||||
return (RelativeVec) getObject(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #get(String)}.
|
||||
*/
|
||||
@Deprecated
|
||||
@NotNull
|
||||
public Object getObject(@NotNull String id) {
|
||||
return args.computeIfAbsent(id, s -> {
|
||||
@ -261,11 +272,16 @@ public final class Arguments {
|
||||
this.returnData = returnData;
|
||||
}
|
||||
|
||||
protected void setArg(@NotNull String id, Object value) {
|
||||
@NotNull
|
||||
public Map<String, Object> getMap() {
|
||||
return args;
|
||||
}
|
||||
|
||||
public void setArg(@NotNull String id, Object value) {
|
||||
this.args.put(id, value);
|
||||
}
|
||||
|
||||
protected void copy(@NotNull Arguments arguments) {
|
||||
public void copy(@NotNull Arguments arguments) {
|
||||
this.args = arguments.args;
|
||||
}
|
||||
|
||||
|
@ -10,8 +10,9 @@ public class CommandData {
|
||||
|
||||
private final Map<String, Object> dataMap = new ConcurrentHashMap<>();
|
||||
|
||||
public void set(@NotNull String key, Object value) {
|
||||
public CommandData set(@NotNull String key, Object value) {
|
||||
this.dataMap.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@ -19,6 +20,10 @@ public class CommandData {
|
||||
return (T) dataMap.get(key);
|
||||
}
|
||||
|
||||
public boolean has(@NotNull String key) {
|
||||
return dataMap.containsKey(key);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<String, Object> getDataMap() {
|
||||
return dataMap;
|
||||
|
@ -3,8 +3,10 @@ package net.minestom.server.command.builder;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
|
||||
import net.minestom.server.command.CommandSender;
|
||||
import net.minestom.server.command.builder.arguments.Argument;
|
||||
import net.minestom.server.command.builder.condition.CommandCondition;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.command.builder.parser.CommandParser;
|
||||
import net.minestom.server.command.builder.parser.CommandSuggestionHolder;
|
||||
import net.minestom.server.command.builder.parser.ValidSyntaxHolder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -32,7 +34,7 @@ public class CommandDispatcher {
|
||||
// Register aliases
|
||||
final String[] aliases = command.getAliases();
|
||||
if (aliases != null) {
|
||||
for (String alias : command.getAliases()) {
|
||||
for (String alias : aliases) {
|
||||
this.commandMap.put(alias.toLowerCase(), command);
|
||||
}
|
||||
}
|
||||
@ -53,54 +55,13 @@ public class CommandDispatcher {
|
||||
this.commands.remove(command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given command.
|
||||
*
|
||||
* @param commandString the command (containing the command name and the args if any)
|
||||
* @return the result of the parsing, null if the command doesn't exist
|
||||
*/
|
||||
@Nullable
|
||||
public CommandResult parse(@NotNull String commandString) {
|
||||
commandString = commandString.trim();
|
||||
|
||||
// Split space
|
||||
final String[] parts = commandString.split(StringUtils.SPACE);
|
||||
final String commandName = parts[0];
|
||||
|
||||
final String[] args = commandString.replaceFirst(Pattern.quote(commandName), "").trim().split(StringUtils.SPACE);
|
||||
|
||||
final Command command = findCommand(commandName);
|
||||
// Check if the command exists
|
||||
if (command == null)
|
||||
return null;
|
||||
|
||||
// Find the used syntax, or check which argument is wrong
|
||||
return findCommandResult(command, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the command exists, and execute it.
|
||||
*
|
||||
* @param source the command source
|
||||
* @param commandString the command with the argument(s)
|
||||
* @return the command data, null if none
|
||||
*/
|
||||
@Nullable
|
||||
public CommandData execute(@NotNull CommandSender source, @NotNull String commandString) {
|
||||
CommandResult result = parse(commandString);
|
||||
if (result != null) {
|
||||
return result.execute(source, commandString);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Set<Command> getCommands() {
|
||||
return Collections.unmodifiableSet(commands);
|
||||
}
|
||||
|
||||
/**
|
||||
* GetS the command class associated with the name;
|
||||
* Gets the command class associated with the name.
|
||||
*
|
||||
* @param commandName the command name
|
||||
* @return the {@link Command} associated with the name, null if not any
|
||||
@ -111,18 +72,78 @@ public class CommandDispatcher {
|
||||
return commandMap.getOrDefault(commandName, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the command exists, and execute it.
|
||||
*
|
||||
* @param source the command source
|
||||
* @param commandString the command with the argument(s)
|
||||
* @return the command result
|
||||
*/
|
||||
@NotNull
|
||||
private CommandResult findCommandResult(@NotNull Command command, @NotNull String[] args) {
|
||||
CommandResult result = new CommandResult();
|
||||
result.command = command;
|
||||
public CommandResult execute(@NotNull CommandSender source, @NotNull String commandString) {
|
||||
CommandResult commandResult = parse(commandString);
|
||||
ParsedCommand parsedCommand = commandResult.parsedCommand;
|
||||
if (parsedCommand != null) {
|
||||
commandResult.commandData = parsedCommand.execute(source, commandString);
|
||||
}
|
||||
return commandResult;
|
||||
}
|
||||
|
||||
Arguments executorArgs = new Arguments();
|
||||
/**
|
||||
* Parses the given command.
|
||||
*
|
||||
* @param commandString the command (containing the command name and the args if any)
|
||||
* @return the parsing result
|
||||
*/
|
||||
@NotNull
|
||||
public CommandResult parse(@NotNull String commandString) {
|
||||
commandString = commandString.trim();
|
||||
|
||||
// Split space
|
||||
final String[] parts = commandString.split(StringUtils.SPACE);
|
||||
final String commandName = parts[0];
|
||||
|
||||
final Command command = findCommand(commandName);
|
||||
// Check if the command exists
|
||||
if (command == null) {
|
||||
return CommandResult.of(CommandResult.Type.UNKNOWN, commandName);
|
||||
}
|
||||
|
||||
// Removes the command's name + the space after
|
||||
final String[] args = commandString.replaceFirst(Pattern.quote(commandName), "").trim().split(StringUtils.SPACE);
|
||||
|
||||
// Find the used syntax
|
||||
ParsedCommand parsedCommand = findParsedCommand(command, args);
|
||||
|
||||
CommandResult result = new CommandResult();
|
||||
result.input = commandString;
|
||||
result.parsedCommand = parsedCommand;
|
||||
if (parsedCommand != null && parsedCommand.executor != null) {
|
||||
// Syntax found
|
||||
result.type = CommandResult.Type.SUCCESS;
|
||||
} else {
|
||||
// Syntax not found, use the default executor (if any)
|
||||
result.type = CommandResult.Type.INVALID_SYNTAX;
|
||||
if (parsedCommand == null) { // Prevent overriding argument callback
|
||||
result.parsedCommand = ParsedCommand.withDefaultExecutor(command);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ParsedCommand findParsedCommand(@NotNull Command command, @NotNull String[] args) {
|
||||
ParsedCommand parsedCommand = new ParsedCommand();
|
||||
parsedCommand.command = command;
|
||||
|
||||
// The default executor should be used if no argument is provided
|
||||
if (args[0].length() == 0) {
|
||||
result.executor = command.getDefaultExecutor();
|
||||
result.arguments = executorArgs;
|
||||
return result;
|
||||
{
|
||||
final CommandExecutor defaultExecutor = command.getDefaultExecutor();
|
||||
if (defaultExecutor != null && args[0].length() == 0) {
|
||||
parsedCommand.executor = defaultExecutor;
|
||||
parsedCommand.arguments = new Arguments();
|
||||
return parsedCommand;
|
||||
}
|
||||
}
|
||||
|
||||
// SYNTAXES PARSING
|
||||
@ -130,128 +151,29 @@ public class CommandDispatcher {
|
||||
// All the registered syntaxes of the command
|
||||
final Collection<CommandSyntax> syntaxes = command.getSyntaxes();
|
||||
// Contains all the fully validated syntaxes (we later find the one with the most amount of arguments)
|
||||
List<ValidSyntaxHolder> validSyntaxes = new ArrayList<>();
|
||||
List<ValidSyntaxHolder> validSyntaxes = new ArrayList<>(syntaxes.size());
|
||||
|
||||
// Contains all the syntaxes that are not fully correct, used to later, retrieve the "most correct syntax"
|
||||
// Number of correct argument - The data about the failing argument
|
||||
Int2ObjectRBTreeMap<CommandSuggestionHolder> syntaxesSuggestions = new Int2ObjectRBTreeMap<>(Collections.reverseOrder());
|
||||
|
||||
for (CommandSyntax syntax : syntaxes) {
|
||||
final Argument<?>[] arguments = syntax.getArguments();
|
||||
final List<Object> argsValues = new ArrayList<>(arguments.length);
|
||||
|
||||
boolean syntaxCorrect = true;
|
||||
// The current index in the raw command string arguments
|
||||
int splitIndex = 0;
|
||||
|
||||
boolean useRemaining = false;
|
||||
// Check the validity of the arguments...
|
||||
for (int argCount = 0; argCount < arguments.length; argCount++) {
|
||||
final boolean lastArgumentIteration = argCount + 1 == arguments.length;
|
||||
final Argument<?> argument = arguments[argCount];
|
||||
useRemaining = argument.useRemaining();
|
||||
|
||||
// the parsed argument value, null if incorrect
|
||||
Object parsedValue;
|
||||
// the argument exception, null if the input is correct
|
||||
ArgumentSyntaxException argumentSyntaxException = null;
|
||||
// true if the arg is valid, false otherwise
|
||||
boolean correct = false;
|
||||
// the raw string representing the correct argument syntax
|
||||
StringBuilder argValue = new StringBuilder();
|
||||
|
||||
if (useRemaining) {
|
||||
final boolean hasArgs = args.length > splitIndex;
|
||||
// Verify if there is any string part available
|
||||
if (hasArgs) {
|
||||
// Argument is supposed to take the rest of the command input
|
||||
for (int i = splitIndex; i < args.length; i++) {
|
||||
final String arg = args[i];
|
||||
if (argValue.length() > 0)
|
||||
argValue.append(StringUtils.SPACE);
|
||||
argValue.append(arg);
|
||||
}
|
||||
|
||||
final String argValueString = argValue.toString();
|
||||
|
||||
try {
|
||||
parsedValue = argument.parse(argValueString);
|
||||
correct = true;
|
||||
argsValues.add(parsedValue);
|
||||
} catch (ArgumentSyntaxException exception) {
|
||||
argumentSyntaxException = exception;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Argument is either single-word or can accept optional delimited space(s)
|
||||
for (int i = splitIndex; i < args.length; i++) {
|
||||
final String rawArg = args[i];
|
||||
|
||||
argValue.append(rawArg);
|
||||
|
||||
final String argValueString = argValue.toString();
|
||||
|
||||
try {
|
||||
parsedValue = argument.parse(argValueString);
|
||||
|
||||
// Prevent quitting the parsing too soon if the argument
|
||||
// does not allow space
|
||||
if (lastArgumentIteration && i + 1 < args.length) {
|
||||
if (!argument.allowSpace())
|
||||
break;
|
||||
argValue.append(StringUtils.SPACE);
|
||||
continue;
|
||||
}
|
||||
|
||||
correct = true;
|
||||
argsValues.add(parsedValue);
|
||||
splitIndex = i + 1;
|
||||
break;
|
||||
} catch (ArgumentSyntaxException exception) {
|
||||
argumentSyntaxException = exception;
|
||||
|
||||
if (!argument.allowSpace())
|
||||
break;
|
||||
argValue.append(StringUtils.SPACE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!correct) {
|
||||
// Argument is not correct, add it to the syntax suggestion with the number
|
||||
// of correct argument(s) and do not check the next syntax argument
|
||||
syntaxCorrect = false;
|
||||
CommandSuggestionHolder suggestionHolder = new CommandSuggestionHolder();
|
||||
suggestionHolder.syntax = syntax;
|
||||
suggestionHolder.argumentSyntaxException = argumentSyntaxException;
|
||||
suggestionHolder.argIndex = argCount;
|
||||
syntaxesSuggestions.put(argCount, suggestionHolder);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the syntax to the list of valid syntaxes if correct
|
||||
if (syntaxCorrect) {
|
||||
if (arguments.length == argsValues.size() || useRemaining) {
|
||||
ValidSyntaxHolder validSyntaxHolder = new ValidSyntaxHolder();
|
||||
validSyntaxHolder.syntax = syntax;
|
||||
validSyntaxHolder.argumentsValue = argsValues;
|
||||
|
||||
validSyntaxes.add(validSyntaxHolder);
|
||||
}
|
||||
}
|
||||
CommandParser.parse(syntax, syntax.getArguments(), args, validSyntaxes, syntaxesSuggestions);
|
||||
}
|
||||
|
||||
// Check if there is at least one correct syntax
|
||||
if (!validSyntaxes.isEmpty()) {
|
||||
Arguments executorArgs = new Arguments();
|
||||
// Search the syntax with all perfect args
|
||||
final CommandSyntax finalSyntax = findMostCorrectSyntax(validSyntaxes, executorArgs);
|
||||
if (finalSyntax != null) {
|
||||
final ValidSyntaxHolder finalValidSyntax = CommandParser.findMostCorrectSyntax(validSyntaxes, executorArgs);
|
||||
if (finalValidSyntax != null) {
|
||||
// A fully correct syntax has been found, use it
|
||||
result.syntax = finalSyntax;
|
||||
result.executor = finalSyntax.getExecutor();
|
||||
result.arguments = executorArgs;
|
||||
return result;
|
||||
final CommandSyntax syntax = finalValidSyntax.syntax;
|
||||
|
||||
parsedCommand.syntax = syntax;
|
||||
parsedCommand.executor = syntax.getExecutor();
|
||||
parsedCommand.arguments = executorArgs;
|
||||
return parsedCommand;
|
||||
}
|
||||
|
||||
}
|
||||
@ -269,151 +191,15 @@ public class CommandDispatcher {
|
||||
// Found the closest syntax with at least 1 correct argument
|
||||
final Argument<?> argument = syntax.getArguments()[argIndex];
|
||||
if (argument.hasErrorCallback()) {
|
||||
result.callback = argument.getCallback();
|
||||
result.argumentSyntaxException = argumentSyntaxException;
|
||||
parsedCommand.callback = argument.getCallback();
|
||||
parsedCommand.argumentSyntaxException = argumentSyntaxException;
|
||||
|
||||
return result;
|
||||
return parsedCommand;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use the default executor at last resort
|
||||
result.executor = command.getDefaultExecutor();
|
||||
result.arguments = executorArgs;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves from the valid syntax map the arguments condition result and get the one with the most
|
||||
* valid arguments.
|
||||
*
|
||||
* @param validSyntaxes the list containing all the valid syntaxes
|
||||
* @param executorArgs the recipient of the argument parsed values
|
||||
* @return the command syntax with all of its arguments correct and with the most arguments count, null if not any
|
||||
*/
|
||||
@Nullable
|
||||
private CommandSyntax findMostCorrectSyntax(@NotNull List<ValidSyntaxHolder> validSyntaxes,
|
||||
@NotNull Arguments executorArgs) {
|
||||
CommandSyntax finalSyntax = null;
|
||||
int maxArguments = 0;
|
||||
Arguments finalArguments = null;
|
||||
|
||||
for (ValidSyntaxHolder validSyntaxHolder : validSyntaxes) {
|
||||
final CommandSyntax syntax = validSyntaxHolder.syntax;
|
||||
|
||||
final Argument<?>[] arguments = syntax.getArguments();
|
||||
final int argumentsCount = arguments.length;
|
||||
final List<Object> argsValues = validSyntaxHolder.argumentsValue;
|
||||
|
||||
final int argsSize = argsValues.size();
|
||||
|
||||
if (argsSize > maxArguments) {
|
||||
finalSyntax = syntax;
|
||||
maxArguments = argsSize;
|
||||
|
||||
// Fill arguments map
|
||||
Arguments syntaxValues = new Arguments();
|
||||
for (int i = 0; i < argumentsCount; i++) {
|
||||
final Argument<?> argument = arguments[i];
|
||||
final Object argumentValue = argsValues.get(i);
|
||||
|
||||
syntaxValues.setArg(argument.getId(), argumentValue);
|
||||
}
|
||||
finalArguments = syntaxValues;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the arguments values
|
||||
if (finalSyntax != null) {
|
||||
executorArgs.copy(finalArguments);
|
||||
}
|
||||
|
||||
return finalSyntax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds the data of a validated syntax.
|
||||
*/
|
||||
private static class ValidSyntaxHolder {
|
||||
private CommandSyntax syntax;
|
||||
/**
|
||||
* (Argument index/Argument parsed object)
|
||||
*/
|
||||
private List<Object> argumentsValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds the data of an invalidated syntax.
|
||||
*/
|
||||
private static class CommandSuggestionHolder {
|
||||
private CommandSyntax syntax;
|
||||
private ArgumentSyntaxException argumentSyntaxException;
|
||||
private int argIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a {@link Command} ready to be executed (already parsed).
|
||||
*/
|
||||
private static class CommandResult {
|
||||
|
||||
// Command
|
||||
private Command command;
|
||||
|
||||
// Command Executor
|
||||
private CommandSyntax syntax;
|
||||
|
||||
private CommandExecutor executor;
|
||||
private Arguments arguments;
|
||||
|
||||
// Argument Callback
|
||||
private ArgumentCallback callback;
|
||||
private ArgumentSyntaxException argumentSyntaxException;
|
||||
|
||||
/**
|
||||
* Executes the command for the given source.
|
||||
* <p>
|
||||
* The command will not be executed if {@link Command#getCondition()}
|
||||
* is not validated.
|
||||
*
|
||||
* @param source the command source
|
||||
* @param commandString the command string
|
||||
* @return the command data, null if none
|
||||
*/
|
||||
@Nullable
|
||||
public CommandData execute(@NotNull CommandSender source, @NotNull String commandString) {
|
||||
// Global listener
|
||||
command.globalListener(source, arguments, commandString);
|
||||
// Command condition check
|
||||
final CommandCondition condition = command.getCondition();
|
||||
if (condition != null) {
|
||||
final boolean result = condition.canUse(source, commandString);
|
||||
if (!result)
|
||||
return null;
|
||||
}
|
||||
// Condition is respected
|
||||
if (executor != null) {
|
||||
// An executor has been found
|
||||
|
||||
if (syntax != null) {
|
||||
// The executor is from a syntax
|
||||
final CommandCondition commandCondition = syntax.getCommandCondition();
|
||||
if (commandCondition == null || commandCondition.canUse(source, commandString)) {
|
||||
arguments.retrieveDefaultValues(syntax.getDefaultValuesMap());
|
||||
executor.apply(source, arguments);
|
||||
}
|
||||
} else {
|
||||
// The executor is probably the default one
|
||||
executor.apply(source, arguments);
|
||||
}
|
||||
} else if (callback != null && argumentSyntaxException != null) {
|
||||
// No syntax has been validated but the faulty argument with a callback has been found
|
||||
// Execute the faulty argument callback
|
||||
callback.apply(source, argumentSyntaxException);
|
||||
}
|
||||
|
||||
return arguments.getReturnData();
|
||||
}
|
||||
|
||||
// No syntax found
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,64 @@
|
||||
package net.minestom.server.command.builder;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class CommandResult {
|
||||
|
||||
protected Type type = Type.UNKNOWN;
|
||||
protected String input;
|
||||
protected ParsedCommand parsedCommand;
|
||||
protected CommandData commandData;
|
||||
|
||||
@NotNull
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getInput() {
|
||||
return input;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ParsedCommand getParsedCommand() {
|
||||
return parsedCommand;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public CommandData getCommandData() {
|
||||
return commandData;
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
/**
|
||||
* Command and syntax successfully found.
|
||||
*/
|
||||
SUCCESS,
|
||||
|
||||
/**
|
||||
* Command found, but the syntax is invalid.
|
||||
* Executor sets to {@link Command#getDefaultExecutor()}.
|
||||
*/
|
||||
INVALID_SYNTAX,
|
||||
|
||||
/**
|
||||
* Command cancelled by an event listener.
|
||||
*/
|
||||
CANCELLED,
|
||||
|
||||
/**
|
||||
* Command is not registered, it is also the default result type.
|
||||
*/
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static CommandResult of(@NotNull Type type, @NotNull String input) {
|
||||
CommandResult result = new CommandResult();
|
||||
result.type = type;
|
||||
result.input = input;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
128
src/main/java/net/minestom/server/command/builder/NodeMaker.java
Normal file
128
src/main/java/net/minestom/server/command/builder/NodeMaker.java
Normal file
@ -0,0 +1,128 @@
|
||||
package net.minestom.server.command.builder;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class NodeMaker {
|
||||
|
||||
private final List<ConfiguredNodes> configuredNodes = new ArrayList<>(2);
|
||||
private final List<DeclareCommandsPacket.Node[]> nodes = new ArrayList<>(2);
|
||||
private final Object2IntMap<DeclareCommandsPacket.Node> nodeIdsMap = new Object2IntOpenHashMap<>();
|
||||
|
||||
private Rule rule;
|
||||
private int ruleCount;
|
||||
|
||||
public NodeMaker(@NotNull DeclareCommandsPacket.Node[] commandNodes){
|
||||
addNodes(commandNodes);
|
||||
}
|
||||
|
||||
public ConfiguredNodes getLatestConfiguredNodes() {
|
||||
if (configuredNodes.isEmpty())
|
||||
return null;
|
||||
return configuredNodes.get(configuredNodes.size() - 1);
|
||||
}
|
||||
|
||||
public DeclareCommandsPacket.Node[] getLatestNodes() {
|
||||
ConfiguredNodes configuredNodes = getLatestConfiguredNodes();
|
||||
return configuredNodes != null ? configuredNodes.nodes : null;
|
||||
}
|
||||
|
||||
public int getNodesCount() {
|
||||
return nodes.size();
|
||||
}
|
||||
|
||||
public void addNodes(@NotNull DeclareCommandsPacket.Node[] nodes) {
|
||||
Options options = null;
|
||||
if (rule != null) {
|
||||
options = rule.listen(nodes, ruleCount++);
|
||||
}
|
||||
if (options == null) {
|
||||
options = new Options();
|
||||
}
|
||||
this.configuredNodes.add(ConfiguredNodes.of(nodes, options));
|
||||
this.nodes.add(nodes);
|
||||
}
|
||||
|
||||
public void setRule(@NotNull Rule rule) {
|
||||
this.rule = rule;
|
||||
}
|
||||
|
||||
public void resetRule() {
|
||||
this.rule = null;
|
||||
this.ruleCount = 0;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<ConfiguredNodes> getConfiguredNodes() {
|
||||
return configuredNodes;
|
||||
}
|
||||
|
||||
public List<DeclareCommandsPacket.Node[]> getNodes() {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Object2IntMap<DeclareCommandsPacket.Node> getNodeIdsMap() {
|
||||
return nodeIdsMap;
|
||||
}
|
||||
|
||||
public static class ConfiguredNodes {
|
||||
private DeclareCommandsPacket.Node[] nodes;
|
||||
private Options options;
|
||||
|
||||
private static ConfiguredNodes of(DeclareCommandsPacket.Node[] nodes, Options options) {
|
||||
ConfiguredNodes configuredNodes = new ConfiguredNodes();
|
||||
configuredNodes.nodes = nodes;
|
||||
configuredNodes.options = options;
|
||||
return configuredNodes;
|
||||
}
|
||||
|
||||
public DeclareCommandsPacket.Node[] getNodes() {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
public Options getOptions() {
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
public interface Rule {
|
||||
@Nullable
|
||||
Options listen(DeclareCommandsPacket.Node[] nodes, int count);
|
||||
}
|
||||
|
||||
public static class Options {
|
||||
|
||||
private boolean updateLastNode = true;
|
||||
private DeclareCommandsPacket.Node[] previousNodes;
|
||||
|
||||
public static Options init() {
|
||||
return new Options();
|
||||
}
|
||||
|
||||
public boolean shouldUpdateLastNode() {
|
||||
return updateLastNode;
|
||||
}
|
||||
|
||||
public Options updateLastNode(boolean updateLastNode) {
|
||||
this.updateLastNode = updateLastNode;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DeclareCommandsPacket.Node[] getPreviousNodes() {
|
||||
return previousNodes;
|
||||
}
|
||||
|
||||
public Options setPreviousNodes(DeclareCommandsPacket.Node[] previousNodes) {
|
||||
this.previousNodes = previousNodes;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
package net.minestom.server.command.builder;
|
||||
|
||||
import net.minestom.server.command.CommandSender;
|
||||
import net.minestom.server.command.builder.condition.CommandCondition;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Represents a {@link Command} ready to be executed (already parsed).
|
||||
*/
|
||||
public class ParsedCommand {
|
||||
|
||||
// Command
|
||||
protected Command command;
|
||||
|
||||
// Command Executor
|
||||
protected CommandSyntax syntax;
|
||||
|
||||
protected CommandExecutor executor;
|
||||
protected Arguments arguments;
|
||||
|
||||
// Argument Callback
|
||||
protected ArgumentCallback callback;
|
||||
protected ArgumentSyntaxException argumentSyntaxException;
|
||||
|
||||
/**
|
||||
* Executes the command for the given source.
|
||||
* <p>
|
||||
* The command will not be executed if {@link Command#getCondition()}
|
||||
* is not validated.
|
||||
*
|
||||
* @param source the command source
|
||||
* @param commandString the command string
|
||||
* @return the command data, null if none
|
||||
*/
|
||||
@Nullable
|
||||
public CommandData execute(@NotNull CommandSender source, @NotNull String commandString) {
|
||||
// Global listener
|
||||
command.globalListener(source, arguments, commandString);
|
||||
// Command condition check
|
||||
final CommandCondition condition = command.getCondition();
|
||||
if (condition != null) {
|
||||
final boolean result = condition.canUse(source, commandString);
|
||||
if (!result)
|
||||
return null;
|
||||
}
|
||||
// Condition is respected
|
||||
if (executor != null) {
|
||||
// An executor has been found
|
||||
|
||||
if (syntax != null) {
|
||||
// The executor is from a syntax
|
||||
final CommandCondition commandCondition = syntax.getCommandCondition();
|
||||
if (commandCondition == null || commandCondition.canUse(source, commandString)) {
|
||||
arguments.retrieveDefaultValues(syntax.getDefaultValuesMap());
|
||||
executor.apply(source, arguments);
|
||||
}
|
||||
} else {
|
||||
// The executor is probably the default one
|
||||
executor.apply(source, arguments);
|
||||
}
|
||||
} else if (callback != null && argumentSyntaxException != null) {
|
||||
// No syntax has been validated but the faulty argument with a callback has been found
|
||||
// Execute the faulty argument callback
|
||||
callback.apply(source, argumentSyntaxException);
|
||||
}
|
||||
|
||||
if (arguments == null) {
|
||||
// Argument callbacks cannot return data
|
||||
return null;
|
||||
}
|
||||
|
||||
return arguments.getReturnData();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static ParsedCommand withDefaultExecutor(@NotNull Command command) {
|
||||
ParsedCommand parsedCommand = new ParsedCommand();
|
||||
parsedCommand.command = command;
|
||||
parsedCommand.executor = command.getDefaultExecutor();
|
||||
parsedCommand.arguments = new Arguments();
|
||||
return parsedCommand;
|
||||
}
|
||||
|
||||
}
|
@ -3,7 +3,9 @@ package net.minestom.server.command.builder.arguments;
|
||||
import net.minestom.server.command.builder.ArgumentCallback;
|
||||
import net.minestom.server.command.builder.Command;
|
||||
import net.minestom.server.command.builder.CommandExecutor;
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@ -69,6 +71,32 @@ public abstract class Argument<T> {
|
||||
@NotNull
|
||||
public abstract T parse(@NotNull String input) throws ArgumentSyntaxException;
|
||||
|
||||
/**
|
||||
* Turns the argument into a list of nodes for command dispatching. Make sure to set the Node's parser.
|
||||
*
|
||||
* @param nodeMaker helper object used to create and modify nodes
|
||||
* @param executable true if this will be the last argument, false otherwise
|
||||
*/
|
||||
public abstract void processNodes(@NotNull NodeMaker nodeMaker, boolean executable);
|
||||
|
||||
/**
|
||||
* Builds an argument node.
|
||||
*
|
||||
* @param argument the argument
|
||||
* @param executable true if this will be the last argument, false otherwise
|
||||
* @return the created {@link DeclareCommandsPacket.Node}
|
||||
*/
|
||||
@NotNull
|
||||
protected static DeclareCommandsPacket.Node simpleArgumentNode(@NotNull Argument<?> argument,
|
||||
boolean executable, boolean redirect, boolean suggestion) {
|
||||
DeclareCommandsPacket.Node argumentNode = new DeclareCommandsPacket.Node();
|
||||
|
||||
argumentNode.flags = DeclareCommandsPacket.getFlag(DeclareCommandsPacket.NodeType.ARGUMENT, executable, redirect, suggestion);
|
||||
argumentNode.name = argument.getId();
|
||||
|
||||
return argumentNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ID of the argument, showed in-game above the chat bar
|
||||
* and used to retrieve the data when the command is parsed in {@link net.minestom.server.command.builder.Arguments}.
|
||||
@ -166,4 +194,18 @@ public abstract class Argument<T> {
|
||||
return callback != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
Argument<?> argument = (Argument<?>) o;
|
||||
|
||||
return id.equals(argument.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id.hashCode();
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package net.minestom.server.command.builder.arguments;
|
||||
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@ -27,4 +29,12 @@ public class ArgumentBoolean extends Argument<Boolean> {
|
||||
throw new ArgumentSyntaxException("Not a boolean", input, NOT_BOOLEAN_ERROR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
|
||||
argumentNode.parser = "brigadier:bool";
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,41 @@
|
||||
package net.minestom.server.command.builder.arguments;
|
||||
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.command.builder.CommandDispatcher;
|
||||
import net.minestom.server.command.builder.CommandResult;
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ArgumentCommand extends Argument<CommandResult> {
|
||||
|
||||
public static final int INVALID_COMMAND_ERROR = 1;
|
||||
|
||||
public ArgumentCommand(@NotNull String id) {
|
||||
super(id, true, true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public CommandResult parse(@NotNull String input) throws ArgumentSyntaxException {
|
||||
CommandDispatcher dispatcher = MinecraftServer.getCommandManager().getDispatcher();
|
||||
CommandResult result = dispatcher.parse(input);
|
||||
|
||||
if (result.getType() != CommandResult.Type.SUCCESS)
|
||||
throw new ArgumentSyntaxException("Invalid command", input, INVALID_COMMAND_ERROR);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
final DeclareCommandsPacket.Node[] lastNodes = nodeMaker.getLatestNodes();
|
||||
|
||||
// FIXME check if lastNodes is null
|
||||
for (DeclareCommandsPacket.Node node : lastNodes) {
|
||||
node.flags |= 0x08; // Redirection mask
|
||||
node.redirectedNode = 0; // Redirect to root
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
package net.minestom.server.command.builder.arguments;
|
||||
|
||||
import net.minestom.server.command.CommandSender;
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.utils.callback.validator.StringArrayValidator;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -36,6 +38,19 @@ public class ArgumentDynamicStringArray extends Argument<String[]> {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, true);
|
||||
|
||||
argumentNode.parser = "brigadier:string";
|
||||
argumentNode.properties = packetWriter -> {
|
||||
packetWriter.writeVarInt(2); // Greedy phrase
|
||||
};
|
||||
argumentNode.suggestionsType = "minecraft:ask_server";
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the dynamic restriction of this dynamic argument.
|
||||
* <p>
|
||||
|
@ -1,8 +1,10 @@
|
||||
package net.minestom.server.command.builder.arguments;
|
||||
|
||||
import net.minestom.server.command.CommandSender;
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.arguments.minecraft.SuggestionType;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.utils.callback.validator.StringValidator;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -43,6 +45,21 @@ public class ArgumentDynamicWord extends Argument<String> {
|
||||
return input;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, true);
|
||||
|
||||
final SuggestionType suggestionType = this.getSuggestionType();
|
||||
|
||||
argumentNode.parser = "brigadier:string";
|
||||
argumentNode.properties = packetWriter -> {
|
||||
packetWriter.writeVarInt(0); // Single word
|
||||
};
|
||||
argumentNode.suggestionsType = suggestionType.getIdentifier();
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the suggestion type of this argument.
|
||||
* <p>
|
||||
|
@ -0,0 +1,47 @@
|
||||
package net.minestom.server.command.builder.arguments;
|
||||
|
||||
import net.minestom.server.command.builder.Arguments;
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.command.builder.parser.CommandParser;
|
||||
import net.minestom.server.command.builder.parser.ValidSyntaxHolder;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ArgumentGroup extends Argument<Arguments> {
|
||||
|
||||
public static final int INVALID_ARGUMENTS_ERROR = 1;
|
||||
|
||||
private final Argument<?>[] group;
|
||||
|
||||
public ArgumentGroup(@NotNull String id, @NotNull Argument<?>... group) {
|
||||
super(id, true, false);
|
||||
this.group = group;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Arguments parse(@NotNull String input) throws ArgumentSyntaxException {
|
||||
List<ValidSyntaxHolder> validSyntaxes = new ArrayList<>();
|
||||
CommandParser.parse(null, group, input.split(StringUtils.SPACE), validSyntaxes, null);
|
||||
|
||||
Arguments arguments = new Arguments();
|
||||
CommandParser.findMostCorrectSyntax(validSyntaxes, arguments);
|
||||
if (validSyntaxes.isEmpty()) {
|
||||
throw new ArgumentSyntaxException("Invalid arguments", input, INVALID_ARGUMENTS_ERROR);
|
||||
}
|
||||
|
||||
return arguments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
for (int i = 0; i < group.length; i++) {
|
||||
final boolean isLast = i == group.length - 1;
|
||||
group[i].processNodes(nodeMaker, executable && isLast);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package net.minestom.server.command.builder.arguments;
|
||||
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ArgumentLiteral extends Argument<String> {
|
||||
|
||||
public static final int INVALID_VALUE_ERROR = 1;
|
||||
|
||||
public ArgumentLiteral(@NotNull String id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String parse(@NotNull String input) throws ArgumentSyntaxException {
|
||||
if (!input.equals(getId()))
|
||||
throw new ArgumentSyntaxException("Invalid literal value", input, INVALID_VALUE_ERROR);
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node literalNode = new DeclareCommandsPacket.Node();
|
||||
literalNode.flags = DeclareCommandsPacket.getFlag(DeclareCommandsPacket.NodeType.LITERAL,
|
||||
executable, false, false);
|
||||
literalNode.name = getId();
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{literalNode});
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package net.minestom.server.command.builder.arguments;
|
||||
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class ArgumentLoop<T> extends Argument<List<T>> {
|
||||
|
||||
public static final int INVALID_INPUT_ERROR = 1;
|
||||
|
||||
private final List<Argument<T>> arguments = new ArrayList<>();
|
||||
|
||||
@SafeVarargs
|
||||
public ArgumentLoop(@NotNull String id, @NotNull Argument<T>... arguments) {
|
||||
super(id, true, true);
|
||||
this.arguments.addAll(Arrays.asList(arguments));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<T> parse(@NotNull String input) throws ArgumentSyntaxException {
|
||||
List<T> result = new ArrayList<>();
|
||||
final String[] split = input.split(StringUtils.SPACE);
|
||||
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
boolean success = false;
|
||||
for (String s : split) {
|
||||
builder.append(s);
|
||||
|
||||
for (Argument<T> argument : arguments) {
|
||||
try {
|
||||
final String inputString = builder.toString();
|
||||
final T value = argument.parse(inputString);
|
||||
success = true;
|
||||
result.add(value);
|
||||
break;
|
||||
} catch (ArgumentSyntaxException ignored) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
builder.setLength(0); // Clear
|
||||
} else {
|
||||
builder.append(StringUtils.SPACE);
|
||||
}
|
||||
}
|
||||
|
||||
if (result.isEmpty() || !success) {
|
||||
throw new ArgumentSyntaxException("Invalid loop, there is no valid argument found", input, INVALID_INPUT_ERROR);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node[] latestNodes = nodeMaker.getLatestNodes();
|
||||
|
||||
for (DeclareCommandsPacket.Node latestNode : latestNodes) {
|
||||
final int id = nodeMaker.getNodeIdsMap().getInt(latestNode);
|
||||
|
||||
for (Argument<T> argument : arguments) {
|
||||
DeclareCommandsPacket.Node[] latestCache = nodeMaker.getLatestNodes();
|
||||
argument.processNodes(nodeMaker, executable);
|
||||
|
||||
NodeMaker.ConfiguredNodes configuredNodes = nodeMaker.getLatestConfiguredNodes();
|
||||
// For the next loop argument to start at the same place
|
||||
configuredNodes.getOptions().setPreviousNodes(latestCache);
|
||||
for (DeclareCommandsPacket.Node lastArgumentNode : configuredNodes.getNodes()) {
|
||||
lastArgumentNode.flags |= 0x08;
|
||||
lastArgumentNode.redirectedNode = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
package net.minestom.server.command.builder.arguments;
|
||||
|
||||
import io.netty.util.internal.StringUtil;
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import org.apache.commons.text.StringEscapeUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -11,6 +14,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
*/
|
||||
public class ArgumentString extends Argument<String> {
|
||||
|
||||
public static final char BACKSLASH = '\\';
|
||||
|
||||
public static final int QUOTE_ERROR = 1;
|
||||
|
||||
public ArgumentString(String id) {
|
||||
@ -23,12 +28,24 @@ public class ArgumentString extends Argument<String> {
|
||||
return staticParse(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
|
||||
|
||||
argumentNode.parser = "brigadier:string";
|
||||
argumentNode.properties = packetWriter -> {
|
||||
packetWriter.writeVarInt(1); // Quotable phrase
|
||||
};
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String staticParse(@NotNull String input) throws ArgumentSyntaxException {
|
||||
// Check if value start and end with quote
|
||||
final char first = input.charAt(0);
|
||||
final char last = input.charAt(input.length() - 1);
|
||||
final boolean quote = first == '\"' && last == '\"';
|
||||
final boolean quote = first == StringUtil.DOUBLE_QUOTE && last == StringUtil.DOUBLE_QUOTE;
|
||||
if (!quote)
|
||||
throw new ArgumentSyntaxException("String argument needs to start and end with quotes", input, QUOTE_ERROR);
|
||||
|
||||
@ -38,9 +55,9 @@ public class ArgumentString extends Argument<String> {
|
||||
// Verify backslashes
|
||||
for (int i = 1; i < input.length(); i++) {
|
||||
final char c = input.charAt(i);
|
||||
if (c == '\"') {
|
||||
if (c == StringUtil.DOUBLE_QUOTE) {
|
||||
final char lastChar = input.charAt(i - 1);
|
||||
if (lastChar != '\\') {
|
||||
if (lastChar != BACKSLASH) {
|
||||
throw new ArgumentSyntaxException("Non-escaped quote", input, QUOTE_ERROR);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.minestom.server.command.builder.arguments;
|
||||
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -19,7 +20,19 @@ public class ArgumentStringArray extends Argument<String[]> {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String[] parse(@NotNull String input) throws ArgumentSyntaxException {
|
||||
public String[] parse(@NotNull String input) {
|
||||
return input.split(Pattern.quote(StringUtils.SPACE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
|
||||
|
||||
argumentNode.parser = "brigadier:string";
|
||||
argumentNode.properties = packetWriter -> {
|
||||
packetWriter.writeVarInt(2); // Greedy phrase
|
||||
};
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,19 @@ import org.jetbrains.annotations.NotNull;
|
||||
*/
|
||||
public class ArgumentType {
|
||||
|
||||
public static ArgumentLiteral Literal(@NotNull String id) {
|
||||
return new ArgumentLiteral(id);
|
||||
}
|
||||
|
||||
public static ArgumentGroup Group(@NotNull String id, @NotNull Argument<?>... arguments) {
|
||||
return new ArgumentGroup(id, arguments);
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <T> ArgumentLoop<T> Loop(@NotNull String id, @NotNull Argument<T>... arguments) {
|
||||
return new ArgumentLoop<>(id, arguments);
|
||||
}
|
||||
|
||||
public static ArgumentBoolean Boolean(@NotNull String id) {
|
||||
return new ArgumentBoolean(id);
|
||||
}
|
||||
@ -62,6 +75,10 @@ public class ArgumentType {
|
||||
return new ArgumentDynamicStringArray(id);
|
||||
}
|
||||
|
||||
public static ArgumentCommand Command(@NotNull String id) {
|
||||
return new ArgumentCommand(id);
|
||||
}
|
||||
|
||||
// Minecraft specific arguments
|
||||
|
||||
public static ArgumentColor Color(@NotNull String id) {
|
||||
|
@ -1,12 +1,14 @@
|
||||
package net.minestom.server.command.builder.arguments;
|
||||
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Represents a single word in the command.
|
||||
@ -68,6 +70,43 @@ public class ArgumentWord extends Argument<String> {
|
||||
return input;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
|
||||
// Add the single word properties + parser
|
||||
final Consumer<DeclareCommandsPacket.Node> wordConsumer = node -> {
|
||||
node.parser = "brigadier:string";
|
||||
node.properties = packetWriter -> {
|
||||
packetWriter.writeVarInt(0); // Single word
|
||||
};
|
||||
};
|
||||
|
||||
final boolean hasRestriction = this.hasRestrictions();
|
||||
if (hasRestriction) {
|
||||
|
||||
// Create a primitive array for mapping
|
||||
DeclareCommandsPacket.Node[] nodes = new DeclareCommandsPacket.Node[this.getRestrictions().length];
|
||||
|
||||
// Create a node for each restrictions as literal
|
||||
for (int i = 0; i < nodes.length; i++) {
|
||||
DeclareCommandsPacket.Node argumentNode = new DeclareCommandsPacket.Node();
|
||||
|
||||
argumentNode.flags = DeclareCommandsPacket.getFlag(DeclareCommandsPacket.NodeType.LITERAL,
|
||||
executable, false, false);
|
||||
argumentNode.name = this.getRestrictions()[i];
|
||||
wordConsumer.accept(argumentNode);
|
||||
nodes[i] = argumentNode;
|
||||
|
||||
}
|
||||
nodeMaker.addNodes(nodes);
|
||||
} else {
|
||||
// Can be any word, add only one argument node
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
|
||||
wordConsumer.accept(argumentNode);
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if this argument allow complete freedom in the word choice or if a list has been defined.
|
||||
*
|
||||
@ -86,17 +125,4 @@ public class ArgumentWord extends Argument<String> {
|
||||
public String[] getRestrictions() {
|
||||
return restrictions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ArgumentWord that = (ArgumentWord) o;
|
||||
return Arrays.equals(restrictions, that.restrictions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(restrictions);
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
package net.minestom.server.command.builder.arguments.minecraft;
|
||||
|
||||
import net.minestom.server.chat.ChatColor;
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.arguments.Argument;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@ -27,4 +29,12 @@ public class ArgumentColor extends Argument<ChatColor> {
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
|
||||
argumentNode.parser = "minecraft:color";
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
package net.minestom.server.command.builder.arguments.minecraft;
|
||||
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.arguments.Argument;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.entity.EntityType;
|
||||
import net.minestom.server.entity.GameMode;
|
||||
import net.minestom.server.network.ConnectionManager;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.registry.Registries;
|
||||
import net.minestom.server.utils.entity.EntityFinder;
|
||||
import net.minestom.server.utils.math.IntRange;
|
||||
@ -14,8 +14,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
// TODO
|
||||
|
||||
/**
|
||||
* Represents the target selector argument.
|
||||
* https://minecraft.gamepedia.com/Commands#Target_selectors
|
||||
@ -27,23 +25,25 @@ public class ArgumentEntity extends Argument<EntityFinder> {
|
||||
public static final int ONLY_PLAYERS_ERROR = -4;
|
||||
public static final int INVALID_ARGUMENT_NAME = -5;
|
||||
public static final int INVALID_ARGUMENT_VALUE = -6;
|
||||
private static final ConnectionManager CONNECTION_MANAGER = MinecraftServer.getConnectionManager();
|
||||
private static final List<String> selectorVariables = Arrays.asList("@p", "@r", "@a", "@e", "@s");
|
||||
private static final List<String> playersOnlySelector = Arrays.asList("@p", "@r", "@a", "@s");
|
||||
private static final List<String> singleOnlySelector = Arrays.asList("@p", "@r", "@s");
|
||||
|
||||
private static final String SELECTOR_PREFIX = "@";
|
||||
private static final List<String> SELECTOR_VARIABLES = Arrays.asList("@p", "@r", "@a", "@e", "@s");
|
||||
private static final List<String> PLAYERS_ONLY_SELECTOR = Arrays.asList("@p", "@r", "@a", "@s");
|
||||
private static final List<String> SINGLE_ONLY_SELECTOR = Arrays.asList("@p", "@r", "@s");
|
||||
// List with all the valid arguments
|
||||
private static final List<String> validArguments = Arrays.asList(
|
||||
private static final List<String> VALID_ARGUMENTS = Arrays.asList(
|
||||
"x", "y", "z",
|
||||
"distance", "dx", "dy", "dz",
|
||||
"scores", "tag", "team", "limit", "sort", "level", "gamemode", "name",
|
||||
"x_rotation", "y_rotation", "type", "nbt", "advancements", "predicate");
|
||||
|
||||
// List with all the easily parsable arguments which only require reading until a specific character (comma)
|
||||
private static final List<String> simpleArguments = Arrays.asList(
|
||||
private static final List<String> SIMPLE_ARGUMENTS = Arrays.asList(
|
||||
"x", "y", "z",
|
||||
"distance", "dx", "dy", "dz",
|
||||
"scores", "tag", "team", "limit", "sort", "level", "gamemode",
|
||||
"x_rotation", "y_rotation", "type", "advancements", "predicate");
|
||||
|
||||
private boolean onlySingleEntity;
|
||||
private boolean onlyPlayers;
|
||||
|
||||
@ -67,38 +67,54 @@ public class ArgumentEntity extends Argument<EntityFinder> {
|
||||
return staticParse(input, onlySingleEntity, onlyPlayers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
|
||||
argumentNode.parser = "minecraft:entity";
|
||||
argumentNode.properties = packetWriter -> {
|
||||
byte mask = 0;
|
||||
if (this.isOnlySingleEntity()) {
|
||||
mask += 1;
|
||||
}
|
||||
if (this.isOnlyPlayers()) {
|
||||
mask += 2;
|
||||
}
|
||||
packetWriter.writeByte(mask);
|
||||
};
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static EntityFinder staticParse(@NotNull String input,
|
||||
boolean onlySingleEntity, boolean onlyPlayers) throws ArgumentSyntaxException {
|
||||
// Check for raw player name
|
||||
if (input.length() <= 16) {
|
||||
if (CONNECTION_MANAGER.getPlayer(input) != null) {
|
||||
return new EntityFinder()
|
||||
.setTargetSelector(EntityFinder.TargetSelector.ALL_PLAYERS)
|
||||
.setName(input, EntityFinder.ToggleableType.INCLUDE);
|
||||
}
|
||||
if (input.length() <= 16 && !input.contains(SELECTOR_PREFIX)) {
|
||||
return new EntityFinder()
|
||||
.setTargetSelector(EntityFinder.TargetSelector.ALL_PLAYERS)
|
||||
.setName(input, EntityFinder.ToggleableType.INCLUDE);
|
||||
}
|
||||
|
||||
// The minimum size is always 0 (for the selector variable, ex: @p)
|
||||
// The minimum size is always 2 (for the selector variable, ex: @p)
|
||||
if (input.length() < 2)
|
||||
throw new ArgumentSyntaxException("Length needs to be > 1", input, INVALID_SYNTAX);
|
||||
|
||||
// The target selector variable always start by '@'
|
||||
if (!input.startsWith("@"))
|
||||
if (!input.startsWith(SELECTOR_PREFIX))
|
||||
throw new ArgumentSyntaxException("Target selector needs to start with @", input, INVALID_SYNTAX);
|
||||
|
||||
final String selectorVariable = input.substring(0, 2);
|
||||
|
||||
// Check if the selector variable used exists
|
||||
if (!selectorVariables.contains(selectorVariable))
|
||||
if (!SELECTOR_VARIABLES.contains(selectorVariable))
|
||||
throw new ArgumentSyntaxException("Invalid selector variable", input, INVALID_SYNTAX);
|
||||
|
||||
// Check if it should only select single entity and if the selector variable valid the condition
|
||||
if (onlySingleEntity && !singleOnlySelector.contains(selectorVariable))
|
||||
if (onlySingleEntity && !SINGLE_ONLY_SELECTOR.contains(selectorVariable))
|
||||
throw new ArgumentSyntaxException("Argument requires only a single entity", input, ONLY_SINGLE_ENTITY_ERROR);
|
||||
|
||||
// Check if it should only select players and if the selector variable valid the condition
|
||||
if (onlyPlayers && !playersOnlySelector.contains(selectorVariable))
|
||||
if (onlyPlayers && !PLAYERS_ONLY_SELECTOR.contains(selectorVariable))
|
||||
throw new ArgumentSyntaxException("Argument requires only players", input, ONLY_PLAYERS_ERROR);
|
||||
|
||||
// Create the EntityFinder which will be used for the rest of the parsing
|
||||
@ -134,7 +150,7 @@ public class ArgumentEntity extends Argument<EntityFinder> {
|
||||
// Replace all unnecessary spaces
|
||||
currentArgument = currentArgument.trim();
|
||||
|
||||
if (!validArguments.contains(currentArgument))
|
||||
if (!VALID_ARGUMENTS.contains(currentArgument))
|
||||
throw new ArgumentSyntaxException("Argument name '" + currentArgument + "' does not exist", input, INVALID_ARGUMENT_NAME);
|
||||
|
||||
i = parseArgument(entityFinder, currentArgument, input, structureData, i);
|
||||
@ -152,7 +168,7 @@ public class ArgumentEntity extends Argument<EntityFinder> {
|
||||
@NotNull String input,
|
||||
@NotNull String structureData, int beginIndex) throws ArgumentSyntaxException {
|
||||
final char comma = ',';
|
||||
final boolean isSimple = simpleArguments.contains(argumentName);
|
||||
final boolean isSimple = SIMPLE_ARGUMENTS.contains(argumentName);
|
||||
|
||||
int finalIndex = beginIndex + 1;
|
||||
StringBuilder valueBuilder = new StringBuilder();
|
||||
|
@ -1,6 +1,8 @@
|
||||
package net.minestom.server.command.builder.arguments.minecraft;
|
||||
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.utils.math.FloatRange;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -52,4 +54,12 @@ public class ArgumentFloatRange extends ArgumentRange<FloatRange> {
|
||||
throw new ArgumentSyntaxException("Invalid number", input, FORMAT_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
|
||||
argumentNode.parser = "minecraft:float_range";
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package net.minestom.server.command.builder.arguments.minecraft;
|
||||
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.utils.math.IntRange;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -57,4 +59,12 @@ public class ArgumentIntRange extends ArgumentRange<IntRange> {
|
||||
throw new ArgumentSyntaxException("Invalid number", input, FORMAT_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
|
||||
argumentNode.parser = "minecraft:int_range";
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
package net.minestom.server.command.builder.arguments.minecraft;
|
||||
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.arguments.Argument;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.item.Material;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.registry.Registries;
|
||||
import net.minestom.server.utils.NBTUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -61,4 +63,12 @@ public class ArgumentItemStack extends Argument<ItemStack> {
|
||||
return itemStack;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
|
||||
argumentNode.parser = "minecraft:item_stack";
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
package net.minestom.server.command.builder.arguments.minecraft;
|
||||
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.arguments.Argument;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBT;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
@ -37,4 +39,12 @@ public class ArgumentNbtCompoundTag extends Argument<NBTCompound> {
|
||||
throw new ArgumentSyntaxException("NBTCompound is invalid", input, INVALID_NBT);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
|
||||
argumentNode.parser = "minecraft:nbt_compound_tag";
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
package net.minestom.server.command.builder.arguments.minecraft;
|
||||
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.arguments.Argument;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBT;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTException;
|
||||
@ -33,4 +35,12 @@ public class ArgumentNbtTag extends Argument<NBT> {
|
||||
throw new ArgumentSyntaxException("Invalid NBT", input, INVALID_NBT);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
|
||||
argumentNode.parser = "minecraft:nbt_tag";
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,10 @@ package net.minestom.server.command.builder.arguments.minecraft;
|
||||
|
||||
import it.unimi.dsi.fastutil.chars.CharArrayList;
|
||||
import it.unimi.dsi.fastutil.chars.CharList;
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.arguments.Argument;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.utils.time.TimeUnit;
|
||||
import net.minestom.server.utils.time.UpdateOption;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -28,27 +30,40 @@ public class ArgumentTime extends Argument<UpdateOption> {
|
||||
@Override
|
||||
public UpdateOption parse(@NotNull String input) throws ArgumentSyntaxException {
|
||||
final char lastChar = input.charAt(input.length() - 1);
|
||||
if (!SUFFIXES.contains(lastChar))
|
||||
throw new ArgumentSyntaxException("Time format is invalid", input, INVALID_TIME_FORMAT);
|
||||
|
||||
// Remove last char
|
||||
input = input.substring(0, input.length() - 1);
|
||||
try {
|
||||
// Check if value is a number
|
||||
final int time = Integer.parseInt(input);
|
||||
TimeUnit timeUnit;
|
||||
if (Character.isDigit(lastChar))
|
||||
timeUnit = TimeUnit.TICK;
|
||||
else if (SUFFIXES.contains(lastChar)) {
|
||||
input = input.substring(0, input.length() - 1);
|
||||
|
||||
TimeUnit timeUnit = null;
|
||||
if (lastChar == 'd') {
|
||||
timeUnit = TimeUnit.DAY;
|
||||
} else if (lastChar == 's') {
|
||||
timeUnit = TimeUnit.SECOND;
|
||||
} else if (lastChar == 't') {
|
||||
timeUnit = TimeUnit.TICK;
|
||||
} else {
|
||||
throw new ArgumentSyntaxException("Time needs to have the unit d, s, t, or none", input, NO_NUMBER);
|
||||
}
|
||||
} else
|
||||
throw new ArgumentSyntaxException("Time needs to have a unit", input, NO_NUMBER);
|
||||
|
||||
try {
|
||||
// Check if value is a number
|
||||
final int time = Integer.parseInt(input);
|
||||
return new UpdateOption(time, timeUnit);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ArgumentSyntaxException("Time needs to be a number", input, NO_NUMBER);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
|
||||
argumentNode.parser = "minecraft:time";
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ public enum SuggestionType {
|
||||
AVAILABLE_SOUNDS("minecraft:available_sounds"),
|
||||
SUMMONABLE_ENTITIES("minecraft:summonable_entities");
|
||||
|
||||
private String identifier;
|
||||
private final String identifier;
|
||||
|
||||
SuggestionType(@NotNull String identifier) {
|
||||
this.identifier = identifier;
|
||||
|
@ -1,6 +1,8 @@
|
||||
package net.minestom.server.command.builder.arguments.minecraft.registry;
|
||||
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.registry.Registries;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -14,4 +16,12 @@ public class ArgumentBlockState extends ArgumentRegistry<Block> {
|
||||
public Block getRegistry(@NotNull String value) {
|
||||
return Registries.getBlock(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
|
||||
argumentNode.parser = "minecraft:block_state";
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package net.minestom.server.command.builder.arguments.minecraft.registry;
|
||||
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.item.Enchantment;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.registry.Registries;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -17,4 +19,12 @@ public class ArgumentEnchantment extends ArgumentRegistry<Enchantment> {
|
||||
public Enchantment getRegistry(@NotNull String value) {
|
||||
return Registries.getEnchantment(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
|
||||
argumentNode.parser = "minecraft:item_enchantment";
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package net.minestom.server.command.builder.arguments.minecraft.registry;
|
||||
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.entity.EntityType;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.registry.Registries;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -17,4 +19,12 @@ public class ArgumentEntityType extends ArgumentRegistry<EntityType> {
|
||||
public EntityType getRegistry(@NotNull String value) {
|
||||
return Registries.getEntityType(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
|
||||
argumentNode.parser = "minecraft:entity_summon";
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package net.minestom.server.command.builder.arguments.minecraft.registry;
|
||||
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.particle.Particle;
|
||||
import net.minestom.server.registry.Registries;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -17,4 +19,12 @@ public class ArgumentParticle extends ArgumentRegistry<Particle> {
|
||||
public Particle getRegistry(@NotNull String value) {
|
||||
return Registries.getParticle(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
|
||||
argumentNode.parser = "minecraft:particle";
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package net.minestom.server.command.builder.arguments.minecraft.registry;
|
||||
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.potion.PotionEffect;
|
||||
import net.minestom.server.registry.Registries;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -17,4 +19,12 @@ public class ArgumentPotionEffect extends ArgumentRegistry<PotionEffect> {
|
||||
public PotionEffect getRegistry(@NotNull String value) {
|
||||
return Registries.getPotionEffect(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
|
||||
argumentNode.parser = "minecraft:mob_effect";
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package net.minestom.server.command.builder.arguments.number;
|
||||
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ArgumentDouble extends ArgumentNumber<Double> {
|
||||
@ -40,4 +42,20 @@ public class ArgumentDouble extends ArgumentNumber<Double> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
|
||||
|
||||
argumentNode.parser = "brigadier:double";
|
||||
argumentNode.properties = packetWriter -> {
|
||||
packetWriter.writeByte(getNumberProperties());
|
||||
if (this.hasMin())
|
||||
packetWriter.writeDouble(this.getMin());
|
||||
if (this.hasMax())
|
||||
packetWriter.writeDouble(this.getMax());
|
||||
};
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package net.minestom.server.command.builder.arguments.number;
|
||||
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ArgumentFloat extends ArgumentNumber<Float> {
|
||||
@ -40,4 +42,20 @@ public class ArgumentFloat extends ArgumentNumber<Float> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
|
||||
|
||||
argumentNode.parser = "brigadier:float";
|
||||
argumentNode.properties = packetWriter -> {
|
||||
packetWriter.writeByte(getNumberProperties());
|
||||
if (this.hasMin())
|
||||
packetWriter.writeFloat(this.getMin());
|
||||
if (this.hasMax())
|
||||
packetWriter.writeFloat(this.getMax());
|
||||
};
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package net.minestom.server.command.builder.arguments.number;
|
||||
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ArgumentInteger extends ArgumentNumber<Integer> {
|
||||
@ -31,4 +33,20 @@ public class ArgumentInteger extends ArgumentNumber<Integer> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
|
||||
|
||||
argumentNode.parser = "brigadier:integer";
|
||||
argumentNode.properties = packetWriter -> {
|
||||
packetWriter.writeByte(getNumberProperties());
|
||||
if (this.hasMin())
|
||||
packetWriter.writeInt(this.getMin());
|
||||
if (this.hasMax())
|
||||
packetWriter.writeInt(this.getMax());
|
||||
};
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package net.minestom.server.command.builder.arguments.number;
|
||||
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ArgumentLong extends ArgumentNumber<Long> {
|
||||
@ -31,4 +33,22 @@ public class ArgumentLong extends ArgumentNumber<Long> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
|
||||
|
||||
// TODO maybe use ArgumentLiteral/ArgumentWord and impose long restriction server side?
|
||||
|
||||
argumentNode.parser = "brigadier:int";
|
||||
argumentNode.properties = packetWriter -> {
|
||||
packetWriter.writeByte(getNumberProperties());
|
||||
if (this.hasMin())
|
||||
packetWriter.writeInt(this.getMin().intValue());
|
||||
if (this.hasMax())
|
||||
packetWriter.writeInt(this.getMax().intValue());
|
||||
};
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -43,6 +43,20 @@ public abstract class ArgumentNumber<T extends Number> extends Argument<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the byteflag based on the number's min/max existance.
|
||||
*
|
||||
* @return A byteflag for argument specification.
|
||||
*/
|
||||
public byte getNumberProperties() {
|
||||
byte result = 0;
|
||||
if (this.hasMin())
|
||||
result += 1;
|
||||
if (this.hasMax())
|
||||
result += 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets if the argument has a minimum.
|
||||
*
|
||||
|
@ -1,6 +1,8 @@
|
||||
package net.minestom.server.command.builder.arguments.relative;
|
||||
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.utils.BlockPosition;
|
||||
import net.minestom.server.utils.location.RelativeBlockPosition;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@ -78,4 +80,12 @@ public class ArgumentRelativeBlockPosition extends ArgumentRelative<RelativeBloc
|
||||
|
||||
return new RelativeBlockPosition(blockPosition, relativeX, relativeY, relativeZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
|
||||
argumentNode.parser = "minecraft:block_pos";
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package net.minestom.server.command.builder.arguments.relative;
|
||||
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.utils.Vector;
|
||||
import net.minestom.server.utils.location.RelativeVec;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@ -67,4 +69,12 @@ public class ArgumentRelativeVec2 extends ArgumentRelative<RelativeVec> {
|
||||
return new RelativeVec(vector, relativeX, false, relativeZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
|
||||
argumentNode.parser = "minecraft:vec2";
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
package net.minestom.server.command.builder.arguments.relative;
|
||||
|
||||
import net.minestom.server.command.builder.NodeMaker;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
|
||||
import net.minestom.server.utils.Vector;
|
||||
import net.minestom.server.utils.location.RelativeVec;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@ -73,4 +75,12 @@ public class ArgumentRelativeVec3 extends ArgumentRelative<RelativeVec> {
|
||||
|
||||
return new RelativeVec(vector, relativeX, relativeY, relativeZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processNodes(@NotNull NodeMaker nodeMaker, boolean executable) {
|
||||
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(this, executable, false, false);
|
||||
argumentNode.parser = "minecraft:vec3";
|
||||
|
||||
nodeMaker.addNodes(new DeclareCommandsPacket.Node[]{argumentNode});
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,178 @@
|
||||
package net.minestom.server.command.builder.parser;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
|
||||
import net.minestom.server.command.builder.Arguments;
|
||||
import net.minestom.server.command.builder.CommandSyntax;
|
||||
import net.minestom.server.command.builder.arguments.Argument;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class CommandParser {
|
||||
|
||||
public static void parse(@Nullable CommandSyntax syntax, @NotNull Argument<?>[] commandArguments, @NotNull String[] inputArguments,
|
||||
@Nullable List<ValidSyntaxHolder> validSyntaxes,
|
||||
@Nullable Int2ObjectRBTreeMap<CommandSuggestionHolder> syntaxesSuggestions) {
|
||||
final Map<Argument<?>, Object> argsValues = new HashMap<>();
|
||||
|
||||
boolean syntaxCorrect = true;
|
||||
// The current index in the raw command string arguments
|
||||
int splitIndex = 0;
|
||||
|
||||
boolean useRemaining = false;
|
||||
// Check the validity of the arguments...
|
||||
for (int argCount = 0; argCount < commandArguments.length; argCount++) {
|
||||
final boolean lastArgumentIteration = argCount + 1 == commandArguments.length;
|
||||
final Argument<?> argument = commandArguments[argCount];
|
||||
useRemaining = argument.useRemaining();
|
||||
|
||||
// the parsed argument value, null if incorrect
|
||||
Object parsedValue;
|
||||
// the argument exception, null if the input is correct
|
||||
ArgumentSyntaxException argumentSyntaxException = null;
|
||||
// true if the arg is valid, false otherwise
|
||||
boolean correct = false;
|
||||
// the raw string representing the correct argument syntax
|
||||
StringBuilder argValue = new StringBuilder();
|
||||
|
||||
if (useRemaining) {
|
||||
final boolean hasArgs = inputArguments.length > splitIndex;
|
||||
// Verify if there is any string part available
|
||||
if (hasArgs) {
|
||||
// Argument is supposed to take the rest of the command input
|
||||
for (int i = splitIndex; i < inputArguments.length; i++) {
|
||||
final String arg = inputArguments[i];
|
||||
if (argValue.length() > 0)
|
||||
argValue.append(StringUtils.SPACE);
|
||||
argValue.append(arg);
|
||||
}
|
||||
|
||||
final String argValueString = argValue.toString();
|
||||
|
||||
try {
|
||||
parsedValue = argument.parse(argValueString);
|
||||
correct = true;
|
||||
argsValues.put(argument, parsedValue);
|
||||
} catch (ArgumentSyntaxException exception) {
|
||||
argumentSyntaxException = exception;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Argument is either single-word or can accept optional delimited space(s)
|
||||
for (int i = splitIndex; i < inputArguments.length; i++) {
|
||||
final String rawArg = inputArguments[i];
|
||||
|
||||
argValue.append(rawArg);
|
||||
|
||||
final String argValueString = argValue.toString();
|
||||
|
||||
try {
|
||||
parsedValue = argument.parse(argValueString);
|
||||
|
||||
// Prevent quitting the parsing too soon if the argument
|
||||
// does not allow space
|
||||
if (lastArgumentIteration && i + 1 < inputArguments.length) {
|
||||
if (!argument.allowSpace())
|
||||
break;
|
||||
argValue.append(StringUtils.SPACE);
|
||||
continue;
|
||||
}
|
||||
|
||||
correct = true;
|
||||
argsValues.put(argument, parsedValue);
|
||||
splitIndex = i + 1;
|
||||
break;
|
||||
} catch (ArgumentSyntaxException exception) {
|
||||
argumentSyntaxException = exception;
|
||||
|
||||
if (!argument.allowSpace())
|
||||
break;
|
||||
argValue.append(StringUtils.SPACE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!correct) {
|
||||
// Argument is not correct, add it to the syntax suggestion with the number
|
||||
// of correct argument(s) and do not check the next syntax argument
|
||||
syntaxCorrect = false;
|
||||
if (syntaxesSuggestions != null) {
|
||||
CommandSuggestionHolder suggestionHolder = new CommandSuggestionHolder();
|
||||
suggestionHolder.syntax = syntax;
|
||||
suggestionHolder.argumentSyntaxException = argumentSyntaxException;
|
||||
suggestionHolder.argIndex = argCount;
|
||||
syntaxesSuggestions.put(argCount, suggestionHolder);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the syntax to the list of valid syntaxes if correct
|
||||
if (syntaxCorrect) {
|
||||
if (commandArguments.length == argsValues.size() || useRemaining) {
|
||||
if (validSyntaxes != null) {
|
||||
ValidSyntaxHolder validSyntaxHolder = new ValidSyntaxHolder();
|
||||
validSyntaxHolder.syntax = syntax;
|
||||
validSyntaxHolder.argumentsValue = argsValues;
|
||||
|
||||
validSyntaxes.add(validSyntaxHolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves from the valid syntax map the arguments condition result and get the one with the most
|
||||
* valid arguments.
|
||||
*
|
||||
* @param validSyntaxes the list containing all the valid syntaxes
|
||||
* @param executorArgs the recipient of the argument parsed values
|
||||
* @return the command syntax with all of its arguments correct and with the most arguments count, null if not any
|
||||
*/
|
||||
@Nullable
|
||||
public static ValidSyntaxHolder findMostCorrectSyntax(@NotNull List<ValidSyntaxHolder> validSyntaxes,
|
||||
@NotNull Arguments executorArgs) {
|
||||
if (validSyntaxes.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ValidSyntaxHolder finalSyntax = null;
|
||||
int maxArguments = 0;
|
||||
Arguments finalArguments = null;
|
||||
|
||||
for (ValidSyntaxHolder validSyntaxHolder : validSyntaxes) {
|
||||
final Map<Argument<?>, Object> argsValues = validSyntaxHolder.argumentsValue;
|
||||
|
||||
final int argsSize = argsValues.size();
|
||||
|
||||
// Check if the syntax has more valid arguments
|
||||
if (argsSize > maxArguments) {
|
||||
finalSyntax = validSyntaxHolder;
|
||||
maxArguments = argsSize;
|
||||
|
||||
// Fill arguments map
|
||||
Arguments syntaxValues = new Arguments();
|
||||
for (Map.Entry<Argument<?>, Object> entry : argsValues.entrySet()) {
|
||||
final Argument<?> argument = entry.getKey();
|
||||
final Object argumentValue = entry.getValue();
|
||||
|
||||
syntaxValues.setArg(argument.getId(), argumentValue);
|
||||
}
|
||||
finalArguments = syntaxValues;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the arguments values
|
||||
if (finalSyntax != null) {
|
||||
executorArgs.copy(finalArguments);
|
||||
}
|
||||
|
||||
return finalSyntax;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package net.minestom.server.command.builder.parser;
|
||||
|
||||
import net.minestom.server.command.builder.CommandSyntax;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
|
||||
/**
|
||||
* Holds the data of an invalidated syntax.
|
||||
*/
|
||||
public class CommandSuggestionHolder {
|
||||
public CommandSyntax syntax;
|
||||
public ArgumentSyntaxException argumentSyntaxException;
|
||||
public int argIndex;
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package net.minestom.server.command.builder.parser;
|
||||
|
||||
import net.minestom.server.command.builder.CommandSyntax;
|
||||
import net.minestom.server.command.builder.arguments.Argument;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Holds the data of a validated syntax.
|
||||
*/
|
||||
public class ValidSyntaxHolder {
|
||||
public CommandSyntax syntax;
|
||||
public Map<Argument<?>, Object> argumentsValue;
|
||||
}
|
@ -74,4 +74,32 @@ public class DeclareCommandsPacket implements ServerPacket {
|
||||
|
||||
}
|
||||
|
||||
public static byte getFlag(@NotNull NodeType type, boolean executable, boolean redirect, boolean suggestionType) {
|
||||
byte result = (byte) type.mask;
|
||||
|
||||
if (executable) {
|
||||
result |= 0x04;
|
||||
}
|
||||
|
||||
if (redirect) {
|
||||
result |= 0x08;
|
||||
}
|
||||
|
||||
if (suggestionType) {
|
||||
result |= 0x10;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public enum NodeType {
|
||||
ROOT(0), LITERAL(0b1), ARGUMENT(0b10), NONE(0x11);
|
||||
|
||||
private final int mask;
|
||||
|
||||
NodeType(int mask) {
|
||||
this.mask = mask;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package demo.commands;
|
||||
import net.minestom.server.command.CommandSender;
|
||||
import net.minestom.server.command.builder.Arguments;
|
||||
import net.minestom.server.command.builder.Command;
|
||||
import net.minestom.server.command.builder.arguments.Argument;
|
||||
import net.minestom.server.command.builder.arguments.ArgumentType;
|
||||
import net.minestom.server.command.builder.arguments.number.ArgumentNumber;
|
||||
import net.minestom.server.command.builder.exception.ArgumentSyntaxException;
|
||||
@ -12,15 +11,15 @@ import net.minestom.server.entity.Player;
|
||||
public class HealthCommand extends Command {
|
||||
|
||||
public HealthCommand() {
|
||||
super("health", "h", "healthbar");
|
||||
super("health");
|
||||
|
||||
setCondition(this::condition);
|
||||
|
||||
setDefaultExecutor(this::defaultExecutor);
|
||||
|
||||
Argument modeArg = ArgumentType.Word("mode").from("set", "add");
|
||||
var modeArg = ArgumentType.Word("mode").from("set", "add");
|
||||
|
||||
Argument valueArg = ArgumentType.Integer("value").between(0, 100);
|
||||
var valueArg = ArgumentType.Integer("value").between(0, 100);
|
||||
|
||||
setArgumentCallback(this::onModeError, modeArg);
|
||||
setArgumentCallback(this::onValueError, valueArg);
|
||||
|
@ -1,15 +1,14 @@
|
||||
package demo.commands;
|
||||
|
||||
import net.minestom.server.chat.ChatColor;
|
||||
import net.minestom.server.chat.ColoredText;
|
||||
import net.minestom.server.command.CommandSender;
|
||||
import net.minestom.server.command.builder.Arguments;
|
||||
import net.minestom.server.command.builder.Command;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.instance.InstanceContainer;
|
||||
import net.minestom.server.instance.batch.BlockBatch;
|
||||
import net.minestom.server.command.builder.CommandResult;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.List;
|
||||
|
||||
import static net.minestom.server.command.builder.arguments.ArgumentType.Integer;
|
||||
import static net.minestom.server.command.builder.arguments.ArgumentType.*;
|
||||
|
||||
public class TestCommand extends Command {
|
||||
|
||||
@ -17,26 +16,17 @@ public class TestCommand extends Command {
|
||||
super("testcmd");
|
||||
setDefaultExecutor(this::usage);
|
||||
|
||||
setDefaultExecutor((sender, args) -> {
|
||||
if (!sender.isPlayer()) {
|
||||
sender.sendMessage("This command may only be run by players.");
|
||||
return;
|
||||
}
|
||||
Player player = sender.asPlayer();
|
||||
addSyntax((sender, args) -> {
|
||||
final CommandResult result = args.get("command");
|
||||
System.out.println("test " + result.getType() + " " + result.getInput());
|
||||
}, Literal("cmd"), Command("command"));
|
||||
|
||||
BlockBatch batch = new BlockBatch((InstanceContainer) player.getInstance());
|
||||
|
||||
int offset = 5;
|
||||
for (int x = 0; x < 50; x += 1) {
|
||||
for (int y = 0; y < 50; y += 1) {
|
||||
for (int z = 0; z < 50; z += 1) {
|
||||
batch.setBlockStateId(x + offset, y + offset+50, z + offset, (short) ThreadLocalRandom.current().nextInt(500));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
batch.flush(() -> sender.sendMessage(ColoredText.of(ChatColor.BRIGHT_GREEN, "Created cube.")));
|
||||
});
|
||||
addSyntax((sender, args) -> {
|
||||
List<Arguments> groups = args.get("groups");
|
||||
System.out.println("size " + groups.size());
|
||||
}, Literal("loop"), Loop("groups",
|
||||
Group("group", Literal("name"), Word("word1")),
|
||||
Group("group2", Literal("delay"), Integer("number2"))));
|
||||
}
|
||||
|
||||
private void usage(CommandSender sender, Arguments arguments) {
|
||||
|
Loading…
Reference in New Issue
Block a user