2020-04-24 03:25:58 +02:00
|
|
|
package net.minestom.server.command;
|
2020-04-05 10:15:21 +02:00
|
|
|
|
2020-08-03 10:35:46 +02:00
|
|
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
|
|
|
import it.unimi.dsi.fastutil.ints.IntList;
|
2020-07-10 16:37:18 +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.*;
|
2020-07-14 13:35:07 +02:00
|
|
|
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;
|
2020-10-15 05:29:48 +02:00
|
|
|
import net.minestom.server.command.builder.arguments.minecraft.registry.ArgumentPotionEffect;
|
2020-07-10 16:37:18 +02:00
|
|
|
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;
|
2020-05-07 15:46:21 +02:00
|
|
|
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-08-04 06:28:21 +02:00
|
|
|
import org.apache.commons.lang3.tuple.Pair;
|
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
|
|
|
|
2020-10-11 18:35:32 +02:00
|
|
|
/**
|
|
|
|
* Manager used to register {@link Command} and {@link CommandProcessor}.
|
|
|
|
* <p>
|
|
|
|
* It is also possible to simulate a command using {@link #execute(CommandSender, String)}.
|
|
|
|
*/
|
|
|
|
public final class CommandManager {
|
2020-04-05 10:15:21 +02:00
|
|
|
|
2020-08-04 04:21:11 +02:00
|
|
|
public static final String COMMAND_PREFIX = "/";
|
|
|
|
|
2020-07-01 21:03:53 +02:00
|
|
|
private boolean running;
|
2020-04-05 10:15:21 +02:00
|
|
|
|
2020-06-21 14:01:03 +02:00
|
|
|
private ConsoleSender consoleSender = new ConsoleSender();
|
|
|
|
|
2020-07-14 13:35:07 +02:00
|
|
|
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() {
|
2020-07-01 21:03:53 +02:00
|
|
|
running = true;
|
2020-06-21 14:01:03 +02:00
|
|
|
// Setup console thread
|
2020-07-01 21:03:53 +02:00
|
|
|
Thread consoleThread = new Thread(() -> {
|
2020-07-21 18:48:15 +02:00
|
|
|
final Scanner scanner = new Scanner(System.in);
|
2020-07-01 21:03:53 +02:00
|
|
|
while (running) {
|
2020-07-10 16:37:18 +02:00
|
|
|
if (scanner.hasNext()) {
|
2020-07-01 21:03:53 +02:00
|
|
|
String command = scanner.nextLine();
|
2020-08-04 04:21:11 +02:00
|
|
|
if (!command.startsWith(COMMAND_PREFIX))
|
2020-07-01 21:03:53 +02:00
|
|
|
continue;
|
2020-08-04 04:21:11 +02:00
|
|
|
command = command.replaceFirst(COMMAND_PREFIX, "");
|
2020-07-01 21:03:53 +02:00
|
|
|
execute(consoleSender, command);
|
|
|
|
}
|
2020-06-21 14:01:03 +02:00
|
|
|
}
|
2020-07-01 21:03:53 +02:00
|
|
|
}, "ConsoleCommand-Thread");
|
|
|
|
consoleThread.setDaemon(true);
|
|
|
|
consoleThread.start();
|
|
|
|
}
|
|
|
|
|
2020-07-23 07:36:49 +02:00
|
|
|
/**
|
2020-10-13 12:49:29 +02:00
|
|
|
* Stop the console responsible for the console commands processing
|
2020-07-23 07:36:49 +02:00
|
|
|
* <p>
|
|
|
|
* WARNING: it cannot be re-run later
|
|
|
|
*/
|
2020-07-01 21:03:53 +02:00
|
|
|
public void stopConsoleThread() {
|
|
|
|
running = false;
|
2020-06-21 14:01:03 +02:00
|
|
|
}
|
|
|
|
|
2020-07-21 18:04:02 +02:00
|
|
|
/**
|
2020-10-11 18:35:32 +02:00
|
|
|
* Register a {@link Command}
|
2020-07-21 18:04:02 +02:00
|
|
|
*
|
|
|
|
* @param command the command to register
|
|
|
|
*/
|
2020-07-14 13:35:07 +02:00
|
|
|
public void register(Command command) {
|
2020-04-05 10:15:21 +02:00
|
|
|
this.dispatcher.register(command);
|
|
|
|
}
|
|
|
|
|
2020-08-04 06:14:42 +02:00
|
|
|
/**
|
2020-10-11 18:35:32 +02:00
|
|
|
* Get the {@link Command} registered by {@link #register(Command)}
|
2020-08-04 06:14:42 +02:00
|
|
|
*
|
|
|
|
* @param commandName the command name
|
|
|
|
* @return the command associated with the name, null if not any
|
|
|
|
*/
|
|
|
|
public Command getCommand(String commandName) {
|
|
|
|
return dispatcher.findCommand(commandName);
|
|
|
|
}
|
|
|
|
|
2020-07-21 18:04:02 +02:00
|
|
|
/**
|
2020-10-11 18:35:32 +02:00
|
|
|
* Register a {@link CommandProcessor}
|
2020-07-21 18:04:02 +02:00
|
|
|
*
|
|
|
|
* @param commandProcessor the command to register
|
|
|
|
*/
|
2020-04-05 10:15:21 +02:00
|
|
|
public void register(CommandProcessor commandProcessor) {
|
|
|
|
this.commandProcessorMap.put(commandProcessor.getCommandName().toLowerCase(), commandProcessor);
|
2020-08-04 04:21:11 +02:00
|
|
|
// Register aliases
|
|
|
|
final String[] aliases = commandProcessor.getAliases();
|
|
|
|
if (aliases != null && aliases.length > 0) {
|
|
|
|
for (String alias : aliases) {
|
|
|
|
this.commandProcessorMap.put(alias.toLowerCase(), commandProcessor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-10-11 18:35:32 +02:00
|
|
|
* Get the {@link CommandProcessor} registered by {@link #register(CommandProcessor)}
|
2020-08-04 04:21:11 +02:00
|
|
|
*
|
|
|
|
* @param commandName the command name
|
|
|
|
* @return the command associated with the name, null if not any
|
|
|
|
*/
|
|
|
|
public CommandProcessor getCommandProcessor(String commandName) {
|
|
|
|
return commandProcessorMap.get(commandName.toLowerCase());
|
2020-04-05 10:15:21 +02:00
|
|
|
}
|
|
|
|
|
2020-07-21 18:04:02 +02:00
|
|
|
/**
|
2020-10-11 18:35:32 +02:00
|
|
|
* Execute a command for a {@link ConsoleSender}
|
2020-07-21 18:04:02 +02:00
|
|
|
*
|
|
|
|
* @param sender the sender of the command
|
|
|
|
* @param command the raw command string (without the command prefix)
|
2020-08-04 04:21:11 +02:00
|
|
|
* @return true if the command hadn't been cancelled and has been successful
|
2020-07-21 18:04:02 +02:00
|
|
|
*/
|
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-10-12 02:56:30 +02:00
|
|
|
// Command event
|
2020-06-21 14:01:03 +02:00
|
|
|
if (sender instanceof Player) {
|
|
|
|
Player player = (Player) sender;
|
2020-04-29 19:23:47 +02:00
|
|
|
|
2020-06-21 14:01:03 +02:00
|
|
|
PlayerCommandEvent playerCommandEvent = new PlayerCommandEvent(player, command);
|
|
|
|
player.callEvent(PlayerCommandEvent.class, playerCommandEvent);
|
2020-04-29 19:23:47 +02:00
|
|
|
|
2020-06-21 14:01:03 +02:00
|
|
|
if (playerCommandEvent.isCancelled())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
command = playerCommandEvent.getCommand();
|
|
|
|
}
|
2020-04-29 19:23:47 +02:00
|
|
|
|
2020-10-12 02:56:30 +02:00
|
|
|
// Process the command
|
2020-04-05 10:15:21 +02:00
|
|
|
try {
|
2020-07-21 18:04:02 +02:00
|
|
|
// Check for rich-command
|
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) {
|
2020-07-21 18:04:02 +02:00
|
|
|
// Check for legacy-command
|
2020-10-12 02:56:30 +02:00
|
|
|
final String[] splitCommand = command.split(" ");
|
|
|
|
final String commandName = splitCommand[0];
|
2020-07-21 18:48:15 +02:00
|
|
|
final CommandProcessor commandProcessor = commandProcessorMap.get(commandName.toLowerCase());
|
2020-04-05 10:15:21 +02:00
|
|
|
if (commandProcessor == null)
|
|
|
|
return false;
|
|
|
|
|
2020-07-21 18:04:02 +02:00
|
|
|
// Execute the legacy-command
|
2020-07-21 18:48:15 +02:00
|
|
|
final String[] args = command.substring(command.indexOf(" ") + 1).split(" ");
|
2020-04-05 10:15:21 +02:00
|
|
|
|
2020-06-21 14:01:03 +02:00
|
|
|
return commandProcessor.process(sender, commandName, args);
|
2020-04-05 10:15:21 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-21 18:04:02 +02:00
|
|
|
/**
|
2020-10-11 18:35:32 +02:00
|
|
|
* Get the {@link ConsoleSender} (which is used as a {@link CommandSender})
|
2020-07-21 18:04:02 +02:00
|
|
|
*
|
2020-10-11 18:35:32 +02:00
|
|
|
* @return the {@link ConsoleSender}
|
2020-07-21 18:04:02 +02:00
|
|
|
*/
|
2020-06-21 14:01:03 +02:00
|
|
|
public ConsoleSender getConsoleSender() {
|
|
|
|
return consoleSender;
|
|
|
|
}
|
|
|
|
|
2020-07-21 18:04:02 +02:00
|
|
|
/**
|
2020-10-11 18:35:32 +02:00
|
|
|
* Get the {@link DeclareCommandsPacket} for a specific player
|
2020-07-21 18:04:02 +02:00
|
|
|
* <p>
|
2020-10-12 02:56:30 +02:00
|
|
|
* Can be used to update the {@link Player} auto-completion list
|
2020-07-21 18:04:02 +02:00
|
|
|
*
|
|
|
|
* @param player the player to get the commands packet
|
|
|
|
* @return the {@link DeclareCommandsPacket} for {@code player}
|
|
|
|
*/
|
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
|
|
|
}
|
|
|
|
|
2020-08-03 06:36:42 +02:00
|
|
|
/**
|
2020-10-12 02:56:30 +02:00
|
|
|
* Build the {@link DeclareCommandsPacket} for a {@link Player}
|
2020-08-03 06:36:42 +02:00
|
|
|
*
|
|
|
|
* @param player the player to build the packet for
|
|
|
|
* @return the commands packet for the specific player
|
|
|
|
*/
|
2020-07-10 18:12:29 +02:00
|
|
|
private DeclareCommandsPacket buildPacket(Player player) {
|
2020-07-10 16:37:18 +02:00
|
|
|
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-08-03 10:35:46 +02:00
|
|
|
IntList rootChildren = new IntArrayList();
|
2020-04-05 10:15:21 +02:00
|
|
|
|
2020-07-14 13:35:07 +02:00
|
|
|
for (Command command : dispatcher.getCommands()) {
|
2020-07-10 18:12:29 +02:00
|
|
|
// Check if player should see this command
|
2020-08-03 06:36:42 +02:00
|
|
|
final 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-08-03 10:35:46 +02:00
|
|
|
IntList cmdChildren = new IntArrayList();
|
|
|
|
final Collection<CommandSyntax> syntaxes = command.getSyntaxes();
|
2020-07-10 18:12:29 +02:00
|
|
|
|
|
|
|
List<String> names = new ArrayList<>();
|
|
|
|
names.add(command.getName());
|
2020-08-08 13:41:25 +02:00
|
|
|
names.addAll(Arrays.asList(command.getAliases()));
|
2020-07-10 18:12:29 +02:00
|
|
|
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-08-04 06:28:21 +02:00
|
|
|
// Pair<CommandName,EnabledTracking>
|
|
|
|
final List<Pair<String, Boolean>> commandsPair = new ArrayList<>();
|
2020-07-10 18:12:29 +02:00
|
|
|
for (CommandProcessor commandProcessor : commandProcessorMap.values()) {
|
2020-08-04 06:28:21 +02:00
|
|
|
final boolean enableTracking = commandProcessor.enableWritingTracking();
|
2020-07-10 18:12:29 +02:00
|
|
|
// Do not show command if return false
|
|
|
|
if (!commandProcessor.hasAccess(player))
|
|
|
|
continue;
|
2020-04-05 10:15:21 +02:00
|
|
|
|
2020-08-04 06:28:21 +02:00
|
|
|
commandsPair.add(Pair.of(commandProcessor.getCommandName(), enableTracking));
|
2020-08-03 06:36:42 +02:00
|
|
|
final String[] aliases = commandProcessor.getAliases();
|
2020-07-10 18:12:29 +02:00
|
|
|
if (aliases == null || aliases.length == 0)
|
|
|
|
continue;
|
|
|
|
for (String alias : aliases) {
|
2020-08-04 06:28:21 +02:00
|
|
|
commandsPair.add(Pair.of(alias, enableTracking));
|
2020-07-10 18:12:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-04 06:28:21 +02:00
|
|
|
for (Pair<String, Boolean> pair : commandsPair) {
|
|
|
|
final String name = pair.getLeft();
|
|
|
|
final boolean tracking = pair.getRight();
|
2020-08-04 04:21:11 +02:00
|
|
|
// Server suggestion (ask_server)
|
|
|
|
{
|
|
|
|
DeclareCommandsPacket.Node tabNode = new DeclareCommandsPacket.Node();
|
2020-08-04 06:28:21 +02:00
|
|
|
tabNode.flags = getFlag(NodeType.ARGUMENT, true, false, tracking);
|
|
|
|
tabNode.name = tracking ? "tab_completion" : "args";
|
2020-08-04 04:21:11 +02:00
|
|
|
tabNode.parser = "brigadier:string";
|
|
|
|
tabNode.properties = packetWriter -> packetWriter.writeVarInt(2); // Greedy phrase
|
|
|
|
tabNode.children = new int[0];
|
2020-08-04 06:28:21 +02:00
|
|
|
if (tracking) {
|
|
|
|
tabNode.suggestionsType = "minecraft:ask_server";
|
|
|
|
}
|
2020-08-04 04:21:11 +02:00
|
|
|
|
|
|
|
nodes.add(tabNode);
|
|
|
|
}
|
|
|
|
|
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);
|
2020-08-04 06:28:21 +02:00
|
|
|
literalNode.name = name;
|
2020-08-04 04:21:11 +02:00
|
|
|
literalNode.children = new int[]{nodes.size() - 1};
|
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);
|
|
|
|
|
2020-05-25 12:25:39 +02:00
|
|
|
declareCommandsPacket.nodes = nodes.toArray(new DeclareCommandsPacket.Node[0]);
|
2020-04-05 10:15:21 +02:00
|
|
|
declareCommandsPacket.rootIndex = nodes.size() - 1;
|
2020-07-10 16:37:18 +02:00
|
|
|
|
|
|
|
return declareCommandsPacket;
|
2020-04-05 10:15:21 +02:00
|
|
|
}
|
|
|
|
|
2020-08-03 06:36:42 +02:00
|
|
|
/**
|
|
|
|
* Add a command's syntaxes to the nodes list
|
|
|
|
*
|
|
|
|
* @param nodes the nodes of the packet
|
|
|
|
* @param cmdChildren the main root of this command
|
|
|
|
* @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)
|
|
|
|
*/
|
2020-08-03 10:35:46 +02:00
|
|
|
private void createCommand(List<DeclareCommandsPacket.Node> nodes, IntList cmdChildren, String name, Collection<CommandSyntax> syntaxes, IntList rootChildren) {
|
2020-07-10 18:12:29 +02:00
|
|
|
|
|
|
|
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
|
2020-08-03 10:35:46 +02:00
|
|
|
IntList argChildren = null;
|
2020-07-10 18:12:29 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
2020-08-04 04:21:11 +02:00
|
|
|
final List<DeclareCommandsPacket.Node> argumentNodes = toNodes(argument, isLast);
|
2020-07-10 18:12:29 +02:00
|
|
|
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) {
|
2020-07-23 07:36:49 +02:00
|
|
|
final int[] children = ArrayUtils.toArray(argChildren);
|
2020-07-10 18:12:29 +02:00
|
|
|
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
|
2020-07-23 07:36:49 +02:00
|
|
|
final int[] children = new int[0];
|
2020-07-10 18:12:29 +02:00
|
|
|
argumentNodes.forEach(node -> node.children = children);
|
|
|
|
} else {
|
|
|
|
// Create children list which will be filled during next iteration
|
2020-08-03 10:35:46 +02:00
|
|
|
argChildren = new IntArrayList();
|
2020-07-10 18:12:29 +02:00
|
|
|
lastNodes = argumentNodes;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2020-07-23 07:36:49 +02:00
|
|
|
final int[] children = ArrayUtils.toArray(cmdChildren);
|
2020-07-10 18:12:29 +02:00
|
|
|
//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;
|
|
|
|
}
|
|
|
|
|
2020-08-03 06:36:42 +02:00
|
|
|
/**
|
|
|
|
* Convert 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
|
|
|
|
*/
|
2020-08-01 07:35:16 +02:00
|
|
|
private List<DeclareCommandsPacket.Node> toNodes(Argument<?> argument, boolean executable) {
|
2020-07-10 18:12:29 +02:00
|
|
|
List<DeclareCommandsPacket.Node> nodes = new ArrayList<>();
|
2020-04-05 10:15:21 +02:00
|
|
|
|
2020-08-03 06:36:42 +02:00
|
|
|
// You can uncomment this to test any brigadier parser on the client
|
2020-07-14 13:35:07 +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-14 13:35:07 +02:00
|
|
|
}*/
|
2020-07-10 22:50:05 +02:00
|
|
|
|
2020-04-05 10:15:21 +02:00
|
|
|
if (argument instanceof ArgumentBoolean) {
|
2020-08-04 06:14:42 +02:00
|
|
|
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
2020-07-10 18:12:29 +02:00
|
|
|
|
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-08-04 06:14:42 +02:00
|
|
|
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
2020-07-10 18:12:29 +02:00
|
|
|
|
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-08-04 06:14:42 +02:00
|
|
|
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
2020-07-10 18:12:29 +02:00
|
|
|
|
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-08-04 06:14:42 +02:00
|
|
|
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
2020-07-10 18:12:29 +02:00
|
|
|
|
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-08-04 06:14:42 +02:00
|
|
|
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
2020-07-10 22:50:05 +02:00
|
|
|
wordConsumer.accept(argumentNode);
|
2020-07-10 18:12:29 +02:00
|
|
|
}
|
2020-08-04 06:14:42 +02:00
|
|
|
} else if (argument instanceof ArgumentDynamicWord) {
|
|
|
|
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, true);
|
|
|
|
|
|
|
|
argumentNode.parser = "brigadier:string";
|
|
|
|
argumentNode.properties = packetWriter -> {
|
|
|
|
packetWriter.writeVarInt(0); // Single word
|
|
|
|
};
|
|
|
|
argumentNode.suggestionsType = "minecraft:ask_server";
|
|
|
|
|
2020-04-05 10:15:21 +02:00
|
|
|
} else if (argument instanceof ArgumentString) {
|
2020-08-04 06:14:42 +02:00
|
|
|
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
2020-07-10 18:12:29 +02:00
|
|
|
|
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-08-04 06:14:42 +02:00
|
|
|
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);
|
2020-07-10 18:12:29 +02:00
|
|
|
|
2020-04-05 10:15:21 +02:00
|
|
|
argumentNode.parser = "brigadier:string";
|
|
|
|
argumentNode.properties = packetWriter -> {
|
|
|
|
packetWriter.writeVarInt(2); // Greedy phrase
|
|
|
|
};
|
2020-08-04 06:14:42 +02:00
|
|
|
argumentNode.suggestionsType = "minecraft:ask_server";
|
2020-07-10 22:50:05 +02:00
|
|
|
} else if (argument instanceof ArgumentColor) {
|
2020-08-04 06:14:42 +02:00
|
|
|
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
2020-07-10 22:50:05 +02:00
|
|
|
argumentNode.parser = "minecraft:color";
|
|
|
|
} else if (argument instanceof ArgumentTime) {
|
2020-08-04 06:14:42 +02:00
|
|
|
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
2020-07-10 22:50:05 +02:00
|
|
|
argumentNode.parser = "minecraft:time";
|
|
|
|
} else if (argument instanceof ArgumentEnchantment) {
|
2020-08-04 06:14:42 +02:00
|
|
|
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
2020-07-10 22:50:05 +02:00
|
|
|
argumentNode.parser = "minecraft:item_enchantment";
|
|
|
|
} else if (argument instanceof ArgumentParticle) {
|
2020-08-04 06:14:42 +02:00
|
|
|
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
2020-07-10 22:50:05 +02:00
|
|
|
argumentNode.parser = "minecraft:particle";
|
2020-10-15 05:29:48 +02:00
|
|
|
} else if (argument instanceof ArgumentPotionEffect) {
|
2020-08-04 06:14:42 +02:00
|
|
|
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
2020-07-10 22:50:05 +02:00
|
|
|
argumentNode.parser = "minecraft:mob_effect";
|
|
|
|
} else if (argument instanceof ArgumentEntityType) {
|
2020-08-04 06:14:42 +02:00
|
|
|
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
2020-07-10 22:50:05 +02:00
|
|
|
argumentNode.parser = "minecraft:entity_summon";
|
2020-07-10 23:51:38 +02:00
|
|
|
} else if (argument instanceof ArgumentIntRange) {
|
2020-08-04 06:14:42 +02:00
|
|
|
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
2020-07-10 23:51:38 +02:00
|
|
|
argumentNode.parser = "minecraft:int_range";
|
|
|
|
} else if (argument instanceof ArgumentFloatRange) {
|
2020-08-04 06:14:42 +02:00
|
|
|
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
2020-07-10 23:51:38 +02:00
|
|
|
argumentNode.parser = "minecraft:float_range";
|
2020-07-14 13:35:07 +02:00
|
|
|
} else if (argument instanceof ArgumentEntities) {
|
|
|
|
ArgumentEntities argumentEntities = (ArgumentEntities) argument;
|
2020-08-04 06:14:42 +02:00
|
|
|
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable, false);
|
2020-07-14 13:35:07 +02:00
|
|
|
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-08-01 07:35:16 +02:00
|
|
|
private byte getNumberProperties(ArgumentNumber<? extends Number> argumentNumber) {
|
2020-07-10 22:50:05 +02:00
|
|
|
byte result = 0;
|
|
|
|
if (argumentNumber.hasMin())
|
|
|
|
result += 1;
|
|
|
|
if (argumentNumber.hasMax())
|
|
|
|
result += 2;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2020-08-03 06:36:42 +02:00
|
|
|
/**
|
|
|
|
* Build 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}
|
|
|
|
*/
|
2020-07-10 18:12:29 +02:00
|
|
|
private DeclareCommandsPacket.Node simpleArgumentNode(List<DeclareCommandsPacket.Node> nodes,
|
2020-08-04 06:14:42 +02:00
|
|
|
Argument<?> argument, boolean executable, boolean suggestion) {
|
2020-07-10 18:12:29 +02:00
|
|
|
DeclareCommandsPacket.Node argumentNode = new DeclareCommandsPacket.Node();
|
|
|
|
nodes.add(argumentNode);
|
|
|
|
|
2020-08-04 06:14:42 +02:00
|
|
|
argumentNode.flags = getFlag(NodeType.ARGUMENT, executable, false, suggestion);
|
2020-07-10 18:12:29 +02:00
|
|
|
argumentNode.name = argument.getId();
|
|
|
|
|
2020-04-05 10:15:21 +02:00
|
|
|
return argumentNode;
|
2020-07-10 16:37:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private byte getFlag(NodeType type, boolean executable, boolean redirect, boolean suggestionType) {
|
|
|
|
byte result = (byte) type.mask;
|
|
|
|
|
|
|
|
if (executable) {
|
2020-08-04 04:21:11 +02:00
|
|
|
result |= 0x04;
|
2020-07-10 16:37:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (redirect) {
|
2020-08-04 04:21:11 +02:00
|
|
|
result |= 0x08;
|
2020-07-10 16:37:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (suggestionType) {
|
2020-07-10 18:12:29 +02:00
|
|
|
result |= 0x10;
|
2020-07-10 16:37:18 +02:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
private enum NodeType {
|
|
|
|
ROOT(0), LITERAL(0b1), ARGUMENT(0b10), NONE(0x11);
|
|
|
|
|
2020-09-29 21:45:16 +02:00
|
|
|
private final int mask;
|
2020-07-10 16:37:18 +02:00
|
|
|
|
|
|
|
NodeType(int mask) {
|
|
|
|
this.mask = mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2020-04-05 10:15:21 +02:00
|
|
|
}
|