Command syntaxes can now contain a CommandCondition

This commit is contained in:
themode 2020-11-04 04:45:46 +01:00
parent b84bcde84d
commit 9781e380b9
10 changed files with 116 additions and 29 deletions

View File

@ -204,7 +204,7 @@ public final class CommandManager {
/**
* Gets the {@link DeclareCommandsPacket} for a specific player.
* <p>
* Can be used to update the {@link Player} auto-completion list.
* Can be used to update a player auto-completion list.
*
* @param player the player to get the commands packet
* @return the {@link DeclareCommandsPacket} for {@code player}
@ -234,7 +234,7 @@ public final class CommandManager {
final CommandCondition commandCondition = command.getCondition();
if (commandCondition != null) {
// Do not show command if return false
if (!commandCondition.apply(player)) {
if (!commandCondition.canUse(player, null)) {
continue;
}
}
@ -247,7 +247,7 @@ public final class CommandManager {
names.add(command.getName());
names.addAll(Arrays.asList(command.getAliases()));
for (String name : names) {
createCommand(nodes, cmdChildren, name, syntaxes, rootChildren);
createCommand(player, nodes, cmdChildren, name, syntaxes, rootChildren);
}
}
@ -311,13 +311,15 @@ public final class CommandManager {
/**
* Adds the command's syntaxes to the nodes list.
*
* @param sender the potential sender of the command
* @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)
*/
private void createCommand(@NotNull List<DeclareCommandsPacket.Node> nodes,
private void createCommand(@NotNull CommandSender sender,
@NotNull List<DeclareCommandsPacket.Node> nodes,
@NotNull IntList cmdChildren,
@NotNull String name,
@NotNull Collection<CommandSyntax> syntaxes,
@ -334,6 +336,13 @@ public final class CommandManager {
Map<Argument, List<DeclareCommandsPacket.Node>> storedArgumentsNodes = new HashMap<>();
for (CommandSyntax syntax : syntaxes) {
final CommandCondition commandCondition = syntax.getCommandCondition();
if (commandCondition != null && !commandCondition.canUse(sender, null)) {
// Sender does not have the right to use this syntax, ignore it
continue;
}
// Represent the last nodes computed in the last iteration
List<DeclareCommandsPacket.Node> lastNodes = null;

View File

@ -104,16 +104,31 @@ public class Command {
/**
* Adds a new syntax in the command.
* <p>
* A syntax is simply a list of arguments
* A syntax is simply a list of arguments.
*
* @param commandCondition the condition to use the syntax
* @param executor the executor to call when the syntax is successfully received
* @param args all the arguments of the syntax
* @return the created {@link CommandSyntax}
*/
public CommandSyntax addSyntax(@Nullable CommandCondition commandCondition,
@NotNull CommandExecutor executor,
@NotNull Argument<?>... args) {
final CommandSyntax syntax = new CommandSyntax(commandCondition, executor, args);
this.syntaxes.add(syntax);
return syntax;
}
/**
* Adds a new syntax in the command without any condition.
*
* @param executor the executor to call when the syntax is successfully received
* @param args all the arguments of the syntax
* @return the created {@link CommandSyntax}
* @see #addSyntax(CommandCondition, CommandExecutor, Argument[])
*/
public CommandSyntax addSyntax(@NotNull CommandExecutor executor, @NotNull Argument<?>... args) {
final CommandSyntax syntax = new CommandSyntax(executor, args);
this.syntaxes.add(syntax);
return syntax;
return addSyntax(null, executor, args);
}
/**

View File

@ -198,6 +198,7 @@ public class CommandDispatcher {
final CommandSyntax finalSyntax = findMostCorrectSyntax(validSyntaxes, syntaxesValues, executorArgs);
if (finalSyntax != null) {
// A fully correct syntax has been found, use it
result.syntax = finalSyntax;
result.executor = finalSyntax.getExecutor();
result.arguments = executorArgs;
return result;
@ -333,6 +334,8 @@ public class CommandDispatcher {
private Command command;
// Command Executor
private CommandSyntax syntax;
private CommandExecutor executor;
private Arguments arguments;
@ -353,17 +356,27 @@ public class CommandDispatcher {
public void execute(@NotNull CommandSender source, @NotNull String commandString) {
// Global listener
command.globalListener(source, arguments, commandString);
// Condition check
// Command condition check
final CommandCondition condition = command.getCondition();
if (condition != null) {
final boolean result = condition.apply(source);
final boolean result = condition.canUse(source, commandString);
if (!result)
return;
}
// Condition is respected
if (executor != null) {
// An executor has been found
executor.apply(source, arguments);
if (syntax != null) {
// The executor is from a syntax
final CommandCondition commandCondition = syntax.getCommandCondition();
if (commandCondition == null || commandCondition.canUse(source, commandString)) {
executor.apply(source, arguments);
}
} else {
// The executor is probably the default one
executor.apply(source, arguments);
}
} else if (callback != null) {
// No syntax has been validated but the faulty argument with a callback has been found
// Execute the faulty argument callback

View File

@ -1,7 +1,10 @@
package 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 org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Represents a syntax in {@link Command}
@ -9,22 +12,39 @@ import org.jetbrains.annotations.NotNull;
*/
public class CommandSyntax {
private final Argument<?>[] args;
private CommandCondition commandCondition;
private CommandExecutor executor;
private final Argument<?>[] args;
protected CommandSyntax(@NotNull CommandExecutor commandExecutor, @NotNull Argument<?>... args) {
protected CommandSyntax(@Nullable CommandCondition commandCondition,
@NotNull CommandExecutor commandExecutor,
@NotNull Argument<?>... args) {
this.commandCondition = commandCondition;
this.executor = commandExecutor;
this.args = args;
}
/**
* Gets all the required {@link Argument} for this syntax.
* Gets the condition to use this syntax.
*
* @return the required arguments
* @return this command condition, null if none
*/
@NotNull
public Argument<?>[] getArguments() {
return args;
@Nullable
public CommandCondition getCommandCondition() {
return commandCondition;
}
/**
* Changes the command condition of this syntax.
* <p>
* Be aware that changing the command condition will not automatically update players auto-completion.
* You can create a new packet containing the changes with
* {@link net.minestom.server.command.CommandManager#createDeclareCommandsPacket(Player)}.
*
* @param commandCondition the new command condition, null to remove it
*/
public void setCommandCondition(@Nullable CommandCondition commandCondition) {
this.commandCondition = commandCondition;
}
/**
@ -46,4 +66,14 @@ public class CommandSyntax {
this.executor = executor;
}
/**
* Gets all the required {@link Argument} for this syntax.
*
* @return the required arguments
*/
@NotNull
public Argument<?>[] getArguments() {
return args;
}
}

View File

@ -2,10 +2,29 @@ package net.minestom.server.command.builder.condition;
import net.minestom.server.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Used to know if the {@link CommandSender} is allowed to run the command.
* Used to know if the {@link CommandSender} is allowed to run the command or a specific syntax.
*/
@FunctionalInterface
public interface CommandCondition {
boolean apply(@NotNull CommandSender source);
/**
* Called when the sender permission needs to be checked.
* <p>
* The first time will be during player connection in order to know
* if the command/syntax should be displayed as tab-completion suggestion,
* {@code commandString} will be null in this case.
* <p>
* Otherwise, {@code commandString} will never be null
* but will instead be the raw command string given by the sender.
* You should in this case warn the sender (eg by sending a message) if the condition is unsuccessful.
*
* @param source the sender of the command
* @param commandString the raw command string,
* null if the method has been called at player login
* @return true if the sender has the right to use the command, false otherwise
*/
boolean canUse(@NotNull CommandSender source, @Nullable String commandString);
}

View File

@ -3,7 +3,7 @@ package demo;
import demo.blocks.BurningTorchBlock;
import demo.blocks.StoneBlock;
import demo.blocks.UpdatableBlockDemo;
import demo.commands.*;
import demo.commands.TestCommand;
import net.minestom.server.MinecraftServer;
import net.minestom.server.command.CommandManager;
import net.minestom.server.instance.block.BlockManager;
@ -27,14 +27,14 @@ public class Main {
blockManager.registerBlockPlacementRule(new RedstonePlacementRule());
CommandManager commandManager = MinecraftServer.getCommandManager();
commandManager.register(new EntitySelectorCommand());
commandManager.register(new TestCommand());
/*commandManager.register(new EntitySelectorCommand());
commandManager.register(new HealthCommand());
commandManager.register(new SimpleCommand());
commandManager.register(new GamemodeCommand());
commandManager.register(new DimensionCommand());
commandManager.register(new ShutdownCommand());
commandManager.register(new TeleportCommand());
commandManager.register(new TeleportCommand());*/
commandManager.setUnknownCommandCallback((sender, command) -> sender.sendMessage("unknown command"));

View File

@ -172,7 +172,7 @@ public class PlayerInit {
player.addEventCallback(PlayerSpawnEvent.class, event -> {
player.setGameMode(GameMode.SURVIVAL);
if(event.isFirstSpawn()){
if (event.isFirstSpawn()) {
player.teleport(new Position(0, 64f, 0));
}

View File

@ -70,7 +70,7 @@ public class GamemodeCommand extends Command {
sender.sendMessage("'" + gamemode + "' is not a valid gamemode!");
}
private boolean isAllowed(CommandSender sender) {
private boolean isAllowed(CommandSender sender, String commandString) {
if (!sender.isPlayer()) {
sender.sendMessage("The command is only available for player");
return false;

View File

@ -28,7 +28,7 @@ public class HealthCommand extends Command {
addSyntax(this::execute, arg0);
}
private boolean condition(CommandSender sender) {
private boolean condition(CommandSender sender, String commandString) {
if (!sender.isPlayer()) {
sender.sendMessage("The command is only available for player");
return false;

View File

@ -17,8 +17,9 @@ public class TestCommand extends Command {
//addSyntax(this::execute, dynamicWord);
}
Argument test = ArgumentType.Word("test").from("hey");
Argument num = ArgumentType.Integer("num");
Argument test = ArgumentType.Word("wordT");
Argument testt = ArgumentType.Word("wordTt");
Argument test2 = ArgumentType.StringArray("array");
setDefaultExecutor((source, args) -> {
System.out.println("DEFAULT");
@ -31,7 +32,7 @@ public class TestCommand extends Command {
addSyntax((source, args) -> {
System.out.println(2);
}, test, num);
}, test, test2);
}
private void usage(CommandSender sender, Arguments arguments) {