Remove command generic + WIP target selector

This commit is contained in:
Felix Cravic 2020-07-14 13:35:07 +02:00
parent 7b53ec0999
commit c8f2c9c078
21 changed files with 335 additions and 87 deletions

View File

@ -68,7 +68,7 @@ dependencies {
annotationProcessor 'org.projectlombok:lombok:1.18.12'
// Pathfinding
implementation 'com.github.MadMartian:hydrazine-path-finding:1.1.0'
api 'com.github.MadMartian:hydrazine-path-finding:1.1.0'
api "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
api 'com.github.jglrxavpok:Hephaistos:v1.0.3'

View File

@ -3,13 +3,10 @@ package fr.themode.demo;
import fr.themode.demo.blocks.BurningTorchBlock;
import fr.themode.demo.blocks.StoneBlock;
import fr.themode.demo.blocks.UpdatableBlockDemo;
import fr.themode.demo.commands.DimensionCommand;
import fr.themode.demo.commands.GamemodeCommand;
import fr.themode.demo.commands.HealthCommand;
import fr.themode.demo.commands.SimpleCommand;
import fr.themode.demo.commands.EntitySelectorCommand;
import fr.themode.demo.commands.TestCommand;
import net.minestom.server.MinecraftServer;
import net.minestom.server.command.CommandManager;
import net.minestom.server.extras.MojangAuth;
import net.minestom.server.instance.block.BlockManager;
import net.minestom.server.instance.block.rule.vanilla.RedstonePlacementRule;
import net.minestom.server.storage.StorageManager;
@ -32,10 +29,12 @@ public class Main {
blockManager.registerBlockPlacementRule(new RedstonePlacementRule());
CommandManager commandManager = MinecraftServer.getCommandManager();
commandManager.register(new HealthCommand());
commandManager.register(new SimpleCommand());
commandManager.register(new GamemodeCommand());
commandManager.register(new DimensionCommand());
commandManager.register(new EntitySelectorCommand());
commandManager.register(new TestCommand());
//commandManager.register(new HealthCommand());
//commandManager.register(new SimpleCommand());
//commandManager.register(new GamemodeCommand());
//commandManager.register(new DimensionCommand());
/*RecipeManager recipeManager = MinecraftServer.getRecipeManager();
ShapelessRecipe shapelessRecipe = new ShapelessRecipe("test", "groupname") {
@ -64,7 +63,7 @@ public class Main {
PlayerInit.init();
MojangAuth.init();
//MojangAuth.init();
minecraftServer.start("localhost", 25565, PlayerInit.getResponseDataConsumer());
}

View File

@ -0,0 +1,26 @@
package fr.themode.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.ArgumentType;
import net.minestom.server.command.builder.arguments.minecraft.ArgumentEntities;
import net.minestom.server.entity.Entity;
import java.util.ArrayList;
public class EntitySelectorCommand extends Command {
public EntitySelectorCommand() {
super("ent");
ArgumentEntities argumentEntities = ArgumentType.Entities("entities");
addSyntax(this::executor, argumentEntities);
}
private void executor(CommandSender commandSender, Arguments arguments) {
ArrayList<Entity> entities = arguments.getEntities("entities");
}
}

View File

@ -13,7 +13,7 @@ import java.util.Optional;
/**
* Command that make a player change gamemode
*/
public class GamemodeCommand extends Command<CommandSender> {
public class GamemodeCommand extends Command {
public GamemodeCommand() {
super("gamemode", "g", "gm");

View File

@ -8,7 +8,7 @@ import net.minestom.server.command.builder.arguments.ArgumentType;
import net.minestom.server.command.builder.arguments.number.ArgumentNumber;
import net.minestom.server.entity.Player;
public class HealthCommand extends Command<CommandSender> {
public class HealthCommand extends Command {
public HealthCommand() {
super("health", "h", "healthbar");

View File

@ -0,0 +1,56 @@
package fr.themode.demo.commands;
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.command.builder.arguments.Argument;
import net.minestom.server.command.builder.arguments.ArgumentType;
import net.minestom.server.entity.Player;
import java.util.Optional;
public class TestCommand extends Command {
public TestCommand() {
super("msg");
setDefaultExecutor(this::usage);
Argument player = ArgumentType.Word("player");
Argument message = ArgumentType.StringArray("array");
addSyntax(this::execute, player, message);
}
private void usage(CommandSender sender, Arguments arguments) {
sender.sendMessage("Usage: /whisper <player> <message>");
}
private void execute(CommandSender sender, Arguments arguments) {
Player player = (Player) sender;
String targetName = arguments.getWord("player");
String[] Message = arguments.getStringArray("array");
Optional<Player> target = player.getInstance().getPlayers().stream().filter(p -> p.getUsername().equalsIgnoreCase(targetName)).findFirst();
if (target.isPresent()) {
if (target.get() == player) {
player.sendMessage("You cannot message yourself");
} else {
String message = "";
for (int i = 0; i < Message.length; i++) {
if (i != 0) {
message = message + " ";
}
message = message + Message[i];
}
player.sendMessage("You -> " + targetName + ": " + message);
target.get().sendMessage(player.getUsername() + " -> You: " + message);
}
} else {
player.sendMessage(ColoredText.ofFormat("{@argument.player.unknown}"));
}
}
private boolean isAllowed(Player player) {
return true; // TODO: permissions
}
}

View File

@ -4,10 +4,7 @@ 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.ArgumentColor;
import net.minestom.server.command.builder.arguments.minecraft.ArgumentFloatRange;
import net.minestom.server.command.builder.arguments.minecraft.ArgumentIntRange;
import net.minestom.server.command.builder.arguments.minecraft.ArgumentTime;
import net.minestom.server.command.builder.arguments.minecraft.*;
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;
@ -33,7 +30,7 @@ public class CommandManager {
private ConsoleSender consoleSender = new ConsoleSender();
private CommandDispatcher<CommandSender> dispatcher = new CommandDispatcher<>();
private CommandDispatcher dispatcher = new CommandDispatcher();
private Map<String, CommandProcessor> commandProcessorMap = new HashMap<>();
public CommandManager() {
@ -59,7 +56,7 @@ public class CommandManager {
running = false;
}
public void register(Command<CommandSender> command) {
public void register(Command command) {
this.dispatcher.register(command);
}
@ -194,9 +191,9 @@ public class CommandManager {
// Contains the children of the main node (all commands name)
ArrayList<Integer> rootChildren = new ArrayList<>();
for (Command<CommandSender> command : dispatcher.getCommands()) {
for (Command command : dispatcher.getCommands()) {
// Check if player should see this command
CommandCondition<Player> commandCondition = command.getCondition();
CommandCondition commandCondition = command.getCondition();
if (commandCondition != null) {
// Do not show command if return false
if (!commandCondition.apply(player)) {
@ -334,13 +331,13 @@ public class CommandManager {
private List<DeclareCommandsPacket.Node> toNodes(Argument argument, boolean executable) {
List<DeclareCommandsPacket.Node> nodes = new ArrayList<>();
DeclareCommandsPacket.Node testNode = simpleArgumentNode(nodes, argument, executable);
/*DeclareCommandsPacket.Node testNode = simpleArgumentNode(nodes, argument, executable);
testNode.parser = "minecraft:entity";
testNode.properties = packetWriter -> packetWriter.writeByte((byte) 0x0);
if (true) {
return nodes;
}
}*/
if (argument instanceof ArgumentBoolean) {
DeclareCommandsPacket.Node argumentNode = simpleArgumentNode(nodes, argument, executable);
@ -449,6 +446,20 @@ public class CommandManager {
} 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);
};
}
return nodes;

View File

@ -1,5 +1,7 @@
package net.minestom.server.command.builder;
public interface ArgumentCallback<S> {
void apply(S source, String value, int error);
import net.minestom.server.command.CommandSender;
public interface ArgumentCallback {
void apply(CommandSender source, String value, int error);
}

View File

@ -2,6 +2,7 @@ package net.minestom.server.command.builder;
import net.minestom.server.chat.ChatColor;
import net.minestom.server.command.builder.structure.Structure;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.EntityType;
import net.minestom.server.item.Enchantment;
import net.minestom.server.particle.Particle;
@ -10,6 +11,7 @@ import net.minestom.server.utils.math.FloatRange;
import net.minestom.server.utils.math.IntRange;
import net.minestom.server.utils.time.UpdateOption;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@ -85,6 +87,10 @@ public class Arguments {
return (FloatRange) getObject(id);
}
public ArrayList<Entity> getEntities(String id) {
return (ArrayList<Entity>) getObject(id);
}
public Object getObject(String id) {
return args.getOrDefault(id, null);
}

View File

@ -7,7 +7,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class Command<S> {
public class Command {
private String name;
private String[] aliases;
@ -31,15 +31,15 @@ public class Command<S> {
return condition;
}
public void setCondition(CommandCondition<S> commandCondition) {
public void setCondition(CommandCondition commandCondition) {
this.condition = commandCondition;
}
public void addCallback(ArgumentCallback<S> callback, Argument argument) {
public void addCallback(ArgumentCallback callback, Argument argument) {
argument.setCallback(callback);
}
public void addSyntax(CommandExecutor<S> executor, Argument... args) {
public void addSyntax(CommandExecutor executor, Argument... args) {
CommandSyntax syntax = new CommandSyntax(args);
syntax.setExecutor(executor);
this.syntaxes.add(syntax);
@ -57,7 +57,7 @@ public class Command<S> {
return defaultExecutor;
}
public void setDefaultExecutor(CommandExecutor<S> executor) {
public void setDefaultExecutor(CommandExecutor executor) {
this.defaultExecutor = executor;
}

View File

@ -1,17 +1,18 @@
package net.minestom.server.command.builder;
import net.minestom.server.command.CommandSender;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.condition.CommandCondition;
import java.util.*;
import java.util.regex.Pattern;
public class CommandDispatcher<S> {
public class CommandDispatcher {
private Map<String, Command<S>> commandMap = new HashMap<>();
private Set<Command<S>> commands = new HashSet<>();
private Map<String, Command> commandMap = new HashMap<>();
private Set<Command> commands = new HashSet<>();
public void register(Command<S> command) {
public void register(Command command) {
this.commandMap.put(command.getName(), command);
for (String alias : command.getAliases()) {
this.commandMap.put(alias, command);
@ -29,7 +30,7 @@ public class CommandDispatcher<S> {
String[] args = commandString.replaceFirst(Pattern.quote(commandName), "").trim().split(spaceRegex);
Command<S> command = findCommand(commandName);
Command command = findCommand(commandName);
// TODO change return
if (command == null)
return null;
@ -37,20 +38,20 @@ public class CommandDispatcher<S> {
return findCommandResult(command, args);
}
public void execute(S source, String commandString) {
public void execute(CommandSender source, String commandString) {
CommandResult result = parse(commandString);
result.execute(source);
}
public Set<Command<S>> getCommands() {
public Set<Command> getCommands() {
return Collections.unmodifiableSet(commands);
}
private Command<S> findCommand(String commandName) {
private Command findCommand(String commandName) {
return commandMap.containsKey(commandName) ? commandMap.get(commandName) : null;
}
private CommandResult findCommandResult(Command<S> command, String[] args) {
private CommandResult findCommandResult(Command command, String[] args) {
CommandResult result = new CommandResult();
result.command = command;
@ -215,7 +216,7 @@ public class CommandDispatcher<S> {
private class CommandResult {
// Command
private Command<S> command;
private Command command;
// Command Executor
private CommandExecutor executor;
@ -226,7 +227,7 @@ public class CommandDispatcher<S> {
private String value;
private int error;
public void execute(S source) {
public void execute(CommandSender source) {
// Condition check
CommandCondition condition = command.getCondition();
if (condition != null) {

View File

@ -1,5 +1,7 @@
package net.minestom.server.command.builder;
public interface CommandExecutor<S> {
void apply(S source, Arguments args);
import net.minestom.server.command.CommandSender;
public interface CommandExecutor {
void apply(CommandSender source, Arguments args);
}

View File

@ -52,26 +52,60 @@ public abstract class Argument<T> {
*/
public abstract int getConditionResult(T value);
/**
* Get the ID of the argument, showed in-game above the chat bar
* and used to retrieve the data when the command is parsed
*
* @return the argument id
*/
public String getId() {
return id;
}
/**
* Get if the argument can contain space
*
* @return true if the argument allows space, false otherwise
*/
public boolean allowSpace() {
return allowSpace;
}
/**
* Get if the argument always use all the remaining characters
* <p>
* ex: /help I am a test - would get you "I am a test"
* if the sole argument does use the remaining
*
* @return true if the argument use all the remaining characters, false otherwise
*/
public boolean useRemaining() {
return useRemaining;
}
/**
* Get the argument callback to check if the argument-specific conditions are validated or not
*
* @return the argument callback
*/
public ArgumentCallback getCallback() {
return callback;
}
/**
* Set the argument callback
*
* @param callback the argument callback
*/
public void setCallback(ArgumentCallback callback) {
this.callback = callback;
}
/**
* Get if the argument has any error callback
*
* @return true if the argument has an error callback, false otherwise
*/
public boolean hasErrorCallback() {
return callback != null;
}

View File

@ -1,9 +1,6 @@
package net.minestom.server.command.builder.arguments;
import net.minestom.server.command.builder.arguments.minecraft.ArgumentColor;
import net.minestom.server.command.builder.arguments.minecraft.ArgumentFloatRange;
import net.minestom.server.command.builder.arguments.minecraft.ArgumentIntRange;
import net.minestom.server.command.builder.arguments.minecraft.ArgumentTime;
import net.minestom.server.command.builder.arguments.minecraft.*;
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;
@ -85,4 +82,8 @@ public class ArgumentType {
return new ArgumentFloatRange(id);
}
public static ArgumentEntities Entities(String id) {
return new ArgumentEntities(id);
}
}

View File

@ -0,0 +1,132 @@
package net.minestom.server.command.builder.arguments.minecraft;
import net.minestom.server.MinecraftServer;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.entity.Entity;
import net.minestom.server.network.ConnectionManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
// TODO
/**
* Represent the target selector argument
* https://minecraft.gamepedia.com/Commands#Target_selectors
*/
public class ArgumentEntities extends Argument<ArrayList<Entity>> {
public static final int INVALID_SYNTAX = -2;
public static final int ONLY_SINGLE_ENTITY_ERROR = -3;
public static final int ONLY_PLAYERS_ERROR = -4;
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 List<String> validArguments = 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");
private boolean onlySingleEntity;
private boolean onlyPlayers;
public ArgumentEntities(String id) {
super(id, true);
}
public ArgumentEntities singleEntity(boolean singleEntity) {
this.onlySingleEntity = singleEntity;
return this;
}
public ArgumentEntities onlyPlayers(boolean onlyPlayers) {
this.onlyPlayers = onlyPlayers;
return this;
}
@Override
public int getCorrectionResult(String value) {
System.out.println("check: " + value);
// Check for raw player name
if (value.length() <= 16) {
if (CONNECTION_MANAGER.getPlayer(value) != null)
return SUCCESS;
}
// The minimum size is always 0 (for the selector variable, ex: @p)
if (value.length() < 2)
return INVALID_SYNTAX;
// The target selector variable always start by '@'
if (!value.startsWith("@"))
return INVALID_SYNTAX;
final String selectorVariable = value.substring(0, 2);
// Check if the selector variable used exists
if (!selectorVariables.contains(selectorVariable))
return INVALID_SYNTAX;
// Check if it should only select single entity and if the selector variable valid the condition
if (onlySingleEntity && !singleOnlySelector.contains(selectorVariable))
return ONLY_SINGLE_ENTITY_ERROR;
// Check if it should only select players and if the selector variable valid the condition
if (onlyPlayers && !playersOnlySelector.contains(selectorVariable))
return ONLY_PLAYERS_ERROR;
// The selector is a single selector variable which verify all the conditions
if (value.length() == 2)
return SUCCESS;
// START PARSING THE STRUCTURE
final String structure = value.substring(2);
// The structure isn't opened or closed properly
if (!structure.startsWith("[") || !structure.endsWith("]"))
return INVALID_SYNTAX;
final String structureData = structure.substring(1, structure.length() - 1);
String currentArgument = "";
for (int i = 0; i < structureData.length(); i++) {
final char c = structureData.charAt(i);
if (c == '=') {
i = retrieveArgument(structureData, currentArgument, i);
} else {
currentArgument += c;
}
}
return 0;
}
private int retrieveArgument(String structureData, String argument, int index) {
int finalIndex = index;
for (int i = index + 1; i < structureData.length(); i++) {
System.out.println("char: " + structureData.charAt(i));
System.out.println("retrieve: " + argument);
}
return finalIndex;
}
@Override
public ArrayList<Entity> parse(String value) {
return null;
}
@Override
public int getConditionResult(ArrayList<Entity> value) {
return SUCCESS;
}
public boolean isOnlySingleEntity() {
return onlySingleEntity;
}
public boolean isOnlyPlayers() {
return onlyPlayers;
}
}

View File

@ -1,32 +0,0 @@
package net.minestom.server.command.builder.arguments.minecraft;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.entity.Entity;
// TODO
/**
* Represent the target selector argument
* https://minecraft.gamepedia.com/Commands#Target_selectors
*/
public class ArgumentEntity extends Argument<Entity> {
public ArgumentEntity(String id, boolean allowSpace) {
super(id, allowSpace);
}
@Override
public int getCorrectionResult(String value) {
return 0;
}
@Override
public Entity parse(String value) {
return null;
}
@Override
public int getConditionResult(Entity value) {
return 0;
}
}

View File

@ -5,8 +5,8 @@ import net.minestom.server.utils.math.FloatRange;
import java.util.regex.Pattern;
/**
* Represent an argument which will give you a {@link FloatRange}
* Chat format: ..3, 3.., 5..10
* Represent an argument which will give you an {@link FloatRange}
* Chat format: ..3, 3.., 5..10, 15
*/
public class ArgumentFloatRange extends ArgumentRange<FloatRange> {
@ -69,7 +69,7 @@ public class ArgumentFloatRange extends ArgumentRange<FloatRange> {
return new FloatRange(min, max);
} else {
final float number = Float.valueOf(value);
return new FloatRange(number, number);
return new FloatRange(number);
}
}
}

View File

@ -5,8 +5,8 @@ import net.minestom.server.utils.math.IntRange;
import java.util.regex.Pattern;
/**
* Represent an argument which will give you a {@link IntRange}
* Chat format: ..3, 3.., 5..10
* Represent an argument which will give you an {@link IntRange}
* Chat format: ..3, 3.., 5..10, 15
*/
public class ArgumentIntRange extends ArgumentRange<IntRange> {
@ -69,7 +69,7 @@ public class ArgumentIntRange extends ArgumentRange<IntRange> {
return new IntRange(min, max);
} else {
final int number = Integer.valueOf(value);
return new IntRange(number, number);
return new IntRange(number);
}
}
}

View File

@ -1,5 +1,7 @@
package net.minestom.server.command.builder.condition;
public interface CommandCondition<S> {
boolean apply(S source);
import net.minestom.server.command.CommandSender;
public interface CommandCondition {
boolean apply(CommandSender source);
}

View File

@ -9,6 +9,10 @@ public class FloatRange {
this.max = max;
}
public FloatRange(float value) {
this(value, value);
}
public float getMin() {
return min;
}

View File

@ -9,6 +9,10 @@ public class IntRange {
this.max = max;
}
public IntRange(int value) {
this(value, value);
}
public int getMin() {
return min;
}