From 5b5cb04f8b6a1eed1f7a521d88cbc2ab9b6640a7 Mon Sep 17 00:00:00 2001 From: kento2 Date: Tue, 17 Feb 2026 16:18:06 +0100 Subject: [PATCH] . --- .../java/de/kentoj/scrow/bukkit/ScrowAPI.java | 10 +++ .../services/command/CommandArgument.java | 4 +- .../services/command/CommandContext.java | 14 --- .../command/CommandExecutionContext.java | 6 -- .../services/command/CommandExecutor.java | 4 +- .../services/command/CommandManager.java | 1 + .../bukkit/services/command/CommandNode.java | 40 --------- .../services/command/CommandReader.java | 14 +++ .../services/command/arg/ArgumentType.java | 6 -- .../command/arg/PlayerArgumentType.java | 28 ------ .../command/cmds2/CommandArgument.java | 12 +++ .../command/cmds2/SuggestionProvider.java | 10 +++ .../cmds2/node/ArgumentCommandNode.java | 14 +++ .../command/cmds2/node/CommandNode.java | 24 +++++ .../cmds2/node/LiteralCommandNode.java | 10 +++ .../command/cmds2/node/RootCommandNode.java | 12 +++ .../command/context/CommandContext.java | 27 ++++++ .../exception/CommandSyntaxException.java | 22 +++++ .../services/command/node/CommandNode.java | 51 +++++++++++ .../command/node/CommandNodeFactory.java | 10 +++ .../command/node/CommandNodeRepository.java | 24 +++++ .../node/CommandNodeWithArguments.java | 34 +++++++ .../node/CommandNodeWithSubCommand.java | 20 +++++ .../services/command/type/ArgumentType.java | 19 ++++ .../command/type/EntityArgumentType.java | 47 ++++++++++ .../kentoj/scrow/bukkit/CoreImplPlugin.java | 5 +- .../kentoj/scrow/bukkit/ScrowAPISurface.java | 12 ++- .../scrow/bukkit/cmds2/BukkitCommand.java | 57 ++++++++++++ .../scrow/bukkit/cmds2/CoinsCommand.java | 53 +++++++++++ .../bukkit/command/AbstractCommandNode.java | 79 ----------------- .../bukkit/command/CommandContextImpl.java | 2 +- .../command/CommandExecutionContextImpl.java | 39 ++++++-- .../bukkit/command/bukkit/ArgumentData.java | 11 +++ .../bukkit/command/bukkit/BukkitCommand.java | 22 +++-- .../command/bukkit/CommandManagerImpl.java | 10 +-- .../bukkit/CommandProcessException.java | 10 --- .../command/bukkit/CommandProcessor.java | 88 ++++++++----------- .../command/node/CommandNodeFactoryImpl.java | 23 +++++ .../bukkit/command/node/CommandNodeImpl.java | 36 ++++++++ .../node/CommandNodeWithArgumentsImpl.java | 50 +++++++++++ .../node/CommandNodeWithSubCommandImpl.java | 32 +++++++ .../bukkit/friends/casd/FriendAddCommand.java | 28 ------ .../bukkit/friends/casd/FriendCommand.java | 19 ---- .../friends/casd/FriendCommandExecutor.java | 13 --- .../bukkit/friends/command/FriendCommand.java | 13 +++ .../bukkit/friends/command/LaunchCommand.java | 13 ++- 46 files changed, 744 insertions(+), 334 deletions(-) delete mode 100644 core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandContext.java delete mode 100644 core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandExecutionContext.java delete mode 100644 core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandNode.java create mode 100644 core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandReader.java delete mode 100644 core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/arg/ArgumentType.java delete mode 100644 core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/arg/PlayerArgumentType.java create mode 100644 core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/cmds2/CommandArgument.java create mode 100644 core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/cmds2/SuggestionProvider.java create mode 100644 core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/cmds2/node/ArgumentCommandNode.java create mode 100644 core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/cmds2/node/CommandNode.java create mode 100644 core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/cmds2/node/LiteralCommandNode.java create mode 100644 core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/cmds2/node/RootCommandNode.java create mode 100644 core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/context/CommandContext.java create mode 100644 core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/exception/CommandSyntaxException.java create mode 100644 core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/node/CommandNode.java create mode 100644 core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/node/CommandNodeFactory.java create mode 100644 core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/node/CommandNodeRepository.java create mode 100644 core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/node/CommandNodeWithArguments.java create mode 100644 core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/node/CommandNodeWithSubCommand.java create mode 100644 core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/type/ArgumentType.java create mode 100644 core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/type/EntityArgumentType.java create mode 100644 core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/cmds2/BukkitCommand.java create mode 100644 core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/cmds2/CoinsCommand.java delete mode 100644 core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/AbstractCommandNode.java create mode 100644 core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/bukkit/ArgumentData.java delete mode 100644 core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/bukkit/CommandProcessException.java create mode 100644 core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/node/CommandNodeFactoryImpl.java create mode 100644 core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/node/CommandNodeImpl.java create mode 100644 core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/node/CommandNodeWithArgumentsImpl.java create mode 100644 core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/node/CommandNodeWithSubCommandImpl.java delete mode 100644 core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/casd/FriendAddCommand.java delete mode 100644 core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/casd/FriendCommand.java delete mode 100644 core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/casd/FriendCommandExecutor.java create mode 100644 core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendCommand.java diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/ScrowAPI.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/ScrowAPI.java index 81d26a5..f267154 100644 --- a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/ScrowAPI.java +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/ScrowAPI.java @@ -6,8 +6,11 @@ import de.kentoj.scrow.bukkit.services.command.CommandManager; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; +import org.bukkit.Bukkit; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; +import reactor.core.scheduler.Scheduler; +import reactor.core.scheduler.Schedulers; @NoArgsConstructor(access = AccessLevel.PRIVATE) public class ScrowAPI { @@ -18,6 +21,8 @@ public class ScrowAPI { private static EconomyService economyService; @Getter private static CommandManager commandManager; + @Getter + private static Scheduler minecraftScheduler; @ApiStatus.Internal public static void setDatabase(@Nullable MongoDatabase database) { @@ -33,5 +38,10 @@ public class ScrowAPI { public static void setCommandManager(CommandManager commandManager) { ScrowAPI.commandManager = commandManager; } + + @ApiStatus.Internal + public static void setMinecraftScheduler(Scheduler minecraftScheduler) { + ScrowAPI.minecraftScheduler = minecraftScheduler; + } } diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandArgument.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandArgument.java index 903e370..082a2cb 100644 --- a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandArgument.java +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandArgument.java @@ -1,10 +1,12 @@ package de.kentoj.scrow.bukkit.services.command; -import de.kentoj.scrow.bukkit.services.command.arg.ArgumentType; +import de.kentoj.scrow.bukkit.services.command.type.ArgumentType; +import de.kentoj.scrow.bukkit.services.command.context.CommandContext; import lombok.Value; import java.util.function.Function; +// TODO convert to interface @Value public class CommandArgument { int index; diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandContext.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandContext.java deleted file mode 100644 index 8394a2d..0000000 --- a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandContext.java +++ /dev/null @@ -1,14 +0,0 @@ -package de.kentoj.scrow.bukkit.services.command; - -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Entity; -import org.bukkit.entity.Player; - -public interface CommandContext { - - CommandSender getSender(); - - Player getSenderPlayer(); - - Entity getSenderEntity(); -} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandExecutionContext.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandExecutionContext.java deleted file mode 100644 index d750801..0000000 --- a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandExecutionContext.java +++ /dev/null @@ -1,6 +0,0 @@ -package de.kentoj.scrow.bukkit.services.command; - -public interface CommandExecutionContext extends CommandContext { - - T getArg(String key); -} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandExecutor.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandExecutor.java index abadfbf..3421389 100644 --- a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandExecutor.java +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandExecutor.java @@ -1,9 +1,11 @@ package de.kentoj.scrow.bukkit.services.command; +import de.kentoj.scrow.bukkit.services.command.context.CommandContext; + public interface CommandExecutor { /** * @return true on success, 1 on failure */ - void execute(CommandExecutionContext ctx); + void execute(CommandContext ctx); } diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandManager.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandManager.java index 1fe5944..19480f8 100644 --- a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandManager.java +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandManager.java @@ -1,5 +1,6 @@ package de.kentoj.scrow.bukkit.services.command; +import de.kentoj.scrow.bukkit.services.command.node.CommandNode; import org.bukkit.plugin.Plugin; public interface CommandManager { diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandNode.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandNode.java deleted file mode 100644 index 325ad20..0000000 --- a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandNode.java +++ /dev/null @@ -1,40 +0,0 @@ -package de.kentoj.scrow.bukkit.services.command; - -import de.kentoj.scrow.bukkit.services.command.arg.ArgumentType; -import org.bukkit.permissions.Permission; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.function.Function; - -// TODO javadoc -public interface CommandNode { - - void addArg(String name, ArgumentType type, @Nullable Function defaultValue); - - default void addArg(String name, ArgumentType type) { - addArg(name, type, null); - } - - void addSubCommand(CommandNode subCommand); - - void setPermission(@Nullable Permission permission); - - default void setPermission(String permission) { - setPermission(new Permission(permission)); - } - - @Nullable Permission getPermission(); - - @Nullable CommandArgument getArg(int index); - - List getSubCommands(); - - String getName(); - - String[] getAliases(); - - String getDescription(); - - @Nullable CommandExecutor getExecutor(); -} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandReader.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandReader.java new file mode 100644 index 0000000..1554289 --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/CommandReader.java @@ -0,0 +1,14 @@ +package de.kentoj.scrow.bukkit.services.command; + +public interface CommandReader { + + boolean isEOF(); + + String readWord(); + + String readQuotedString(); + + int getCursor(); + + void setCursor(int cursor); +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/arg/ArgumentType.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/arg/ArgumentType.java deleted file mode 100644 index d21a1d6..0000000 --- a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/arg/ArgumentType.java +++ /dev/null @@ -1,6 +0,0 @@ -package de.kentoj.scrow.bukkit.services.command.arg; - -public interface ArgumentType { - - S parseInput(String str); -} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/arg/PlayerArgumentType.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/arg/PlayerArgumentType.java deleted file mode 100644 index 17b9909..0000000 --- a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/arg/PlayerArgumentType.java +++ /dev/null @@ -1,28 +0,0 @@ -package de.kentoj.scrow.bukkit.services.command.arg; - -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; -import org.bukkit.entity.Player; - -import java.util.UUID; - -// FIXME module stuff -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class PlayerArgumentType { - - public static final ArgumentType player = str -> { - if (str.length() == 36) { - var uuid = UUID.fromString(str); - return Bukkit.getPlayer(uuid); - } else { - return Bukkit.getPlayer(str); - } - }; - - public static final ArgumentType offlinePlayer = str -> { - var uuid = UUID.fromString(str); - return Bukkit.getOfflinePlayer(uuid); - }; -} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/cmds2/CommandArgument.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/cmds2/CommandArgument.java new file mode 100644 index 0000000..70029d2 --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/cmds2/CommandArgument.java @@ -0,0 +1,12 @@ +package de.kentoj.scrow.bukkit.services.command.cmds2; + +import de.kentoj.scrow.bukkit.services.command.context.CommandContext; +import de.kentoj.scrow.bukkit.services.command.type.ArgumentType; +import org.jetbrains.annotations.Nullable; + +public interface CommandArgument { + + ArgumentType getType(); + + @Nullable T getDefaultValue(CommandContext ctx); +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/cmds2/SuggestionProvider.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/cmds2/SuggestionProvider.java new file mode 100644 index 0000000..008a833 --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/cmds2/SuggestionProvider.java @@ -0,0 +1,10 @@ +package de.kentoj.scrow.bukkit.services.command.cmds2; + +import de.kentoj.scrow.bukkit.services.command.context.CommandContext; + +import java.util.Set; + +public interface SuggestionProvider { + + Set suggest(CommandContext context); +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/cmds2/node/ArgumentCommandNode.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/cmds2/node/ArgumentCommandNode.java new file mode 100644 index 0000000..f48b9e5 --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/cmds2/node/ArgumentCommandNode.java @@ -0,0 +1,14 @@ +package de.kentoj.scrow.bukkit.services.command.cmds2.node; + +import de.kentoj.scrow.bukkit.services.command.context.CommandContext; +import de.kentoj.scrow.bukkit.services.command.type.ArgumentType; + +import java.util.function.Function; + +public non-sealed interface ArgumentCommandNode extends CommandNode { + + ArgumentType getArgument(String name); + + void addArgument(String name, ArgumentType arg); + void addArgument(String name, ArgumentType arg, Function getDefault); +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/cmds2/node/CommandNode.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/cmds2/node/CommandNode.java new file mode 100644 index 0000000..43dc85a --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/cmds2/node/CommandNode.java @@ -0,0 +1,24 @@ +package de.kentoj.scrow.bukkit.services.command.cmds2.node; + +import de.kentoj.scrow.bukkit.services.command.CommandExecutor; +import de.kentoj.scrow.bukkit.services.command.cmds2.SuggestionProvider; +import org.jetbrains.annotations.Nullable; + +public sealed interface CommandNode permits LiteralCommandNode, ArgumentCommandNode, RootCommandNode { + + void setName(String name); + String getName(); + + void addSubNode(CommandNode node); + @Nullable CommandNode getSubNode(String rawArg); + boolean hasNextNode(); + + void setExecutor(@Nullable CommandExecutor executor); + @Nullable CommandExecutor getExecutor(); + + void setSuggestionProvider(SuggestionProvider suggestionProvider); + SuggestionProvider getSuggestionProvider(); + + void setDescription(@Nullable String description); + @Nullable String getDescription(); +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/cmds2/node/LiteralCommandNode.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/cmds2/node/LiteralCommandNode.java new file mode 100644 index 0000000..c3f96ac --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/cmds2/node/LiteralCommandNode.java @@ -0,0 +1,10 @@ +package de.kentoj.scrow.bukkit.services.command.cmds2.node; + +import org.bukkit.permissions.Permission; + +public non-sealed interface LiteralCommandNode extends CommandNode { + + Permission getRequiredPermission(); + + void setRequiredPermission(Permission permission); +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/cmds2/node/RootCommandNode.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/cmds2/node/RootCommandNode.java new file mode 100644 index 0000000..9dde42e --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/cmds2/node/RootCommandNode.java @@ -0,0 +1,12 @@ +package de.kentoj.scrow.bukkit.services.command.cmds2.node; + +import java.util.Set; + +public non-sealed interface RootCommandNode extends CommandNode, LiteralCommandNode { + + Set getAliases(); + + void setAliases(Set aliases); + + void addAliases(String... aliases); +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/context/CommandContext.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/context/CommandContext.java new file mode 100644 index 0000000..dc50762 --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/context/CommandContext.java @@ -0,0 +1,27 @@ +package de.kentoj.scrow.bukkit.services.command.context; + +import de.kentoj.scrow.bukkit.services.command.exception.CommandSyntaxException; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.Nullable; + +public interface CommandContext { + + CommandSender getSender(); + + Player getSenderPlayer(); + + Entity getSenderEntity(); + + /** + * @return Object that is parsed from the raw argument + * @throws CommandSyntaxException if no value given and no default value supplied + */ + T getArg(String key); + + /** + * @return string argument used at invocation + */ + @Nullable String getRawArgument(String key); +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/exception/CommandSyntaxException.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/exception/CommandSyntaxException.java new file mode 100644 index 0000000..39fde35 --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/exception/CommandSyntaxException.java @@ -0,0 +1,22 @@ +package de.kentoj.scrow.bukkit.services.command.exception; + +/** + * When a command has a syntax issue. + * {@code CommandSyntaxException::getMessage} should return a user-friendly message explaining the issue. + */ +public class CommandSyntaxException extends RuntimeException { + + /** + * @param message user-friendly message + */ + public CommandSyntaxException(String message) { + super(message); + } + + /** + * @param message user-friendly message + */ + public CommandSyntaxException(String message, Throwable throwable) { + super(message + ": " + throwable.getMessage()); + } +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/node/CommandNode.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/node/CommandNode.java new file mode 100644 index 0000000..e35d0ab --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/node/CommandNode.java @@ -0,0 +1,51 @@ +package de.kentoj.scrow.bukkit.services.command.node; + +import de.kentoj.scrow.bukkit.services.command.CommandExecutor; +import org.bukkit.permissions.Permission; +import org.jetbrains.annotations.Nullable; + +import java.util.Set; + +// TODO javadoc +public interface CommandNode { + + void setPermission(@Nullable Permission permission); + + default void setPermission(String permission) { + setPermission(new Permission(permission)); + } + + void setDescription(String description); + + void setAliases(Set aliases); + + void addAlias(String... aliases); + + void setExecutor(@Nullable CommandExecutor executor); + + @Nullable Permission getPermission(); + + String getName(); + + Set getAliases(); + + String getDescription(); + + /** + * Function that should be called when invoking this command. + * The executor shall only be called when this node is the last CommandNode in the invocation. + *
+     * - RootCommand(CommandNode)
+     *   - has executor
+     *   - has sub-command(CommandNode)
+     *     - has executor
+     * 
+ * In this case, the sub-command is optional. If the sub-command is used, the sub-command's executor will be used. + * Else the RootCommand's executor will be used. + */ + @Nullable CommandExecutor getExecutor(); + + default boolean hasExecutor() { + return getExecutor() != null; + } +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/node/CommandNodeFactory.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/node/CommandNodeFactory.java new file mode 100644 index 0000000..d6cb179 --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/node/CommandNodeFactory.java @@ -0,0 +1,10 @@ +package de.kentoj.scrow.bukkit.services.command.node; + +public interface CommandNodeFactory { + + CommandNodeWithSubCommand createNodeWithSubCommands(String name); + + CommandNodeWithArguments createNodeWithArguments(String name); + + CommandNode createRootNode(String name); +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/node/CommandNodeRepository.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/node/CommandNodeRepository.java new file mode 100644 index 0000000..9ac039b --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/node/CommandNodeRepository.java @@ -0,0 +1,24 @@ +package de.kentoj.scrow.bukkit.services.command.node; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.jetbrains.annotations.ApiStatus; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class CommandNodeRepository { + + private static CommandNodeFactory commandNodeFactory; + + public static CommandNodeWithArguments withArguments(String name) { + return commandNodeFactory.createNodeWithArguments(name); + } + + public static CommandNodeWithSubCommand withSubCommands(String name) { + return commandNodeFactory.createNodeWithSubCommands(name); + } + + @ApiStatus.Internal + public static void setCommandNodeFactory(CommandNodeFactory commandNodeFactory) { + CommandNodeRepository.commandNodeFactory = commandNodeFactory; + } +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/node/CommandNodeWithArguments.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/node/CommandNodeWithArguments.java new file mode 100644 index 0000000..78577af --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/node/CommandNodeWithArguments.java @@ -0,0 +1,34 @@ +package de.kentoj.scrow.bukkit.services.command.node; + +import de.kentoj.scrow.bukkit.services.command.CommandArgument; +import de.kentoj.scrow.bukkit.services.command.context.CommandContext; +import de.kentoj.scrow.bukkit.services.command.type.ArgumentType; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Function; + +/** + * A CommandNode which can have arguments + */ +public interface CommandNodeWithArguments extends CommandNode { + + /** + * Adds an argument to the node. + * @param defaultValue Function returning a defaultValue if no value is given at invocation. \ + * May be null if the argument should be required and not optional. + * @throws IllegalStateException if the previous argument is optional(defaultValue is set) and this is not. + */ + void addArg(String name, ArgumentType type, @Nullable Function defaultValue); + + /** + * Adds a required argument to the node + * @throws IllegalStateException if the previous argument is optional(defaultValue is set) and this is not. + */ + default void addArg(String name, ArgumentType type) { + addArg(name, type, null); + } + + @Nullable CommandArgument getArg(int index); + + @Nullable CommandArgument getArg(String name); +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/node/CommandNodeWithSubCommand.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/node/CommandNodeWithSubCommand.java new file mode 100644 index 0000000..733206e --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/node/CommandNodeWithSubCommand.java @@ -0,0 +1,20 @@ +package de.kentoj.scrow.bukkit.services.command.node; + +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * A CommandNode which can have sub-commands + */ +public interface CommandNodeWithSubCommand extends CommandNode { + + List getSubCommands(); + + void addSubCommand(CommandNode subCommand); + + /** + * @return SubCommand node where a name or alias matches label + */ + @Nullable CommandNode getSubCommand(String label); +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/type/ArgumentType.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/type/ArgumentType.java new file mode 100644 index 0000000..acb9ac8 --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/type/ArgumentType.java @@ -0,0 +1,19 @@ +package de.kentoj.scrow.bukkit.services.command.type; + +import de.kentoj.scrow.bukkit.services.command.CommandReader; +import de.kentoj.scrow.bukkit.services.command.context.CommandContext; +import de.kentoj.scrow.bukkit.services.command.exception.CommandSyntaxException; + +import java.util.Set; + +public interface ArgumentType { + + /** + * @throws CommandSyntaxException if the given value may not be used + */ + void checkValue(CommandContext ctx, T value, String rawValue) throws CommandSyntaxException; + + Set getDefaultSuggestions(CommandContext ctx); + + T parseInput(CommandReader reader); +} diff --git a/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/type/EntityArgumentType.java b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/type/EntityArgumentType.java new file mode 100644 index 0000000..5c46ce2 --- /dev/null +++ b/core-bukkit-api/src/main/java/de/kentoj/scrow/bukkit/services/command/type/EntityArgumentType.java @@ -0,0 +1,47 @@ +package de.kentoj.scrow.bukkit.services.command.type; + +import de.kentoj.scrow.bukkit.services.command.CommandReader; +import de.kentoj.scrow.bukkit.services.command.context.CommandContext; +import de.kentoj.scrow.bukkit.services.command.exception.CommandSyntaxException; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +// TODO add livingEntity + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class EntityArgumentType { + + private static final ArgumentType player = new ArgumentType<>() { + @Override + public void checkValue(CommandContext ctx, Player value, String rawValue) throws CommandSyntaxException { + if (value == null) + throw new CommandSyntaxException("Player is not online -- " + rawValue); + } + + @Override + public Set getDefaultSuggestions(CommandContext ctx) { + return Bukkit.getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toSet()); + } + + @Override + public Player parseInput(CommandReader reader) { + final var word = reader.readWord(); + if (word.length() == 36) { + var uuid = UUID.fromString(word); + return Bukkit.getPlayer(uuid); + } else { + return Bukkit.getPlayerExact(word); + } + } + }; + + public static ArgumentType player() { + return player; + } +} diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/CoreImplPlugin.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/CoreImplPlugin.java index 44d2b86..59c9bec 100644 --- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/CoreImplPlugin.java +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/CoreImplPlugin.java @@ -1,9 +1,6 @@ package de.kentoj.scrow.bukkit; -import de.kentoj.scrow.bukkit.friends.casd.FriendCommand; import de.kentoj.scrow.bukkit.friends.command.LaunchCommand; -import me.lucko.commodore.CommodoreProvider; -import org.bukkit.command.PluginCommand; import org.bukkit.plugin.java.JavaPlugin; import reactor.core.publisher.Mono; @@ -13,7 +10,7 @@ public class CoreImplPlugin extends JavaPlugin { @Override public void onEnable() { - ScrowAPISurface.initScrowAPI(false); + ScrowAPISurface.initScrowAPI(this, false); { ScrowAPI.getCommandManager().register(new LaunchCommand(), this); diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/ScrowAPISurface.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/ScrowAPISurface.java index 0d9e68f..506ecdf 100644 --- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/ScrowAPISurface.java +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/ScrowAPISurface.java @@ -5,21 +5,30 @@ import com.mongodb.MongoClientSettings; import com.mongodb.reactivestreams.client.MongoClient; import com.mongodb.reactivestreams.client.MongoClients; import de.kentoj.scrow.bukkit.command.bukkit.CommandManagerImpl; +import de.kentoj.scrow.bukkit.command.node.CommandNodeFactoryImpl; import de.kentoj.scrow.bukkit.economy.InMemoryEconomyService; import de.kentoj.scrow.bukkit.economy.MongoEconomyService; +import de.kentoj.scrow.bukkit.services.command.node.CommandNodeRepository; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.bson.UuidRepresentation; import org.bson.codecs.configuration.CodecRegistries; import org.bson.codecs.pojo.PojoCodecProvider; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.Nullable; +import reactor.core.scheduler.Schedulers; @NoArgsConstructor(access = AccessLevel.PRIVATE) public class ScrowAPISurface { private static @Nullable MongoClient databaseClient = null; - public static void initScrowAPI(boolean enableDatabase) { + public static void initScrowAPI(Plugin plugin, boolean enableDatabase) { + ScrowAPI.setMinecraftScheduler(Schedulers.fromExecutor(cmd -> { + Bukkit.getScheduler().runTask(plugin, cmd); + })); + if (enableDatabase) { var pojoCodecProvider = PojoCodecProvider.builder().automatic(true).build(); var codecRegistry = CodecRegistries.fromRegistries( @@ -39,6 +48,7 @@ public class ScrowAPISurface { new MongoEconomyService(ScrowAPI.getDatabase()) : new InMemoryEconomyService() ); + CommandNodeRepository.setCommandNodeFactory(new CommandNodeFactoryImpl()); ScrowAPI.setCommandManager(new CommandManagerImpl()); } diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/cmds2/BukkitCommand.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/cmds2/BukkitCommand.java new file mode 100644 index 0000000..8688cab --- /dev/null +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/cmds2/BukkitCommand.java @@ -0,0 +1,57 @@ +package de.kentoj.scrow.bukkit.cmds2; + +import de.kentoj.scrow.bukkit.services.command.cmds2.node.ArgumentCommandNode; +import de.kentoj.scrow.bukkit.services.command.cmds2.node.CommandNode; +import de.kentoj.scrow.bukkit.services.command.cmds2.node.LiteralCommandNode; +import de.kentoj.scrow.bukkit.services.command.cmds2.node.RootCommandNode; +import de.kentoj.scrow.bukkit.services.command.exception.CommandSyntaxException; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; + +public class BukkitCommand extends Command { + + private final RootCommandNode rootCommandNode; + + protected BukkitCommand(RootCommandNode rootCommandNode) { + super(rootCommandNode.getName()); + this.rootCommandNode = rootCommandNode; + + if (rootCommandNode.getDescription() != null) { + super.setDescription(rootCommandNode.getDescription()); + } + if (rootCommandNode.getAliases() != null) { + super.setAliases(rootCommandNode.getAliases().stream().toList()); + } + // TODO usage string? + } + + private void processCommand(@NotNull CommandSender sender, @NotNull String commandLabel, + @NotNull String[] args) throws CommandSyntaxException { + CommandNode node = rootCommandNode; + int argsIndex = 0; + + while (node.hasNextNode()) { + if (args.length - 1 < argsIndex) { + if (node) + } + + if (node instanceof LiteralCommandNode literal) { + + node = literal.getSubNode(args[argsIndex]); + if (node == null) { + throw new CommandSyntaxException("Invalid argument -- " + args[argsIndex]) + } + } else if (node instanceof ArgumentCommandNode) { + + } + + argsIndex++; + } + } + + @Override + public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { + return true; + } +} diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/cmds2/CoinsCommand.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/cmds2/CoinsCommand.java new file mode 100644 index 0000000..76992ae --- /dev/null +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/cmds2/CoinsCommand.java @@ -0,0 +1,53 @@ +package de.kentoj.scrow.bukkit.cmds2; + +import de.kentoj.scrow.bukkit.ScrowAPI; +import de.kentoj.scrow.bukkit.services.command.CommandExecutor; +import de.kentoj.scrow.bukkit.services.command.cmds2.node.ArgumentCommandNode; +import de.kentoj.scrow.bukkit.services.command.cmds2.node.LiteralCommandNode; +import de.kentoj.scrow.bukkit.services.command.context.CommandContext; +import de.kentoj.scrow.bukkit.services.command.type.EntityArgumentType; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import reactor.core.scheduler.Schedulers; + +import java.util.logging.Level; + +@SuppressWarnings("DataFlowIssue") +public class CoinsCommand implements CommandExecutor { + + public CoinsCommand() { + final ArgumentCommandNode node = null; + node.addArgument("player", EntityArgumentType.player(), CommandContext::getSenderPlayer); + node.setExecutor(this::displayCoins); + + final LiteralCommandNode setLiteral; + node.addSubNode(setLiteral); + setLiteral + { + final ArgumentCommandNode specifyPlayerNode = null /* TODO */; + + node.addSubNode(specifyPlayerNode); + specifyPlayerNode.addArgument("player", EntityArgumentType.player(), CommandContext::getSenderPlayer); + specifyPlayerNode.setExecutor(this::setCoins); + } + } + + private void displayCoins(CommandContext ctx) { + final Player player = ctx.getArg("player"); + + ScrowAPI.getEconomyService() + .getCoins(player.getUniqueId()) + .subscribeOn(Schedulers.boundedElastic()) + .publishOn(ScrowAPI.getMinecraftScheduler()) + .subscribe(coins -> { + player.sendMessage("You have " + coins + "$"); + }, err -> { + player.sendMessage("Error fetching coins: " + err.getMessage()); + Bukkit.getLogger().log(Level.SEVERE, "Failed fetching coins for " + player.getUniqueId(), err); + }); + } + + public void setCoins(CommandContext ctx) { + + } +} diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/AbstractCommandNode.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/AbstractCommandNode.java deleted file mode 100644 index cd27119..0000000 --- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/AbstractCommandNode.java +++ /dev/null @@ -1,79 +0,0 @@ -package de.kentoj.scrow.bukkit.command; - -import com.google.common.collect.ImmutableList; -import de.kentoj.scrow.bukkit.services.command.CommandContext; -import de.kentoj.scrow.bukkit.services.command.CommandExecutor; -import de.kentoj.scrow.bukkit.services.command.CommandArgument; -import de.kentoj.scrow.bukkit.services.command.CommandNode; -import de.kentoj.scrow.bukkit.services.command.arg.ArgumentType; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.Setter; -import org.bukkit.permissions.Permission; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Function; - -public abstract class AbstractCommandNode implements CommandNode { - - private final List subCommands = new ArrayList<>(); - private final List> args = new ArrayList<>(); - - @Getter - private final String name; - @Getter - private final String[] aliases; - @Getter - private @Nullable Permission permission = null; - @Getter - @Setter(value = AccessLevel.PROTECTED) - private @Nullable String description; - @Getter - @Setter(value = AccessLevel.PROTECTED) - private @Nullable CommandExecutor executor = null; - - public AbstractCommandNode(String name, String... aliases) { - this.name = name; - this.aliases = aliases; - } - - @Override - public void addArg(String name, ArgumentType type, Function defaultValue) { - var index = args.size(); - if (index > 0) { - boolean isPreviousArgumentOptional = args.get(index - 1).getDefaultValue() != null; - boolean isNewArgumentOptional = defaultValue != null; - if (isPreviousArgumentOptional && !isNewArgumentOptional) { - // TODO document in javadoc - throw new IllegalStateException("an optional argument(an argument with a defaultValue set) may not be followed by a required argument"); - } - } - args.add(new CommandArgument<>(index, name, type, defaultValue)); - // TODO - } - - @Override - public void addSubCommand(CommandNode subCommand) { - subCommands.add(subCommand); - } - - @Override - public void setPermission(@Nullable Permission permission) { - this.permission = permission; - } - - @Override - public @Nullable CommandArgument getArg(int index) { - return args.stream() - .filter(ra -> ra.getIndex() == index) - .findFirst() - .orElse(null); - } - - @Override - public List getSubCommands() { - return ImmutableList.copyOf(subCommands); - } -} \ No newline at end of file diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/CommandContextImpl.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/CommandContextImpl.java index f326835..262872d 100644 --- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/CommandContextImpl.java +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/CommandContextImpl.java @@ -1,6 +1,6 @@ package de.kentoj.scrow.bukkit.command; -import de.kentoj.scrow.bukkit.services.command.CommandContext; +import de.kentoj.scrow.bukkit.services.command.context.CommandContext; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.bukkit.command.CommandSender; diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/CommandExecutionContextImpl.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/CommandExecutionContextImpl.java index 96f238e..be2a85a 100644 --- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/CommandExecutionContextImpl.java +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/CommandExecutionContextImpl.java @@ -1,21 +1,48 @@ package de.kentoj.scrow.bukkit.command; -import de.kentoj.scrow.bukkit.services.command.CommandExecutionContext; +import de.kentoj.scrow.bukkit.command.bukkit.ArgumentData; +import de.kentoj.scrow.bukkit.services.command.CommandArgument; +import de.kentoj.scrow.bukkit.services.command.exception.CommandSyntaxException; import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.Nullable; import java.util.Map; -public class CommandExecutionContextImpl extends CommandContextImpl implements CommandExecutionContext { +public class CommandExecutionContextImpl extends CommandContextImpl implements CommadContext { - private final Map args; + private final Map argumentDataMap; - public CommandExecutionContextImpl(CommandSender sender, Map args) { + public CommandExecutionContextImpl(CommandSender sender, Map argumentDataMap) { super(sender); - this.args = args; + this.argumentDataMap = argumentDataMap; + } + + @Override + public @Nullable String getRawArgument(String key) { + var data = argumentDataMap.get(key); + if (data.getRawValue() == null) { + if (data.getArgument().getDefaultValue() == null) { + throw new CommandSyntaxException("usage: missing arg value for " + key); + } + return null; + } + return data.getRawValue(); } @SuppressWarnings("unchecked") public T getArg(String key) { - return (T) args.get(key); + var data = argumentDataMap.get(key); + var arg = (CommandArgument) data.getArgument(); + var raw = getRawArgument(key); + + T value; + if (raw != null) { + value = arg.getType().parseInput(raw); + } else { + value = arg.getDefaultValue().apply(this); + } + + arg.getType().checkValue(this, value, raw); + return value; } } diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/bukkit/ArgumentData.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/bukkit/ArgumentData.java new file mode 100644 index 0000000..3fe8a21 --- /dev/null +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/bukkit/ArgumentData.java @@ -0,0 +1,11 @@ +package de.kentoj.scrow.bukkit.command.bukkit; + +import de.kentoj.scrow.bukkit.services.command.CommandArgument; +import lombok.Value; +import org.jetbrains.annotations.Nullable; + +@Value +public class ArgumentData { + CommandArgument argument; + @Nullable String rawValue; +} diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/bukkit/BukkitCommand.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/bukkit/BukkitCommand.java index aa3427f..43b6cae 100644 --- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/bukkit/BukkitCommand.java +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/bukkit/BukkitCommand.java @@ -1,36 +1,34 @@ package de.kentoj.scrow.bukkit.command.bukkit; -import de.kentoj.scrow.bukkit.services.command.CommandNode; +import de.kentoj.scrow.bukkit.services.command.exception.CommandSyntaxException; +import de.kentoj.scrow.bukkit.services.command.node.CommandNode; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.jetbrains.annotations.NotNull; -import java.util.List; +import java.util.Set; public class BukkitCommand extends Command { - private static final CommandProcessor commandProcessor = new CommandProcessor(); - private final CommandNode rootCommandNode; - protected BukkitCommand(CommandNode rootCommandNode, @NotNull String name, @NotNull String description, @NotNull String usageMessage, @NotNull List aliases) { - super(name, description, usageMessage, aliases); + protected BukkitCommand(CommandNode rootCommandNode, @NotNull String name, @NotNull String description, @NotNull String usageMessage, @NotNull Set aliases) { + super(name, description, usageMessage, aliases.stream().toList()); this.rootCommandNode = rootCommandNode; } @Override public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { - final Runnable runnable; + final var commandProcessor = new CommandProcessor(rootCommandNode, sender, args); + try { - runnable = commandProcessor.toRunnable(rootCommandNode, sender, args); - } catch (CommandProcessException e) { - sender.sendMessage(e.getMessage()); + commandProcessor.process(); + } catch (CommandSyntaxException e) { + sender.sendMessage("error: " + e.getMessage()); return true; } catch (IllegalStateException e) { throw new RuntimeException("failed processing command " + rootCommandNode.getName(), e); } - - runnable.run(); return true; } } diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/bukkit/CommandManagerImpl.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/bukkit/CommandManagerImpl.java index 52d9cf2..d03deab 100644 --- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/bukkit/CommandManagerImpl.java +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/bukkit/CommandManagerImpl.java @@ -1,14 +1,12 @@ package de.kentoj.scrow.bukkit.command.bukkit; import de.kentoj.scrow.bukkit.services.command.CommandManager; -import de.kentoj.scrow.bukkit.services.command.CommandNode; +import de.kentoj.scrow.bukkit.services.command.node.CommandNode; import org.bukkit.Bukkit; import org.bukkit.command.Command; import org.bukkit.command.CommandMap; import org.bukkit.plugin.Plugin; -import java.util.Arrays; - public class CommandManagerImpl implements CommandManager { private final CommandMap commandMap; @@ -25,8 +23,8 @@ public class CommandManagerImpl implements CommandManager { @Override public void register(CommandNode rootCommandNode, Plugin plugin) { - var bukkitCmd = toBukkitCommand(rootCommandNode); - commandMap.register(plugin.getName(), bukkitCmd); + var bukkitCommand = toBukkitCommand(rootCommandNode); + commandMap.register(plugin.getName(), bukkitCommand); } private Command toBukkitCommand(CommandNode commandNode) { @@ -35,7 +33,7 @@ public class CommandManagerImpl implements CommandManager { commandNode.getName(), commandNode.getDescription(), "", - Arrays.stream(commandNode.getAliases()).toList() + commandNode.getAliases() ); } } diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/bukkit/CommandProcessException.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/bukkit/CommandProcessException.java deleted file mode 100644 index 2feb26b..0000000 --- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/bukkit/CommandProcessException.java +++ /dev/null @@ -1,10 +0,0 @@ -package de.kentoj.scrow.bukkit.command.bukkit; - -public class CommandProcessException extends RuntimeException { - public CommandProcessException(String message) { - super(message); - } - public CommandProcessException(String message, Throwable throwable) { - super(message + ": " + throwable.getMessage()); - } -} diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/bukkit/CommandProcessor.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/bukkit/CommandProcessor.java index cff9d9a..ba3ddff 100644 --- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/bukkit/CommandProcessor.java +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/bukkit/CommandProcessor.java @@ -1,78 +1,62 @@ package de.kentoj.scrow.bukkit.command.bukkit; -import com.google.common.base.Strings; -import de.kentoj.scrow.bukkit.command.CommandContextImpl; import de.kentoj.scrow.bukkit.command.CommandExecutionContextImpl; import de.kentoj.scrow.bukkit.services.command.CommandArgument; -import de.kentoj.scrow.bukkit.services.command.CommandContext; -import de.kentoj.scrow.bukkit.services.command.CommandNode; +import de.kentoj.scrow.bukkit.services.command.CommandExecutor; +import de.kentoj.scrow.bukkit.services.command.context.CommandContext; +import de.kentoj.scrow.bukkit.services.command.exception.CommandSyntaxException; +import de.kentoj.scrow.bukkit.services.command.node.CommandNode; +import de.kentoj.scrow.bukkit.services.command.node.CommandNodeWithArguments; +import de.kentoj.scrow.bukkit.services.command.node.CommandNodeWithSubCommand; +import lombok.AllArgsConstructor; import lombok.RequiredArgsConstructor; import org.bukkit.command.CommandSender; import org.jetbrains.annotations.Nullable; -import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.function.Function; -import java.util.stream.Collectors; -@RequiredArgsConstructor +@AllArgsConstructor public class CommandProcessor { - public Runnable toRunnable(final CommandNode rootCommandNode, final CommandSender sender, final String[] args) throws CommandProcessException { - final var argToValueFunMap = new HashMap>(); - CommandNode lastNode = rootCommandNode; + private final Map argumentDataMap = new HashMap<>(); + private CommandNode node; + private final CommandSender sender; + private final String[] args; + + public void process() throws CommandSyntaxException { for (int cursor = 0; true; cursor++) { - final var arg = lastNode.getArg(cursor); - if (arg != null) { - final var valueFun = getArgValueFun(cursor, arg); - argToValueFunMap.put(arg.getName(), valueFun); - } else { - if (lastNode.getSubCommands().isEmpty()) break; - final var subcommand = getSubCommand(lastNode, args[cursor]); + if (args.length - 1 < cursor) { + if (node.getExecutor() == null) throw new CommandSyntaxException("Incomplete command"); + execute(node.getExecutor()); + return; + } + + if (node instanceof CommandNodeWithArguments nodeWA) { + final var arg = nodeWA.getArg(cursor); + assert arg != null; + final var data = new ArgumentData(arg, args[cursor]); + argumentDataMap.put(arg.getName(), data); + } else if (node instanceof CommandNodeWithSubCommand nodeWS) { + if (nodeWS.getSubCommands().isEmpty()) break; + final var subcommand = nodeWS.getSubCommand(args[cursor]); if (subcommand == null) { - final var subcommands = lastNode.getSubCommands().stream().map(CommandNode::getName).toList(); - throw new CommandProcessException("Illegal argument -- " + args[cursor] + "\nExpected arguments: " + String.join(", ", subcommands)); + final var subcommands = nodeWS.getSubCommands().stream().map(CommandNode::getName).toList(); + throw new CommandSyntaxException("Illegal argument -- " + args[cursor] + "\nExpected arguments: " + String.join(", ", subcommands)); } - lastNode = subcommand; + this.node = nodeWS; } } - if (lastNode.getExecutor() == null) throw new IllegalStateException("Last CommandNode must have an executor"); - final CommandNode finalLastNode = lastNode; - return () -> { - var ctx = new CommandExecutionContextImpl(sender, remap(argToValueFunMap, sender)); - finalLastNode.getExecutor().execute(ctx); - }; + if (node.getExecutor() == null) throw new IllegalStateException("Last CommandNode must have an executor"); + execute(node.getExecutor()); } - private Map remap(Map> argToValueFunMap, CommandSender sender) { - var ctx = new CommandContextImpl(sender); - return argToValueFunMap.entrySet() - .stream() - .collect(Collectors.toMap( - Map.Entry::getKey, - ent -> ent.getValue().apply(ctx) - )); - } - - private Function getArgValueFun(int argIndex, CommandArgument arg) throws CommandProcessException { - if (args.length - 1 < argIndex) { - if (arg.getDefaultValue() == null) - throw new CommandProcessException("usage: missing arg value for " + arg.getName()); - return arg.getDefaultValue(); - } else { - return __ -> arg.getType().parseInput(args[argIndex]); - } - } - - private @Nullable CommandNode getSubCommand(CommandNode commandNode, String label) { - return commandNode.getSubCommands() - .stream() - .filter(c -> Arrays.stream(c.getAliases()).toList().contains(label)) - .findFirst() - .orElse(null); + private void execute(CommandExecutor executor) { + var ctx = new CommandExecutionContextImpl(sender, argumentDataMap); + executor.execute(ctx); } } diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/node/CommandNodeFactoryImpl.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/node/CommandNodeFactoryImpl.java new file mode 100644 index 0000000..864aa19 --- /dev/null +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/node/CommandNodeFactoryImpl.java @@ -0,0 +1,23 @@ +package de.kentoj.scrow.bukkit.command.node; + +import de.kentoj.scrow.bukkit.services.command.node.CommandNode; +import de.kentoj.scrow.bukkit.services.command.node.CommandNodeFactory; +import de.kentoj.scrow.bukkit.services.command.node.CommandNodeWithArguments; +import de.kentoj.scrow.bukkit.services.command.node.CommandNodeWithSubCommand; + +public class CommandNodeFactoryImpl implements CommandNodeFactory { + @Override + public CommandNodeWithSubCommand createNodeWithSubCommands(String name) { + return new CommandNodeWithSubCommandImpl(name); + } + + @Override + public CommandNodeWithArguments createNodeWithArguments(String name) { + return new CommandNodeWithArgumentsImpl(name); + } + + @Override + public CommandNode createRootNode(String name) { + return new CommandNodeImpl(name); + } +} diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/node/CommandNodeImpl.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/node/CommandNodeImpl.java new file mode 100644 index 0000000..2dad138 --- /dev/null +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/node/CommandNodeImpl.java @@ -0,0 +1,36 @@ +package de.kentoj.scrow.bukkit.command.node; + +import de.kentoj.scrow.bukkit.services.command.CommandExecutor; +import de.kentoj.scrow.bukkit.services.command.node.CommandNode; +import lombok.Getter; +import lombok.Setter; +import org.bukkit.permissions.Permission; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +@Getter +public class CommandNodeImpl implements CommandNode { + + private final String name; + + @Setter + private Set aliases = new HashSet<>(); + @Setter + private @Nullable Permission permission = null; + @Setter + private @Nullable String description; + @Setter + private @Nullable CommandExecutor executor = null; + + public CommandNodeImpl(String name) { + this.name = name; + } + + @Override + public void addAlias(String... aliases) { + this.aliases.addAll(Arrays.asList(aliases)); + } +} \ No newline at end of file diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/node/CommandNodeWithArgumentsImpl.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/node/CommandNodeWithArgumentsImpl.java new file mode 100644 index 0000000..e0ef60a --- /dev/null +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/node/CommandNodeWithArgumentsImpl.java @@ -0,0 +1,50 @@ +package de.kentoj.scrow.bukkit.command.node; + + +import de.kentoj.scrow.bukkit.services.command.CommandArgument; +import de.kentoj.scrow.bukkit.services.command.context.CommandContext; +import de.kentoj.scrow.bukkit.services.command.node.CommandNodeWithArguments; +import de.kentoj.scrow.bukkit.services.command.type.ArgumentType; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +public class CommandNodeWithArgumentsImpl extends CommandNodeImpl implements CommandNodeWithArguments { + + private final List> args = new ArrayList<>(); + + public CommandNodeWithArgumentsImpl(String name) { + super(name); + } + + @Override + public void addArg(String name, ArgumentType type, Function defaultValue) { + var index = args.size(); + if (index > 0) { + boolean isPreviousArgumentOptional = args.get(index - 1).getDefaultValue() != null; + boolean isNewArgumentOptional = defaultValue != null; + if (isPreviousArgumentOptional && !isNewArgumentOptional) { + throw new IllegalStateException("an optional argument(an argument with a defaultValue set) may not be followed by a required argument"); + } + } + args.add(new CommandArgument<>(index, name, type, defaultValue)); + } + + @Override + public @Nullable CommandArgument getArg(String name) { + return args.stream() + .filter(ra -> ra.getName().equals(name)) + .findFirst() + .orElse(null); + } + + @Override + public @Nullable CommandArgument getArg(int index) { + return args.stream() + .filter(ra -> ra.getIndex() == index) + .findFirst() + .orElse(null); + } +} diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/node/CommandNodeWithSubCommandImpl.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/node/CommandNodeWithSubCommandImpl.java new file mode 100644 index 0000000..842a1f9 --- /dev/null +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/command/node/CommandNodeWithSubCommandImpl.java @@ -0,0 +1,32 @@ +package de.kentoj.scrow.bukkit.command.node; + +import de.kentoj.scrow.bukkit.services.command.node.CommandNode; +import de.kentoj.scrow.bukkit.services.command.node.CommandNodeWithSubCommand; +import lombok.Getter; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +public class CommandNodeWithSubCommandImpl extends CommandNodeImpl implements CommandNodeWithSubCommand { + + @Getter + private final List subCommands = new ArrayList<>(); + + public CommandNodeWithSubCommandImpl(String name) { + super(name); + } + + @Override + public void addSubCommand(CommandNode subCommand) { + subCommands.add(subCommand); + } + + @Override + public @Nullable CommandNode getSubCommand(String label) { + return subCommands.stream() + .filter(n -> n.getName().equals(label) || n.getAliases().contains(label)) + .findFirst() + .orElse(null); + } +} \ No newline at end of file diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/casd/FriendAddCommand.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/casd/FriendAddCommand.java deleted file mode 100644 index 20b7120..0000000 --- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/casd/FriendAddCommand.java +++ /dev/null @@ -1,28 +0,0 @@ -package de.kentoj.scrow.bukkit.friends.casd; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.LiteralMessage; -import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; -import com.mojang.brigadier.tree.ArgumentCommandNode; -import de.kentoj.scrow.bukkit.util.command.PlayerArgument; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import org.bukkit.entity.Player; -import org.bukkit.util.Vector; - -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class FriendAddCommand implements Command { - - @Override - public int run(CommandContext ctx) throws CommandSyntaxException { - final Player player = PlayerArgument.resolve(ctx, "player"); - if (player == null) { - throw new SimpleCommandExceptionType(new LiteralMessage("Player is not online")).create(); - } - - player.setVelocity(new Vector(0, 2, 0)); - return 0; - } -} diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/casd/FriendCommand.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/casd/FriendCommand.java deleted file mode 100644 index a6ebaed..0000000 --- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/casd/FriendCommand.java +++ /dev/null @@ -1,19 +0,0 @@ -package de.kentoj.scrow.bukkit.friends.casd; - -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.builder.LiteralArgumentBuilder; -import com.mojang.brigadier.builder.RequiredArgumentBuilder; - -public class FriendCommand { - - public static LiteralArgumentBuilder create() { - return LiteralArgumentBuilder.literal("friends") - .then(LiteralArgumentBuilder.literal("add")) - .then(LiteralArgumentBuilder.literal("list")) - .then(LiteralArgumentBuilder.literal("requests")) - .then(LiteralArgumentBuilder.literal("remove")) - .then(LiteralArgumentBuilder.literal("jump")) - .then(LiteralArgumentBuilder.literal("accept")) - .then(LiteralArgumentBuilder.literal("deny")); - } -} diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/casd/FriendCommandExecutor.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/casd/FriendCommandExecutor.java deleted file mode 100644 index b8cc074..0000000 --- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/casd/FriendCommandExecutor.java +++ /dev/null @@ -1,13 +0,0 @@ -package de.kentoj.scrow.bukkit.friends.casd; - -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.jetbrains.annotations.NotNull; - -public class FriendCommandExecutor implements CommandExecutor { - @Override - public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - return false; - } -} diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendCommand.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendCommand.java new file mode 100644 index 0000000..6307f31 --- /dev/null +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/FriendCommand.java @@ -0,0 +1,13 @@ +package de.kentoj.scrow.bukkit.friends.command; + +import de.kentoj.scrow.bukkit.services.command.node.CommandNodeRepository; +import de.kentoj.scrow.bukkit.services.command.node.CommandNodeWithSubCommand; + +public class FriendCommand { + + private final CommandNodeWithSubCommand node = CommandNodeRepository.withSubCommands("friend"); + + public FriendCommand() { + this.node.addAlias("f"); + } +} diff --git a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/LaunchCommand.java b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/LaunchCommand.java index 4128f08..efd2ff0 100644 --- a/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/LaunchCommand.java +++ b/core-bukkit-impl/src/main/java/de/kentoj/scrow/bukkit/friends/command/LaunchCommand.java @@ -1,23 +1,22 @@ package de.kentoj.scrow.bukkit.friends.command; -import de.kentoj.scrow.bukkit.command.AbstractCommandNode; -import de.kentoj.scrow.bukkit.services.command.CommandContext; -import de.kentoj.scrow.bukkit.services.command.CommandExecutionContext; +import de.kentoj.scrow.bukkit.command.node.CommandNodeWithArgumentsImpl; import de.kentoj.scrow.bukkit.services.command.CommandExecutor; -import de.kentoj.scrow.bukkit.services.command.arg.PlayerArgumentType; +import de.kentoj.scrow.bukkit.services.command.context.CommandContext; +import de.kentoj.scrow.bukkit.services.command.type.EntityArgumentType; import net.md_5.bungee.api.chat.TranslatableComponent; import org.bukkit.entity.Player; import org.bukkit.permissions.Permission; import org.bukkit.util.Vector; -public class LaunchCommand extends AbstractCommandNode implements CommandExecutor { +public class LaunchCommand extends CommandNodeWithArgumentsImpl implements CommandExecutor { public LaunchCommand() { - super("launch", "lunch"); + super("launch"); this.setDescription("launches a player in the air"); this.setPermission(new Permission("bukkitcore.cmd.launch")); - this.addArg("player", PlayerArgumentType.player, CommandContext::getSenderPlayer); + this.addArg("player", EntityArgumentType.player(), CommandContext::getSenderPlayer); this.setExecutor(this); }