├── .editorconfig ├── .github └── workflows │ ├── build.yml │ └── maven-publish.yml ├── .gitignore ├── api ├── pom.xml └── src │ ├── main │ └── java │ │ └── team │ │ └── unnamed │ │ └── commandflow │ │ ├── Authorizer.java │ │ ├── CommandContext.java │ │ ├── CommandManager.java │ │ ├── ContextSnapshot.java │ │ ├── ErrorHandler.java │ │ ├── Namespace.java │ │ ├── NamespaceImpl.java │ │ ├── ParseResult.java │ │ ├── SimpleCommandContext.java │ │ ├── SimpleCommandManager.java │ │ ├── SimpleErrorHandler.java │ │ ├── annotated │ │ ├── AnnotatedCommandTreeBuilder.java │ │ ├── AnnotatedCommandTreeBuilderImpl.java │ │ ├── CommandClass.java │ │ ├── ReflectionInstanceCreator.java │ │ ├── ReflectionSubCommandHandler.java │ │ ├── SubCommandInstanceCreator.java │ │ ├── action │ │ │ ├── ReflectiveAction.java │ │ │ └── ValueGetter.java │ │ ├── annotation │ │ │ ├── ArgOrSub.java │ │ │ ├── Command.java │ │ │ ├── ConsumeAll.java │ │ │ ├── Flag.java │ │ │ ├── Handler.java │ │ │ ├── Limit.java │ │ │ ├── Named.java │ │ │ ├── OptArg.java │ │ │ ├── ParentArg.java │ │ │ ├── Range.java │ │ │ ├── Required.java │ │ │ ├── Rewrites.java │ │ │ ├── Sender.java │ │ │ ├── Strict.java │ │ │ ├── SubCommandClasses.java │ │ │ ├── Suggestions.java │ │ │ ├── Switch.java │ │ │ ├── Text.java │ │ │ └── Usage.java │ │ ├── builder │ │ │ ├── AnnotatedCommandBuilder.java │ │ │ ├── AnnotatedCommandBuilderImpl.java │ │ │ ├── Buildable.java │ │ │ ├── CommandActionNode.java │ │ │ ├── CommandBuilderNodesImpl.java │ │ │ ├── CommandDataNode.java │ │ │ ├── CommandModifiersNode.java │ │ │ ├── CommandPartsNode.java │ │ │ └── SubCommandsNode.java │ │ ├── modifier │ │ │ └── CommandModifierFactory.java │ │ └── part │ │ │ ├── AbstractModule.java │ │ │ ├── DelegatePartModifier.java │ │ │ ├── Key.java │ │ │ ├── Module.java │ │ │ ├── PartFactory.java │ │ │ ├── PartInjector.java │ │ │ ├── PartModifier.java │ │ │ ├── SimplePartInjector.java │ │ │ └── defaults │ │ │ ├── DefaultsModule.java │ │ │ ├── factory │ │ │ ├── ArgumentStackPartFactory.java │ │ │ ├── BooleanPartFactory.java │ │ │ ├── ContextFactory.java │ │ │ ├── DoublePartFactory.java │ │ │ ├── EnumPartFactory.java │ │ │ ├── FloatPartFactory.java │ │ │ ├── IntegerPartFactory.java │ │ │ ├── LongPartFactory.java │ │ │ ├── StringPartFactory.java │ │ │ ├── StringTextPartFactory.java │ │ │ └── SwitchPartFactory.java │ │ │ └── modifier │ │ │ ├── LimitModifier.java │ │ │ ├── OptionalModifier.java │ │ │ ├── RewritesModifier.java │ │ │ ├── SuggestionsModifier.java │ │ │ └── ValueFlagModifier.java │ │ ├── command │ │ ├── Action.java │ │ ├── Command.java │ │ ├── SimpleCommand.java │ │ └── modifiers │ │ │ ├── CommandModifier.java │ │ │ ├── CommandModifiers.java │ │ │ ├── FallbackCommandModifiers.java │ │ │ ├── ModifierPhase.java │ │ │ ├── ModifierUtils.java │ │ │ ├── SequentialCommandModifier.java │ │ │ └── SimpleCommandModifiers.java │ │ ├── exception │ │ ├── ArgumentException.java │ │ ├── ArgumentParseException.java │ │ ├── CommandException.java │ │ ├── CommandUsage.java │ │ ├── InvalidSubCommandException.java │ │ ├── NoMoreArgumentsException.java │ │ ├── NoPermissionsException.java │ │ └── StopParseException.java │ │ ├── executor │ │ ├── DefaultExecutor.java │ │ └── Executor.java │ │ ├── input │ │ ├── InputTokenizer.java │ │ ├── QuotedSpaceTokenizer.java │ │ └── StringSpaceTokenizer.java │ │ ├── part │ │ ├── ArgumentPart.java │ │ ├── CommandPart.java │ │ ├── Parts.java │ │ ├── PartsWrapper.java │ │ ├── SinglePartWrapper.java │ │ ├── defaults │ │ │ ├── ArgumentRewriterPart.java │ │ │ ├── ArgumentStackPart.java │ │ │ ├── BooleanPart.java │ │ │ ├── ContextPart.java │ │ │ ├── DoublePart.java │ │ │ ├── EmptyPart.java │ │ │ ├── EnumPart.java │ │ │ ├── FirstMatchPart.java │ │ │ ├── FloatPart.java │ │ │ ├── IntegerPart.java │ │ │ ├── LimitingPart.java │ │ │ ├── LongPart.java │ │ │ ├── OptionalPart.java │ │ │ ├── PrimitivePart.java │ │ │ ├── SequentialCommandPart.java │ │ │ ├── StringPart.java │ │ │ ├── SubCommandPart.java │ │ │ ├── SuggestionsModifierPart.java │ │ │ ├── SwitchPart.java │ │ │ └── ValueFlagPart.java │ │ └── visitor │ │ │ ├── CommandPartVisitor.java │ │ │ └── UnwrappedCommandPartVisitor.java │ │ ├── stack │ │ ├── ArgumentStack.java │ │ ├── SimpleArgumentStack.java │ │ └── StackSnapshot.java │ │ ├── translator │ │ ├── ComponentRendererTranslator.java │ │ ├── DefaultMapTranslationProvider.java │ │ ├── DefaultTranslator.java │ │ ├── TranslationProvider.java │ │ └── Translator.java │ │ └── usage │ │ ├── DefaultUsageBuilder.java │ │ └── UsageBuilder.java │ └── test │ └── java │ └── team │ └── unnamed │ └── commandflow │ ├── InputTokenizerTest.java │ ├── ParameterizedPartTest.java │ └── annotated │ └── CustomComponentParserTest.java ├── brigadier ├── common │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── team │ │ └── unnamed │ │ └── commandflow │ │ └── brigadier │ │ ├── BrigadierCommandManager.java │ │ ├── CommandBrigadierConverter.java │ │ ├── MultipleHeadNode.java │ │ ├── MultipleHeadNodeBuilder.java │ │ ├── PermissionRequirement.java │ │ └── mappings │ │ ├── BrigadierCommandNodeMappings.java │ │ ├── BrigadierCommandNodeMappingsImpl.java │ │ ├── CommandNodeMapping.java │ │ └── defaults │ │ ├── BooleanMapping.java │ │ ├── DoubleMapping.java │ │ ├── GeneralMapping.java │ │ ├── IntegerMapping.java │ │ ├── LongMapping.java │ │ └── StringMapping.java ├── paper │ ├── .gitignore │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── team │ │ └── unnamed │ │ └── commandflow │ │ └── brigadier │ │ └── BrigadierProvider.java └── pom.xml ├── bukkit ├── commandmap │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── team │ │ └── unnamed │ │ └── commandflow │ │ └── bukkit │ │ ├── BukkitCommandWrapper.java │ │ ├── BukkitMapCommandManager.java │ │ └── PluginBukkitCommandWrapper.java ├── common │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── team │ │ └── unnamed │ │ └── commandflow │ │ └── bukkit │ │ ├── BukkitAuthorizer.java │ │ ├── BukkitCommandManager.java │ │ ├── BukkitCommonConstants.java │ │ ├── BukkitDefaultTranslationProvider.java │ │ ├── annotation │ │ ├── Exact.java │ │ └── PlayerOrSource.java │ │ ├── factory │ │ ├── BukkitModule.java │ │ ├── CommandSenderFactory.java │ │ ├── GameModeFactory.java │ │ ├── OfflinePlayerPartFactory.java │ │ ├── PlayerPartFactory.java │ │ ├── PlayerSenderFactory.java │ │ └── WorldFactory.java │ │ ├── part │ │ ├── BukkitParts.java │ │ ├── CommandSenderPart.java │ │ ├── GameModePart.java │ │ ├── OfflinePlayerPart.java │ │ ├── PlayerPart.java │ │ ├── PlayerSenderPart.java │ │ └── WorldPart.java │ │ └── sender │ │ ├── DefaultMessageSender.java │ │ ├── MessageSender.java │ │ └── MessageUtils.java ├── paper │ ├── .gitignore │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── team │ │ └── unnamed │ │ └── commandflow │ │ └── bukkit │ │ └── paper │ │ └── PaperMessageSender.java ├── plugin │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── team.unnamed.commandflow.bukkit │ │ ├── BukkitCommandExecutor.java │ │ ├── BukkitPluginCommandManager.java │ │ └── DefaultBukkitCommandExecutor.java └── pom.xml ├── bungee ├── pom.xml └── src │ └── main │ └── java │ └── team │ └── unnamed │ └── commandflow │ └── bungee │ ├── BungeeAuthorizer.java │ ├── BungeeCommandManager.java │ ├── BungeeCommandWrapper.java │ ├── BungeeDefaultTranslationProvider.java │ ├── MessageUtils.java │ ├── annotation │ └── ProxiedPlayerOrSource.java │ ├── factory │ ├── BungeeModule.java │ ├── CommandSenderPartFactory.java │ ├── ProxiedPlayerPartFactory.java │ └── ProxiedPlayerSenderPartFactory.java │ └── part │ ├── CommandSenderPart.java │ ├── ProxiedPlayerPart.java │ └── ProxiedPlayerSenderPart.java ├── discord ├── pom.xml └── src │ └── main │ └── java │ └── team │ └── unnamed │ └── commandflow │ └── discord │ ├── DiscordAuthorizer.java │ ├── DiscordCommandManager.java │ ├── DiscordDefaultTranslationProvider.java │ ├── MessageListener.java │ ├── factory │ ├── DiscordModule.java │ ├── MemberPartFactory.java │ ├── MemberSenderPartFactory.java │ ├── MessagePartFactory.java │ ├── TextChannelPartFactory.java │ ├── UserPartFactory.java │ └── UserSenderPartFactory.java │ ├── part │ ├── MemberPart.java │ ├── MemberSenderPart.java │ ├── MessagePart.java │ ├── TextChannelPart.java │ ├── UserPart.java │ └── UserSenderPart.java │ └── utils │ ├── ArgumentsUtils.java │ └── MessageUtils.java ├── docs ├── annotated │ ├── annotated.md │ ├── argument.md │ ├── basic.md │ ├── command-class.md │ ├── command-tree-builder.md │ ├── index.txt │ ├── multiple-arguments.md │ ├── optional-arguments.md │ ├── part-injector.md │ ├── subcommand-instance-creator.md │ ├── with-permission.md │ ├── with-switches.md │ └── with-value-flags.md ├── concepts │ ├── command-context.md │ ├── command-manager.md │ ├── command-part.md │ ├── command.md │ ├── index.txt │ └── namespaces.md ├── configuration │ ├── authorizer.md │ ├── configuration.md │ ├── index.txt │ └── tokenizer.md ├── execution │ ├── context.md │ ├── execution.md │ └── index.txt ├── getting-started.md ├── imperatively │ ├── argument.md │ ├── basic.md │ ├── index.txt │ ├── intro.md │ ├── multiple-arguments.md │ ├── optional-arguments.md │ ├── with-permission.md │ ├── with-switches.md │ └── with-value-flags.md ├── index.txt ├── installation.md └── platforms │ ├── brigadier.md │ ├── bukkit.md │ ├── bungeecord.md │ ├── discord-jda.md │ ├── index.txt │ ├── platforms.md │ └── velocity.md ├── license.txt ├── pom.xml ├── readme.md └── velocity ├── pom.xml └── src └── main └── java └── team └── unnamed └── commandflow └── velocity ├── VelocityAuthorizer.java ├── VelocityCommandManager.java ├── VelocityCommandWrapper.java ├── VelocityDefaultTranslationProvider.java ├── annotation └── PlayerOrSource.java ├── factory ├── CommandSourcePartFactory.java ├── PlayerPartFactory.java ├── PlayerSenderPartFactory.java └── VelocityModule.java └── part ├── CommandSenderPart.java ├── PlayerPart.java └── PlayerSenderPart.java /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 4 7 | indent_style = space 8 | insert_final_newline = false 9 | 10 | ij_continuation_indent_size = 8 11 | ij_java_class_count_to_use_import_on_demand = 10 -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: 'Maven Package' 2 | 3 | on: [ 'push', 'pull_request' ] 4 | 5 | jobs: 6 | build: 7 | if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }} 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v4 11 | - uses: actions/setup-java@v3 12 | with: 13 | java-version: '17' 14 | distribution: 'temurin' 15 | - name: Build with Maven 16 | run: mvn -B package --file pom.xml -------------------------------------------------------------------------------- /.github/workflows/maven-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a package using Maven and then publish it to GitHub packages when a release is created 2 | # For more information see: https://github.com/actions/setup-java#apache-maven-with-a-settings-path 3 | 4 | name: Maven Package to Unnamed Repo and github packages 5 | 6 | on: 7 | release: 8 | types: [created] 9 | jobs: 10 | publish: 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: read 14 | packages: write 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: Set up Java for publishing to unnamed repo 18 | uses: actions/setup-java@v3 19 | with: 20 | java-version: '17' 21 | distribution: 'temurin' 22 | server-id: unnamed-releases 23 | server-username: MAVEN_USERNAME 24 | server-password: MAVEN_PASSWORD 25 | - name: Publish to unnamed repo 26 | run: mvn --batch-mode deploy 27 | env: 28 | MAVEN_USERNAME: ${{ secrets.USERNAME }} 29 | MAVEN_PASSWORD: ${{ secrets.TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | .idea/ 26 | target/ 27 | *.iml 28 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/Authorizer.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow; 2 | 3 | public interface Authorizer { 4 | 5 | /** 6 | * If an executor has certain permission, meaning that it is authorized. 7 | * 8 | * @param namespace The {@link Namespace} that contains injected values like the source. 9 | * @param permission The permission to check against. 10 | * @return If the source is authorized to this permission, true if the permission is empty. 11 | */ 12 | boolean isAuthorized(Namespace namespace, String permission); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/NamespaceImpl.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow; 2 | 3 | import java.util.Map; 4 | import java.util.Objects; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | 7 | final class NamespaceImpl implements Namespace { 8 | 9 | private final Map, Map> backing; 10 | 11 | public NamespaceImpl() { 12 | backing = new ConcurrentHashMap<>(); 13 | } 14 | 15 | @Override 16 | @SuppressWarnings("unchecked") 17 | public T getObject(Class clazz, String name) { 18 | return (T) backing.getOrDefault(clazz, new ConcurrentHashMap<>()).get(name); 19 | } 20 | 21 | @Override 22 | public void setObject(Class clazz, String name, T object) { 23 | Map map = backing.computeIfAbsent(clazz, key -> new ConcurrentHashMap<>()); 24 | 25 | map.put(name, object); 26 | } 27 | 28 | @Override 29 | public boolean equals(Object o) { 30 | if (this == o) return true; 31 | if (!(o instanceof NamespaceImpl)) return false; 32 | NamespaceImpl namespace = (NamespaceImpl) o; 33 | return Objects.equals(backing, namespace.backing); 34 | } 35 | 36 | @Override 37 | public int hashCode() { 38 | return Objects.hash(backing); 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/ParseResult.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow; 2 | 3 | import team.unnamed.commandflow.exception.CommandException; 4 | 5 | import java.util.Optional; 6 | 7 | public interface ParseResult { 8 | 9 | Optional getContext(); 10 | 11 | Optional getException(); 12 | 13 | default boolean isError() { 14 | return getException().isPresent(); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/SimpleErrorHandler.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.Objects; 6 | 7 | public class SimpleErrorHandler implements ErrorHandler { 8 | 9 | private final Map, ErrorConsumer> exceptionHandlers; 10 | private ErrorConsumer defaultHandler; 11 | 12 | public SimpleErrorHandler() { 13 | exceptionHandlers = new HashMap<>(); 14 | defaultHandler = THROW_CONSUMER; 15 | } 16 | 17 | @Override 18 | public void addExceptionHandler(Class type, ErrorConsumer consumer) { 19 | Objects.requireNonNull(consumer, "The error handler can't be null"); 20 | exceptionHandlers.put(type, consumer); 21 | } 22 | 23 | @Override 24 | public void setFallbackHandler(ErrorConsumer consumer) { 25 | defaultHandler = Objects.requireNonNull(consumer, "The fallback error handler can't be null"); 26 | } 27 | 28 | @Override 29 | @SuppressWarnings("unchecked") 30 | public boolean handleException(Namespace namespace, Throwable exception) throws Throwable { 31 | Class throwableType = exception.getClass(); 32 | 33 | ErrorConsumer consumer = (ErrorConsumer) exceptionHandlers.getOrDefault(throwableType, defaultHandler); 34 | return consumer.accept(namespace, exception); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/CommandClass.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated; 2 | 3 | /** 4 | * A marker interface for Commands based on annotations. 5 | */ 6 | public interface CommandClass { 7 | } 8 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/ReflectionInstanceCreator.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated; 2 | 3 | import java.lang.reflect.Constructor; 4 | import java.lang.reflect.InvocationTargetException; 5 | 6 | public class ReflectionInstanceCreator implements SubCommandInstanceCreator { 7 | 8 | @Override 9 | public CommandClass createInstance(Class clazz, CommandClass parent) { 10 | try { 11 | Constructor constructor; 12 | boolean useUpperClass = true; 13 | 14 | try { 15 | constructor = clazz.getConstructor(parent.getClass()); 16 | } catch (NoSuchMethodException e) { 17 | constructor = clazz.getConstructor(); 18 | useUpperClass = false; 19 | } 20 | 21 | boolean accessible = constructor.isAccessible(); 22 | constructor.setAccessible(true); 23 | 24 | CommandClass instance; 25 | 26 | if (useUpperClass) { 27 | instance = (CommandClass) constructor.newInstance(parent); 28 | } else { 29 | instance = (CommandClass) constructor.newInstance(); 30 | } 31 | constructor.setAccessible(accessible); 32 | 33 | return instance; 34 | } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { 35 | e.printStackTrace(); 36 | } 37 | 38 | return null; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/ReflectionSubCommandHandler.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated; 2 | 3 | import team.unnamed.commandflow.command.Command; 4 | import team.unnamed.commandflow.exception.ArgumentException; 5 | import team.unnamed.commandflow.exception.ArgumentParseException; 6 | import team.unnamed.commandflow.part.defaults.SubCommandPart; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import java.lang.reflect.InvocationTargetException; 11 | import java.lang.reflect.Method; 12 | 13 | public class ReflectionSubCommandHandler implements SubCommandPart.SubCommandHandler { 14 | 15 | private final CommandClass handler; 16 | private final Method handlerMethod; 17 | 18 | public ReflectionSubCommandHandler(CommandClass handler, Method handlerMethod) { 19 | this.handler = handler; 20 | this.handlerMethod = handlerMethod; 21 | } 22 | 23 | @Override 24 | public void handle(SubCommandPart.@NotNull HandlerContext context, @NotNull String label, @Nullable Command command) throws ArgumentParseException { 25 | try { 26 | boolean accessible = handlerMethod.isAccessible(); 27 | 28 | handlerMethod.setAccessible(true); 29 | handlerMethod.invoke(handler, context, label, command); 30 | handlerMethod.setAccessible(accessible); 31 | } catch (IllegalAccessException | InvocationTargetException e) { 32 | Throwable cause = e.getCause(); 33 | if (cause instanceof ArgumentException) { 34 | throw (ArgumentException) cause; 35 | } 36 | 37 | throw new ArgumentParseException("Internal error.", cause) 38 | .setArgument(context.getPart()) 39 | .setCommand(context.getContext().getCommand()); 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/SubCommandInstanceCreator.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated; 2 | 3 | /** 4 | * An interface with the purpose of creating a {@link CommandClass} instance that is a sub commands class of another 5 | * {@link CommandClass}, with that class being the parent of this one. 6 | */ 7 | public interface SubCommandInstanceCreator { 8 | 9 | /** 10 | * Create an instance of a specified {@link CommandClass} with a given {@link CommandClass} as parent. 11 | * 12 | * @param clazz The type of the {@link CommandClass} to create an instance of. 13 | * @param parent The parent instance of this {@link CommandClass}. 14 | * @return A {@link CommandClass} instance. 15 | */ 16 | CommandClass createInstance(Class clazz, CommandClass parent); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/action/ValueGetter.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.action; 2 | 3 | import team.unnamed.commandflow.CommandContext; 4 | import team.unnamed.commandflow.part.CommandPart; 5 | 6 | public interface ValueGetter { 7 | Object getValue(CommandContext commandContext); 8 | 9 | static ValueGetter forPart(CommandPart part) { 10 | return commandContext -> 11 | commandContext.getValue(part).orElse(null); 12 | } 13 | 14 | static ValueGetter forPart(String partName, int index) { 15 | return commandContext -> 16 | commandContext.getPart(partName, index) 17 | .flatMap(commandContext::getValue).orElse(null); 18 | } 19 | 20 | static ValueGetter forOptionalPart(CommandPart part) { 21 | return commandContext -> 22 | commandContext.getValue(part); 23 | } 24 | 25 | static ValueGetter forOptionalPart(String partName, int index) { 26 | return commandContext -> 27 | commandContext.getPart(partName, index) 28 | .flatMap(commandContext::getValue); 29 | } 30 | 31 | static ValueGetter forPartValues(CommandPart part) { 32 | return commandContext -> 33 | commandContext.getValues(part); 34 | } 35 | 36 | static ValueGetter forPartValues(String partName, int index) { 37 | return commandContext -> 38 | commandContext.getPart(partName, index) 39 | .map(commandContext::getValues); 40 | } 41 | 42 | static ValueGetter forPartRaw(CommandPart part) { 43 | return commandContext -> 44 | commandContext.getRaw(part); 45 | } 46 | 47 | static ValueGetter ofInstance(Object object) { 48 | return commandContext -> object; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/annotation/ArgOrSub.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target({ElementType.TYPE}) 10 | public @interface ArgOrSub { 11 | 12 | /** 13 | * If the order of parsing should be reversed to first try parsing the subcommand and later on the arguments 14 | * @return A boolean indicating whether the order of parsing should be reversed. 15 | */ 16 | boolean value() default false; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/annotation/Command.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target({ElementType.METHOD, ElementType.TYPE}) 10 | public @interface Command { 11 | 12 | String[] names(); 13 | 14 | String desc() default ""; 15 | 16 | String permission() default ""; 17 | 18 | String permissionMessage() default "%translatable:command.no-permission%"; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/annotation/ConsumeAll.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.PARAMETER) 10 | public @interface ConsumeAll { 11 | } 12 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/annotation/Flag.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.PARAMETER) 10 | public @interface Flag { 11 | 12 | /** 13 | * The short name used for this flag 14 | * @return A string representing the short name for this flag 15 | */ 16 | String value(); 17 | 18 | boolean allowFullName() default false; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/annotation/Handler.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target({ElementType.METHOD, ElementType.TYPE}) 10 | public @interface Handler { 11 | } 12 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/annotation/Limit.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.PARAMETER) 10 | public @interface Limit { 11 | 12 | int value(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/annotation/Named.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.PARAMETER) 10 | public @interface Named { 11 | 12 | String value(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/annotation/OptArg.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.annotation; 2 | 3 | import team.unnamed.commandflow.command.Command; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * The purpose of this annotation is to define the default value of a parameter 12 | * in an {@link Command}. 13 | */ 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target(ElementType.PARAMETER) 16 | public @interface OptArg { 17 | 18 | String[] value() default {}; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/annotation/ParentArg.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.PARAMETER) 10 | public @interface ParentArg { 11 | 12 | /** 13 | * The index used to get this argument CommandPart value, 0 by default 14 | * but can be changed if more than 1 part with the same name is present 15 | * 16 | * @return An integer representing the index of this argument's CommandPart 17 | */ 18 | int value() default 0; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/annotation/Range.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.annotation; 2 | 3 | public @interface Range { 4 | 5 | int min(); 6 | 7 | int max(); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/annotation/Required.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Indicates that a command class that contains subcommands 10 | * Requires a subcommand to be executed 11 | */ 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Target(ElementType.TYPE) 14 | public @interface Required { 15 | } 16 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/annotation/Rewrites.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.PARAMETER) 10 | public @interface Rewrites { 11 | 12 | Rewrite[] value(); 13 | 14 | @Retention(RetentionPolicy.RUNTIME) 15 | @Target(ElementType.ANNOTATION_TYPE) 16 | @interface Rewrite { 17 | 18 | String to(); 19 | 20 | String[] from(); 21 | 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/annotation/Sender.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.PARAMETER) 10 | public @interface Sender { 11 | } 12 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/annotation/Strict.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * The sole purpose of this annotation is to allow the creation of strict optional arguments, which are not the 10 | * old default behaviour of the command manager 11 | */ 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Target(ElementType.PARAMETER) 14 | public @interface Strict { 15 | } 16 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/annotation/SubCommandClasses.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.annotation; 2 | 3 | import team.unnamed.commandflow.annotated.CommandClass; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * This annotation has the purpose to allow multiple layers of subcommands 12 | * on parametric commands. This annotation will register the specified command classes 13 | * as subcommands for the annotated command class 14 | */ 15 | @Retention(RetentionPolicy.RUNTIME) 16 | @Target(ElementType.TYPE) 17 | public @interface SubCommandClasses { 18 | 19 | Class[] value(); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/annotation/Suggestions.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.PARAMETER) 10 | public @interface Suggestions { 11 | String[] suggestions(); 12 | } 13 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/annotation/Switch.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.PARAMETER) 10 | public @interface Switch { 11 | 12 | /** 13 | * The short name used for this flag 14 | * 15 | * @return A string representing the short name for this flag 16 | */ 17 | String value(); 18 | 19 | boolean allowFullName() default false; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/annotation/Text.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.PARAMETER) 10 | public @interface Text { 11 | } 12 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/annotation/Usage.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Changes the usage for the command annotated with this annotation, allowing custom usages instead of the default 10 | * auto generated ones. 11 | */ 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Target({ElementType.METHOD, ElementType.TYPE}) 14 | public @interface Usage { 15 | 16 | /** 17 | * @return A string representing the usage for this command. 18 | */ 19 | String value(); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/builder/AnnotatedCommandBuilder.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.builder; 2 | 3 | import team.unnamed.commandflow.annotated.part.PartInjector; 4 | import team.unnamed.commandflow.command.Action; 5 | import team.unnamed.commandflow.command.Command; 6 | import team.unnamed.commandflow.part.CommandPart; 7 | 8 | public interface AnnotatedCommandBuilder { 9 | 10 | /** 11 | * Creates a new builder to create a {@link Command} instance. 12 | *

