Minestom/src/main/java/net/minestom/server/command/CommandManager.java

516 lines
21 KiB
Java
Raw Normal View History

2020-04-24 03:25:58 +02:00
package net.minestom.server.command;
2020-04-05 10:15:21 +02:00
import net.minestom.server.command.builder.Command;
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.*;
2020-07-10 22:50:05 +02:00
import net.minestom.server.command.builder.arguments.minecraft.registry.ArgumentEnchantment;
import net.minestom.server.command.builder.arguments.minecraft.registry.ArgumentEntityType;
import net.minestom.server.command.builder.arguments.minecraft.registry.ArgumentParticle;
import net.minestom.server.command.builder.arguments.minecraft.registry.ArgumentPotion;
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;
2020-07-10 22:50:05 +02:00
import net.minestom.server.command.builder.arguments.number.ArgumentNumber;
2020-07-10 18:12:29 +02:00
import net.minestom.server.command.builder.condition.CommandCondition;
2020-04-24 03:25:58 +02:00
import net.minestom.server.entity.Player;
import net.minestom.server.event.player.PlayerCommandEvent;
2020-04-24 03:25:58 +02:00
import net.minestom.server.network.packet.server.play.DeclareCommandsPacket;
import net.minestom.server.utils.ArrayUtils;
2020-05-23 04:20:01 +02:00
import net.minestom.server.utils.validate.Check;
2020-04-05 10:15:21 +02:00
2020-06-21 14:01:03 +02:00
import java.util.*;
2020-07-10 22:50:05 +02:00
import java.util.function.Consumer;
2020-04-05 10:15:21 +02:00
public class CommandManager {
private boolean running;
2020-04-05 10:15:21 +02:00
private String commandPrefix = "/";
2020-06-21 14:01:03 +02:00
private ConsoleSender consoleSender = new ConsoleSender();
private CommandDispatcher dispatcher = new CommandDispatcher();
2020-04-05 10:15:21 +02:00
private Map<String, CommandProcessor> commandProcessorMap = new HashMap<>();
2020-06-21 14:01:03 +02:00
public CommandManager() {
running = true;
2020-06-21 14:01:03 +02:00
// Setup console thread
Thread consoleThread = new Thread(() -> {
2020-06-21 14:01:03 +02:00
Scanner scanner = new Scanner(System.in);
while (running) {
if (scanner.hasNext()) {
String command = scanner.nextLine();
if (!command.startsWith(commandPrefix))
continue;
command = command.replaceFirst(commandPrefix, "");
execute(consoleSender, command);
}
2020-06-21 14:01:03 +02:00
}
}, "ConsoleCommand-Thread");
consoleThread.setDaemon(true);
consoleThread.start();
}
public void stopConsoleThread() {
running = false;
2020-06-21 14:01:03 +02:00
}
public void register(Command command) {
2020-04-05 10:15:21 +02:00
this.dispatcher.register(command);
}
public void register(CommandProcessor commandProcessor) {
this.commandProcessorMap.put(commandProcessor.getCommandName().toLowerCase(), commandProcessor);
}
2020-06-21 14:01:03 +02:00
public boolean execute(CommandSender sender, String command) {
Check.notNull(sender, "Source cannot be null");
2020-05-23 04:20:01 +02:00
Check.notNull(command, "Command string cannot be null");
2020-04-05 10:15:21 +02:00
2020-06-21 14:01:03 +02:00
if (sender instanceof Player) {
Player player = (Player) sender;
2020-06-21 14:01:03 +02:00
PlayerCommandEvent playerCommandEvent = new PlayerCommandEvent(player, command);
player.callEvent(PlayerCommandEvent.class, playerCommandEvent);
2020-06-21 14:01:03 +02:00
if (playerCommandEvent.isCancelled())
return false;
command = playerCommandEvent.getCommand();
}
2020-04-05 10:15:21 +02:00
try {
2020-06-21 14:01:03 +02:00
this.dispatcher.execute(sender, command);
2020-04-05 10:15:21 +02:00
return true;
} catch (NullPointerException e) {
String[] splitted = command.split(" ");
String commandName = splitted[0];
CommandProcessor commandProcessor = commandProcessorMap.get(commandName.toLowerCase());
if (commandProcessor == null)
return false;
String[] args = command.substring(command.indexOf(" ") + 1).split(" ");
2020-06-21 14:01:03 +02:00
return commandProcessor.process(sender, commandName, args);
2020-04-05 10:15:21 +02:00
}
}
public String getCommandPrefix() {
return commandPrefix;
}
public void setCommandPrefix(String commandPrefix) {
this.commandPrefix = commandPrefix;
}
2020-06-21 14:01:03 +02:00
public ConsoleSender getConsoleSender() {
return consoleSender;
}
2020-04-26 06:34:08 +02:00
public DeclareCommandsPacket createDeclareCommandsPacket(Player player) {
2020-07-10 18:12:29 +02:00
return buildPacket(player);
2020-04-05 10:15:21 +02:00
}
/*private DeclareCommandsPacket buildPacket(Player player) {
2020-04-26 06:34:08 +02:00
DeclareCommandsPacket declareCommandsPacket = new DeclareCommandsPacket();
2020-04-05 17:46:29 +02:00
List<String> commands = new ArrayList<>();
2020-06-21 14:01:03 +02:00
for (Command<CommandSender> command : dispatcher.getCommands()) {
2020-04-26 06:34:08 +02:00
CommandCondition<Player> commandCondition = command.getCondition();
if (commandCondition != null) {
// Do not show command if return false
if (!commandCondition.apply(player)) {
continue;
}
}
2020-04-05 17:46:29 +02:00
commands.add(command.getName());
for (String alias : command.getAliases()) {
commands.add(alias);
}
}
2020-04-26 06:34:08 +02:00
2020-04-05 17:46:29 +02:00
for (CommandProcessor commandProcessor : commandProcessorMap.values()) {
2020-04-26 06:34:08 +02:00
// Do not show command if return false
if (!commandProcessor.hasAccess(player))
continue;
2020-04-05 17:46:29 +02:00
commands.add(commandProcessor.getCommandName());
String[] aliases = commandProcessor.getAliases();
if (aliases == null || aliases.length == 0)
continue;
for (String alias : aliases) {
commands.add(alias);
}
2020-04-05 17:46:29 +02:00
}
List<DeclareCommandsPacket.Node> nodes = new ArrayList<>();
ArrayList<Integer> rootChildren = new ArrayList<>();
DeclareCommandsPacket.Node argNode = new DeclareCommandsPacket.Node();
argNode.flags = 0b10;
argNode.name = "arg";
argNode.parser = "brigadier:string";
argNode.properties = packetWriter -> {
packetWriter.writeVarInt(0);
};
int argOffset = nodes.size();
nodes.add(argNode);
argNode.children = new int[]{argOffset};
for (String commandName : commands) {
DeclareCommandsPacket.Node literalNode = new DeclareCommandsPacket.Node();
literalNode.flags = 0b1;
literalNode.name = commandName;
literalNode.children = new int[]{argOffset};
rootChildren.add(nodes.size());
nodes.add(literalNode);
}
DeclareCommandsPacket.Node rootNode = new DeclareCommandsPacket.Node();
rootNode.flags = 0;
rootNode.children = ArrayUtils.toArray(rootChildren);
nodes.add(rootNode);
declareCommandsPacket.nodes = nodes.toArray(new DeclareCommandsPacket.Node[0]);
2020-04-05 17:46:29 +02:00
declareCommandsPacket.rootIndex = nodes.size() - 1;
2020-04-26 06:34:08 +02:00
return declareCommandsPacket;
}*/
2020-04-05 17:46:29 +02:00
2020-07-10 18:12:29 +02:00
private DeclareCommandsPacket buildPacket(Player player) {
DeclareCommandsPacket declareCommandsPacket = new DeclareCommandsPacket();
2020-04-05 10:15:21 +02:00
List<DeclareCommandsPacket.Node> nodes = new ArrayList<>();
2020-07-10 18:12:29 +02:00
// Contains the children of the main node (all commands name)
2020-04-05 10:15:21 +02:00
ArrayList<Integer> rootChildren = new ArrayList<>();
for (Command command : dispatcher.getCommands()) {
2020-07-10 18:12:29 +02:00
// Check if player should see this command
CommandCondition commandCondition = command.getCondition();
2020-07-10 18:12:29 +02:00
if (commandCondition != null) {
// Do not show command if return false
if (!commandCondition.apply(player)) {
continue;
}
}
// The main root of this command
2020-04-05 10:15:21 +02:00
ArrayList<Integer> cmdChildren = new ArrayList<>();
2020-07-10 18:12:29 +02:00
Collection<CommandSyntax> syntaxes = command.getSyntaxes();
List<String> names = new ArrayList<>();
names.add(command.getName());
for (String alias : command.getAliases()) {
names.add(alias);
}
for (String name : names) {
createCommand(nodes, cmdChildren, name, syntaxes, rootChildren);
}
2020-04-05 10:15:21 +02:00
2020-07-10 18:12:29 +02:00
}
2020-07-10 22:50:05 +02:00
2020-07-10 18:12:29 +02:00
final List<String> simpleCommands = new ArrayList<>();
for (CommandProcessor commandProcessor : commandProcessorMap.values()) {
// Do not show command if return false
if (!commandProcessor.hasAccess(player))
continue;
2020-04-05 10:15:21 +02:00
2020-07-10 18:12:29 +02:00
simpleCommands.add(commandProcessor.getCommandName());
String[] aliases = commandProcessor.getAliases();
if (aliases == null || aliases.length == 0)
continue;
for (String alias : aliases) {
simpleCommands.add(alias);
}
}
for (String simpleCommand : simpleCommands) {
// TODO server suggestion
2020-04-05 10:15:21 +02:00
DeclareCommandsPacket.Node literalNode = new DeclareCommandsPacket.Node();
2020-07-10 18:12:29 +02:00
literalNode.flags = getFlag(NodeType.LITERAL, true, false, false);
literalNode.name = simpleCommand;
literalNode.children = new int[0];
//literalNode.suggestionsType = "minecraft:ask_server";
2020-04-05 10:15:21 +02:00
rootChildren.add(nodes.size());
nodes.add(literalNode);
}
DeclareCommandsPacket.Node rootNode = new DeclareCommandsPacket.Node();
rootNode.flags = 0;
rootNode.children = ArrayUtils.toArray(rootChildren);
nodes.add(rootNode);
declareCommandsPacket.nodes = nodes.toArray(new DeclareCommandsPacket.Node[0]);
2020-04-05 10:15:21 +02:00
declareCommandsPacket.rootIndex = nodes.size() - 1;
return declareCommandsPacket;
2020-04-05 10:15:21 +02:00
}
2020-07-10 18:12:29 +02:00
private void createCommand(List<DeclareCommandsPacket.Node> nodes, ArrayList<Integer> cmdChildren, String name, Collection<CommandSyntax> syntaxes, ArrayList<Integer> rootChildren) {
DeclareCommandsPacket.Node literalNode = createMainNode(name, syntaxes.isEmpty());
rootChildren.add(nodes.size());
nodes.add(literalNode);
for (CommandSyntax syntax : syntaxes) {
// Represent the last nodes computed in the last iteration
List<DeclareCommandsPacket.Node> lastNodes = null;
// Represent the children of the last node
ArrayList<Integer> argChildren = null;
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;
List<DeclareCommandsPacket.Node> argumentNodes = toNodes(argument, isLast);
for (DeclareCommandsPacket.Node node : argumentNodes) {
final int childId = nodes.size();
if (isFirst) {
// Add to main command child
cmdChildren.add(childId);
} else {
// Add to previous argument children
argChildren.add(childId);
}
if (lastNodes != null) {
int[] children = ArrayUtils.toArray(argChildren);
lastNodes.forEach(n -> 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
int[] children = new int[0];
argumentNodes.forEach(node -> node.children = children);
} else {
// Create children list which will be filled during next iteration
argChildren = new ArrayList<>();
lastNodes = argumentNodes;
}
}
}
int[] children = ArrayUtils.toArray(cmdChildren);
//System.out.println("test " + children.length + " : " + children[0]);
literalNode.children = children;
if (children.length > 0) {
literalNode.redirectedNode = children[0];
}
}
private DeclareCommandsPacket.Node createMainNode(String name, boolean executable) {
DeclareCommandsPacket.Node literalNode = new DeclareCommandsPacket.Node();
literalNode.flags = getFlag(NodeType.LITERAL, executable, false, false);
literalNode.name = name;
return literalNode;
}
private List<DeclareCommandsPacket.Node> toNodes(Argument argument, boolean executable) {
List<DeclareCommandsPacket.Node> nodes = new ArrayList<>();
2020-04-05 10:15:21 +02:00
/*DeclareCommandsPacket.Node testNode = simpleArgumentNode(nodes, argument, executable);
2020-07-11 14:16:36 +02:00
testNode.parser = "minecraft:entity";
testNode.properties = packetWriter -> packetWriter.writeByte((byte) 0x0);
2020-07-10 22:50:05 +02:00
if (true) {
return nodes;
}*/
2020-07-10 22:50:05 +02:00
2020-04-05 10:15:21 +02:00
if (argument instanceof ArgumentBoolean) {
2020-07-10 18:12:29 +02:00
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable);
2020-04-05 10:15:21 +02:00
argumentNode.parser = "brigadier:bool";
argumentNode.properties = packetWriter -> packetWriter.writeByte((byte) 0);
} else if (argument instanceof ArgumentDouble) {
2020-07-10 18:12:29 +02:00
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable);
2020-04-05 10:15:21 +02:00
ArgumentDouble argumentDouble = (ArgumentDouble) argument;
argumentNode.parser = "brigadier:double";
argumentNode.properties = packetWriter -> {
2020-07-10 22:50:05 +02:00
packetWriter.writeByte(getNumberProperties(argumentDouble));
if (argumentDouble.hasMin())
packetWriter.writeDouble(argumentDouble.getMin());
if (argumentDouble.hasMax())
packetWriter.writeDouble(argumentDouble.getMax());
2020-04-05 10:15:21 +02:00
};
} else if (argument instanceof ArgumentFloat) {
2020-07-10 18:12:29 +02:00
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable);
2020-04-05 10:15:21 +02:00
ArgumentFloat argumentFloat = (ArgumentFloat) argument;
argumentNode.parser = "brigadier:float";
argumentNode.properties = packetWriter -> {
2020-07-10 22:50:05 +02:00
packetWriter.writeByte(getNumberProperties(argumentFloat));
if (argumentFloat.hasMin())
packetWriter.writeFloat(argumentFloat.getMin());
if (argumentFloat.hasMax())
packetWriter.writeFloat(argumentFloat.getMax());
2020-04-05 10:15:21 +02:00
};
} else if (argument instanceof ArgumentInteger) {
2020-07-10 18:12:29 +02:00
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable);
2020-04-05 10:15:21 +02:00
ArgumentInteger argumentInteger = (ArgumentInteger) argument;
argumentNode.parser = "brigadier:integer";
argumentNode.properties = packetWriter -> {
2020-07-10 22:50:05 +02:00
packetWriter.writeByte(getNumberProperties(argumentInteger));
if (argumentInteger.hasMin())
packetWriter.writeInt(argumentInteger.getMin());
if (argumentInteger.hasMax())
packetWriter.writeInt(argumentInteger.getMax());
2020-04-05 10:15:21 +02:00
};
} else if (argument instanceof ArgumentWord) {
2020-07-10 18:12:29 +02:00
ArgumentWord argumentWord = (ArgumentWord) argument;
2020-07-10 22:50:05 +02:00
// Add the single word properties + parser
final Consumer<DeclareCommandsPacket.Node> wordConsumer = node -> {
node.parser = "brigadier:string";
node.properties = packetWriter -> {
packetWriter.writeVarInt(0); // Single word
};
};
2020-07-10 18:12:29 +02:00
final boolean hasRestriction = argumentWord.hasRestrictions();
if (hasRestriction) {
2020-07-10 22:50:05 +02:00
// Create a node for each restrictions as literal
2020-07-10 18:12:29 +02:00
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;
2020-07-10 22:50:05 +02:00
wordConsumer.accept(argumentNode);
2020-07-10 18:12:29 +02:00
}
} else {
2020-07-10 22:50:05 +02:00
// Can be any word, add only one argument node
2020-07-10 18:12:29 +02:00
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable);
2020-07-10 22:50:05 +02:00
wordConsumer.accept(argumentNode);
2020-07-10 18:12:29 +02:00
}
2020-04-05 10:15:21 +02:00
} else if (argument instanceof ArgumentString) {
2020-07-10 18:12:29 +02:00
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable);
2020-04-05 10:15:21 +02:00
argumentNode.parser = "brigadier:string";
argumentNode.properties = packetWriter -> {
packetWriter.writeVarInt(1); // Quotable phrase
};
} else if (argument instanceof ArgumentStringArray) {
2020-07-10 18:12:29 +02:00
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable);
2020-04-05 10:15:21 +02:00
argumentNode.parser = "brigadier:string";
argumentNode.properties = packetWriter -> {
packetWriter.writeVarInt(2); // Greedy phrase
};
2020-07-10 22:50:05 +02:00
} else if (argument instanceof ArgumentColor) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable);
argumentNode.parser = "minecraft:color";
} else if (argument instanceof ArgumentTime) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable);
argumentNode.parser = "minecraft:time";
} else if (argument instanceof ArgumentEnchantment) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable);
argumentNode.parser = "minecraft:item_enchantment";
} else if (argument instanceof ArgumentParticle) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable);
argumentNode.parser = "minecraft:particle";
} else if (argument instanceof ArgumentPotion) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable);
argumentNode.parser = "minecraft:mob_effect";
} else if (argument instanceof ArgumentEntityType) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable);
argumentNode.parser = "minecraft:entity_summon";
} else if (argument instanceof ArgumentIntRange) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable);
argumentNode.parser = "minecraft:int_range";
} else if (argument instanceof ArgumentFloatRange) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable);
argumentNode.parser = "minecraft:float_range";
} else if (argument instanceof ArgumentEntities) {
ArgumentEntities argumentEntities = (ArgumentEntities) argument;
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable);
argumentNode.parser = "minecraft:entity";
argumentNode.properties = packetWriter -> {
byte mask = 0;
if (argumentEntities.isOnlySingleEntity()) {
mask += 1;
}
if (argumentEntities.isOnlyPlayers()) {
mask += 2;
}
packetWriter.writeByte(mask);
};
2020-04-05 10:15:21 +02:00
}
2020-07-10 18:12:29 +02:00
return nodes;
}
2020-07-10 22:50:05 +02:00
private byte getNumberProperties(ArgumentNumber argumentNumber) {
byte result = 0;
if (argumentNumber.hasMin())
result += 1;
if (argumentNumber.hasMax())
result += 2;
return result;
}
2020-07-10 18:12:29 +02:00
private DeclareCommandsPacket.Node simpleArgumentNode(List<DeclareCommandsPacket.Node> nodes,
Argument argument, boolean executable) {
DeclareCommandsPacket.Node argumentNode = new DeclareCommandsPacket.Node();
nodes.add(argumentNode);
argumentNode.flags = getFlag(NodeType.ARGUMENT, executable, false, false);
argumentNode.name = argument.getId();
2020-04-05 10:15:21 +02:00
return argumentNode;
}
private byte getFlag(NodeType type, boolean executable, boolean redirect, boolean suggestionType) {
byte result = (byte) type.mask;
if (executable) {
result |= 0x4;
}
if (redirect) {
result |= 0x8;
}
if (suggestionType) {
2020-07-10 18:12:29 +02:00
result |= 0x10;
}
return result;
}
private enum NodeType {
ROOT(0), LITERAL(0b1), ARGUMENT(0b10), NONE(0x11);
private int mask;
NodeType(int mask) {
this.mask = mask;
}
}
2020-04-05 10:15:21 +02:00
}