From 0d58201e3f929b57b1318a008550c2bf587a3958 Mon Sep 17 00:00:00 2001 From: dfsek Date: Sun, 7 Mar 2021 23:44:19 -0700 Subject: [PATCH] mock command API --- .../terra/api/command/CommandManager.java | 9 ++ .../terra/api/command/CommandTemplate.java | 5 ++ .../terra/api/command/ExecutionState.java | 45 ++++++++++ .../api/command/TerraCommandManager.java | 85 +++++++++++++++++++ .../api/command/annotation/Argument.java | 16 ++++ .../terra/api/command/annotation/Command.java | 16 ++++ .../terra/api/command/annotation/Flag.java | 16 ++++ .../api/command/annotation/Subcommand.java | 18 ++++ .../dfsek/terra/commands/ReloadCommand.java | 13 +++ .../terra/commands/StructureCommand.java | 29 +++++++ .../structure/StructureExportCommand.java | 13 +++ .../structure/StructureLoadCommand.java | 25 ++++++ .../dfsek/terra/bukkit/command/Command.java | 9 +- .../terra/bukkit/command/PlayerCommand.java | 11 +-- 14 files changed, 301 insertions(+), 9 deletions(-) create mode 100644 common/src/main/java/com/dfsek/terra/api/command/CommandManager.java create mode 100644 common/src/main/java/com/dfsek/terra/api/command/CommandTemplate.java create mode 100644 common/src/main/java/com/dfsek/terra/api/command/ExecutionState.java create mode 100644 common/src/main/java/com/dfsek/terra/api/command/TerraCommandManager.java create mode 100644 common/src/main/java/com/dfsek/terra/api/command/annotation/Argument.java create mode 100644 common/src/main/java/com/dfsek/terra/api/command/annotation/Command.java create mode 100644 common/src/main/java/com/dfsek/terra/api/command/annotation/Flag.java create mode 100644 common/src/main/java/com/dfsek/terra/api/command/annotation/Subcommand.java create mode 100644 common/src/main/java/com/dfsek/terra/commands/ReloadCommand.java create mode 100644 common/src/main/java/com/dfsek/terra/commands/StructureCommand.java create mode 100644 common/src/main/java/com/dfsek/terra/commands/structure/StructureExportCommand.java create mode 100644 common/src/main/java/com/dfsek/terra/commands/structure/StructureLoadCommand.java diff --git a/common/src/main/java/com/dfsek/terra/api/command/CommandManager.java b/common/src/main/java/com/dfsek/terra/api/command/CommandManager.java new file mode 100644 index 000000000..3f5bae579 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/command/CommandManager.java @@ -0,0 +1,9 @@ +package com.dfsek.terra.api.command; + +import java.util.List; + +public interface CommandManager { + void execute(String command, List args); + + void register(String name, Class clazz); +} diff --git a/common/src/main/java/com/dfsek/terra/api/command/CommandTemplate.java b/common/src/main/java/com/dfsek/terra/api/command/CommandTemplate.java new file mode 100644 index 000000000..bb01ab198 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/command/CommandTemplate.java @@ -0,0 +1,5 @@ +package com.dfsek.terra.api.command; + +public interface CommandTemplate { + void execute(ExecutionState state); +} diff --git a/common/src/main/java/com/dfsek/terra/api/command/ExecutionState.java b/common/src/main/java/com/dfsek/terra/api/command/ExecutionState.java new file mode 100644 index 000000000..515993a0b --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/command/ExecutionState.java @@ -0,0 +1,45 @@ +package com.dfsek.terra.api.command; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public final class ExecutionState { + private final Set flags = new HashSet<>(); + private final Map args = new HashMap<>(); + + protected ExecutionState() { + + } + + protected void addFlag(String flag) { + flags.add(flag); + } + + protected void addArgument(String arg, String value) { + args.put(arg, value); + } + + @SuppressWarnings("unchecked") + public T getArgument(String argument, Class clazz) { + + String value = args.get(argument); + if(value == null) throw new IllegalArgumentException("Argument \"" + argument + "\" does not exist!"); + + if(clazz == int.class || clazz == Integer.class) { + return (T) new Integer(Integer.parseInt(value)); + } + if(clazz == double.class || clazz == Double.class) { + return (T) new Double(Double.parseDouble(value)); + } + + // TODO: type loaders + + return (T) value; + } + + public boolean hasFlag(String flag) { + return flags.contains(flag); + } +} diff --git a/common/src/main/java/com/dfsek/terra/api/command/TerraCommandManager.java b/common/src/main/java/com/dfsek/terra/api/command/TerraCommandManager.java new file mode 100644 index 000000000..6c7ebfe77 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/command/TerraCommandManager.java @@ -0,0 +1,85 @@ +package com.dfsek.terra.api.command; + +import com.dfsek.terra.api.command.annotation.Argument; +import com.dfsek.terra.api.command.annotation.Command; +import com.dfsek.terra.api.command.annotation.Subcommand; + +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TerraCommandManager implements CommandManager { + Map commands = new HashMap<>(); + + @Override + public void execute(String commandName, List args) { + + ExecutionState state = new ExecutionState(); + + CommandHolder commandHolder = commands.get(commandName); + Class commandClass = commandHolder.clazz; + + if(!commandClass.isAnnotationPresent(Command.class)) { + invoke(commandClass, state); + } + + Command command = commandClass.getAnnotation(Command.class); + + if(command.arguments().length == 0 && command.subcommands().length == 0) { + if(args.isEmpty()) invoke(commandClass, state); + else throw new IllegalArgumentException("Expected 0 arguments, found " + args.size()); + } + + if(commandHolder.subcommands.containsKey(args.get(0))) { + invoke(commandClass, state); + } + + + boolean req = true; + for(Argument argument : command.arguments()) { + if(!req && argument.required()) + throw new IllegalStateException("Required arguments must come first! Arguments: " + Arrays.toString(command.arguments())); + req = argument.required(); + + String arg = args.get(0); + + if(arg.startsWith("-")) { // flags have started. + + } + + } + } + + private void invoke(Class clazz, ExecutionState state) { + try { + clazz.getConstructor().newInstance().execute(state); + } catch(InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + e.printStackTrace(); + } + } + + @Override + public void register(String name, Class clazz) { + commands.put(name, new CommandHolder(clazz)); + } + + private static final class CommandHolder { + private final Class clazz; + private final Map subcommands = new HashMap<>(); + + private CommandHolder(Class clazz) { + this.clazz = clazz; + if(clazz.isAnnotationPresent(Command.class)) { + Command command = clazz.getAnnotation(Command.class); + for(Subcommand subcommand : command.subcommands()) { + subcommands.put(subcommand.value(), subcommand); + for(String alias : subcommand.aliases()) { + subcommands.put(alias, subcommand); + } + } + } + } + } +} diff --git a/common/src/main/java/com/dfsek/terra/api/command/annotation/Argument.java b/common/src/main/java/com/dfsek/terra/api/command/annotation/Argument.java new file mode 100644 index 000000000..b020a03d3 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/command/annotation/Argument.java @@ -0,0 +1,16 @@ +package com.dfsek.terra.api.command.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.ANNOTATION_TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface Argument { + String value(); + + boolean required() default true; + + Class type() default String.class; +} diff --git a/common/src/main/java/com/dfsek/terra/api/command/annotation/Command.java b/common/src/main/java/com/dfsek/terra/api/command/annotation/Command.java new file mode 100644 index 000000000..5eebc1ec3 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/command/annotation/Command.java @@ -0,0 +1,16 @@ +package com.dfsek.terra.api.command.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface Command { + Argument[] arguments() default {}; + + Flag[] flags() default {}; + + Subcommand[] subcommands() default {}; +} diff --git a/common/src/main/java/com/dfsek/terra/api/command/annotation/Flag.java b/common/src/main/java/com/dfsek/terra/api/command/annotation/Flag.java new file mode 100644 index 000000000..836b0eb0f --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/command/annotation/Flag.java @@ -0,0 +1,16 @@ +package com.dfsek.terra.api.command.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.ANNOTATION_TYPE) +public @interface Flag { + String value(); + + String shorthand() default ""; + + String defaultValue() default ""; +} diff --git a/common/src/main/java/com/dfsek/terra/api/command/annotation/Subcommand.java b/common/src/main/java/com/dfsek/terra/api/command/annotation/Subcommand.java new file mode 100644 index 000000000..7f729ba0a --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/api/command/annotation/Subcommand.java @@ -0,0 +1,18 @@ +package com.dfsek.terra.api.command.annotation; + +import com.dfsek.terra.api.command.CommandTemplate; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.ANNOTATION_TYPE) +public @interface Subcommand { + String value(); + + String[] aliases() default {}; + + Class clazz(); +} diff --git a/common/src/main/java/com/dfsek/terra/commands/ReloadCommand.java b/common/src/main/java/com/dfsek/terra/commands/ReloadCommand.java new file mode 100644 index 000000000..119429dc6 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/commands/ReloadCommand.java @@ -0,0 +1,13 @@ +package com.dfsek.terra.commands; + +import com.dfsek.terra.api.command.CommandTemplate; +import com.dfsek.terra.api.command.ExecutionState; +import com.dfsek.terra.api.command.annotation.Command; + +@Command() +public class ReloadCommand implements CommandTemplate { + @Override + public void execute(ExecutionState state) { + + } +} diff --git a/common/src/main/java/com/dfsek/terra/commands/StructureCommand.java b/common/src/main/java/com/dfsek/terra/commands/StructureCommand.java new file mode 100644 index 000000000..a2d176ea7 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/commands/StructureCommand.java @@ -0,0 +1,29 @@ +package com.dfsek.terra.commands; + +import com.dfsek.terra.api.command.CommandTemplate; +import com.dfsek.terra.api.command.ExecutionState; +import com.dfsek.terra.api.command.annotation.Command; +import com.dfsek.terra.api.command.annotation.Subcommand; +import com.dfsek.terra.commands.structure.StructureExportCommand; +import com.dfsek.terra.commands.structure.StructureLoadCommand; + +@Command( + subcommands = { + @Subcommand( + clazz = StructureExportCommand.class, + value = "export", + aliases = "ex" + ), + @Subcommand( + clazz = StructureLoadCommand.class, + value = "load", + aliases = "ld" + ) + } +) +public class StructureCommand implements CommandTemplate { + @Override + public void execute(ExecutionState state) { + + } +} diff --git a/common/src/main/java/com/dfsek/terra/commands/structure/StructureExportCommand.java b/common/src/main/java/com/dfsek/terra/commands/structure/StructureExportCommand.java new file mode 100644 index 000000000..8d36c6c56 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/commands/structure/StructureExportCommand.java @@ -0,0 +1,13 @@ +package com.dfsek.terra.commands.structure; + +import com.dfsek.terra.api.command.CommandTemplate; +import com.dfsek.terra.api.command.ExecutionState; +import com.dfsek.terra.api.command.annotation.Command; + +@Command +public class StructureExportCommand implements CommandTemplate { + @Override + public void execute(ExecutionState state) { + + } +} diff --git a/common/src/main/java/com/dfsek/terra/commands/structure/StructureLoadCommand.java b/common/src/main/java/com/dfsek/terra/commands/structure/StructureLoadCommand.java new file mode 100644 index 000000000..49ced1583 --- /dev/null +++ b/common/src/main/java/com/dfsek/terra/commands/structure/StructureLoadCommand.java @@ -0,0 +1,25 @@ +package com.dfsek.terra.commands.structure; + +import com.dfsek.terra.api.command.CommandTemplate; +import com.dfsek.terra.api.command.ExecutionState; +import com.dfsek.terra.api.command.annotation.Command; +import com.dfsek.terra.api.command.annotation.Flag; + +@Command( + flags = { + @Flag(value = "rotation", + defaultValue = "0", + shorthand = "r" + ), + @Flag(value = "load", + defaultValue = "FULL", + shorthand = "l" + ) + } +) +public class StructureLoadCommand implements CommandTemplate { + @Override + public void execute(ExecutionState state) { + + } +} diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/command/Command.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/command/Command.java index 93b1435ab..6fd8bbfee 100644 --- a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/command/Command.java +++ b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/command/Command.java @@ -13,7 +13,7 @@ import java.util.Arrays; import java.util.List; /** - * Represents a command or subcommand, can be nested via getSubCommands. + * Represents a command or subcommands, can be nested via getSubCommands. */ public abstract class Command implements CommandExecutor, TabCompleter { private final TerraPlugin main; @@ -29,7 +29,8 @@ public abstract class Command implements CommandExecutor, TabCompleter { } /** - * Gets the name of the command/subcommand + * Gets the name of the command/subcommands + * * @return Name of command */ public abstract String getName(); @@ -47,7 +48,7 @@ public abstract class Command implements CommandExecutor, TabCompleter { * (if defined) will be sent to the player. * * @param sender Source of the command - * @param command Command which was executed + * @param command CommandTemplate which was executed * @param label Alias of the command which was used * @param args Passed command arguments * @return true if a valid command, otherwise false @@ -67,7 +68,7 @@ public abstract class Command implements CommandExecutor, TabCompleter { * (if defined) will be sent to the player. * * @param sender Source of the command - * @param command Command which was executed + * @param command CommandTemplate which was executed * @param label Alias of the command which was used * @param args Passed command arguments * @return true if a valid command, otherwise false diff --git a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/command/PlayerCommand.java b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/command/PlayerCommand.java index 4f19eb741..cf52d897d 100644 --- a/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/command/PlayerCommand.java +++ b/platforms/bukkit/src/main/java/com/dfsek/terra/bukkit/command/PlayerCommand.java @@ -19,10 +19,10 @@ public abstract class PlayerCommand extends Command { * If false is returned, then the "usage" plugin.yml entry for this command * (if defined) will be sent to the player. * - * @param sender Source of the command - * @param command Command which was executed - * @param label Alias of the command which was used - * @param args Passed command arguments + * @param sender Source of the command + * @param command CommandTemplate which was executed + * @param label Alias of the command which was used + * @param args Passed command arguments * @return true if a valid command, otherwise false */ @Override @@ -34,6 +34,7 @@ public abstract class PlayerCommand extends Command { Player p = (Player) sender; return execute(p, command, label, args); } + /** * Executes the given command, returning its success. *
@@ -41,7 +42,7 @@ public abstract class PlayerCommand extends Command { * (if defined) will be sent to the player. * * @param sender Player that executed command - * @param command Command which was executed + * @param command CommandTemplate which was executed * @param label Alias of the command which was used * @param args Passed command arguments * @return true if a valid command, otherwise false