13 | * While being similar to {@link Command.Builder} it allows to create commands based on annotations, 14 | * that means that the {@link Action} or {@link CommandPart} instances 15 | * for the build command will be created based on a given method. 16 | * 17 | * @param name The name of {@link Command} to build. 18 | * @return A {@link CommandDataNode} which will allow building a complete {@link Command} tree. 19 | */ 20 | CommandDataNode newCommand(String name); 21 | 22 | static AnnotatedCommandBuilder create(PartInjector injector) { 23 | return new AnnotatedCommandBuilderImpl(injector); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/builder/AnnotatedCommandBuilderImpl.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.builder; 2 | 3 | import team.unnamed.commandflow.annotated.part.PartInjector; 4 | 5 | final class AnnotatedCommandBuilderImpl implements AnnotatedCommandBuilder { 6 | 7 | private final PartInjector injector; 8 | 9 | AnnotatedCommandBuilderImpl(PartInjector injector) { 10 | this.injector = injector; 11 | } 12 | 13 | @Override 14 | public CommandDataNode newCommand(String name) { 15 | return new CommandBuilderNodesImpl(name, injector); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/builder/Buildable.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.builder; 2 | 3 | import team.unnamed.commandflow.command.Command; 4 | 5 | public interface Buildable { 6 | 7 | /** 8 | * Builds a command based on the internal state of this class. 9 | * 10 | * @return A non null {@link Command} instance. 11 | */ 12 | Command build(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/builder/CommandActionNode.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.builder; 2 | 3 | import team.unnamed.commandflow.annotated.CommandClass; 4 | import team.unnamed.commandflow.command.Action; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.lang.reflect.Method; 8 | 9 | public interface CommandActionNode extends Buildable { 10 | 11 | /** 12 | * Sets the {@link Action} of this command to a one created based on a {@link Method} with annotations, actually 13 | * delegating the {@link Action} setting to {@link CommandActionNode#action(Action)}. 14 | * 15 | * @param method The method which will be the {@link Action}. 16 | * @param commandClass The {@link CommandClass} instance in which this method is present. 17 | * @return A {@link SubCommandsNode} instance, which will allow continuing the building process of this command. 18 | */ 19 | @NotNull SubCommandsNode action(@NotNull Method method, 20 | @NotNull CommandClass commandClass); 21 | 22 | /** 23 | * Sets the {@link Action} for this command to a given one. 24 | * 25 | * @param action The {@link Action} for this command. 26 | * @return A {@link SubCommandsNode} instance, which will allow continuing the building process of this command. 27 | */ 28 | @NotNull SubCommandsNode action(@NotNull Action action); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/builder/CommandModifiersNode.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.builder; 2 | 3 | import team.unnamed.commandflow.annotated.CommandClass; 4 | import team.unnamed.commandflow.command.modifiers.CommandModifier; 5 | import team.unnamed.commandflow.command.modifiers.ModifierPhase; 6 | import team.unnamed.commandflow.part.CommandPart; 7 | import org.jetbrains.annotations.NotNull; 8 | import team.unnamed.commandflow.annotated.modifier.CommandModifierFactory; 9 | import team.unnamed.commandflow.command.Command; 10 | 11 | import java.lang.annotation.Annotation; 12 | import java.lang.reflect.Method; 13 | import java.util.List; 14 | 15 | public interface CommandModifiersNode extends Buildable { 16 | 17 | /** 18 | * Sets the {@link CommandModifier} list of this command to a one created based on a {@link Method} with annotations. 19 | * The actual {@link CommandModifier} instances will be created based on the annotations that the method has, those 20 | * are created using a {@link CommandModifierFactory} provided by the PartInjector. 21 | * 22 | * @param method The method which parameters will be converted to the list of {@link CommandPart} instances of the comand. 23 | * @param handler The {@link CommandClass} instance in which this method is present. 24 | * @return A {@link CommandModifiersNode} instance, which will allow continuing the building process of this command. 25 | */ 26 | @NotNull CommandModifiersNode ofMethod(@NotNull Method method, 27 | @NotNull CommandClass handler); 28 | 29 | @NotNull CommandModifiersNode addModifiers(List annotations); 30 | 31 | 32 | @NotNull CommandModifiersNode addModifier(@NotNull ModifierPhase phase, @NotNull CommandModifier modifier); 33 | 34 | /** 35 | * This method gives you the next step of the process of building a {@link Command}. 36 | * 37 | * @return A {@link CommandPartsNode} instance, which will allow continuing the building process of this command. 38 | */ 39 | @NotNull CommandPartsNode parts(); 40 | } 41 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/builder/SubCommandsNode.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.builder; 2 | 3 | import team.unnamed.commandflow.command.Command; 4 | import team.unnamed.commandflow.part.defaults.SubCommandPart; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import java.lang.annotation.Annotation; 9 | 10 | // TODO: Add documentation for this class. 11 | public interface SubCommandsNode extends Buildable { 12 | 13 | SubCommandsNode addSubCommand(@NotNull Command command); 14 | 15 | SubCommandsNode addSubCommand(@NotNull CommandDataNode commandDataNode); 16 | 17 | SubCommandsNode setSubCommandHandler(@Nullable SubCommandPart.SubCommandHandler subCommandHandler); 18 | 19 | SubCommandsNode setModifiers(Annotation... modifiers); 20 | 21 | default SubCommandsNode argumentsOrSubCommand() { 22 | return argumentsOrSubCommand(false); 23 | } 24 | 25 | SubCommandsNode argumentsOrSubCommand(boolean reversed); 26 | 27 | SubCommandsNode optional(); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/modifier/CommandModifierFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.modifier; 2 | 3 | import team.unnamed.commandflow.command.modifiers.CommandModifier; 4 | import team.unnamed.commandflow.command.modifiers.CommandModifiers; 5 | 6 | import java.lang.annotation.Annotation; 7 | import java.util.List; 8 | 9 | public interface CommandModifierFactory { 10 | /** 11 | * Creates and adds one or more {@linkplain CommandModifier}'s to the {@link CommandModifiers.Builder} 12 | * 13 | * @param builder The builder to add the modifiers to. 14 | * @param annotations The annotations at the context (command class or method) 15 | * @return The CommandModifier added into the specified builder. 16 | */ 17 | CommandModifier modify(CommandModifiers.Builder builder, List annotations); 18 | } 19 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/part/AbstractModule.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.part; 2 | 3 | import team.unnamed.commandflow.part.CommandPart; 4 | 5 | import java.lang.annotation.Annotation; 6 | import java.lang.reflect.Type; 7 | 8 | /** 9 | * A default implementation of {@linkplain Module}, you just need to implement the configure method. 10 | */ 11 | public abstract class AbstractModule implements Module { 12 | 13 | private PartInjector injector; 14 | 15 | @Override 16 | public final void configure(PartInjector injector) { 17 | this.injector = injector; 18 | configure(); 19 | this.injector = null; 20 | } 21 | 22 | /** 23 | * Returns the current {@linkplain PartInjector} used for bindings 24 | * 25 | * @return The {@linkplain PartInjector} that every bind method is being delegated to. 26 | */ 27 | protected final PartInjector getInjector() { 28 | if (this.injector == null) { 29 | throw new IllegalStateException("The bind methods only can be called when the module is installed on an injector!"); 30 | } 31 | return this.injector; 32 | } 33 | 34 | protected final void bindModifier(Class annotation, PartModifier partModifier) { 35 | getInjector().bindModifier(annotation, partModifier); 36 | } 37 | 38 | protected final void bindFactory(Type type, PartFactory partFactory) { 39 | getInjector().bindFactory(type, partFactory); 40 | } 41 | 42 | protected final void bindFactory(Key key, PartFactory factory) { 43 | getInjector().bindFactory(key, factory); 44 | } 45 | 46 | protected final void bind(Key key, CommandPart part) { 47 | getInjector().bindPart(key, part); 48 | } 49 | 50 | protected final void bind(Type type, CommandPart part) { 51 | getInjector().bindPart(type, part); 52 | } 53 | 54 | public abstract void configure(); 55 | 56 | } 57 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/part/DelegatePartModifier.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.part; 2 | 3 | import team.unnamed.commandflow.part.CommandPart; 4 | 5 | import java.lang.annotation.Annotation; 6 | import java.util.List; 7 | 8 | public class DelegatePartModifier implements PartModifier { 9 | 10 | private final List delegates; 11 | 12 | public DelegatePartModifier(List delegates) { 13 | this.delegates = delegates; 14 | } 15 | 16 | @Override 17 | public CommandPart modify(CommandPart original, List modifiers) { 18 | CommandPart part = original; 19 | for (PartModifier delegate : delegates) { 20 | part = delegate.modify(part, modifiers); 21 | } 22 | 23 | return part; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/part/Key.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.part; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | import java.lang.annotation.Annotation; 7 | import java.lang.reflect.Type; 8 | import java.util.Objects; 9 | 10 | public class Key { 11 | 12 | private final Type type; 13 | private final Class annotation; 14 | 15 | public Key(@NotNull Type type, @Nullable Class annotation) { 16 | this.type = type; 17 | this.annotation = annotation; 18 | } 19 | 20 | public Key(@NotNull Type type) { 21 | this(type, null); 22 | } 23 | 24 | @NotNull 25 | public Type getType() { 26 | return type; 27 | } 28 | 29 | @Nullable 30 | public Class getAnnotation() { 31 | return annotation; 32 | } 33 | 34 | @Override 35 | public boolean equals(Object o) { 36 | if (this == o) return true; 37 | if (!(o instanceof Key)) return false; 38 | Key key = (Key) o; 39 | return type.equals(key.type) && 40 | Objects.equals(annotation, key.annotation); 41 | } 42 | 43 | @Override 44 | public int hashCode() { 45 | return Objects.hash(type, annotation); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/part/Module.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.part; 2 | 3 | /** 4 | * An aggregation of multiple bindings of {@linkplain PartModifier} or {@linkplain PartFactory}, allowing to automatically register 5 | * a set of modifiers or factories. 6 | */ 7 | @FunctionalInterface 8 | public interface Module { 9 | 10 | /** 11 | * The configure method, where you add the bindings for {@linkplain PartModifier}s or a set of {@linkplain PartFactory}. 12 | */ 13 | void configure(PartInjector injector); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/part/PartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.part; 2 | 3 | import team.unnamed.commandflow.part.CommandPart; 4 | 5 | import java.lang.annotation.Annotation; 6 | import java.util.List; 7 | 8 | public interface PartFactory { 9 | 10 | /** 11 | * Creates a new {@link CommandPart} with the given name 12 | * 13 | * @param name The name for the {@link CommandPart} 14 | * @param modifiers The modifiers for the {@link CommandPart} 15 | * @return A new {@link CommandPart} with the given name and using the specified modifiers 16 | */ 17 | CommandPart createPart(String name, List modifiers); 18 | 19 | default T getAnnotation(List modifiers, Class type) { 20 | T finalModifier = null; 21 | 22 | for (Annotation modifier : modifiers) { 23 | if (modifier.annotationType() == type) { 24 | finalModifier = (T) modifier; 25 | 26 | break; 27 | } 28 | } 29 | 30 | return finalModifier; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/part/PartModifier.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.part; 2 | 3 | import team.unnamed.commandflow.part.CommandPart; 4 | 5 | import java.lang.annotation.Annotation; 6 | import java.util.List; 7 | 8 | public interface PartModifier { 9 | 10 | /** 11 | * Modifies the given {@link CommandPart} wrapping or changing values of it 12 | * 13 | * @param original The original {@link CommandPart} to modify. 14 | * @param modifiers The modifiers for the {@link CommandPart} 15 | * @return The modified version of {@link CommandPart}. 16 | */ 17 | CommandPart modify(CommandPart original, List modifiers); 18 | 19 | default T getModifier(List modifiers, Class type) { 20 | T finalModifier = null; 21 | 22 | for (Annotation modifier : modifiers) { 23 | if (modifier.annotationType() == type) { 24 | finalModifier = (T) modifier; 25 | 26 | break; 27 | } 28 | } 29 | 30 | return finalModifier; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/part/defaults/factory/ArgumentStackPartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.part.defaults.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.PartFactory; 4 | import team.unnamed.commandflow.part.CommandPart; 5 | import team.unnamed.commandflow.part.defaults.ArgumentStackPart; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.util.List; 9 | 10 | public class ArgumentStackPartFactory implements PartFactory { 11 | 12 | @Override 13 | public CommandPart createPart(String name, List modifiers) { 14 | return new ArgumentStackPart(name); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/part/defaults/factory/BooleanPartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.part.defaults.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.PartFactory; 4 | import team.unnamed.commandflow.part.CommandPart; 5 | import team.unnamed.commandflow.part.defaults.BooleanPart; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.util.List; 9 | 10 | public class BooleanPartFactory implements PartFactory { 11 | 12 | @Override 13 | public CommandPart createPart(String name, List modifiers) { 14 | return new BooleanPart(name); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/part/defaults/factory/ContextFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.part.defaults.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.PartFactory; 4 | import team.unnamed.commandflow.part.CommandPart; 5 | import team.unnamed.commandflow.part.defaults.ContextPart; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.util.List; 9 | 10 | public class ContextFactory implements PartFactory { 11 | 12 | @Override 13 | public CommandPart createPart(String name, List modifiers) { 14 | return new ContextPart(name); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/part/defaults/factory/DoublePartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.part.defaults.factory; 2 | 3 | import team.unnamed.commandflow.annotated.annotation.Range; 4 | import team.unnamed.commandflow.annotated.part.PartFactory; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | import team.unnamed.commandflow.part.defaults.DoublePart; 7 | 8 | import java.lang.annotation.Annotation; 9 | import java.util.List; 10 | 11 | public class DoublePartFactory implements PartFactory { 12 | 13 | @Override 14 | public CommandPart createPart(String name, List modifiers) { 15 | Range range = getAnnotation(modifiers, Range.class); 16 | 17 | if (range != null) { 18 | return new DoublePart(name, range.min(), range.max()); 19 | } 20 | 21 | return new DoublePart(name); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/part/defaults/factory/EnumPartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.part.defaults.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.PartFactory; 4 | import team.unnamed.commandflow.part.CommandPart; 5 | import team.unnamed.commandflow.part.Parts; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.util.List; 9 | 10 | public class EnumPartFactory implements PartFactory { 11 | 12 | private final Class> enumType; 13 | 14 | public EnumPartFactory(Class> enumType) { 15 | if (enumType == null) { 16 | throw new IllegalArgumentException("The enum type can't be null!"); 17 | } 18 | 19 | this.enumType = enumType; 20 | } 21 | 22 | @Override 23 | public CommandPart createPart(String name, List modifiers) { 24 | return Parts.enumPart(name, enumType); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/part/defaults/factory/FloatPartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.part.defaults.factory; 2 | 3 | import team.unnamed.commandflow.annotated.annotation.Range; 4 | import team.unnamed.commandflow.annotated.part.PartFactory; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | import team.unnamed.commandflow.part.defaults.FloatPart; 7 | 8 | import java.lang.annotation.Annotation; 9 | import java.util.List; 10 | 11 | public class FloatPartFactory implements PartFactory { 12 | 13 | @Override 14 | public CommandPart createPart(String name, List modifiers) { 15 | Range range = getAnnotation(modifiers, Range.class); 16 | 17 | if (range != null) { 18 | return new FloatPart(name, range.min(), range.max()); 19 | } 20 | 21 | return new FloatPart(name); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/part/defaults/factory/IntegerPartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.part.defaults.factory; 2 | 3 | import team.unnamed.commandflow.annotated.annotation.Range; 4 | import team.unnamed.commandflow.annotated.part.PartFactory; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | import team.unnamed.commandflow.part.defaults.IntegerPart; 7 | 8 | import java.lang.annotation.Annotation; 9 | import java.util.List; 10 | 11 | public class IntegerPartFactory implements PartFactory { 12 | 13 | @Override 14 | public CommandPart createPart(String name, List modifiers) { 15 | Range range = getAnnotation(modifiers, Range.class); 16 | 17 | if (range != null) { 18 | return new IntegerPart(name, range.min(), range.max()); 19 | } 20 | 21 | return new IntegerPart(name); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/part/defaults/factory/LongPartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.part.defaults.factory; 2 | 3 | import team.unnamed.commandflow.annotated.annotation.Range; 4 | import team.unnamed.commandflow.annotated.part.PartFactory; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | import team.unnamed.commandflow.part.defaults.FloatPart; 7 | import team.unnamed.commandflow.part.defaults.LongPart; 8 | 9 | import java.lang.annotation.Annotation; 10 | import java.util.List; 11 | 12 | public class LongPartFactory implements PartFactory { 13 | 14 | @Override 15 | public CommandPart createPart(String name, List modifiers) { 16 | Range range = getAnnotation(modifiers, Range.class); 17 | 18 | if (range != null) { 19 | return new LongPart(name, range.min(), range.max()); 20 | } 21 | 22 | return new FloatPart(name); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/part/defaults/factory/StringPartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.part.defaults.factory; 2 | 3 | import team.unnamed.commandflow.annotated.annotation.ConsumeAll; 4 | import team.unnamed.commandflow.annotated.part.PartFactory; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | import team.unnamed.commandflow.part.defaults.StringPart; 7 | 8 | import java.lang.annotation.Annotation; 9 | import java.util.List; 10 | 11 | public class StringPartFactory implements PartFactory { 12 | 13 | @Override 14 | public CommandPart createPart(String name, List modifiers) { 15 | return new StringPart(name, getAnnotation(modifiers, ConsumeAll.class) != null); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/part/defaults/factory/StringTextPartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.part.defaults.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.PartFactory; 4 | import team.unnamed.commandflow.part.CommandPart; 5 | import team.unnamed.commandflow.part.defaults.StringPart; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.util.List; 9 | 10 | public class StringTextPartFactory implements PartFactory { 11 | 12 | @Override 13 | public CommandPart createPart(String name, List modifiers) { 14 | return new StringPart(name, true, true); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/part/defaults/factory/SwitchPartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.part.defaults.factory; 2 | 3 | import team.unnamed.commandflow.annotated.annotation.Switch; 4 | import team.unnamed.commandflow.annotated.part.PartFactory; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | import team.unnamed.commandflow.part.defaults.SwitchPart; 7 | 8 | import java.lang.annotation.Annotation; 9 | import java.util.List; 10 | 11 | public class SwitchPartFactory implements PartFactory { 12 | 13 | @Override 14 | public CommandPart createPart(String name, List modifiers) { 15 | Switch flag = getAnnotation(modifiers, Switch.class); 16 | 17 | String shortName = flag != null ? flag.value() + "" : name; 18 | 19 | return new SwitchPart(name, shortName, flag != null && flag.allowFullName()); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/part/defaults/modifier/LimitModifier.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.part.defaults.modifier; 2 | 3 | import team.unnamed.commandflow.annotated.annotation.Limit; 4 | import team.unnamed.commandflow.annotated.part.PartModifier; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | import team.unnamed.commandflow.part.Parts; 7 | 8 | import java.lang.annotation.Annotation; 9 | import java.util.List; 10 | 11 | public class LimitModifier implements PartModifier { 12 | 13 | @Override 14 | public CommandPart modify(CommandPart original, List modifiers) { 15 | Limit limit = getModifier(modifiers, Limit.class); 16 | 17 | if (limit == null) { 18 | return original; 19 | } 20 | 21 | return Parts.limit(original, limit.value()); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/part/defaults/modifier/OptionalModifier.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.part.defaults.modifier; 2 | 3 | import team.unnamed.commandflow.annotated.annotation.OptArg; 4 | import team.unnamed.commandflow.annotated.annotation.Strict; 5 | import team.unnamed.commandflow.annotated.part.PartModifier; 6 | import team.unnamed.commandflow.part.CommandPart; 7 | import team.unnamed.commandflow.part.Parts; 8 | 9 | import java.lang.annotation.Annotation; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | 13 | public class OptionalModifier implements PartModifier { 14 | @Override 15 | public CommandPart modify(CommandPart original, List modifiers) { 16 | OptArg optional = getModifier(modifiers, OptArg.class); 17 | 18 | if (optional == null) { 19 | return original; 20 | } 21 | 22 | if (getModifier(modifiers, Strict.class) != null) { 23 | return Parts.strictOptional(original, Arrays.asList(optional.value())); 24 | } 25 | 26 | return Parts.optional(original,Arrays.asList(optional.value())); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/part/defaults/modifier/RewritesModifier.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.part.defaults.modifier; 2 | 3 | import team.unnamed.commandflow.annotated.annotation.Rewrites; 4 | import team.unnamed.commandflow.annotated.part.PartModifier; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | import team.unnamed.commandflow.part.defaults.ArgumentRewriterPart; 7 | 8 | import java.lang.annotation.Annotation; 9 | import java.util.List; 10 | 11 | public class RewritesModifier implements PartModifier { 12 | 13 | @Override 14 | public CommandPart modify(CommandPart original, List modifiers) { 15 | Rewrites rewrites = getModifier(modifiers, Rewrites.class); 16 | 17 | ArgumentRewriterPart rewriterPart = new ArgumentRewriterPart(original); 18 | 19 | for (Rewrites.Rewrite rewrite : rewrites.value()) { 20 | rewriterPart.addRewrite(rewrite.to(), rewrite.from()); 21 | } 22 | 23 | return rewriterPart; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/part/defaults/modifier/SuggestionsModifier.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.part.defaults.modifier; 2 | 3 | import team.unnamed.commandflow.annotated.annotation.Suggestions; 4 | import team.unnamed.commandflow.annotated.part.PartModifier; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | import team.unnamed.commandflow.part.defaults.SuggestionsModifierPart; 7 | 8 | import java.lang.annotation.Annotation; 9 | import java.util.Arrays; 10 | import java.util.List; 11 | 12 | public class SuggestionsModifier implements PartModifier { 13 | @Override 14 | public CommandPart modify(CommandPart original, List modifiers) { 15 | Suggestions suggestions = getModifier(modifiers, Suggestions.class); 16 | 17 | return new SuggestionsModifierPart(original, Arrays.asList(suggestions.suggestions())); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/annotated/part/defaults/modifier/ValueFlagModifier.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated.part.defaults.modifier; 2 | 3 | import team.unnamed.commandflow.annotated.annotation.Flag; 4 | import team.unnamed.commandflow.annotated.part.PartModifier; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | import team.unnamed.commandflow.part.defaults.ValueFlagPart; 7 | 8 | import java.lang.annotation.Annotation; 9 | import java.util.List; 10 | 11 | public class ValueFlagModifier implements PartModifier { 12 | 13 | @Override 14 | public CommandPart modify(CommandPart original, List modifiers) { 15 | Flag flag = getModifier(modifiers, Flag.class); 16 | 17 | String shortName = flag != null ? flag.value() + "" : original.getName(); 18 | 19 | return new ValueFlagPart(shortName, flag != null && flag.allowFullName(), original); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/command/Action.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.command; 2 | 3 | import team.unnamed.commandflow.CommandContext; 4 | import team.unnamed.commandflow.CommandManager; 5 | import team.unnamed.commandflow.exception.CommandException; 6 | 7 | public interface Action { 8 | 9 | /** 10 | * A NOP default Action used for {@link Command} instances which action isn't defined 11 | */ 12 | Action NULL_ACTION = context -> false; 13 | 14 | /** 15 | * The action to execute when the {@link Command} is being called/run by the {@link CommandManager}. 16 | * 17 | * @param context The {@link CommandContext} for this executed command. 18 | * @return If the action was executed sucessfully or not, if the value is false, the usage will be send to the source. 19 | * @throws CommandException If an error occurs while executing the action. 20 | */ 21 | boolean execute(CommandContext context) throws CommandException; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/command/modifiers/CommandModifier.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.command.modifiers; 2 | 3 | import team.unnamed.commandflow.CommandContext; 4 | import team.unnamed.commandflow.stack.ArgumentStack; 5 | 6 | public interface CommandModifier { 7 | 8 | /** 9 | * Tries to intercept the command in a certain phase of its execution, allowing it to cancel the execution. 10 | * This allows us to implement things like cooldowns, or other things that modify the command. 11 | * 12 | * @param context The {@link CommandContext} for the command, at least the command to start parsing/executing is set. 13 | * @param stack The {@link ArgumentStack} at its current position without any modification. 14 | * @param phase The {@link ModifierPhase} in which this modifier is being called. 15 | * @return Whether the command should continue being parsed/executed or not. 16 | */ 17 | boolean modify(CommandContext context, ArgumentStack stack, ModifierPhase phase); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/command/modifiers/ModifierPhase.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.command.modifiers; 2 | 3 | /** 4 | * This enum represents the different phases in which a modifier may be applied. 5 | */ 6 | public enum ModifierPhase { 7 | /** 8 | * The modifier is applied before the command is parsed, just after the context is created. 9 | *

10 | * Only in this phase the stack is available, in the next phases the stack doesn't exists so it's set to null. 11 | */ 12 | PRE_PARSE, 13 | /** 14 | * The modifier is applied after the command is parsed, just before the command is executed. 15 | *

16 | * Another name for this could be POST_PARSE. 17 | */ 18 | PRE_EXECUTE, 19 | /** 20 | * The modifier is applied after the command is executed. 21 | *

22 | * This phase can't cancel anything, as it is the last phase, and the command is already executed, trying to cancel 23 | * something is useless, and only will prevent the next modifier from being called. 24 | */ 25 | POST_EXECUTE 26 | } 27 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/command/modifiers/ModifierUtils.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.command.modifiers; 2 | 3 | import java.util.LinkedList; 4 | import java.util.Map; 5 | 6 | class ModifierUtils { 7 | 8 | public static void removeModifier(CommandModifier modifier, ModifierPhase phase, Map modifiersByPhase) { 9 | if (!modifiersByPhase.remove(phase, modifier)) { 10 | CommandModifier phaseModifier = modifiersByPhase.get(phase); 11 | 12 | if (phaseModifier instanceof SequentialCommandModifier) { 13 | SequentialCommandModifier seqModifier = (SequentialCommandModifier) phaseModifier; 14 | 15 | seqModifier.removeModifier(modifier); 16 | } 17 | } else { 18 | modifiersByPhase.put(phase, new SequentialCommandModifier(new LinkedList<>())); 19 | } 20 | } 21 | 22 | public static SequentialCommandModifier getSequential(ModifierPhase phase, Map modifiersByPhase) { 23 | CommandModifier modifier = modifiersByPhase.get(phase); 24 | 25 | if (!(modifier instanceof SequentialCommandModifier)) { 26 | SequentialCommandModifier seqModifier = new SequentialCommandModifier(new LinkedList<>()); 27 | seqModifier.addModifier(modifier); 28 | 29 | modifiersByPhase.put(phase, seqModifier); 30 | 31 | return seqModifier; 32 | } 33 | 34 | return (SequentialCommandModifier) modifier; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/exception/ArgumentException.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.exception; 2 | 3 | import team.unnamed.commandflow.command.Command; 4 | import team.unnamed.commandflow.part.CommandPart; 5 | import net.kyori.adventure.text.Component; 6 | 7 | public class ArgumentException extends CommandException { 8 | 9 | private CommandPart argument; 10 | 11 | public ArgumentException() { 12 | } 13 | 14 | public ArgumentException(Component message) { 15 | super(message); 16 | } 17 | 18 | public ArgumentException(String message) { 19 | super(message); 20 | } 21 | 22 | public ArgumentException(String message, Throwable cause) { 23 | super(message, cause); 24 | } 25 | 26 | public ArgumentException(Throwable cause) { 27 | super(cause); 28 | } 29 | 30 | @Override 31 | public ArgumentException setCommand(Command argument) { 32 | // Yes it is 33 | return (ArgumentException) super.setCommand(argument); 34 | } 35 | 36 | public ArgumentException setArgument(CommandPart argument) { 37 | this.argument = argument; 38 | return this; 39 | } 40 | 41 | public CommandPart getArgument() { 42 | return argument; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/exception/ArgumentParseException.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.exception; 2 | 3 | import net.kyori.adventure.text.Component; 4 | 5 | public class ArgumentParseException extends ArgumentException { 6 | 7 | public ArgumentParseException() { 8 | } 9 | 10 | public ArgumentParseException(String message) { 11 | super(message); 12 | } 13 | 14 | public ArgumentParseException(String message, Throwable cause) { 15 | super(message, cause); 16 | } 17 | 18 | public ArgumentParseException(Throwable cause) { 19 | super(cause); 20 | } 21 | 22 | public ArgumentParseException(Component message) { 23 | super(message); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/exception/CommandUsage.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.exception; 2 | 3 | import net.kyori.adventure.text.Component; 4 | 5 | public class CommandUsage extends CommandException { 6 | 7 | public CommandUsage() { 8 | } 9 | 10 | public CommandUsage(Component message) { 11 | super(message); 12 | } 13 | 14 | public CommandUsage(String message) { 15 | super(message); 16 | } 17 | 18 | public CommandUsage(String message, Throwable cause) { 19 | super(message, cause); 20 | } 21 | 22 | public CommandUsage(Throwable cause) { 23 | super(cause); 24 | } 25 | 26 | @Override 27 | public synchronized Throwable fillInStackTrace() { 28 | return this; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/exception/InvalidSubCommandException.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.exception; 2 | 3 | import net.kyori.adventure.text.Component; 4 | 5 | public class InvalidSubCommandException extends ArgumentParseException { 6 | 7 | public InvalidSubCommandException() { 8 | super(); 9 | } 10 | 11 | public InvalidSubCommandException(String message) { 12 | super(message); 13 | } 14 | 15 | public InvalidSubCommandException(String message, Throwable cause) { 16 | super(message, cause); 17 | } 18 | 19 | public InvalidSubCommandException(Throwable cause) { 20 | super(cause); 21 | } 22 | 23 | public InvalidSubCommandException(Component message) { 24 | super(message); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/exception/NoMoreArgumentsException.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.exception; 2 | 3 | import net.kyori.adventure.text.Component; 4 | 5 | public class NoMoreArgumentsException extends ArgumentException { 6 | 7 | public NoMoreArgumentsException() { 8 | } 9 | 10 | public NoMoreArgumentsException(String message) { 11 | super(message); 12 | } 13 | 14 | public NoMoreArgumentsException(int size, int position) { 15 | super(Component.translatable("argument.no-more").args(Component.text(size), Component.text(position))); 16 | } 17 | 18 | public NoMoreArgumentsException(Component message) { 19 | super(message); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/exception/NoPermissionsException.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.exception; 2 | 3 | import net.kyori.adventure.text.Component; 4 | 5 | public class NoPermissionsException extends CommandException { 6 | 7 | public NoPermissionsException() { 8 | } 9 | 10 | public NoPermissionsException(Component message) { 11 | super(message); 12 | } 13 | 14 | public NoPermissionsException(String message) { 15 | super(message); 16 | } 17 | 18 | @Override 19 | public synchronized Throwable fillInStackTrace() { 20 | return this; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/exception/StopParseException.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.exception; 2 | 3 | /** 4 | * Thrown when we should stop parsing the command, without doing any further processing. 5 | */ 6 | public class StopParseException extends CommandException { 7 | 8 | public StopParseException() { 9 | } 10 | 11 | @Override 12 | public synchronized Throwable fillInStackTrace() { 13 | return this; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/executor/DefaultExecutor.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.executor; 2 | 3 | import team.unnamed.commandflow.CommandContext; 4 | import team.unnamed.commandflow.command.Command; 5 | import team.unnamed.commandflow.exception.CommandUsage; 6 | import team.unnamed.commandflow.usage.UsageBuilder; 7 | 8 | public class DefaultExecutor implements Executor { 9 | 10 | @Override 11 | public boolean execute(CommandContext commandContext, UsageBuilder builder) { 12 | Command toExecute = commandContext.getCommand(); 13 | 14 | if (toExecute != null) { 15 | if (!toExecute.getAction().execute(commandContext)) { 16 | throw new CommandUsage(builder.getUsage(commandContext)) 17 | .setCommand(toExecute); 18 | } 19 | } else { 20 | return false; 21 | } 22 | 23 | return true; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/executor/Executor.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.executor; 2 | 3 | import team.unnamed.commandflow.CommandContext; 4 | import team.unnamed.commandflow.command.Action; 5 | import team.unnamed.commandflow.command.Command; 6 | import team.unnamed.commandflow.exception.CommandException; 7 | import team.unnamed.commandflow.usage.UsageBuilder; 8 | 9 | /** 10 | * This class has the functionality of actually calling the {@link Action} of the command 11 | * based on the given {@link CommandContext}. 12 | */ 13 | public interface Executor { 14 | 15 | /** 16 | * Executes the right {@link Action} based on the given {@link CommandContext}. 17 | * 18 | * @param commandContext The {@link CommandContext} of the {@link Command} to execute. 19 | * @param builder The {@link UsageBuilder} used to generate an usage text if the {@link Action} returns false or if 20 | * the command execution fails for a valid reason. 21 | * @return If the {@link Action} for the given {@link CommandContext} could be executed. 22 | * @throws CommandException If the {@link Action} throws an exception. 23 | */ 24 | boolean execute(CommandContext commandContext, UsageBuilder builder) throws CommandException; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/input/InputTokenizer.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.input; 2 | 3 | import java.util.List; 4 | 5 | public interface InputTokenizer { 6 | 7 | /** 8 | * Converts the given input {@link String} into a list of tokens 9 | * 10 | * @param line The string to tokenize 11 | * @return A modifiable list of tokens for the given String 12 | */ 13 | List tokenize(String line); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/input/StringSpaceTokenizer.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.input; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | public class StringSpaceTokenizer implements InputTokenizer { 8 | 9 | /** 10 | * {@inheritDoc} 11 | */ 12 | @Override 13 | public List tokenize(String line) { 14 | return new ArrayList<>(Arrays.asList(line.split(" ", -1))); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/part/CommandPart.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.part; 2 | 3 | import team.unnamed.commandflow.CommandContext; 4 | import team.unnamed.commandflow.exception.ArgumentParseException; 5 | import team.unnamed.commandflow.part.visitor.CommandPartVisitor; 6 | import team.unnamed.commandflow.stack.ArgumentStack; 7 | import net.kyori.adventure.text.Component; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import java.util.List; 11 | 12 | public interface CommandPart { 13 | 14 | String getName(); 15 | 16 | @Nullable 17 | default Component getLineRepresentation() { 18 | return null; 19 | } 20 | 21 | void parse(CommandContext context, ArgumentStack stack, @Nullable CommandPart caller) throws ArgumentParseException; 22 | 23 | @Nullable 24 | default List getSuggestions(CommandContext commandContext, ArgumentStack stack) { 25 | return null; 26 | } 27 | 28 | /** 29 | * If this part should be parsed on another thread 30 | * 31 | * @return A boolean indicating whether this part should be parsed on another thread. 32 | */ 33 | default boolean isAsync() { 34 | return false; 35 | } 36 | 37 | default T acceptVisitor(CommandPartVisitor visitor) { 38 | return visitor.visit(this); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/part/PartsWrapper.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.part; 2 | 3 | import team.unnamed.commandflow.part.visitor.CommandPartVisitor; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * An interface to denote the {@link CommandPart} types that are wrappers for other parts, 9 | * this is used specifically for a {@link CommandPart} that wraps multiple parts. 10 | */ 11 | public interface PartsWrapper extends CommandPart { 12 | 13 | /** 14 | * The {@link CommandPart} instances that are wrapped by this wrapper. 15 | * 16 | * @return The {@link CommandPart} instances that are wrapped by this wrapper. 17 | */ 18 | List getParts(); 19 | 20 | @Override 21 | default T acceptVisitor(CommandPartVisitor visitor) { 22 | return visitor.visit(this); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/part/SinglePartWrapper.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.part; 2 | 3 | import team.unnamed.commandflow.part.visitor.CommandPartVisitor; 4 | 5 | /** 6 | * An interface to denote the {@link CommandPart} types that are wrappers for other parts, 7 | * this is used specifically for a {@link CommandPart} that wraps only one part. 8 | */ 9 | public interface SinglePartWrapper extends CommandPart { 10 | 11 | /** 12 | * The {@link CommandPart} that is wrapped by this wrapper. 13 | * 14 | * @return The {@link CommandPart} that is wrapped by this wrapper. 15 | */ 16 | CommandPart getPart(); 17 | 18 | @Override 19 | default T acceptVisitor(CommandPartVisitor visitor) { 20 | return visitor.visit(this); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/part/defaults/ArgumentStackPart.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.part.defaults; 2 | 3 | import team.unnamed.commandflow.CommandContext; 4 | import team.unnamed.commandflow.exception.ArgumentParseException; 5 | import team.unnamed.commandflow.part.ArgumentPart; 6 | import team.unnamed.commandflow.part.CommandPart; 7 | import team.unnamed.commandflow.stack.ArgumentStack; 8 | import team.unnamed.commandflow.stack.SimpleArgumentStack; 9 | import team.unnamed.commandflow.stack.StackSnapshot; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Collections; 13 | import java.util.List; 14 | 15 | /** 16 | * A {@linkplain CommandPart} that provides direct access to the remaining {@linkplain ArgumentStack}. 17 | *

18 | * Actually this shouldn't be an {@linkplain ArgumentPart} since it isn't using the arguments directly, but this is the best way to represent it. 19 | */ 20 | public class ArgumentStackPart implements ArgumentPart { 21 | 22 | private final String name; 23 | 24 | public ArgumentStackPart(String name) { 25 | this.name = name; 26 | } 27 | 28 | @Override 29 | public List parseValue(CommandContext context, ArgumentStack stack, CommandPart parent) throws ArgumentParseException { 30 | StackSnapshot snapshot = stack.getSnapshot(); 31 | stack.markAsConsumed(); 32 | 33 | ArgumentStack newStack = new SimpleArgumentStack(new ArrayList<>()); 34 | newStack.applySnapshot(snapshot, true); 35 | 36 | return Collections.singletonList(newStack); 37 | } 38 | 39 | @Override 40 | public String getName() { 41 | return name; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/part/defaults/BooleanPart.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.part.defaults; 2 | 3 | import team.unnamed.commandflow.CommandContext; 4 | import team.unnamed.commandflow.exception.ArgumentParseException; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | import team.unnamed.commandflow.stack.ArgumentStack; 7 | 8 | import java.util.Arrays; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | /** 13 | * A {@linkplain CommandPart} that parses one argument as a boolean, with the values true or false being allowed. 14 | */ 15 | public class BooleanPart extends PrimitivePart { 16 | 17 | public BooleanPart(String name) { 18 | super(name); 19 | } 20 | 21 | @Override 22 | public List parseValue(CommandContext context, ArgumentStack stack, CommandPart parent) throws ArgumentParseException { 23 | return Collections.singletonList(stack.nextBoolean()); 24 | } 25 | 26 | @Override 27 | public List getSuggestions(CommandContext commandContext, ArgumentStack stack) { 28 | String prefix = stack.hasNext() ? stack.next() : null; 29 | 30 | if (prefix == null) { 31 | return Collections.emptyList(); 32 | } 33 | 34 | if (prefix.isEmpty()) { 35 | return Arrays.asList("true", "false"); 36 | } 37 | 38 | if (prefix.equalsIgnoreCase("true") || prefix.equalsIgnoreCase("false")) { 39 | return Collections.emptyList(); 40 | } 41 | 42 | if (prefix.startsWith("t")) { 43 | return Collections.singletonList("true"); 44 | } 45 | 46 | if (prefix.startsWith("f")) { 47 | return Collections.singletonList("false"); 48 | } 49 | 50 | return Collections.emptyList(); 51 | } 52 | 53 | } -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/part/defaults/ContextPart.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.part.defaults; 2 | 3 | import team.unnamed.commandflow.CommandContext; 4 | import team.unnamed.commandflow.exception.ArgumentParseException; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | import team.unnamed.commandflow.stack.ArgumentStack; 7 | 8 | /** 9 | * A {@linkplain CommandPart} that provides direct access to the {@linkplain CommandContext}. 10 | */ 11 | public class ContextPart implements CommandPart { 12 | 13 | private final String name; 14 | 15 | public ContextPart(String name) { 16 | this.name = name; 17 | } 18 | 19 | @Override 20 | public String getName() { 21 | return name; 22 | } 23 | 24 | @Override 25 | public void parse(CommandContext context, ArgumentStack stack, CommandPart parent) throws ArgumentParseException { 26 | context.setValue(this, context); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/part/defaults/EmptyPart.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.part.defaults; 2 | 3 | import team.unnamed.commandflow.CommandContext; 4 | import team.unnamed.commandflow.exception.ArgumentParseException; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | import team.unnamed.commandflow.stack.ArgumentStack; 7 | 8 | /** 9 | * A placeholder {@linkplain CommandPart}, it does absolutely nothing. 10 | */ 11 | public class EmptyPart implements CommandPart { 12 | 13 | private final String name; 14 | 15 | public EmptyPart(String name) { 16 | this.name = name; 17 | } 18 | 19 | @Override 20 | public String getName() { 21 | return name; 22 | } 23 | 24 | @Override 25 | public void parse(CommandContext context, ArgumentStack stack, CommandPart parent) throws ArgumentParseException { 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/part/defaults/FloatPart.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.part.defaults; 2 | 3 | import team.unnamed.commandflow.CommandContext; 4 | import team.unnamed.commandflow.exception.ArgumentParseException; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | import team.unnamed.commandflow.stack.ArgumentStack; 7 | import net.kyori.adventure.text.Component; 8 | 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | public class FloatPart extends PrimitivePart { 13 | 14 | private final float max; 15 | private final float min; 16 | 17 | private final boolean ranged; 18 | 19 | public FloatPart(String name) { 20 | this(name, 0, 0, false); 21 | } 22 | 23 | public FloatPart(String name, float min, float max) { 24 | this(name, min, max, true); 25 | } 26 | 27 | private FloatPart(String name, float min, float max, boolean ranged) { 28 | super(name); 29 | 30 | this.max = max; 31 | this.min = min; 32 | 33 | this.ranged = ranged; 34 | } 35 | 36 | @Override 37 | public List parseValue(CommandContext context, ArgumentStack stack, CommandPart parent) throws ArgumentParseException { 38 | float next = stack.nextFloat(); 39 | if (ranged && (next > max || next < min)) { 40 | Component message = Component.translatable("number.out-range").args(Component.text(next), Component.text(min), Component.text(max)); 41 | 42 | throw new ArgumentParseException(message); 43 | } 44 | 45 | return Collections.singletonList(next); 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/part/defaults/IntegerPart.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.part.defaults; 2 | 3 | import team.unnamed.commandflow.CommandContext; 4 | import team.unnamed.commandflow.exception.ArgumentParseException; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | import team.unnamed.commandflow.stack.ArgumentStack; 7 | import net.kyori.adventure.text.Component; 8 | 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | import static net.kyori.adventure.text.Component.text; 13 | 14 | public class IntegerPart extends PrimitivePart { 15 | 16 | private final int max; 17 | private final int min; 18 | 19 | private final boolean ranged; 20 | 21 | public IntegerPart(String name) { 22 | this(name, 0, 0, false); 23 | } 24 | 25 | public IntegerPart(String name, int min, int max) { 26 | this(name, min, max, true); 27 | } 28 | 29 | private IntegerPart(String name, int min, int max, boolean ranged) { 30 | super(name); 31 | 32 | this.max = max; 33 | this.min = min; 34 | 35 | this.ranged = ranged; 36 | } 37 | 38 | @Override 39 | public List parseValue(CommandContext context, ArgumentStack stack, CommandPart parent) throws ArgumentParseException { 40 | int next = stack.nextInt(); 41 | if (ranged && (next > max || next < min)) { 42 | Component message = Component.translatable("number.out-range", text(next), text(min), text(max)); 43 | 44 | throw new ArgumentParseException(message); 45 | } 46 | 47 | return Collections.singletonList(next); 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/part/defaults/LongPart.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.part.defaults; 2 | 3 | import team.unnamed.commandflow.CommandContext; 4 | import team.unnamed.commandflow.exception.ArgumentParseException; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | import team.unnamed.commandflow.stack.ArgumentStack; 7 | import net.kyori.adventure.text.Component; 8 | 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | public class LongPart extends PrimitivePart { 13 | 14 | private final long max; 15 | private final long min; 16 | private final boolean ranged; 17 | 18 | /** 19 | * Creates a PrimitivePart instance with the given name. 20 | * @param name The name for this part. 21 | */ 22 | private LongPart(String name, long min, long max, boolean ranged) { 23 | super(name); 24 | this.min = min; 25 | this.max = max; 26 | this.ranged = ranged; 27 | } 28 | 29 | public LongPart(String name, long min, long max) { 30 | this(name, min, max, true); 31 | } 32 | 33 | public LongPart(String name) { 34 | this(name, 0L, 0L, false); 35 | } 36 | 37 | @Override 38 | public List parseValue(CommandContext context, ArgumentStack stack, CommandPart caller) throws ArgumentParseException { 39 | long next = stack.nextLong(); 40 | 41 | if (ranged && (next > max || next < min)) { 42 | throw new ArgumentParseException( 43 | Component.translatable("number.out-range") 44 | .args( 45 | Component.text(next), 46 | Component.text(min), 47 | Component.text(max) 48 | ) 49 | ); 50 | } 51 | 52 | return Collections.singletonList(next); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/part/defaults/PrimitivePart.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.part.defaults; 2 | 3 | import team.unnamed.commandflow.CommandContext; 4 | import team.unnamed.commandflow.part.ArgumentPart; 5 | import team.unnamed.commandflow.stack.ArgumentStack; 6 | 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | /** 11 | * A part that has the option to use all the available primitive arguments 12 | * like {@linkplain Integer}, {@linkplain Double}, {@linkplain Float}, {@linkplain Boolean}, {@linkplain String}. 13 | */ 14 | public abstract class PrimitivePart implements ArgumentPart { 15 | 16 | private final String name; 17 | 18 | /** 19 | * Creates a PrimitivePart instance with the given name. 20 | * 21 | * @param name The name for this part. 22 | */ 23 | public PrimitivePart(String name) { 24 | this.name = name; 25 | } 26 | 27 | @Override 28 | public String getName() { 29 | return name; 30 | } 31 | 32 | @Override 33 | public List getSuggestions(CommandContext commandContext, ArgumentStack stack) { 34 | if (stack.hasNext()) { 35 | stack.next(); 36 | } 37 | 38 | return Collections.emptyList(); 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/part/visitor/CommandPartVisitor.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.part.visitor; 2 | 3 | import team.unnamed.commandflow.part.ArgumentPart; 4 | import team.unnamed.commandflow.part.CommandPart; 5 | import team.unnamed.commandflow.part.PartsWrapper; 6 | import team.unnamed.commandflow.part.SinglePartWrapper; 7 | import team.unnamed.commandflow.part.defaults.SubCommandPart; 8 | 9 | public interface CommandPartVisitor { 10 | 11 | R visit(CommandPart part); 12 | 13 | R visit(ArgumentPart argumentPart); 14 | 15 | R visit(PartsWrapper partsWrapper); 16 | 17 | R visit(SinglePartWrapper singlePartWrapper); 18 | 19 | R visit(SubCommandPart subCommandPart); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/part/visitor/UnwrappedCommandPartVisitor.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.part.visitor; 2 | 3 | import team.unnamed.commandflow.part.PartsWrapper; 4 | import team.unnamed.commandflow.part.defaults.FirstMatchPart; 5 | import team.unnamed.commandflow.part.defaults.SequentialCommandPart; 6 | 7 | public interface UnwrappedCommandPartVisitor extends CommandPartVisitor { 8 | 9 | @Override 10 | default R visit(PartsWrapper partsWrapper) { 11 | if (partsWrapper instanceof SequentialCommandPart) { 12 | return visitSequential((SequentialCommandPart) partsWrapper); 13 | } 14 | 15 | if (partsWrapper instanceof FirstMatchPart) { 16 | return visitFirstMatch((FirstMatchPart) partsWrapper); 17 | } 18 | 19 | return visitWrapper(partsWrapper); 20 | } 21 | 22 | R visitWrapper(PartsWrapper partsWrapper); 23 | 24 | R visitSequential(SequentialCommandPart sequentialCommandPart); 25 | 26 | R visitFirstMatch(FirstMatchPart firstMatchPart); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/stack/ArgumentStack.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.stack; 2 | 3 | import team.unnamed.commandflow.exception.ArgumentParseException; 4 | import team.unnamed.commandflow.exception.NoMoreArgumentsException; 5 | 6 | import java.util.List; 7 | 8 | public interface ArgumentStack { 9 | 10 | boolean hasNext(); 11 | 12 | String next() throws NoMoreArgumentsException; 13 | 14 | String peek() throws NoMoreArgumentsException; 15 | 16 | String current(); 17 | 18 | String remove(); 19 | 20 | int getPosition(); 21 | 22 | int getSize(); 23 | 24 | int getArgumentsLeft(); 25 | 26 | String nextQuoted(); 27 | 28 | int nextInt() throws ArgumentParseException; 29 | 30 | float nextFloat() throws ArgumentParseException; 31 | 32 | double nextDouble() throws ArgumentParseException; 33 | 34 | byte nextByte() throws ArgumentParseException; 35 | 36 | boolean nextBoolean() throws ArgumentParseException; 37 | 38 | long nextLong() throws ArgumentParseException; 39 | 40 | void markAsConsumed(); 41 | 42 | List getBacking(); 43 | 44 | ArgumentStack getSlice(int start, int end); 45 | 46 | default ArgumentStack getSliceFrom(int start) { 47 | return getSlice(start, getSize()); 48 | } 49 | 50 | default ArgumentStack getSliceTo(int end) { 51 | return getSlice(getPosition(), end); 52 | } 53 | 54 | default ArgumentStack getSlice(int size) { 55 | return getSliceTo(getPosition() + size); 56 | } 57 | 58 | default StackSnapshot getSnapshot() { 59 | return getSnapshot(true); 60 | } 61 | 62 | StackSnapshot getSnapshot(boolean useCurrentPos); 63 | 64 | default void applySnapshot(StackSnapshot snapshot) { 65 | applySnapshot(snapshot, true); 66 | } 67 | 68 | void applySnapshot(StackSnapshot snapshot, boolean changeArgs); 69 | 70 | } 71 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/stack/StackSnapshot.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.stack; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Objects; 6 | 7 | /** 8 | * An immutable blackbox containing a copy of a {@link ArgumentStack} at a specific time, 9 | * being able to restore the state of a {@link ArgumentStack} to this state. 10 | * 11 | * @see ArgumentStack#applySnapshot(StackSnapshot) 12 | */ 13 | public class StackSnapshot { 14 | 15 | final List backing; 16 | final int position; 17 | 18 | public StackSnapshot(ArgumentStack stack, int position) { 19 | this.backing = new ArrayList<>(stack.getBacking()); 20 | this.position = position; 21 | } 22 | 23 | public int getPosition() { 24 | return position; 25 | } 26 | 27 | @Override 28 | public boolean equals(Object o) { 29 | if (this == o) return true; 30 | if (o == null || getClass() != o.getClass()) return false; 31 | StackSnapshot snapshot = (StackSnapshot) o; 32 | return position == snapshot.position && 33 | Objects.equals(backing, snapshot.backing); 34 | } 35 | 36 | @Override 37 | public int hashCode() { 38 | return Objects.hash(backing, position); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/translator/ComponentRendererTranslator.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.translator; 2 | 3 | import team.unnamed.commandflow.Namespace; 4 | import net.kyori.adventure.text.Component; 5 | import net.kyori.adventure.text.renderer.ComponentRenderer; 6 | import net.kyori.adventure.text.renderer.TranslatableComponentRenderer; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import java.text.MessageFormat; 11 | import java.util.function.Function; 12 | 13 | public class ComponentRendererTranslator implements Translator { 14 | 15 | private TranslationProvider provider; 16 | private ComponentRenderer renderer; 17 | 18 | public ComponentRendererTranslator(TranslationProvider provider, ComponentRenderer renderer) { 19 | this.provider = provider; 20 | this.renderer = renderer; 21 | } 22 | 23 | public ComponentRendererTranslator(TranslationProvider translationProvider) { 24 | provider = translationProvider; 25 | renderer = new TranslatableComponentRenderer() { 26 | @Override 27 | protected @Nullable MessageFormat translate(@NotNull String key, @NotNull Namespace context) { 28 | String translationFound = provider.getTranslation(context, key); 29 | 30 | if (translationFound == null) { 31 | return null; 32 | } 33 | 34 | return new MessageFormat(translationFound); 35 | } 36 | }; 37 | } 38 | 39 | @Override 40 | public Component translate(Component component, Namespace namespace) { 41 | return renderer.render(component, namespace); 42 | } 43 | 44 | @Override 45 | public void setProvider(TranslationProvider provider) { 46 | this.provider = provider; 47 | } 48 | 49 | @Override 50 | public void setConverterFunction(Function stringToComponent) { 51 | // NOP, this translator doesn't allow this 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/translator/DefaultMapTranslationProvider.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.translator; 2 | 3 | import team.unnamed.commandflow.Namespace; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | public class DefaultMapTranslationProvider implements TranslationProvider { 9 | 10 | protected Map translations; 11 | 12 | public DefaultMapTranslationProvider() { 13 | translations = new HashMap<>(); 14 | translations.put("command.subcommand.invalid", "The subcommand %s doesn't exist!"); 15 | translations.put("command.no-permission", "No permission."); 16 | translations.put("argument.no-more","No more arguments were found, size: %s position: %s"); 17 | translations.put("number.out-range", "The number %s is not within the range min: %s max: %s"); 18 | translations.put("invalid.byte", "The number %s is not a valid byte!"); 19 | translations.put("invalid.integer", "The number %s is not a valid integer!"); 20 | translations.put("invalid.float", "The number %s is not a valid float!"); 21 | translations.put("invalid.double", "The number %s is not a valid double!"); 22 | translations.put("invalid.boolean", "The string %s is not a valid boolean!"); 23 | translations.put("invalid.enum-value", "The value %s is not valid, the valid values are: %s"); 24 | translations.put("invalid.long", "The number %s is not a valid long!"); 25 | } 26 | 27 | public String getTranslation(String key) { 28 | return translations.get(key); 29 | } 30 | 31 | @Override 32 | public String getTranslation(Namespace namespace, String key) { 33 | return getTranslation(key); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/translator/TranslationProvider.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.translator; 2 | 3 | import team.unnamed.commandflow.Namespace; 4 | 5 | public interface TranslationProvider { 6 | 7 | String getTranslation(Namespace namespace, String key); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/translator/Translator.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.translator; 2 | 3 | import team.unnamed.commandflow.Namespace; 4 | import net.kyori.adventure.text.Component; 5 | import net.kyori.adventure.text.TextComponent; 6 | 7 | import java.util.function.Function; 8 | 9 | /** 10 | * An interface that allows to convert a {@link net.kyori.adventure.text.TranslatableComponent} into a 11 | * {@link net.kyori.adventure.text.TextComponent}. 12 | */ 13 | public interface Translator { 14 | 15 | /** 16 | * Translates the given {@link Component} if it is a {@link net.kyori.adventure.text.TranslatableComponent} 17 | * otherwise returns the same instance. 18 | * 19 | * @param namespace The {@link Namespace} of the command containing injected objects. 20 | * @param component The {@link Component} to translate 21 | * @return The translated {@link net.kyori.adventure.text.TextComponent} or the same instance if it isn't a {@link net.kyori.adventure.text.TranslatableComponent} 22 | */ 23 | Component translate(Component component, Namespace namespace); 24 | 25 | /** 26 | * Changes the {@link TranslationProvider} used for this Translator instance. 27 | * 28 | * @param provider The new {@link TranslationProvider} instance. 29 | */ 30 | void setProvider(TranslationProvider provider); 31 | 32 | /** 33 | * Changes the Function used by this {@link Translator} to convert a String into a {@link TextComponent}. 34 | * 35 | * @param stringToComponent The new function used by this {@link Translator} instance. 36 | */ 37 | void setConverterFunction(Function stringToComponent); 38 | 39 | } 40 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/usage/DefaultUsageBuilder.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.usage; 2 | 3 | import team.unnamed.commandflow.CommandContext; 4 | import team.unnamed.commandflow.command.Command; 5 | import net.kyori.adventure.text.Component; 6 | 7 | public class DefaultUsageBuilder implements UsageBuilder { 8 | @Override 9 | public Component getUsage(CommandContext commandContext) { 10 | Command toExecute = commandContext.getCommand(); 11 | 12 | Component usage = toExecute.getUsage(); 13 | 14 | String label = String.join(" ", commandContext.getLabels()); 15 | 16 | Component labelComponent = Component.text(label); 17 | 18 | if (usage != null) { 19 | return labelComponent.append(Component.text(" ")).append(usage); 20 | } 21 | 22 | Component partComponents = toExecute.getPart().getLineRepresentation(); 23 | 24 | if (partComponents != null) { 25 | labelComponent = labelComponent.append(Component.text(" ")).append(partComponents); 26 | } 27 | 28 | return labelComponent; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /api/src/main/java/team/unnamed/commandflow/usage/UsageBuilder.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.usage; 2 | 3 | import team.unnamed.commandflow.CommandContext; 4 | import net.kyori.adventure.text.Component; 5 | import team.unnamed.commandflow.translator.Translator; 6 | 7 | /** 8 | * Creates a usage based on the CommandContext that you give to this class. 9 | * This class should be stateless, that means that it shouldn't have a defined state at any moment. 10 | */ 11 | public interface UsageBuilder { 12 | 13 | /** 14 | * Gets a usage based on the given {@link CommandContext}. 15 | * 16 | * @param commandContext The {@link CommandContext} used to create a usage. 17 | * @return A {@link Component} containing the usage for this command, shouldn't be sent directly to the 18 | * any executor without being translated first by a {@link Translator}. 19 | */ 20 | Component getUsage(CommandContext commandContext); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /api/src/test/java/team/unnamed/commandflow/InputTokenizerTest.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow; 2 | 3 | import team.unnamed.commandflow.input.InputTokenizer; 4 | import team.unnamed.commandflow.input.QuotedSpaceTokenizer; 5 | import org.junit.jupiter.api.Assertions; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import java.util.List; 9 | 10 | public class InputTokenizerTest { 11 | 12 | @Test 13 | public void testQuotedSpaceTokenizer() { 14 | 15 | InputTokenizer tokenizer = new QuotedSpaceTokenizer(); 16 | List tokens = tokenizer.tokenize( 17 | "command subcommand \"single ' quote\" 'double \" quote' other 'nonclosed non\"closed" 18 | ); 19 | 20 | Assertions.assertEquals(7, tokens.size()); 21 | Assertions.assertEquals("command", tokens.get(0)); 22 | Assertions.assertEquals("subcommand", tokens.get(1)); 23 | Assertions.assertEquals("single ' quote", tokens.get(2)); 24 | Assertions.assertEquals("double \" quote", tokens.get(3)); 25 | Assertions.assertEquals("other", tokens.get(4)); 26 | Assertions.assertEquals("'nonclosed", tokens.get(5)); 27 | Assertions.assertEquals("non\"closed", tokens.get(6)); 28 | 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /api/src/test/java/team/unnamed/commandflow/annotated/CustomComponentParserTest.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.annotated; 2 | 3 | import team.unnamed.commandflow.annotated.annotation.Command; 4 | import team.unnamed.commandflow.annotated.annotation.Usage; 5 | import team.unnamed.commandflow.annotated.part.PartInjector; 6 | import net.kyori.adventure.text.Component; 7 | import org.junit.jupiter.api.Test; 8 | 9 | import java.util.List; 10 | import java.util.Locale; 11 | 12 | import static org.junit.jupiter.api.Assertions.assertEquals; 13 | 14 | class CustomComponentParserTest { 15 | @Test 16 | void test() { 17 | PartInjector partInjector = PartInjector.create(); 18 | AnnotatedCommandTreeBuilder builder = AnnotatedCommandTreeBuilder.create(partInjector); 19 | 20 | // a component parser that always creates plain text components with "Hello " as the prefix 21 | builder.setComponentParser(string -> Component.text("Hello, " + string.toLowerCase(Locale.ROOT))); 22 | 23 | List commands = builder.fromClass(new TestCommand()); 24 | assertEquals(1, commands.size(), "Expected one command only"); 25 | team.unnamed.commandflow.command.Command command = commands.get(0); 26 | assertEquals("test", command.getName()); 27 | assertEquals("admin", command.getPermission()); 28 | assertEquals(Component.text("Hello, you don't have permission to do this!"), command.getPermissionMessage()); 29 | assertEquals(Component.text("Hello, this is the usage"), command.getUsage()); 30 | } 31 | 32 | @Command(names = "test", permissionMessage = "You don't have permission to do this!", permission = "admin") 33 | @Usage("This is the usage") 34 | public static class TestCommand implements CommandClass { 35 | @Command(names = "") 36 | void run() { 37 | System.out.println("Hello world!"); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /brigadier/common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | team.unnamed 8 | commandflow-brigadier 9 | 0.7.2 10 | ../pom.xml 11 | 12 | 13 | commandflow-brigadier-common 14 | jar 15 | 16 | -------------------------------------------------------------------------------- /brigadier/common/src/main/java/team/unnamed/commandflow/brigadier/PermissionRequirement.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.brigadier; 2 | 3 | import team.unnamed.commandflow.Authorizer; 4 | import team.unnamed.commandflow.Namespace; 5 | 6 | import java.util.function.Function; 7 | import java.util.function.Predicate; 8 | 9 | public class PermissionRequirement implements Predicate { 10 | 11 | private final String permission; 12 | private final Authorizer authorizer; 13 | private final Function senderMapping; 14 | 15 | public PermissionRequirement(String permission, Authorizer authorizer, Function senderMapping) { 16 | this.permission = permission; 17 | this.authorizer = authorizer; 18 | this.senderMapping = senderMapping; 19 | } 20 | 21 | @Override 22 | public boolean test(T o) { 23 | V sender = senderMapping.apply(o); 24 | 25 | Namespace namespace = Namespace.create(); 26 | //noinspection unchecked 27 | namespace.setObject((Class) sender.getClass(), "sender", sender); 28 | 29 | return authorizer.isAuthorized(namespace, permission); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /brigadier/common/src/main/java/team/unnamed/commandflow/brigadier/mappings/BrigadierCommandNodeMappingsImpl.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.brigadier.mappings; 2 | 3 | import java.lang.reflect.Type; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.Optional; 7 | 8 | final class BrigadierCommandNodeMappingsImpl implements BrigadierCommandNodeMappings { 9 | 10 | private final Map> mappingMap; 11 | 12 | BrigadierCommandNodeMappingsImpl(Map> mappingMap) { 13 | this.mappingMap = mappingMap; 14 | } 15 | 16 | @Override 17 | public Optional> getMapping(Type type) { 18 | return Optional.ofNullable(mappingMap.get(type)); 19 | } 20 | 21 | @Override 22 | public BrigadierCommandNodeMappings.Builder toBuilder() { 23 | return new Builder<>(mappingMap); 24 | } 25 | 26 | final static class Builder implements BrigadierCommandNodeMappings.Builder { 27 | 28 | private final Map> mappingMap; 29 | 30 | Builder() { 31 | mappingMap = new HashMap<>(); 32 | } 33 | 34 | Builder(Map> mappingMap) { 35 | this.mappingMap = new HashMap<>(mappingMap); 36 | } 37 | 38 | @Override 39 | public BrigadierCommandNodeMappings.Builder addMapping(Type type, CommandNodeMapping mapping) { 40 | mappingMap.put(type, mapping); 41 | return this; 42 | } 43 | 44 | @Override 45 | public BrigadierCommandNodeMappings build() { 46 | return new BrigadierCommandNodeMappingsImpl<>(mappingMap); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /brigadier/common/src/main/java/team/unnamed/commandflow/brigadier/mappings/CommandNodeMapping.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.brigadier.mappings; 2 | 3 | import com.mojang.brigadier.tree.CommandNode; 4 | import team.unnamed.commandflow.part.ArgumentPart; 5 | 6 | /** 7 | * Represents the mapping between a command-flow's {@link ArgumentPart} type into a brigadier's {@link CommandNode}. 8 | * @param The type of the command executor. 9 | */ 10 | public interface CommandNodeMapping { 11 | /** 12 | * Converts a specified {@link team.unnamed.commandflow.part.CommandPart} into a {@link CommandNode}. 13 | * @param argumentPart The {@linkplain ArgumentPart} to convert. 14 | * @return A non null {@linkplain CommandNode} 15 | */ 16 | CommandNode convert(ArgumentPart argumentPart); 17 | } 18 | -------------------------------------------------------------------------------- /brigadier/common/src/main/java/team/unnamed/commandflow/brigadier/mappings/defaults/BooleanMapping.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.brigadier.mappings.defaults; 2 | 3 | import com.mojang.brigadier.arguments.BoolArgumentType; 4 | import com.mojang.brigadier.builder.RequiredArgumentBuilder; 5 | import com.mojang.brigadier.tree.CommandNode; 6 | import team.unnamed.commandflow.brigadier.mappings.CommandNodeMapping; 7 | import team.unnamed.commandflow.part.ArgumentPart; 8 | 9 | public class BooleanMapping implements CommandNodeMapping { 10 | @Override 11 | public CommandNode convert(ArgumentPart part) { 12 | return RequiredArgumentBuilder.argument(part.getName(), BoolArgumentType.bool()).build(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /brigadier/common/src/main/java/team/unnamed/commandflow/brigadier/mappings/defaults/DoubleMapping.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.brigadier.mappings.defaults; 2 | 3 | import team.unnamed.commandflow.brigadier.mappings.CommandNodeMapping; 4 | import com.mojang.brigadier.arguments.DoubleArgumentType; 5 | import com.mojang.brigadier.builder.RequiredArgumentBuilder; 6 | import com.mojang.brigadier.tree.CommandNode; 7 | import team.unnamed.commandflow.part.ArgumentPart; 8 | 9 | public class DoubleMapping implements CommandNodeMapping { 10 | @Override 11 | public CommandNode convert(ArgumentPart part) { 12 | return RequiredArgumentBuilder.argument(part.getName(), DoubleArgumentType.doubleArg()).build(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /brigadier/common/src/main/java/team/unnamed/commandflow/brigadier/mappings/defaults/IntegerMapping.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.brigadier.mappings.defaults; 2 | 3 | import team.unnamed.commandflow.brigadier.mappings.CommandNodeMapping; 4 | import com.mojang.brigadier.arguments.IntegerArgumentType; 5 | import com.mojang.brigadier.builder.RequiredArgumentBuilder; 6 | import com.mojang.brigadier.tree.CommandNode; 7 | import team.unnamed.commandflow.part.ArgumentPart; 8 | 9 | public class IntegerMapping implements CommandNodeMapping { 10 | @Override 11 | public CommandNode convert(ArgumentPart part) { 12 | return RequiredArgumentBuilder.argument(part.getName(), IntegerArgumentType.integer()).build(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /brigadier/common/src/main/java/team/unnamed/commandflow/brigadier/mappings/defaults/LongMapping.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.brigadier.mappings.defaults; 2 | 3 | import team.unnamed.commandflow.brigadier.mappings.CommandNodeMapping; 4 | import com.mojang.brigadier.arguments.LongArgumentType; 5 | import com.mojang.brigadier.builder.RequiredArgumentBuilder; 6 | import com.mojang.brigadier.tree.CommandNode; 7 | import team.unnamed.commandflow.part.ArgumentPart; 8 | 9 | public class LongMapping implements CommandNodeMapping { 10 | @Override 11 | public CommandNode convert(ArgumentPart part) { 12 | return RequiredArgumentBuilder.argument(part.getName(), LongArgumentType.longArg()).build(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /brigadier/common/src/main/java/team/unnamed/commandflow/brigadier/mappings/defaults/StringMapping.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.brigadier.mappings.defaults; 2 | 3 | import team.unnamed.commandflow.brigadier.mappings.CommandNodeMapping; 4 | import com.mojang.brigadier.arguments.StringArgumentType; 5 | import com.mojang.brigadier.builder.RequiredArgumentBuilder; 6 | import com.mojang.brigadier.tree.CommandNode; 7 | import team.unnamed.commandflow.part.ArgumentPart; 8 | import team.unnamed.commandflow.part.defaults.StringPart; 9 | 10 | public class StringMapping implements CommandNodeMapping { 11 | @Override 12 | public CommandNode convert(ArgumentPart part) { 13 | StringPart stringPart = (StringPart) part; 14 | 15 | if (stringPart.isConsumeAll()) { 16 | return RequiredArgumentBuilder.argument(part.getName(), StringArgumentType.greedyString()).build(); 17 | } else { 18 | return RequiredArgumentBuilder.argument(part.getName(), StringArgumentType.word()).build(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /brigadier/paper/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store -------------------------------------------------------------------------------- /brigadier/paper/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | team.unnamed 8 | commandflow-brigadier 9 | 0.7.2 10 | ../pom.xml 11 | 12 | 13 | commandflow-brigadier-paper 14 | jar 15 | 16 | 17 | 18 | team.unnamed 19 | commandflow-bukkit-common 20 | ${project.version} 21 | provided 22 | 23 | 24 | team.unnamed 25 | commandflow-brigadier-common 26 | ${project.version} 27 | provided 28 | 29 | 30 | io.papermc.paper 31 | paper-api 32 | 1.19.1-R0.1-SNAPSHOT 33 | provided 34 | 35 | 36 | io.papermc.paper 37 | paper-mojangapi 38 | 1.19.1-R0.1-SNAPSHOT 39 | provided 40 | 41 | 42 | -------------------------------------------------------------------------------- /brigadier/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | team.unnamed 8 | commandflow 9 | 0.7.2 10 | ../pom.xml 11 | 12 | 13 | commandflow-brigadier 14 | pom 15 | 16 | 17 | common 18 | paper 19 | 20 | 21 | 22 | 23 | minecraft-repo 24 | https://libraries.minecraft.net/ 25 | 26 | 27 | spigotmc-repo 28 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 29 | 30 | 31 | papermc-repo 32 | https://repo.papermc.io/repository/maven-public/ 33 | 34 | 35 | 36 | 37 | team.unnamed 38 | commandflow-api 39 | ${project.version} 40 | compile 41 | 42 | 43 | com.mojang 44 | brigadier 45 | 1.0.17 46 | compile 47 | 48 | 49 | com.google.guava 50 | guava 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /bukkit/commandmap/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | team.unnamed 8 | commandflow-bukkit 9 | 0.7.2 10 | ../pom.xml 11 | 12 | 13 | commandflow-bukkit-commandmap 14 | jar 15 | 16 | 17 | 18 | team.unnamed 19 | commandflow-bukkit-common 20 | ${project.version} 21 | 22 | 23 | -------------------------------------------------------------------------------- /bukkit/commandmap/src/main/java/team/unnamed/commandflow/bukkit/PluginBukkitCommandWrapper.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bukkit; 2 | 3 | import org.bukkit.command.PluginIdentifiableCommand; 4 | import org.bukkit.plugin.Plugin; 5 | import team.unnamed.commandflow.command.Command; 6 | import team.unnamed.commandflow.translator.Translator; 7 | 8 | public class PluginBukkitCommandWrapper extends BukkitCommandWrapper implements PluginIdentifiableCommand { 9 | 10 | private final Plugin plugin; 11 | 12 | public PluginBukkitCommandWrapper(Command command, 13 | BukkitCommandManager dispatcher, 14 | Translator translator, 15 | Plugin plugin) { 16 | super(command, dispatcher, translator); 17 | 18 | this.plugin = plugin; 19 | } 20 | 21 | @Override 22 | public Plugin getPlugin() { 23 | return plugin; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /bukkit/common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | team.unnamed 8 | commandflow-bukkit 9 | 0.7.2 10 | ../pom.xml 11 | 12 | 13 | commandflow-bukkit-common 14 | jar 15 | 16 | -------------------------------------------------------------------------------- /bukkit/common/src/main/java/team/unnamed/commandflow/bukkit/BukkitAuthorizer.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bukkit; 2 | 3 | import team.unnamed.commandflow.Authorizer; 4 | import team.unnamed.commandflow.Namespace; 5 | import org.bukkit.command.CommandSender; 6 | 7 | public class BukkitAuthorizer implements Authorizer { 8 | 9 | @Override 10 | public boolean isAuthorized(Namespace namespace, String permission) { 11 | if (permission.isEmpty()) { 12 | return true; 13 | } 14 | 15 | CommandSender sender = namespace.getObject(CommandSender.class, BukkitCommonConstants.SENDER_NAMESPACE); 16 | 17 | return sender.hasPermission(permission); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /bukkit/common/src/main/java/team/unnamed/commandflow/bukkit/BukkitCommonConstants.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bukkit; 2 | 3 | public final class BukkitCommonConstants { 4 | public static final String SENDER_NAMESPACE = "sender"; 5 | public static final String COMMAND_MANAGER_NAMESPACE = "commandManager"; 6 | } 7 | -------------------------------------------------------------------------------- /bukkit/common/src/main/java/team/unnamed/commandflow/bukkit/BukkitDefaultTranslationProvider.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bukkit; 2 | 3 | import team.unnamed.commandflow.translator.DefaultMapTranslationProvider; 4 | 5 | public class BukkitDefaultTranslationProvider extends DefaultMapTranslationProvider { 6 | 7 | public BukkitDefaultTranslationProvider() { 8 | translations.put("player.offline", "The player %s is offline!"); 9 | translations.put("sender.unknown", "The sender for the command is unknown!"); 10 | translations.put("sender.only-player", "Only players can execute this command!"); 11 | translations.put("invalid.gamemode", "The gamemode %s is not valid!"); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /bukkit/common/src/main/java/team/unnamed/commandflow/bukkit/annotation/Exact.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bukkit.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.PARAMETER) 10 | public @interface Exact { 11 | } 12 | -------------------------------------------------------------------------------- /bukkit/common/src/main/java/team/unnamed/commandflow/bukkit/annotation/PlayerOrSource.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bukkit.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.PARAMETER) 10 | public @interface PlayerOrSource { 11 | } 12 | -------------------------------------------------------------------------------- /bukkit/common/src/main/java/team/unnamed/commandflow/bukkit/factory/BukkitModule.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bukkit.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.AbstractModule; 4 | import team.unnamed.commandflow.annotated.part.Key; 5 | import team.unnamed.commandflow.annotated.annotation.Sender; 6 | 7 | import org.bukkit.GameMode; 8 | import org.bukkit.OfflinePlayer; 9 | import org.bukkit.World; 10 | import org.bukkit.command.CommandSender; 11 | import org.bukkit.entity.Player; 12 | 13 | public class BukkitModule extends AbstractModule { 14 | 15 | @Override 16 | public void configure() { 17 | bindFactory(CommandSender.class, new CommandSenderFactory()); 18 | bindFactory(OfflinePlayer.class, new OfflinePlayerPartFactory()); 19 | bindFactory(Player.class, new PlayerPartFactory()); 20 | bindFactory(World.class, new WorldFactory()); 21 | bindFactory(GameMode.class, new GameModeFactory()); 22 | bindFactory(new Key(Player.class, Sender.class), new PlayerSenderFactory()); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /bukkit/common/src/main/java/team/unnamed/commandflow/bukkit/factory/CommandSenderFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bukkit.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.PartFactory; 4 | import team.unnamed.commandflow.bukkit.part.CommandSenderPart; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.util.List; 9 | 10 | public class CommandSenderFactory implements PartFactory { 11 | 12 | @Override 13 | public CommandPart createPart(String name, List modifiers) { 14 | return new CommandSenderPart(name); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /bukkit/common/src/main/java/team/unnamed/commandflow/bukkit/factory/GameModeFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bukkit.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.PartFactory; 4 | import team.unnamed.commandflow.bukkit.part.GameModePart; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.util.List; 9 | 10 | public class GameModeFactory implements PartFactory { 11 | 12 | @Override 13 | public CommandPart createPart(String name, List modifiers) { 14 | return new GameModePart(name); 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /bukkit/common/src/main/java/team/unnamed/commandflow/bukkit/factory/OfflinePlayerPartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bukkit.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.PartFactory; 4 | import team.unnamed.commandflow.bukkit.annotation.PlayerOrSource; 5 | import team.unnamed.commandflow.bukkit.part.OfflinePlayerPart; 6 | import team.unnamed.commandflow.part.CommandPart; 7 | 8 | import java.lang.annotation.Annotation; 9 | import java.util.List; 10 | 11 | public class OfflinePlayerPartFactory implements PartFactory { 12 | 13 | @Override 14 | public CommandPart createPart(String name, List modifiers) { 15 | boolean orSource = getAnnotation(modifiers, PlayerOrSource.class) != null; 16 | 17 | return new OfflinePlayerPart(name, orSource); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /bukkit/common/src/main/java/team/unnamed/commandflow/bukkit/factory/PlayerPartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bukkit.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.PartFactory; 4 | import team.unnamed.commandflow.bukkit.annotation.Exact; 5 | import team.unnamed.commandflow.bukkit.annotation.PlayerOrSource; 6 | import team.unnamed.commandflow.bukkit.part.PlayerPart; 7 | import team.unnamed.commandflow.part.CommandPart; 8 | 9 | import java.lang.annotation.Annotation; 10 | import java.util.List; 11 | 12 | public class PlayerPartFactory implements PartFactory { 13 | 14 | @Override 15 | public CommandPart createPart(String name, List modifiers) { 16 | boolean orSource = getAnnotation(modifiers, PlayerOrSource.class) != null; 17 | boolean exact = getAnnotation(modifiers, Exact.class) != null; 18 | 19 | return new PlayerPart(name, exact, orSource); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /bukkit/common/src/main/java/team/unnamed/commandflow/bukkit/factory/PlayerSenderFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bukkit.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.PartFactory; 4 | import team.unnamed.commandflow.bukkit.part.PlayerSenderPart; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.util.List; 9 | 10 | public class PlayerSenderFactory implements PartFactory { 11 | 12 | @Override 13 | public CommandPart createPart(String name, List modifiers) { 14 | return new PlayerSenderPart(name); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /bukkit/common/src/main/java/team/unnamed/commandflow/bukkit/factory/WorldFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bukkit.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.PartFactory; 4 | import team.unnamed.commandflow.bukkit.part.WorldPart; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.util.List; 9 | 10 | public class WorldFactory implements PartFactory { 11 | 12 | @Override 13 | public CommandPart createPart(String name, List modifiers) { 14 | return new WorldPart(name); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /bukkit/common/src/main/java/team/unnamed/commandflow/bukkit/part/CommandSenderPart.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bukkit.part; 2 | 3 | import team.unnamed.commandflow.CommandContext; 4 | import team.unnamed.commandflow.bukkit.BukkitCommonConstants; 5 | import team.unnamed.commandflow.exception.ArgumentParseException; 6 | import team.unnamed.commandflow.exception.CommandException; 7 | import team.unnamed.commandflow.part.CommandPart; 8 | import team.unnamed.commandflow.stack.ArgumentStack; 9 | import net.kyori.adventure.text.Component; 10 | import org.bukkit.command.CommandSender; 11 | 12 | import java.util.Objects; 13 | 14 | public class CommandSenderPart implements CommandPart { 15 | 16 | private final String name; 17 | 18 | public CommandSenderPart(String name) { 19 | this.name = name; 20 | } 21 | 22 | @Override 23 | public String getName() { 24 | return name; 25 | } 26 | 27 | @Override 28 | public void parse(CommandContext context, ArgumentStack stack, CommandPart parent) throws ArgumentParseException { 29 | CommandSender sender = context.getObject(CommandSender.class, BukkitCommonConstants.SENDER_NAMESPACE); 30 | 31 | if (sender != null) { 32 | context.setValue(this, sender); 33 | 34 | return; 35 | } 36 | 37 | throw new CommandException(Component.translatable("sender.unknown")); 38 | } 39 | 40 | @Override 41 | public boolean equals(Object o) { 42 | if (this == o) return true; 43 | if (!(o instanceof CommandSenderPart)) return false; 44 | CommandSenderPart that = (CommandSenderPart) o; 45 | return Objects.equals(name, that.name); 46 | } 47 | 48 | @Override 49 | public int hashCode() { 50 | return Objects.hash(name); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /bukkit/common/src/main/java/team/unnamed/commandflow/bukkit/part/PlayerSenderPart.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bukkit.part; 2 | 3 | import team.unnamed.commandflow.CommandContext; 4 | import team.unnamed.commandflow.bukkit.BukkitCommonConstants; 5 | import team.unnamed.commandflow.exception.ArgumentParseException; 6 | import team.unnamed.commandflow.exception.CommandException; 7 | import team.unnamed.commandflow.part.CommandPart; 8 | import team.unnamed.commandflow.stack.ArgumentStack; 9 | import net.kyori.adventure.text.Component; 10 | import org.bukkit.command.CommandSender; 11 | import org.bukkit.entity.Player; 12 | 13 | import java.util.Objects; 14 | 15 | public class PlayerSenderPart implements CommandPart { 16 | 17 | private final String name; 18 | 19 | public PlayerSenderPart(String name) { 20 | this.name = name; 21 | } 22 | 23 | @Override 24 | public String getName() { 25 | return name; 26 | } 27 | 28 | @Override 29 | public void parse(CommandContext context, ArgumentStack stack, CommandPart parent) throws ArgumentParseException { 30 | CommandSender sender = context.getObject(CommandSender.class, BukkitCommonConstants.SENDER_NAMESPACE); 31 | 32 | if (sender != null) { 33 | if (sender instanceof Player) { 34 | context.setValue(this, sender); 35 | 36 | return; 37 | } 38 | 39 | throw new ArgumentParseException(Component.translatable("sender.only-player")); 40 | } 41 | 42 | throw new CommandException(Component.translatable("sender.unknown")); 43 | } 44 | 45 | @Override 46 | public boolean equals(Object o) { 47 | if (this == o) return true; 48 | if (!(o instanceof PlayerSenderPart)) return false; 49 | PlayerSenderPart that = (PlayerSenderPart) o; 50 | return Objects.equals(name, that.name); 51 | } 52 | 53 | @Override 54 | public int hashCode() { 55 | return Objects.hash(name); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /bukkit/common/src/main/java/team/unnamed/commandflow/bukkit/part/WorldPart.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bukkit.part; 2 | 3 | import team.unnamed.commandflow.CommandContext; 4 | import team.unnamed.commandflow.exception.ArgumentParseException; 5 | import team.unnamed.commandflow.part.ArgumentPart; 6 | import team.unnamed.commandflow.part.CommandPart; 7 | import team.unnamed.commandflow.stack.ArgumentStack; 8 | import org.bukkit.Bukkit; 9 | import org.bukkit.World; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Collections; 13 | import java.util.List; 14 | 15 | public class WorldPart implements ArgumentPart { 16 | 17 | private final String name; 18 | 19 | public WorldPart(String name) { 20 | this.name = name; 21 | } 22 | 23 | @Override 24 | public List parseValue(CommandContext context, ArgumentStack stack, CommandPart parent) throws ArgumentParseException { 25 | return Collections.singletonList(checkedWorld(stack)); 26 | } 27 | 28 | @Override 29 | public List getSuggestions(CommandContext commandContext, ArgumentStack stack) { 30 | String prefix = stack.hasNext() ? stack.next() : null; 31 | 32 | if (prefix == null) { 33 | return Collections.emptyList(); 34 | } 35 | 36 | List suggestions = new ArrayList<>(); 37 | 38 | for (World world : Bukkit.getWorlds()) { 39 | if (world.getName().startsWith(prefix)) { 40 | suggestions.add(world.getName()); 41 | } 42 | } 43 | 44 | if (suggestions.size() == 1 && Bukkit.getWorld(suggestions.get(0)) != null) { 45 | return Collections.emptyList(); 46 | } 47 | 48 | return suggestions; 49 | } 50 | 51 | private World checkedWorld(ArgumentStack stack) { 52 | World world = Bukkit.getWorld(stack.next()); 53 | if (world == null) { 54 | throw new ArgumentParseException("World not exist!"); 55 | } 56 | return world; 57 | } 58 | 59 | @Override 60 | public String getName() { 61 | return name; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /bukkit/common/src/main/java/team/unnamed/commandflow/bukkit/sender/DefaultMessageSender.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bukkit.sender; 2 | 3 | import net.kyori.adventure.text.Component; 4 | import net.md_5.bungee.api.chat.BaseComponent; 5 | import org.bukkit.command.CommandSender; 6 | 7 | public class DefaultMessageSender implements MessageSender { 8 | @Override 9 | public void sendMessage(CommandSender sender, Component component) { 10 | BaseComponent[] components = MessageUtils.kyoriToBungee(component); 11 | 12 | MessageUtils.sendMessage(sender, components); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /bukkit/common/src/main/java/team/unnamed/commandflow/bukkit/sender/MessageSender.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bukkit.sender; 2 | 3 | import net.kyori.adventure.text.Component; 4 | import org.bukkit.command.CommandSender; 5 | 6 | public interface MessageSender { 7 | void sendMessage(CommandSender sender, Component component); 8 | } 9 | -------------------------------------------------------------------------------- /bukkit/common/src/main/java/team/unnamed/commandflow/bukkit/sender/MessageUtils.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bukkit.sender; 2 | 3 | import net.kyori.adventure.text.Component; 4 | import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; 5 | import net.md_5.bungee.api.chat.BaseComponent; 6 | import net.md_5.bungee.chat.ComponentSerializer; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | 10 | public class MessageUtils { 11 | 12 | public static void sendMessage(CommandSender sender, BaseComponent[] components) { 13 | if (sender instanceof Player) { 14 | ((Player) sender).spigot().sendMessage(components); 15 | } else { 16 | StringBuilder builder = new StringBuilder(); 17 | 18 | for (BaseComponent component : components) { 19 | builder.append(component.toLegacyText()); 20 | } 21 | 22 | sender.sendMessage(builder.toString()); 23 | } 24 | } 25 | 26 | public static BaseComponent[] kyoriToBungee(Component component) { 27 | GsonComponentSerializer componentSerializer = GsonComponentSerializer.gson(); 28 | 29 | String serializedComponent = componentSerializer.serialize(component); 30 | 31 | return ComponentSerializer.parse(serializedComponent); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /bukkit/paper/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store -------------------------------------------------------------------------------- /bukkit/paper/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | team.unnamed 8 | commandflow-bukkit 9 | 0.7.2 10 | ../pom.xml 11 | 12 | 13 | commandflow-bukkit-paper 14 | jar 15 | 16 | 17 | 17 18 | 17 19 | 20 | 21 | 22 | 23 | papermc 24 | https://repo.papermc.io/repository/maven-public/ 25 | 26 | 27 | 28 | 29 | 30 | io.papermc.paper 31 | paper-api 32 | 1.18.2-R0.1-SNAPSHOT 33 | provided 34 | 35 | 36 | team.unnamed 37 | commandflow-bukkit-common 38 | ${project.version} 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /bukkit/paper/src/main/java/team/unnamed/commandflow/bukkit/paper/PaperMessageSender.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bukkit.paper; 2 | 3 | import net.kyori.adventure.text.Component; 4 | import org.bukkit.command.CommandSender; 5 | import team.unnamed.commandflow.bukkit.sender.MessageSender; 6 | 7 | public class PaperMessageSender implements MessageSender { 8 | @Override 9 | public void sendMessage(CommandSender sender, Component component) { 10 | sender.sendMessage(component); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /bukkit/plugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | team.unnamed 8 | commandflow-bukkit 9 | 0.7.2 10 | ../pom.xml 11 | 12 | 13 | commandflow-bukkit-plugin 14 | jar 15 | 16 | 17 | 18 | team.unnamed 19 | commandflow-bukkit-common 20 | ${project.version} 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /bukkit/plugin/src/main/java/team.unnamed.commandflow.bukkit/DefaultBukkitCommandExecutor.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bukkit; 2 | 3 | import team.unnamed.commandflow.CommandManager; 4 | 5 | public class DefaultBukkitCommandExecutor implements BukkitCommandExecutor { 6 | 7 | private final CommandManager commandManager; 8 | 9 | public DefaultBukkitCommandExecutor(CommandManager commandManager) { 10 | this.commandManager = commandManager; 11 | } 12 | 13 | @Override 14 | public CommandManager commandManager() { 15 | return commandManager; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /bukkit/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | team.unnamed 8 | commandflow 9 | 0.7.2 10 | ../pom.xml 11 | 12 | 13 | commandflow-bukkit 14 | pom 15 | 16 | 17 | commandmap 18 | common 19 | paper 20 | plugin 21 | 22 | 23 | 24 | 25 | spigotmc-repo 26 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 27 | 28 | 29 | 30 | 31 | 32 | org.spigotmc 33 | spigot-api 34 | 1.8.8-R0.1-SNAPSHOT 35 | provided 36 | 37 | 38 | team.unnamed 39 | commandflow-api 40 | ${project.version} 41 | compile 42 | 43 | 44 | -------------------------------------------------------------------------------- /bungee/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | team.unnamed 8 | commandflow 9 | 0.7.2 10 | 11 | 12 | commandflow-bungee 13 | jar 14 | 15 | 16 | 17 | bungeecord-repo 18 | https://oss.sonatype.org/content/repositories/snapshots 19 | 20 | 21 | 22 | 23 | 24 | team.unnamed 25 | commandflow-api 26 | ${project.version} 27 | compile 28 | 29 | 30 | net.md-5 31 | bungeecord-api 32 | 1.15-SNAPSHOT 33 | provided 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /bungee/src/main/java/team/unnamed/commandflow/bungee/BungeeAuthorizer.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bungee; 2 | 3 | import team.unnamed.commandflow.Authorizer; 4 | import team.unnamed.commandflow.Namespace; 5 | import net.md_5.bungee.api.CommandSender; 6 | 7 | public class BungeeAuthorizer implements Authorizer { 8 | 9 | @Override 10 | public boolean isAuthorized(Namespace namespace, String permission) { 11 | if (permission.isEmpty()) { 12 | return true; 13 | } 14 | 15 | CommandSender sender = namespace.getObject(CommandSender.class, BungeeCommandManager.SENDER_NAMESPACE); 16 | return sender.hasPermission(permission); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /bungee/src/main/java/team/unnamed/commandflow/bungee/BungeeDefaultTranslationProvider.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bungee; 2 | 3 | import team.unnamed.commandflow.translator.DefaultMapTranslationProvider; 4 | 5 | public class BungeeDefaultTranslationProvider extends DefaultMapTranslationProvider { 6 | 7 | public BungeeDefaultTranslationProvider() { 8 | translations.put("player.offline", "The player %s is offline!"); 9 | translations.put("sender.unknown", "The sender for the command is unknown!"); 10 | translations.put("sender.only-player", "Only players can execute this command!"); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /bungee/src/main/java/team/unnamed/commandflow/bungee/MessageUtils.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bungee; 2 | 3 | import net.kyori.adventure.text.Component; 4 | import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; 5 | import net.md_5.bungee.api.chat.BaseComponent; 6 | import net.md_5.bungee.chat.ComponentSerializer; 7 | 8 | public class MessageUtils { 9 | 10 | public static BaseComponent[] kyoriToBungee(Component component) { 11 | GsonComponentSerializer componentSerializer = GsonComponentSerializer.gson(); 12 | 13 | String serializedComponent = componentSerializer.serialize(component); 14 | 15 | return ComponentSerializer.parse(serializedComponent); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /bungee/src/main/java/team/unnamed/commandflow/bungee/annotation/ProxiedPlayerOrSource.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bungee.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.PARAMETER) 10 | public @interface ProxiedPlayerOrSource { 11 | } 12 | -------------------------------------------------------------------------------- /bungee/src/main/java/team/unnamed/commandflow/bungee/factory/BungeeModule.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bungee.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.AbstractModule; 4 | import team.unnamed.commandflow.annotated.part.Key; 5 | import team.unnamed.commandflow.annotated.annotation.Sender; 6 | import net.md_5.bungee.api.CommandSender; 7 | import net.md_5.bungee.api.connection.ProxiedPlayer; 8 | 9 | public class BungeeModule extends AbstractModule { 10 | 11 | @Override 12 | public void configure() { 13 | bindFactory(CommandSender.class, new CommandSenderPartFactory()); 14 | bindFactory(ProxiedPlayer.class, new ProxiedPlayerPartFactory()); 15 | bindFactory(new Key(ProxiedPlayer.class, Sender.class), new ProxiedPlayerSenderPartFactory()); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /bungee/src/main/java/team/unnamed/commandflow/bungee/factory/CommandSenderPartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bungee.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.PartFactory; 4 | import team.unnamed.commandflow.bungee.part.CommandSenderPart; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.util.List; 9 | 10 | public class CommandSenderPartFactory implements PartFactory { 11 | 12 | @Override 13 | public CommandPart createPart(String name, List modifiers) { 14 | return new CommandSenderPart(name); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /bungee/src/main/java/team/unnamed/commandflow/bungee/factory/ProxiedPlayerPartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bungee.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.PartFactory; 4 | import team.unnamed.commandflow.bungee.annotation.ProxiedPlayerOrSource; 5 | import team.unnamed.commandflow.bungee.part.ProxiedPlayerPart; 6 | import team.unnamed.commandflow.part.CommandPart; 7 | 8 | import java.lang.annotation.Annotation; 9 | import java.util.List; 10 | 11 | public class ProxiedPlayerPartFactory implements PartFactory { 12 | 13 | @Override 14 | public CommandPart createPart(String name, List modifiers) { 15 | boolean orSource = getAnnotation(modifiers, ProxiedPlayerOrSource.class) != null; 16 | 17 | return new ProxiedPlayerPart(name, orSource); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /bungee/src/main/java/team/unnamed/commandflow/bungee/factory/ProxiedPlayerSenderPartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bungee.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.PartFactory; 4 | import team.unnamed.commandflow.bungee.part.ProxiedPlayerSenderPart; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.util.List; 9 | 10 | public class ProxiedPlayerSenderPartFactory implements PartFactory { 11 | 12 | @Override 13 | public CommandPart createPart(String name, List modifiers) { 14 | return new ProxiedPlayerSenderPart(name); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /bungee/src/main/java/team/unnamed/commandflow/bungee/part/CommandSenderPart.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bungee.part; 2 | 3 | import team.unnamed.commandflow.CommandContext; 4 | import team.unnamed.commandflow.bungee.BungeeCommandManager; 5 | import team.unnamed.commandflow.exception.ArgumentParseException; 6 | import team.unnamed.commandflow.exception.CommandException; 7 | import team.unnamed.commandflow.part.CommandPart; 8 | import team.unnamed.commandflow.stack.ArgumentStack; 9 | import net.kyori.adventure.text.Component; 10 | import net.md_5.bungee.api.CommandSender; 11 | 12 | import java.util.Objects; 13 | 14 | public class CommandSenderPart implements CommandPart { 15 | 16 | private final String name; 17 | 18 | public CommandSenderPart(String name) { 19 | this.name = name; 20 | } 21 | 22 | @Override 23 | public String getName() { 24 | return this.name; 25 | } 26 | 27 | @Override 28 | public void parse(CommandContext context, ArgumentStack stack, CommandPart parent) throws ArgumentParseException { 29 | CommandSender sender = context.getObject(CommandSender.class, BungeeCommandManager.SENDER_NAMESPACE); 30 | 31 | if (sender != null) { 32 | context.setValue(this, sender); 33 | 34 | return; 35 | } 36 | 37 | throw new CommandException(Component.translatable("sender.unknown")); 38 | } 39 | 40 | @Override 41 | public boolean equals(Object o) { 42 | if (this == o) return true; 43 | if (!(o instanceof CommandSenderPart)) return false; 44 | CommandSenderPart that = (CommandSenderPart) o; 45 | return Objects.equals(name, that.name); 46 | } 47 | 48 | @Override 49 | public int hashCode() { 50 | return Objects.hash(name); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /bungee/src/main/java/team/unnamed/commandflow/bungee/part/ProxiedPlayerSenderPart.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.bungee.part; 2 | 3 | import team.unnamed.commandflow.CommandContext; 4 | import team.unnamed.commandflow.bungee.BungeeCommandManager; 5 | import team.unnamed.commandflow.exception.ArgumentParseException; 6 | import team.unnamed.commandflow.exception.CommandException; 7 | import team.unnamed.commandflow.part.CommandPart; 8 | import team.unnamed.commandflow.stack.ArgumentStack; 9 | import net.kyori.adventure.text.Component; 10 | import net.md_5.bungee.api.CommandSender; 11 | import net.md_5.bungee.api.connection.ProxiedPlayer; 12 | 13 | import java.util.Objects; 14 | 15 | public class ProxiedPlayerSenderPart implements CommandPart { 16 | 17 | private final String name; 18 | 19 | public ProxiedPlayerSenderPart(String name) { 20 | this.name = name; 21 | } 22 | 23 | @Override 24 | public String getName() { 25 | return name; 26 | } 27 | 28 | @Override 29 | public void parse(CommandContext context, ArgumentStack stack, CommandPart parent) throws ArgumentParseException { 30 | CommandSender sender = context.getObject(CommandSender.class, BungeeCommandManager.SENDER_NAMESPACE); 31 | 32 | if (sender != null) { 33 | if (sender instanceof ProxiedPlayer) { 34 | context.setValue(this, sender); 35 | 36 | return; 37 | } 38 | 39 | throw new ArgumentParseException(Component.translatable("sender.only-player")); 40 | } 41 | 42 | throw new CommandException(Component.translatable("sender.unknown")); 43 | } 44 | 45 | @Override 46 | public boolean equals(Object o) { 47 | if (this == o) return true; 48 | if (!(o instanceof ProxiedPlayerSenderPart)) return false; 49 | ProxiedPlayerSenderPart that = (ProxiedPlayerSenderPart) o; 50 | return Objects.equals(name, that.name); 51 | } 52 | 53 | @Override 54 | public int hashCode() { 55 | return Objects.hash(name); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /discord/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | team.unnamed 8 | commandflow 9 | 0.7.2 10 | ../pom.xml 11 | 12 | commandflow-discord 13 | jar 14 | 15 | 16 | 17 | jcenter 18 | jcenter-bintray 19 | https://jcenter.bintray.com 20 | 21 | 22 | 23 | 24 | 25 | net.dv8tion 26 | JDA 27 | 5.0.0-beta.17 28 | 29 | 30 | team.unnamed 31 | commandflow-api 32 | ${project.version} 33 | compile 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /discord/src/main/java/team/unnamed/commandflow/discord/DiscordAuthorizer.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.discord; 2 | 3 | import team.unnamed.commandflow.Authorizer; 4 | import team.unnamed.commandflow.Namespace; 5 | import net.dv8tion.jda.api.Permission; 6 | import net.dv8tion.jda.api.entities.Member; 7 | import net.dv8tion.jda.api.entities.Message; 8 | import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; 9 | 10 | public class DiscordAuthorizer implements Authorizer { 11 | 12 | @Override 13 | public boolean isAuthorized(Namespace namespace, String permission) { 14 | if (permission == null || permission.trim().isEmpty()) { 15 | return true; 16 | } 17 | 18 | Member member = namespace.getObject(Member.class, DiscordCommandManager.MEMBER_NAMESPACE); 19 | Message message = namespace.getObject(Message.class, DiscordCommandManager.MESSAGE_NAMESPACE); 20 | 21 | if (!message.isFromGuild()) { 22 | return true; 23 | } 24 | 25 | GuildChannel channel = message.getGuildChannel(); 26 | Permission permissionValue; 27 | 28 | try { 29 | permissionValue = Permission.valueOf(permission.toUpperCase()); 30 | } catch (IllegalArgumentException e) { 31 | message.getChannel().sendMessage("Invalid permission: `" + permission + "`").queue(); 32 | return false; 33 | } 34 | 35 | return member.hasPermission(channel, permissionValue); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /discord/src/main/java/team/unnamed/commandflow/discord/DiscordDefaultTranslationProvider.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.discord; 2 | 3 | import team.unnamed.commandflow.translator.DefaultMapTranslationProvider; 4 | 5 | public class DiscordDefaultTranslationProvider extends DefaultMapTranslationProvider { 6 | 7 | public DiscordDefaultTranslationProvider() { 8 | translations.put("unknown.member", "The member is unknown!"); 9 | translations.put("unknown.channel", "The channel is unknown!"); 10 | translations.put("unknown.user", "The user is unknown!"); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /discord/src/main/java/team/unnamed/commandflow/discord/factory/DiscordModule.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.discord.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.AbstractModule; 4 | import team.unnamed.commandflow.annotated.part.Key; 5 | import team.unnamed.commandflow.annotated.annotation.Sender; 6 | import net.dv8tion.jda.api.entities.Member; 7 | import net.dv8tion.jda.api.entities.Message; 8 | import net.dv8tion.jda.api.entities.User; 9 | import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; 10 | 11 | public class DiscordModule extends AbstractModule { 12 | 13 | @Override 14 | public void configure() { 15 | bindFactory(Member.class, new MemberPartFactory()); 16 | bindFactory(User.class, new UserPartFactory()); 17 | bindFactory(Message.class, new MessagePartFactory()); 18 | bindFactory(TextChannel.class, new TextChannelPartFactory()); 19 | bindFactory(new Key(Member.class, Sender.class), new MemberSenderPartFactory()); 20 | bindFactory(new Key(User.class, Sender.class), new UserSenderPartFactory()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /discord/src/main/java/team/unnamed/commandflow/discord/factory/MemberPartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.discord.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.PartFactory; 4 | import team.unnamed.commandflow.discord.part.MemberPart; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.util.List; 9 | 10 | public class MemberPartFactory implements PartFactory { 11 | 12 | @Override 13 | public CommandPart createPart(String name, List modifiers) { 14 | return new MemberPart(name); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /discord/src/main/java/team/unnamed/commandflow/discord/factory/MemberSenderPartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.discord.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.PartFactory; 4 | import team.unnamed.commandflow.discord.part.MemberSenderPart; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.util.List; 9 | 10 | public class MemberSenderPartFactory implements PartFactory { 11 | 12 | @Override 13 | public CommandPart createPart(String name, List modifiers) { 14 | return new MemberSenderPart(name); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /discord/src/main/java/team/unnamed/commandflow/discord/factory/MessagePartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.discord.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.PartFactory; 4 | import team.unnamed.commandflow.discord.part.MessagePart; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.util.List; 9 | 10 | public class MessagePartFactory implements PartFactory { 11 | 12 | @Override 13 | public CommandPart createPart(String name, List modifiers) { 14 | return new MessagePart(name); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /discord/src/main/java/team/unnamed/commandflow/discord/factory/TextChannelPartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.discord.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.PartFactory; 4 | import team.unnamed.commandflow.discord.part.TextChannelPart; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.util.List; 9 | 10 | public class TextChannelPartFactory implements PartFactory { 11 | 12 | @Override 13 | public CommandPart createPart(String name, List modifiers) { 14 | return new TextChannelPart(name); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /discord/src/main/java/team/unnamed/commandflow/discord/factory/UserPartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.discord.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.PartFactory; 4 | import team.unnamed.commandflow.discord.part.UserPart; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.util.List; 9 | 10 | public class UserPartFactory implements PartFactory { 11 | 12 | @Override 13 | public CommandPart createPart(String name, List modifiers) { 14 | return new UserPart(name); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /discord/src/main/java/team/unnamed/commandflow/discord/factory/UserSenderPartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.discord.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.PartFactory; 4 | import team.unnamed.commandflow.discord.part.UserSenderPart; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.util.List; 9 | 10 | public class UserSenderPartFactory implements PartFactory { 11 | 12 | @Override 13 | public CommandPart createPart(String name, List modifiers) { 14 | return new UserSenderPart(name); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /discord/src/main/java/team/unnamed/commandflow/discord/part/MemberSenderPart.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.discord.part; 2 | 3 | import team.unnamed.commandflow.CommandContext; 4 | import team.unnamed.commandflow.discord.DiscordCommandManager; 5 | import team.unnamed.commandflow.exception.ArgumentParseException; 6 | import team.unnamed.commandflow.exception.CommandException; 7 | import team.unnamed.commandflow.part.CommandPart; 8 | import team.unnamed.commandflow.stack.ArgumentStack; 9 | import net.dv8tion.jda.api.entities.Member; 10 | import net.kyori.adventure.text.Component; 11 | 12 | import java.util.Objects; 13 | 14 | public class MemberSenderPart implements CommandPart { 15 | 16 | private final String name; 17 | 18 | public MemberSenderPart(String name) { 19 | this.name = name; 20 | } 21 | 22 | @Override 23 | public String getName() { 24 | return this.name; 25 | } 26 | 27 | @Override 28 | public void parse(CommandContext context, ArgumentStack stack, CommandPart parent) throws ArgumentParseException { 29 | Member member = context.getObject(Member.class, DiscordCommandManager.MEMBER_NAMESPACE); 30 | 31 | if (member != null) { 32 | context.setValue(this, member); 33 | return; 34 | } 35 | 36 | throw new CommandException(Component.translatable("unknown.member")); 37 | } 38 | 39 | @Override 40 | public boolean equals(Object o) { 41 | if (this == o) return true; 42 | if (!(o instanceof MemberSenderPart)) return false; 43 | MemberSenderPart that = (MemberSenderPart) o; 44 | return Objects.equals(name, that.name); 45 | } 46 | 47 | @Override 48 | public int hashCode() { 49 | return Objects.hash(name); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /discord/src/main/java/team/unnamed/commandflow/discord/part/MessagePart.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.discord.part; 2 | 3 | import team.unnamed.commandflow.CommandContext; 4 | import team.unnamed.commandflow.discord.DiscordCommandManager; 5 | import team.unnamed.commandflow.exception.ArgumentParseException; 6 | import team.unnamed.commandflow.part.CommandPart; 7 | import team.unnamed.commandflow.stack.ArgumentStack; 8 | import net.dv8tion.jda.api.entities.Message; 9 | 10 | import java.util.Objects; 11 | 12 | public class MessagePart implements CommandPart { 13 | 14 | private final String name; 15 | 16 | public MessagePart(String name) { 17 | this.name = name; 18 | } 19 | 20 | @Override 21 | public String getName() { 22 | return null; 23 | } 24 | 25 | @Override 26 | public void parse(CommandContext context, ArgumentStack stack, CommandPart parent) throws ArgumentParseException { 27 | Message message = context.getObject(Message.class, DiscordCommandManager.MESSAGE_NAMESPACE); 28 | 29 | context.setValue(this, message); 30 | } 31 | 32 | @Override 33 | public boolean equals(Object o) { 34 | if (this == o) return true; 35 | if (!(o instanceof MemberPart)) return false; 36 | MessagePart that = (MessagePart) o; 37 | return Objects.equals(name, that.name); 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | return Objects.hash(name); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /discord/src/main/java/team/unnamed/commandflow/discord/part/UserSenderPart.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.discord.part; 2 | 3 | import team.unnamed.commandflow.CommandContext; 4 | import team.unnamed.commandflow.discord.DiscordCommandManager; 5 | import team.unnamed.commandflow.exception.ArgumentParseException; 6 | import team.unnamed.commandflow.exception.CommandException; 7 | import team.unnamed.commandflow.part.CommandPart; 8 | import team.unnamed.commandflow.stack.ArgumentStack; 9 | import net.dv8tion.jda.api.entities.User; 10 | import net.kyori.adventure.text.Component; 11 | 12 | public class UserSenderPart implements CommandPart { 13 | 14 | private final String name; 15 | 16 | public UserSenderPart(String name) { 17 | this.name = name; 18 | } 19 | 20 | @Override 21 | public String getName() { 22 | return this.name; 23 | } 24 | 25 | @Override 26 | public void parse(CommandContext context, ArgumentStack stack, CommandPart parent) throws ArgumentParseException { 27 | User user = context.getObject(User.class, DiscordCommandManager.USER_NAMESPACE); 28 | 29 | if (user != null) { 30 | context.setValue(this, user); 31 | 32 | return; 33 | } 34 | 35 | throw new CommandException(Component.translatable("unknown.user")); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /discord/src/main/java/team/unnamed/commandflow/discord/utils/ArgumentsUtils.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.discord.utils; 2 | 3 | public final class ArgumentsUtils { 4 | 5 | public static boolean isValidSnowflake(String argument) { 6 | if (argument.length() != 18) { 7 | return false; 8 | } 9 | try { 10 | Long.parseLong(argument); 11 | return true; 12 | } catch (NumberFormatException ignored) { 13 | return false; 14 | } 15 | } 16 | 17 | public static boolean isValidTag(String argument) { 18 | String[] args = argument.split("#"); 19 | if (args.length < 2) { 20 | return false; 21 | } 22 | return args[args.length - 1].length() == 4; 23 | } 24 | 25 | public static boolean isUserMention(String argument) { 26 | return isMention(argument, "@!"); 27 | } 28 | 29 | public static boolean isChannelMention(String argument) { 30 | return isMention(argument, "#"); 31 | } 32 | 33 | public static boolean isRoleMention(String argument) { 34 | return isMention(argument, "&"); 35 | } 36 | 37 | private static boolean isMention(String argument, String mentionTypeIdentifier) { 38 | return argument.startsWith("<" + mentionTypeIdentifier) && argument.endsWith(">") 39 | && argument.length() >= (20 + mentionTypeIdentifier.length()); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /discord/src/main/java/team/unnamed/commandflow/discord/utils/MessageUtils.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.discord.utils; 2 | 3 | import net.kyori.adventure.text.Component; 4 | import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; 5 | 6 | public class MessageUtils { 7 | 8 | public static String componentToString(Component component) { 9 | PlainTextComponentSerializer serializer = PlainTextComponentSerializer.plainText(); 10 | 11 | return serializer.serialize(component); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /docs/annotated/annotated.md: -------------------------------------------------------------------------------- 1 | ## Annotated Commands 2 | 3 | `command-flow` allows developers to create full command trees in a declarative way 4 | using annotated classes and methods. 5 | 6 | The annotated command API is just an alternative way to create `Command` instances, 7 | we use this instead of the classic `Command.builder(String)` method. 8 | 9 | ### Comparison 10 | 11 | You can make a side-by-side comparison of the two approaches in the following table 12 | and links: 13 | 14 | | Imperative | Annotated | 15 | |--------------------------------------------------------------------------|-----------------------------------------------------------------------| 16 | | [Basic Command Creation](../imperatively/basic.md) | [Basic Command Creation](../annotated/basic.md) | 17 | | [Command with single Argument](../imperatively/argument.md) | [Command with single Argument](../annotated/argument.md) | 18 | | [Command with multiple Arguments](../imperatively/multiple-arguments.md) | [Command with multiple Arguments](../annotated/multiple-arguments.md) | 19 | | [Command with optional Arguments](../imperatively/optional-arguments.md) | [Command with optional Arguments](../annotated/optional-arguments.md) | 20 | | [Command with Permission](../imperatively/with-permission.md) | [Command with Permission](../annotated/with-permission.md) | 21 | | [Command with Switch Arguments](../imperatively/with-switches.md) | [Command with Switch Arguments](../annotated/with-switches.md) | 22 | | [Command with Value Flags](../imperatively/with-value-flags.md) | [Command with Value Flags](../annotated/with-value-flags.md) | 23 | 24 | ### Elements of the Annotated Command API 25 | 26 | The annotated command API is composed of: 27 | - The `@Command` annotation and others. 28 | - The `AnnotatedCommandTreeBuilder` interface -------------------------------------------------------------------------------- /docs/annotated/argument.md: -------------------------------------------------------------------------------- 1 | ## #2 With Arguments 2 | 3 | Check this command example with a single argument: 4 | 5 | ```java 6 | @Command(names = "hello") 7 | public class TestCommand implements CommandClass { 8 | 9 | @Command(names = "") 10 | public void run(String name) { 11 | System.out.println("Hi " + name); 12 | } 13 | 14 | } 15 | ``` 16 | 17 | In this example: 18 | - Executing `hello yusshu` will print `Hi yusshu` 19 | - Executing `hello Fixed` will print `Hi Fixed` 20 | - Executing `hello` will result in an error -------------------------------------------------------------------------------- /docs/annotated/basic.md: -------------------------------------------------------------------------------- 1 | ## #1 Basic Command 2 | 3 | Check this basic command example, with no arguments, no subcommands and no permissions, 4 | using the annotated command creation approach: 5 | 6 | ```java 7 | @Command(names = "test") 8 | public class TestCommand implements CommandClass { 9 | 10 | @Command(names = "") 11 | public void run() { 12 | System.out.println("Hello World!"); 13 | } 14 | 15 | } 16 | ``` 17 | 18 | Executing `test` will print `Hello World!` -------------------------------------------------------------------------------- /docs/annotated/command-tree-builder.md: -------------------------------------------------------------------------------- 1 | ## Command Builder 2 | 3 | The `AnnotatedCommandTreeBuilder` is the interface responsible for building `Command` 4 | instances from annotated commands and classes. 5 | 6 | ### Creating an AnnotatedCommandTreeBuilder 7 | 8 | To create a `AnnotatedCommandTreeBuilder` we must create a `PartInjector` first and 9 | *(optionally)* a `SubCommandInstanceCreator`. More about this in the next pages. 10 | 11 | ```java 12 | PartInjector injector = ...; 13 | SubCommandInstanceCreator instanceCreator = ...; 14 | 15 | AnnotatedCommandTreeBuilder builder = AnnotatedCommandTreeBuilder.create(injector, instanceCreator); 16 | ``` 17 | 18 | ### Building a Command 19 | 20 | Now that we have a `AnnotatedCommandTreeBuilder` we can build a command (or multiple commands). 21 | To do this we must call the `fromClass` method with the class of the command we want to build. 22 | 23 | Note that this method returns a list of commands, since a single class may contain multiple 24 | root commands on it. 25 | 26 | ```java 27 | // create the builder 28 | AnnotatedCommandTreeBuilder builder = ...; 29 | 30 | // build the Command list from our class 31 | List commands = builder.fromClass(MyCommand.class); 32 | 33 | // now we can register them using the CommandManager#registerCommands convenience method 34 | commandManager.registerCommands(commands); 35 | ``` -------------------------------------------------------------------------------- /docs/annotated/index.txt: -------------------------------------------------------------------------------- 1 | annotated.md 2 | command-class.md 3 | command-tree-builder.md 4 | part-injector.md 5 | subcommand-instance-creator.md 6 | basic.md 7 | argument.md 8 | multiple-arguments.md 9 | optional-arguments.md 10 | with-permission.md 11 | with-switches.md 12 | with-value-flags.md -------------------------------------------------------------------------------- /docs/annotated/multiple-arguments.md: -------------------------------------------------------------------------------- 1 | ## #3 Command with Multiple Args 2 | 3 | This example shows how to create a command with multiple arguments 4 | using the annotated approach: 5 | 6 | ```java 7 | @Command(names = "greet") 8 | public class GreetingCommand implements CommandClass { 9 | 10 | @Command(names = "") 11 | public void run(String name, boolean formal) { 12 | if (formal) { 13 | System.out.println("Hello, " + name + "!"); 14 | } else { 15 | System.out.println("Hi, " + name + "!"); 16 | } 17 | } 18 | 19 | } 20 | ``` 21 | 22 | - Executing `greet John false` will print `Hi, John!` 23 | - Executing `greet John true` will print `Hello, John!` 24 | - Executing `greet John` will result in a usage error 25 | - Executing `greet` will result in a usage error -------------------------------------------------------------------------------- /docs/annotated/optional-arguments.md: -------------------------------------------------------------------------------- 1 | ## #4 With Optional Args 2 | 3 | This example shows how to create a command with optional arguments 4 | with the annotated approach: 5 | 6 | ```java 7 | @Command(names = "greet") 8 | public class GreetingCommand implements CommandClass { 9 | 10 | @Command(names = "") 11 | public void run(String name, @OptArg("Mr.") String title) { 12 | System.out.println("Hello, " + title + " " + name + "!"); 13 | } 14 | 15 | } 16 | ``` 17 | 18 | The `@OptArg` annotation is used to mark an argument as optional, and 19 | it accepts a default value as a parameter, which will be used if the 20 | argument is not present in the input. 21 | 22 | - Executing `greet John` will print `Hello, Mr. John!` 23 | - Executing `greet John Dr.` will print `Hello, Dr. John!` 24 | - Executing `greet John Mr.` will print `Hello, Mr. John!` -------------------------------------------------------------------------------- /docs/annotated/part-injector.md: -------------------------------------------------------------------------------- 1 | ## Part Injector 2 | 3 | The `PartInjector` is a registry which holds the registered PartFactories 4 | and PartModifiers. 5 | 6 | - A `PartFactory` is a class that provides a way to create an specific type of part. 7 | - A `PartModifier` is like a PartFactory, the difference is that it may wrap the original part 8 | or modify it instead of creating a new one. 9 | 10 | ### Creating a PartInjector 11 | 12 | Creating a `PartInjector` is simple, just do the following: 13 | 14 | ```java 15 | PartInjector partInjector = PartInjector.create(); 16 | 17 | // install the default bindings! 18 | // parts for native and core types like String, Boolean, Double, Float, Integer, 19 | // Text(String), ArgumentStack, CommandContext, also modifiers like LimitModifier, 20 | // OptionalModifier, ValueFlagModifier 21 | partInjector.install(new DefaultsModule()); 22 | ``` 23 | 24 | ### Platform Specific 25 | 26 | Some of the platform-specific subprojects include a `Module` for the `PartInjector` 27 | that can be easily installed to obtain the default bindings for that platform. 28 | 29 | For example, for Bukkit (`commandflow-bukkit` subproject): 30 | ```java 31 | // install bindings for the default bindings for Bukkit, such as 32 | // CommandSender, OfflinePlayer, Player, World, GameMode and @Sender Player 33 | partInjector.install(new BukkitModule()); 34 | ``` -------------------------------------------------------------------------------- /docs/annotated/with-permission.md: -------------------------------------------------------------------------------- 1 | ## #5 With Permission 2 | 3 | This example shows how to create a command with a permission requirement 4 | with the annotated approach: 5 | 6 | ```java 7 | @Command(names = "greet", permission = "myperm.command.greet") 8 | public class GreetingCommand implements CommandClass { 9 | 10 | @Command(names = "") 11 | public void run(String name) { 12 | System.out.println("Hello, " + name + "!"); 13 | } 14 | 15 | } 16 | ``` 17 | 18 | The `@Command` annotation accepts a `permission` parameter, which is 19 | used to specify the permission required to execute the command. 20 | 21 | Check the [Authorizer page](../configuration/authorizer.md) for more information 22 | on how to specify how the `CommandManager` should check for permissions. -------------------------------------------------------------------------------- /docs/annotated/with-switches.md: -------------------------------------------------------------------------------- 1 | ## #6 With Switches 2 | 3 | This example shows how to create a command with "switch" arguments 4 | (boolean flags) with the imperative approach, note that switch arguments 5 | values are always present. Presence of the switch argument indicates `true`, 6 | and its absence indicates `false`. 7 | 8 | 9 | ```java 10 | @Command(names = "test") 11 | public class TestCommand implements CommandClass { 12 | 13 | @Command(names = "") 14 | public void run(String name, @Switch("g") boolean goodBye) { 15 | if (goodBye) { 16 | System.out.println("Goodbye " + name); 17 | return; 18 | } 19 | System.out.println("Hi " + name); 20 | } 21 | 22 | } 23 | ``` 24 | 25 | 26 | - Executing `test Fixed` will print `Hi Fixed` 27 | - Executing `test -g Fixed` will print `Goodbye Fixed` 28 | - Executing `test Fixed -g` will print `Goodbye Fixed` -------------------------------------------------------------------------------- /docs/annotated/with-value-flags.md: -------------------------------------------------------------------------------- 1 | ## #7 With Value Flags 2 | 3 | This example shows how to create a command with value flag arguments 4 | the main difference between a value flag and a switch is that the value flag 5 | takes the next argument as its value, and the switch does not. 6 | 7 | For example: `-g true`, `-p 25565`, `-n Fixed` 8 | 9 | 10 | ```java 11 | @Command(names = "test") 12 | public class TestCommand implements CommandClass { 13 | 14 | @Command(names = "") 15 | public void run(String name, @Flag("g") String greeting) { 16 | System.out.println(greeting + " " + name); 17 | } 18 | 19 | } 20 | ``` 21 | 22 | 23 | - Executing `test Fixed` will print `Hi Fixed` 24 | - Executing `test Fixed -g GoodBye` will print `GoodBye Fixed` 25 | - Executing `test Fixed -g Hello` will print `Hello Fixed` 26 | - Executing `test -g Fixed` will throw a `NoMoreArguments` exception, meaning that the parsing failed 27 | because the `Fixed` argument was taken as the value for the flag and no argument 28 | is remaining for the name. 29 | - Executing `test Fixed -g` will throw a `NoMoreArguments` exception, meaning that the parsing failed 30 | because the flag doesn't has any argument left to use. -------------------------------------------------------------------------------- /docs/concepts/command-context.md: -------------------------------------------------------------------------------- 1 | ## Command Context 2 | 3 | A mutable object which contains the context for a command execution, including but not 4 | limited to the values for every parsed part, the raw arguments list and the raw arguments 5 | for every part, the labels and the Command execution path (which path of subcommands was taken). -------------------------------------------------------------------------------- /docs/concepts/command-manager.md: -------------------------------------------------------------------------------- 1 | ## Command Manager 2 | 3 | The CommandManager manages the command registration, parsing and execution. Also provides 4 | a way to obtain command suggestions *(which can be used for tab-completion in programs like 5 | CLI applications or Minecraft plugins)* for a given input. 6 | 7 | ### Creating a Command Manager 8 | 9 | Depending on your platform, you may want to use a different implementation of the 10 | `CommandManager`. Check your [platform](../platforms/platforms.md)'s documentation 11 | to get more information. 12 | 13 | However, there is a default implementation called `SimpleCommandManager` which can be 14 | used in any platform, but it doesn't provide any implementation-specific features. 15 | 16 | 17 | ```java 18 | CommandManager commandManager = new SimpleCommandManager(); 19 | ``` 20 | 21 | 22 | ### Registering commands 23 | 24 | To register a command, you need to create a `Command` object and register it using 25 | the `CommandManager.registerCommand(Command)` method. 26 | 27 | 28 | ```java 29 | // create CommandManager 30 | CommandManager manager = ...; 31 | 32 | // create Command using builder 33 | Command command = Command.builder("test") 34 | .description("A test command") 35 | .action(context -> { 36 | System.out.println("Hello world!"); 37 | }) 38 | .build(); 39 | 40 | // register the command 41 | manager.registerCommand(command); 42 | ``` 43 | -------------------------------------------------------------------------------- /docs/concepts/command-part.md: -------------------------------------------------------------------------------- 1 | ## Command Part 2 | 3 | This is the second most fundamental component, it can be understood as every argument 4 | of a [Command](./command.md), including things like subcommands, flags, non positional 5 | arguments, etc. 6 | 7 | It can use arguments from the argument list, or provide them using any other means. They 8 | can also forward the parsing responsibility to another part and just act as a modifier. 9 | 10 | Most of the default parts can be found at the `Parts` class. -------------------------------------------------------------------------------- /docs/concepts/command.md: -------------------------------------------------------------------------------- 1 | ## Command 2 | 3 | Commands are the most fundamental component of the framework. It contains all the 4 | information related to a command, included but not limited to name, aliases, permission, 5 | parts, etc. 6 | 7 | Commands are created using the `Command.builder(String)` method, which returns an 8 | `Command.Builder` instance where you can set all the information of the command. 9 | 10 | Example: 11 | 12 | ```java 13 | Command command = Command.builder("Test") 14 | .action(context -> { 15 | System.out.println("Hi!"); 16 | }) 17 | .build(); 18 | ``` 19 | -------------------------------------------------------------------------------- /docs/concepts/index.txt: -------------------------------------------------------------------------------- 1 | command.md 2 | command-manager.md 3 | command-part.md 4 | command-context.md 5 | namespaces.md -------------------------------------------------------------------------------- /docs/concepts/namespaces.md: -------------------------------------------------------------------------------- 1 | ## Namespaces 2 | 3 | *(If you use an already implemented platform and annotated commands, you don't need 4 | to use Namespaces, yay!)* 5 | 6 | A namespace is a mapping/set of the arguments that are injected into the execution. 7 | For example, we can inject the user executor of a command and use it later. 8 | 9 | Creating a namespace: 10 | 11 | ```java 12 | // Create a namespace 13 | Namespace namespace = Namespace.create(); 14 | ``` 15 | 16 | 17 | Now we can set any object in the namespace, by type and name, for example, suppose we 18 | have a `User` class: 19 | 20 | ```java 21 | namespace.setObject(User.class, "USER", new User("Fixed", 16)); 22 | ``` 23 | 24 | 25 | And now, we can retrieve the object from the namespace, using the exact same 26 | type and name, for example: 27 | 28 | ```java 29 | User user = namespace.getObject(User.class, "USER"); 30 | 31 | System.out.println(user.getName()); // Fixed 32 | ``` 33 | 34 | 35 | -------------------------------------------------------------------------------- /docs/configuration/configuration.md: -------------------------------------------------------------------------------- 1 | ## Configuring the Command Manager 2 | 3 | The `CommandManager` allows developers to fully configurate its behavior and 4 | how to determine certain aspects of the command parsing & dispatching process. 5 | 6 | The `CommandManager` can also be extended for platform-specific features and 7 | native integration, like, automatically registering commands in a platform 8 | command registry. -------------------------------------------------------------------------------- /docs/configuration/index.txt: -------------------------------------------------------------------------------- 1 | configuration.md 2 | authorizer.md 3 | tokenizer.md -------------------------------------------------------------------------------- /docs/configuration/tokenizer.md: -------------------------------------------------------------------------------- 1 | ## Input Tokenizer 2 | 3 | The input tokenizer is responsible for converting a simple input string into a 4 | list of tokens. The tokens are then used by the parser to enter the desired 5 | command path and either dispatch a command or get suggestions for the next 6 | token. 7 | 8 | The `InputTokenizer` is a functional interface that can be set to the 9 | `CommandManager` via the `CommandManager#setInputTokenizer` method. 10 | 11 | For example: 12 | 13 | ```java 14 | // create a tokenizer 15 | InputTokenizer tokenizer = ...; 16 | 17 | // set the tokenizer 18 | CommandManager manager = ...; 19 | manager.setInputTokenizer(tokenizer); 20 | ``` 21 | 22 | ### Default Implementations 23 | 24 | `command-flow` provides two default implementations of the `InputTokenizer` 25 | interface, `StringSpaceTokenizer` and `QuotedSpaceTokenizer`: 26 | 27 | - `StringSpaceTokenizer` - This tokenizer splits the input string by spaces 28 | and returns the resulting tokens. This is the default tokenizer used by 29 | `CommandManager` if no other tokenizer is set. 30 | - `QuotedSpaceTokenizer` - This tokenizer splits the input string by spaces 31 | but also supports quoted strings. For example, the input string 32 | `hello "world of commands"` would be split into the tokens `hello` and 33 | `world of commands`. 34 | 35 | 36 | -------------------------------------------------------------------------------- /docs/execution/context.md: -------------------------------------------------------------------------------- 1 | ## Command Context 2 | 3 | As we have seen in [Command Execution](./execution.md), we can access previously set 4 | variables in the execution namespace, from a command action, awesome! 5 | 6 | But now, we are going to explore more about the command context, and how we can 7 | access more information about the command execution. 8 | 9 | ### Information 10 | 11 | The `CommandContext` contains information about the command execution, such as: 12 | - A reference to the command *(it may be root or sub command)* being executed 13 | - A reference to the root command being executed 14 | - The execution path 15 | - The input tokens or arguments 16 | - The used labels (which name/alias was used for every command and sub-command) 17 | - A command part value 18 | - All the data from the execution namespace 19 | 20 | ### Example 21 | 22 | 23 | ```java 24 | // create the command manager 25 | CommandManager manager = ...; 26 | 27 | // create & register our command 28 | Command command = Command.builder("test") 29 | .addAlias("testalias") 30 | .action(context -> { 31 | // Here we have an action of the command, and here we can use the context for this command 32 | // The CommandContext is the result of parsing the arguments of a command. 33 | // It also extends Namespace, so you can use the Namespace methods on it. 34 | 35 | // The labels are the names for every command/subcommand executed. 36 | // This is the name of the last subcommand/command 37 | String label = context.getLabels().get(context.getLabels().size() - 1); 38 | 39 | System.out.println("Label: " + label); 40 | }) 41 | .build(); 42 | manager.registerCommand(command); 43 | 44 | // execute the command 45 | manager.execute(Namespace.create(), "testalias"); // Will print 'Label: testalias' 46 | manager.execute(Namespace.create(), "test"); // Will print 'Label: test' 47 | ``` 48 | 49 | -------------------------------------------------------------------------------- /docs/execution/index.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FixedDev/command-flow/1d7255d062364251091e46b94402bc504f224aaf/docs/execution/index.txt -------------------------------------------------------------------------------- /docs/getting-started.md: -------------------------------------------------------------------------------- 1 | ## Getting Started 2 | 3 | Welcome to the `command-flow` documentation 4 | 5 | `command-flow` is a flexible and platform-agnostic command framework 6 | for Java. With this framework you can *imperatively* create command trees 7 | using builders or *declaratively* using annotated classes and methods. 8 | 9 | See the following example: 10 | ```java 11 | @Command("test") 12 | public class TestCommand implements CommandClass { 13 | 14 | @Command("hello") 15 | public void hello(CommandSender sender) { 16 | sender.sendMessage("Hello World!"); 17 | } 18 | 19 | } 20 | ``` 21 | 22 | ### Features 23 | - Easily create commands using builders or annotations -------------------------------------------------------------------------------- /docs/imperatively/argument.md: -------------------------------------------------------------------------------- 1 | ## #2 With Arguments 2 | 3 | Check this command example with a single argument: 4 | 5 | 6 | ```java 7 | 8 | 9 | //... 10 | 11 | // create an (argument) part of type string, with the name 'name' 12 | CommandPart nameArg = string("name"); 13 | 14 | // create the command 15 | Command helloCommand = Command.builder("hello") 16 | .addPart(nameArg) 17 | .action(context -> { 18 | // get the value of the name argument and print 'Hi ' 19 | context.getValue(nameArg).ifPresent(name -> { 20 | System.out.println("Hi " + name); 21 | }); 22 | }) 23 | .build(); 24 | ``` 25 | 26 | 27 | In this example: 28 | - Executing `hello yusshu` will print `Hi yusshu` 29 | - Executing `hello Fixed` will print `Hi Fixed` 30 | - Executing `hello` will print nothing -------------------------------------------------------------------------------- /docs/imperatively/basic.md: -------------------------------------------------------------------------------- 1 | ## #1 Basic Command 2 | 3 | Check this basic command example, with no arguments, no subcommands and no permissions, 4 | using the imperative command creation approach: 5 | 6 | 7 | ```java 8 | Command testCommand = Command.builder("test") 9 | .action(context -> { 10 | System.out.println("Hello World!"); 11 | }) 12 | .build(); 13 | ``` 14 | 15 | 16 | Executing `test` will print `Hello World!` -------------------------------------------------------------------------------- /docs/imperatively/index.txt: -------------------------------------------------------------------------------- 1 | intro.md 2 | basic.md 3 | argument.md 4 | multiple-arguments.md 5 | optional-arguments.md 6 | with-permission.md 7 | with-switches.md 8 | with-value-flags.md -------------------------------------------------------------------------------- /docs/imperatively/intro.md: -------------------------------------------------------------------------------- 1 | ## Imperative Command Creation 2 | 3 | We can imperatively create commands using the command builder interface. This 4 | way to create commands is the most flexible one, but also the most verbose. 5 | 6 | An alternative to this approach is to use the [annotation-based command creation](../annotated/annotated.md). 7 | 8 | ### Comparison 9 | 10 | You can make a side-by-side comparison of the two approaches in the following table 11 | and links: 12 | 13 | | Imperative | Annotated | 14 | |--------------------------------------------------------------------------|-----------------------------------------------------------------------| 15 | | [Basic Command Creation](../imperatively/basic.md) | [Basic Command Creation](../annotated/basic.md) | 16 | | [Command with single Argument](../imperatively/argument.md) | [Command with single Argument](../annotated/argument.md) | 17 | | [Command with multiple Arguments](../imperatively/multiple-arguments.md) | [Command with multiple Arguments](../annotated/multiple-arguments.md) | 18 | | [Command with optional Arguments](../imperatively/optional-arguments.md) | [Command with optional Arguments](../annotated/optional-arguments.md) | 19 | | [Command with Permission](../imperatively/with-permission.md) | [Command with Permission](../annotated/with-permission.md) | 20 | | [Command with Switch Arguments](../imperatively/with-switches.md) | [Command with Switch Arguments](../annotated/with-switches.md) | 21 | | [Command with Value Flags](../imperatively/with-value-flags.md) | [Command with Value Flags](../annotated/with-value-flags.md) | -------------------------------------------------------------------------------- /docs/imperatively/multiple-arguments.md: -------------------------------------------------------------------------------- 1 | ## #3 Command with Multiple Args 2 | 3 | This example shows how to create a command with multiple arguments 4 | using the imperative approach: 5 | 6 | 7 | ```java 8 | // Here we create a StringPart(String argument) with the name "name" 9 | CommandPart name = string("name"); 10 | 11 | // Here we create a BooleanPart(boolean argument) with the name "formal" 12 | CommandPart formalPart = booleanPart("formal"); 13 | 14 | Command greetCommand = Command.builder("greet") 15 | // Here we add a part into the Command 16 | .addPart(name) 17 | // You can add multiple parts into a command 18 | // They will be added into a main part called SequentialCommandPart 19 | // Which will call every part of the Command in a sequence to parse every argument. 20 | .addPart(formalPart) 21 | .action(context -> { 22 | // The values for a Part(argument) may not be present, check if they're 23 | // before trying to use them 24 | boolean formal = context.getValue(formalPart).orElse(false); 25 | context.getValue(name).ifPresent(s -> { 26 | if (formal) { 27 | System.out.println("Hello, " + s + "!"); 28 | } else { 29 | System.out.println("Hi, " + s + "!"); 30 | } 31 | }); 32 | }) 33 | .build(); 34 | ``` 35 | 36 | 37 | - Executing `greet John false` will print `Hi, John!` 38 | - Executing `greet John true` will print `Hello, John!` 39 | - Executing `greet John` will print `Hi, John!` 40 | - Executing `greet` will print nothing -------------------------------------------------------------------------------- /docs/imperatively/optional-arguments.md: -------------------------------------------------------------------------------- 1 | ## #4 With Optional Args 2 | 3 | This example shows how to create a command with optional arguments 4 | with the imperative approach: 5 | 6 | 7 | ```java 8 | CommandPart titleValue = string("title"); 9 | 10 | // This makes the titleValue argument optional, with the default value "Mr." 11 | CommandPart title = optional(titleValue, Collections.singletonList("Mr.")); 12 | 13 | CommandPart name = string("name"); 14 | 15 | Command greetCommand = Command.builder("greet") 16 | .addPart(name) 17 | .addPart(title) 18 | .action(context -> { 19 | String nameString = context.getValue(nameValue).orElse("User"); 20 | 21 | // Should be present every time, since it has a default value 22 | // If the default value is not valid for the specified part, then the argument could be absent 23 | String titleString = context.getValue(titleValue).get(); 24 | 25 | System.out.println("Hello, " + titleString + " " + nameString); 26 | }) 27 | .build(); 28 | ``` 29 | 30 | 31 | - Executing `greet John` will print `Hello, Mr. John!` 32 | - Executing `greet John Dr.` will print `Hello, Dr. John!` 33 | - Executing `greet John Mr.` will print `Hello, Mr. John!` -------------------------------------------------------------------------------- /docs/imperatively/with-permission.md: -------------------------------------------------------------------------------- 1 | ## #5 With Permission 2 | 3 | This example shows how to create a command with a permission requirement 4 | with the annotated approach: 5 | 6 | 7 | ```java 8 | Command testUserCommand = Command.builder("test") 9 | // We set the permission of the test command into admin 10 | .permission("admin") 11 | .action(context -> { 12 | System.out.println("Hi"); 13 | }) 14 | .build(); 15 | ``` 16 | 17 | 18 | Executing the `testUserCommand` will print `Hi` if the user has the `admin` 19 | permission, but will result in a `NoPermissionException` if the user does not. 20 | 21 | Check the [Authorizer page](../configuration/authorizer.md) for more information 22 | on how to specify how the `CommandManager` should check for permissions. -------------------------------------------------------------------------------- /docs/imperatively/with-switches.md: -------------------------------------------------------------------------------- 1 | ## #6 With Switches 2 | 3 | This example shows how to create a command with "switch" arguments 4 | (boolean flags) with the imperative approach, note that switch arguments 5 | values are always present. Presence of the switch argument indicates `true`, 6 | and its absence indicates `false`. 7 | 8 | 9 | ```java 10 | CommandPart name = string("name"); 11 | 12 | // If the -g argument is present, then the switch value will be true, otherwise false 13 | // It can be in any position, but the parts registered before will take priority, 14 | // that means that if the name part is registered before the switch part, the -g only 15 | // can be after the name 16 | CommandPart goodByeSwitch = switchPart("goodBye", "g"); 17 | 18 | Command testUserCommand = Command.builder("test") 19 | .addPart(goodByeSwitch) 20 | .addPart(name) 21 | .action(context -> { 22 | // The value for a switch is never absent 23 | boolean goodBye = context.getValue(goodByeSwitch).get(); 24 | 25 | context.getValue(name).ifPresent(s -> { 26 | if (goodBye) { 27 | System.out.println("Goodbye " + s); 28 | return; 29 | } 30 | System.out.println("Hi " + s); 31 | }); 32 | }) 33 | .build(); 34 | ``` 35 | 36 | 37 | - Executing `test Fixed` will print `Hi Fixed` 38 | - Executing `test -g Fixed` will print `Goodbye Fixed` 39 | - Executing `test Fixed -g` will print `Goodbye Fixed` -------------------------------------------------------------------------------- /docs/imperatively/with-value-flags.md: -------------------------------------------------------------------------------- 1 | ## #7 With Value Flags 2 | 3 | This example shows how to create a command with value flag arguments 4 | the main difference between a value flag and a switch is that the value flag 5 | takes the next argument as its value, and the switch does not. 6 | 7 | For example: `-g true`, `-p 25565`, `-n Fixed` 8 | 9 | 10 | ```java 11 | CommandPart name = string("name"); 12 | 13 | // This part is like a switch part, the difference is that when the main "switch" is found 14 | // the part provided in the first argument takes the parsing, consuming one or more arguments 15 | // from the stack at that position. 16 | CommandPart greetingValue = string("greeting"); 17 | CommandPart greetingValueFlag = valueFlag(greetingValue, "g"); 18 | 19 | Command testUserCommand = Command.builder("test") 20 | .addPart(greetingValueFlag) 21 | .addPart(name) 22 | .action(context -> { 23 | // The value for a value flag can be absent 24 | String greeting = context.getValue(greetingValue).orElse("Hi"); 25 | context.getValue(name).ifPresent(s -> { 26 | System.out.println(greeting + " " + s); 27 | }); 28 | }) 29 | .build(); 30 | ``` 31 | 32 | 33 | - Executing `test Fixed` will print `Hi Fixed` 34 | - Executing `test Fixed -g GoodBye` will print `GoodBye Fixed` 35 | - Executing `test Fixed -g Hello` will print `Hello Fixed` 36 | - Executing `test -g Fixed` will throw a `NoMoreArguments` exception, meaning that the parsing failed 37 | because the `Fixed` argument was taken as the value for the flag and no argument 38 | is remaining for the name. 39 | - Executing `test Fixed -g` will throw a `NoMoreArguments` exception, meaning that the parsing failed 40 | because the flag doesn't has any argument left to use. -------------------------------------------------------------------------------- /docs/index.txt: -------------------------------------------------------------------------------- 1 | getting-started.md 2 | installation.md 3 | concepts 4 | configuration 5 | imperatively 6 | annotated 7 | execution 8 | platforms -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | ## Installation 2 | 3 | You can add `command-flow` to your project using [Gradle](https://gradle.org/), 4 | [Maven](https://maven.apache.org/) or manually downloading the JAR files 5 | 6 | ### Gradle 7 | ```kotlin 8 | repositories { 9 | maven("https://repo.unnamed.team/repository/unnamed-public/") 10 | } 11 | ``` 12 | ```kotlin 13 | dependencies { 14 | implementation("team.unnamed:commandflow-api:%%REPLACE_latestReleaseOrSnapshot{team.unnamed:commandflow-api}%%") 15 | } 16 | ``` 17 | 18 | ### Maven 19 | ```xml 20 | 21 | unnamed-public 22 | https://repo.unnamed.team/repository/unnamed-public/ 23 | 24 | ``` 25 | ```xml 26 | 27 | team.unnamed 28 | commandflow-api 29 | %%REPLACE_latestReleaseOrSnapshot{team.unnamed:commandflow-api}%% 30 | 31 | ``` 32 | -------------------------------------------------------------------------------- /docs/platforms/brigadier.md: -------------------------------------------------------------------------------- 1 | ## Bukkit with Brigadier -------------------------------------------------------------------------------- /docs/platforms/bungeecord.md: -------------------------------------------------------------------------------- 1 | ## BungeeCord -------------------------------------------------------------------------------- /docs/platforms/discord-jda.md: -------------------------------------------------------------------------------- 1 | ## Discord (JDA) -------------------------------------------------------------------------------- /docs/platforms/index.txt: -------------------------------------------------------------------------------- 1 | platforms.md 2 | bukkit.md 3 | brigadier.md 4 | velocity.md 5 | bungeecord.md 6 | discord-jda.md -------------------------------------------------------------------------------- /docs/platforms/platforms.md: -------------------------------------------------------------------------------- 1 | ## Platforms 2 | 3 | `command-flow` core is platform-agnostic, however, it provides a way to integrate with 4 | platform-specific features, such as native command registration, tab-completion/suggestions, 5 | etc. 6 | 7 | At the moment we support the following platforms: 8 | - [Bukkit](bukkit.md) 9 | - [Bukkit with Brigadier](brigadier.md) 10 | - [BungeeCord](bungeecord.md) 11 | - [Discord with JDA](discord-jda.md) 12 | - [Velocity](velocity.md) 13 | 14 | However, you can make your own platform implementation easily. -------------------------------------------------------------------------------- /docs/platforms/velocity.md: -------------------------------------------------------------------------------- 1 | ## Velocity -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2022 Gilberto Garcia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # command-flow 2 | [![Build Status](https://img.shields.io/github/actions/workflow/status/unnamed/command-flow/build.yml?branch=master)](https://github.com/unnamed/command-flow/actions/workflows/build.yml) 3 | [![MIT License](https://img.shields.io/badge/license-MIT-blue)](license.txt) 4 | [![Discord](https://img.shields.io/discord/683899335405994062)](https://discord.gg/xbba2fy) 5 | 6 | A flexible command framework which removes lots of boilerplate code used in commands 7 | 8 | Check the [documentation](https://unnamed.team/docs/command-flow) for installation and 9 | usage information for this project 10 | -------------------------------------------------------------------------------- /velocity/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | team.unnamed 8 | commandflow 9 | 0.7.2 10 | ../pom.xml 11 | 12 | commandflow-velocity 13 | jar 14 | 15 | 16 | 17 | papermc 18 | https://repo.papermc.io/repository/maven-public/ 19 | 20 | 21 | 22 | 23 | 24 | team.unnamed 25 | commandflow-api 26 | ${project.version} 27 | compile 28 | 29 | 30 | com.velocitypowered 31 | velocity-api 32 | 3.1.2-SNAPSHOT 33 | provided 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /velocity/src/main/java/team/unnamed/commandflow/velocity/VelocityAuthorizer.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.velocity; 2 | 3 | import com.velocitypowered.api.command.CommandSource; 4 | import team.unnamed.commandflow.Authorizer; 5 | import team.unnamed.commandflow.Namespace; 6 | 7 | public class VelocityAuthorizer implements Authorizer { 8 | 9 | @Override 10 | public boolean isAuthorized(Namespace namespace, String permission) { 11 | if (permission.isEmpty()) { 12 | return true; 13 | } 14 | 15 | CommandSource sender = namespace.getObject(CommandSource.class, VelocityCommandManager.SENDER_NAMESPACE); 16 | return sender.hasPermission(permission); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /velocity/src/main/java/team/unnamed/commandflow/velocity/VelocityDefaultTranslationProvider.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.velocity; 2 | 3 | import team.unnamed.commandflow.translator.DefaultMapTranslationProvider; 4 | 5 | public class VelocityDefaultTranslationProvider extends DefaultMapTranslationProvider { 6 | 7 | public VelocityDefaultTranslationProvider() { 8 | translations.put("player.offline", "The player %s is offline!"); 9 | translations.put("sender.unknown", "The sender for the command is unknown!"); 10 | translations.put("sender.only-player", "Only players can execute this command!"); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /velocity/src/main/java/team/unnamed/commandflow/velocity/annotation/PlayerOrSource.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.velocity.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.PARAMETER) 10 | public @interface PlayerOrSource { 11 | } 12 | -------------------------------------------------------------------------------- /velocity/src/main/java/team/unnamed/commandflow/velocity/factory/CommandSourcePartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.velocity.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.PartFactory; 4 | import team.unnamed.commandflow.part.CommandPart; 5 | import team.unnamed.commandflow.velocity.part.CommandSenderPart; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.util.List; 9 | 10 | public class CommandSourcePartFactory implements PartFactory { 11 | 12 | @Override 13 | public CommandPart createPart(String name, List modifiers) { 14 | return new CommandSenderPart(name); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /velocity/src/main/java/team/unnamed/commandflow/velocity/factory/PlayerPartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.velocity.factory; 2 | 3 | import com.velocitypowered.api.proxy.ProxyServer; 4 | import team.unnamed.commandflow.annotated.part.PartFactory; 5 | import team.unnamed.commandflow.part.CommandPart; 6 | import team.unnamed.commandflow.velocity.annotation.PlayerOrSource; 7 | import team.unnamed.commandflow.velocity.part.PlayerPart; 8 | 9 | import java.lang.annotation.Annotation; 10 | import java.util.List; 11 | 12 | public class PlayerPartFactory implements PartFactory { 13 | 14 | private final ProxyServer proxyServer; 15 | 16 | public PlayerPartFactory(ProxyServer proxyServer) { 17 | this.proxyServer = proxyServer; 18 | } 19 | 20 | @Override 21 | public CommandPart createPart(String name, List modifiers) { 22 | boolean orSource = getAnnotation(modifiers, PlayerOrSource.class) != null; 23 | 24 | return new PlayerPart(proxyServer, name, orSource); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /velocity/src/main/java/team/unnamed/commandflow/velocity/factory/PlayerSenderPartFactory.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.velocity.factory; 2 | 3 | import team.unnamed.commandflow.annotated.part.PartFactory; 4 | import team.unnamed.commandflow.part.CommandPart; 5 | import team.unnamed.commandflow.velocity.part.PlayerSenderPart; 6 | 7 | import java.lang.annotation.Annotation; 8 | import java.util.List; 9 | 10 | public class PlayerSenderPartFactory implements PartFactory { 11 | 12 | @Override 13 | public CommandPart createPart(String name, List modifiers) { 14 | return new PlayerSenderPart(name); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /velocity/src/main/java/team/unnamed/commandflow/velocity/factory/VelocityModule.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.velocity.factory; 2 | 3 | import com.velocitypowered.api.command.CommandSource; 4 | import com.velocitypowered.api.proxy.Player; 5 | import com.velocitypowered.api.proxy.ProxyServer; 6 | import team.unnamed.commandflow.annotated.part.AbstractModule; 7 | import team.unnamed.commandflow.annotated.part.Key; 8 | import team.unnamed.commandflow.annotated.annotation.Sender; 9 | 10 | public class VelocityModule extends AbstractModule { 11 | 12 | private final ProxyServer proxyServer; 13 | 14 | public VelocityModule(ProxyServer proxyServer) { 15 | this.proxyServer = proxyServer; 16 | } 17 | 18 | @Override 19 | public void configure() { 20 | bindFactory(CommandSource.class, new CommandSourcePartFactory()); 21 | bindFactory(Player.class, new PlayerPartFactory(proxyServer)); 22 | bindFactory(new Key(Player.class, Sender.class), new PlayerSenderPartFactory()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /velocity/src/main/java/team/unnamed/commandflow/velocity/part/CommandSenderPart.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.velocity.part; 2 | 3 | import com.velocitypowered.api.command.CommandSource; 4 | import team.unnamed.commandflow.CommandContext; 5 | import team.unnamed.commandflow.exception.ArgumentParseException; 6 | import team.unnamed.commandflow.exception.CommandException; 7 | import team.unnamed.commandflow.part.CommandPart; 8 | import team.unnamed.commandflow.stack.ArgumentStack; 9 | import team.unnamed.commandflow.velocity.VelocityCommandManager; 10 | import net.kyori.adventure.text.Component; 11 | 12 | import java.util.Objects; 13 | 14 | public class CommandSenderPart implements CommandPart { 15 | 16 | private final String name; 17 | 18 | public CommandSenderPart(String name) { 19 | this.name = name; 20 | } 21 | 22 | @Override 23 | public String getName() { 24 | return this.name; 25 | } 26 | 27 | @Override 28 | public void parse(CommandContext context, ArgumentStack stack, CommandPart parent) throws ArgumentParseException { 29 | CommandSource sender = context.getObject(CommandSource.class, VelocityCommandManager.SENDER_NAMESPACE); 30 | 31 | if (sender != null) { 32 | context.setValue(this, sender); 33 | 34 | return; 35 | } 36 | 37 | throw new CommandException(Component.translatable("sender.unknown")); 38 | } 39 | 40 | @Override 41 | public boolean equals(Object o) { 42 | if (this == o) return true; 43 | if (!(o instanceof CommandSenderPart)) return false; 44 | CommandSenderPart that = (CommandSenderPart) o; 45 | return Objects.equals(name, that.name); 46 | } 47 | 48 | @Override 49 | public int hashCode() { 50 | return Objects.hash(name); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /velocity/src/main/java/team/unnamed/commandflow/velocity/part/PlayerSenderPart.java: -------------------------------------------------------------------------------- 1 | package team.unnamed.commandflow.velocity.part; 2 | 3 | import com.velocitypowered.api.command.CommandSource; 4 | import com.velocitypowered.api.proxy.Player; 5 | import team.unnamed.commandflow.CommandContext; 6 | import team.unnamed.commandflow.exception.ArgumentParseException; 7 | import team.unnamed.commandflow.exception.CommandException; 8 | import team.unnamed.commandflow.part.CommandPart; 9 | import team.unnamed.commandflow.stack.ArgumentStack; 10 | import team.unnamed.commandflow.velocity.VelocityCommandManager; 11 | import net.kyori.adventure.text.Component; 12 | 13 | import java.util.Objects; 14 | 15 | public class PlayerSenderPart implements CommandPart { 16 | 17 | private final String name; 18 | 19 | public PlayerSenderPart(String name) { 20 | this.name = name; 21 | } 22 | 23 | @Override 24 | public String getName() { 25 | return name; 26 | } 27 | 28 | @Override 29 | public void parse(CommandContext context, ArgumentStack stack, CommandPart parent) throws ArgumentParseException { 30 | CommandSource sender = context.getObject(CommandSource.class, VelocityCommandManager.SENDER_NAMESPACE); 31 | 32 | if (sender != null) { 33 | if (sender instanceof Player) { 34 | context.setValue(this, sender); 35 | 36 | return; 37 | } 38 | 39 | throw new ArgumentParseException(Component.translatable("sender.only-player")); 40 | } 41 | 42 | throw new CommandException(Component.translatable("sender.unknown")); 43 | } 44 | 45 | @Override 46 | public boolean equals(Object o) { 47 | if (this == o) return true; 48 | if (!(o instanceof PlayerSenderPart)) return false; 49 | PlayerSenderPart that = (PlayerSenderPart) o; 50 | return Objects.equals(name, that.name); 51 | } 52 | 53 | @Override 54 | public int hashCode() { 55 | return Objects.hash(name); 56 | } 57 | } 58 | --------------------------------------------------------------------------------