├── .github └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── LICENSE ├── README.md ├── adventure ├── build.gradle └── src │ └── main │ └── java │ └── dev │ └── velix │ └── imperat │ └── adventure │ ├── AdventureProvider.java │ ├── CastingAdventure.java │ ├── EmptyAdventure.java │ └── TreeHelpProvider.java ├── brigadier ├── build.gradle └── src │ └── main │ └── java │ └── dev │ └── velix │ └── imperat │ ├── ArgumentTypeResolver.java │ ├── BaseBrigadierManager.java │ └── BrigadierManager.java ├── build.gradle ├── bukkit ├── build.gradle └── src │ └── main │ └── java │ └── dev │ └── velix │ └── imperat │ ├── BukkitAdventure.java │ ├── BukkitConfigBuilder.java │ ├── BukkitImperat.java │ ├── BukkitPermissionResolver.java │ ├── BukkitSource.java │ ├── BukkitUtil.java │ ├── ImperatBukkitHelpTopic.java │ ├── InternalBukkitCommand.java │ ├── Version.java │ ├── brigadier │ ├── BukkitBrigadierManager.java │ ├── DefaultArgTypeResolvers.java │ └── MinecraftArgumentType.java │ ├── commodore │ ├── AbstractCommodore.java │ ├── BrigadierUnsupportedException.java │ ├── Commodore.java │ ├── CommodoreProvider.java │ ├── LegacyPaperCommodore.java │ ├── ModernPaperCommodore.java │ └── ReflectionCommodore.java │ ├── exception │ ├── UnknownOfflinePlayerException.java │ ├── UnknownPlayerException.java │ └── UnknownWorldException.java │ ├── selector │ ├── EntityCondition.java │ ├── SelectionParameterInput.java │ ├── SelectionType.java │ ├── TargetSelector.java │ └── field │ │ ├── AbstractField.java │ │ ├── NumericField.java │ │ ├── Range.java │ │ ├── RangedNumericField.java │ │ ├── SelectionField.java │ │ ├── SelectionFields.java │ │ ├── filters │ │ ├── DistanceField.java │ │ ├── GamemodeField.java │ │ ├── LevelField.java │ │ ├── NameField.java │ │ ├── PredicateField.java │ │ ├── PredicateFields.java │ │ ├── TagField.java │ │ └── TypeField.java │ │ ├── operators │ │ ├── LimitOperatorField.java │ │ ├── OperatorField.java │ │ ├── OperatorFields.java │ │ ├── SortOperatorField.java │ │ └── SortOption.java │ │ └── provider │ │ ├── FieldProvider.java │ │ └── FieldProviderImpl.java │ └── type │ ├── ParameterLocation.java │ ├── ParameterOfflinePlayer.java │ ├── ParameterPlayer.java │ └── ParameterTargetSelector.java ├── bungee ├── build.gradle └── src │ └── main │ └── java │ └── dev │ └── velix │ └── imperat │ ├── BungeeConfigBuilder.java │ ├── BungeeImperat.java │ ├── BungeeSource.java │ ├── InternalBungeeCommand.java │ ├── adventure │ └── BungeeAdventure.java │ ├── exception │ └── UnknownPlayerException.java │ ├── resolvers │ └── BungeePermissionResolver.java │ └── type │ └── ParameterProxiedPlayer.java ├── cli ├── build.gradle └── src │ └── main │ └── java │ └── dev │ └── velix │ └── imperat │ ├── CommandLineConfigBuilder.java │ ├── CommandLineImperat.java │ └── ConsoleSource.java ├── core ├── build.gradle └── src │ ├── main │ └── java │ │ └── dev │ │ └── velix │ │ └── imperat │ │ ├── AnnotationInjector.java │ │ ├── BaseImperat.java │ │ ├── CommandHelpHandler.java │ │ ├── CommandRegistrar.java │ │ ├── ConfigBuilder.java │ │ ├── FlagRegistrar.java │ │ ├── Imperat.java │ │ ├── ImperatConfig.java │ │ ├── ImperatConfigImpl.java │ │ ├── ProcessorRegistrar.java │ │ ├── ResolverRegistrar.java │ │ ├── SourceWrapper.java │ │ ├── ThrowableHandler.java │ │ ├── annotations │ │ ├── Async.java │ │ ├── Command.java │ │ ├── ContextResolved.java │ │ ├── Cooldown.java │ │ ├── Default.java │ │ ├── DefaultProvider.java │ │ ├── Dependency.java │ │ ├── Description.java │ │ ├── Flag.java │ │ ├── GlobalAttachmentMode.java │ │ ├── Greedy.java │ │ ├── Help.java │ │ ├── Inherit.java │ │ ├── Named.java │ │ ├── Optional.java │ │ ├── Permission.java │ │ ├── PostProcessor.java │ │ ├── PreProcessor.java │ │ ├── Range.java │ │ ├── SubCommand.java │ │ ├── Suggest.java │ │ ├── SuggestionProvider.java │ │ ├── Switch.java │ │ ├── Usage.java │ │ ├── Values.java │ │ ├── base │ │ │ ├── AnnotationFactory.java │ │ │ ├── AnnotationHelper.java │ │ │ ├── AnnotationParser.java │ │ │ ├── AnnotationParserImpl.java │ │ │ ├── AnnotationReader.java │ │ │ ├── AnnotationReaderImpl.java │ │ │ ├── AnnotationRegistry.java │ │ │ ├── AnnotationReplacer.java │ │ │ ├── MethodCommandExecutor.java │ │ │ ├── SourceOrderHelper.java │ │ │ └── element │ │ │ │ ├── ClassElement.java │ │ │ │ ├── CommandClassVisitor.java │ │ │ │ ├── MethodElement.java │ │ │ │ ├── ParameterElement.java │ │ │ │ ├── ParseElement.java │ │ │ │ ├── RootCommandClass.java │ │ │ │ ├── SimpleCommandClassVisitor.java │ │ │ │ └── selector │ │ │ │ ├── ElementSelector.java │ │ │ │ ├── MethodRules.java │ │ │ │ ├── Rule.java │ │ │ │ ├── RuleCondition.java │ │ │ │ └── SimpleElementSelector.java │ │ └── parameters │ │ │ ├── AnnotatedParameter.java │ │ │ ├── AnnotationParameterDecorator.java │ │ │ └── NumericParameterDecorator.java │ │ ├── command │ │ ├── AttachmentMode.java │ │ ├── Command.java │ │ ├── CommandCoordinator.java │ │ ├── CommandExecution.java │ │ ├── CommandImpl.java │ │ ├── CommandUsage.java │ │ ├── CommandUsageImpl.java │ │ ├── ContextResolverFactory.java │ │ ├── ContextResolverRegistry.java │ │ ├── CooldownHolder.java │ │ ├── Description.java │ │ ├── DescriptionHolder.java │ │ ├── NumericComparator.java │ │ ├── PermissionHolder.java │ │ ├── ReturnResolverRegistry.java │ │ ├── SourceResolverRegistry.java │ │ ├── UsageComparator.java │ │ ├── UsageMap.java │ │ ├── cooldown │ │ │ ├── CooldownHandler.java │ │ │ ├── DefaultCooldownHandler.java │ │ │ └── UsageCooldown.java │ │ ├── parameters │ │ │ ├── CommandParameter.java │ │ │ ├── ConstrainedParameterTypeDecorator.java │ │ │ ├── FlagBuilder.java │ │ │ ├── FlagCommandParameter.java │ │ │ ├── FlagParameter.java │ │ │ ├── InputParameter.java │ │ │ ├── NormalCommandParameter.java │ │ │ ├── NumericParameter.java │ │ │ ├── NumericRange.java │ │ │ ├── OptionalValueSupplier.java │ │ │ ├── ParameterBuilder.java │ │ │ ├── StrictParameterList.java │ │ │ └── type │ │ │ │ ├── ArrayParameterType.java │ │ │ │ ├── BaseParameterType.java │ │ │ │ ├── CollectionParameterType.java │ │ │ │ ├── MapParameterType.java │ │ │ │ ├── ParameterBoolean.java │ │ │ │ ├── ParameterCommand.java │ │ │ │ ├── ParameterEnum.java │ │ │ │ ├── ParameterFlag.java │ │ │ │ ├── ParameterNumber.java │ │ │ │ ├── ParameterString.java │ │ │ │ ├── ParameterType.java │ │ │ │ ├── ParameterTypes.java │ │ │ │ ├── ParameterUUID.java │ │ │ │ └── ParameterWord.java │ │ ├── processors │ │ │ ├── ChainImpl.java │ │ │ ├── CommandPostProcessor.java │ │ │ ├── CommandPreProcessor.java │ │ │ ├── CommandProcessingChain.java │ │ │ ├── CommandProcessor.java │ │ │ └── impl │ │ │ │ ├── DefaultProcessors.java │ │ │ │ ├── UsageCooldownProcessor.java │ │ │ │ └── UsagePermissionProcessor.java │ │ ├── returns │ │ │ ├── BaseReturnResolver.java │ │ │ └── ReturnResolver.java │ │ ├── suggestions │ │ │ ├── AutoCompleter.java │ │ │ ├── CompletionArg.java │ │ │ ├── NativeAutoCompleter.java │ │ │ └── SuggestionResolverRegistry.java │ │ └── tree │ │ │ ├── ArgumentNode.java │ │ │ ├── CommandDispatch.java │ │ │ ├── CommandNode.java │ │ │ ├── CommandTree.java │ │ │ ├── CommandTreeVisualizer.java │ │ │ └── ParameterNode.java │ │ ├── context │ │ ├── ArgumentQueue.java │ │ ├── ArgumentQueueImpl.java │ │ ├── Context.java │ │ ├── ExecutionContext.java │ │ ├── FlagData.java │ │ ├── ParamTypeRegistry.java │ │ ├── ResolvedContext.java │ │ ├── Source.java │ │ ├── SuggestionContext.java │ │ └── internal │ │ │ ├── Argument.java │ │ │ ├── CommandInputStream.java │ │ │ ├── CommandInputStreamImpl.java │ │ │ ├── ContextFactory.java │ │ │ ├── ContextImpl.java │ │ │ ├── Cursor.java │ │ │ ├── DefaultContextFactory.java │ │ │ ├── ExtractedInputFlag.java │ │ │ ├── PositionShiftCondition.java │ │ │ ├── ResolvedContextImpl.java │ │ │ ├── ShiftOperation.java │ │ │ ├── ShiftTarget.java │ │ │ ├── SmartUsageResolve.java │ │ │ └── SuggestionContextImpl.java │ │ ├── exception │ │ ├── AmbiguousUsageAdditionException.java │ │ ├── CooldownException.java │ │ ├── ImperatException.java │ │ ├── InvalidCommandUsageException.java │ │ ├── InvalidSourceException.java │ │ ├── InvalidSyntaxException.java │ │ ├── InvalidUUIDException.java │ │ ├── NoHelpException.java │ │ ├── NoHelpPageException.java │ │ ├── NumberOutOfRangeException.java │ │ ├── PermissionDeniedException.java │ │ ├── SelfHandledException.java │ │ ├── SourceException.java │ │ └── ThrowableResolver.java │ │ ├── help │ │ ├── CommandHelp.java │ │ ├── DefaultFormatter.java │ │ ├── HelpHyphen.java │ │ ├── HelpProvider.java │ │ ├── HelpTemplate.java │ │ ├── HelpTemplateImpl.java │ │ ├── HyphenContent.java │ │ ├── PaginatedHelpTemplate.java │ │ ├── TemplatePagination.java │ │ ├── UsageDisplayer.java │ │ └── UsageFormatter.java │ │ ├── placeholders │ │ ├── Placeholder.java │ │ ├── PlaceholderImpl.java │ │ ├── PlaceholderRegistry.java │ │ └── PlaceholderResolver.java │ │ ├── resolvers │ │ ├── ContextResolver.java │ │ ├── DependencySupplier.java │ │ ├── PermissionResolver.java │ │ ├── SourceResolver.java │ │ └── SuggestionResolver.java │ │ ├── util │ │ ├── AnnotationMap.java │ │ ├── ImperatDebugger.java │ │ ├── Patterns.java │ │ ├── Preconditions.java │ │ ├── Registry.java │ │ ├── StringUtils.java │ │ ├── TypeUtility.java │ │ ├── TypeVisitor.java │ │ ├── TypeWrap.java │ │ ├── asm │ │ │ ├── DefaultMethodCallerFactory.java │ │ │ ├── MethodCaller.java │ │ │ ├── MethodCallerFactory.java │ │ │ └── MethodHandlesCallerFactory.java │ │ ├── reflection │ │ │ ├── FieldAccessor.java │ │ │ └── Reflections.java │ │ └── text │ │ │ ├── PaginatedText.java │ │ │ └── TextPage.java │ │ └── verification │ │ ├── SimpleVerifier.java │ │ ├── TypeTolerantVerifier.java │ │ └── UsageVerifier.java │ └── test │ └── java │ └── dev │ └── velix │ └── imperat │ ├── TestRun.java │ ├── TestSmartUsageResolve.java │ ├── advanced │ ├── DurationParameterType.java │ ├── DurationParser.java │ └── GuildMOTDCommand.java │ ├── commands │ ├── Duration.java │ ├── EmptyCmd.java │ ├── KingdomChatCommand.java │ ├── MyCustomAnnotation.java │ ├── ParameterDuration.java │ ├── RankCommand.java │ ├── Test2Command.java │ ├── Test3Command.java │ ├── TestCommands.java │ ├── TestCustomAnnotationCmd.java │ ├── Utilities.java │ └── annotations │ │ ├── FirstOptionalArgumentCmd.java │ │ ├── FirstSub.java │ │ ├── KitCommand.java │ │ ├── SecondSub.java │ │ ├── TestCommand.java │ │ ├── contextresolver │ │ ├── ContextResolvingCmd.java │ │ ├── PlayerData.java │ │ └── PlayerDataContextResolver.java │ │ └── examples │ │ ├── AnnotatedGroupCommand.java │ │ ├── BanCommand.java │ │ ├── GitCommand.java │ │ ├── Group.java │ │ ├── GroupRegistry.java │ │ ├── GroupSuggestionResolver.java │ │ ├── MessageCmd.java │ │ └── OptionalArgCommand.java │ ├── components │ ├── TestImperat.java │ ├── TestImperatConfig.java │ ├── TestPlaceholder.java │ └── TestSource.java │ ├── misc │ ├── CustomCooldownProcessor.java │ ├── CustomEnum.java │ ├── CustomEnumParameterType.java │ ├── ParameterGroup.java │ └── Test4Cmd.java │ └── processors │ ├── CustomPostProcessor.java │ └── CustomPreProcessor.java ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── logo.png ├── minestom ├── build.gradle └── src │ └── main │ └── java │ └── dev │ └── velix │ └── imperat │ ├── ArgumentDecorator.java │ ├── InternalMinestomCommand.java │ ├── MinestomConfigBuilder.java │ ├── MinestomImperat.java │ ├── MinestomSource.java │ ├── SyntaxDataLoader.java │ └── exception │ └── UnknownPlayerException.java ├── paper ├── build.gradle └── src │ └── main │ └── java │ ├── com │ └── destroystokyo │ │ └── paper │ │ └── event │ │ └── brigadier │ │ └── AsyncPlayerSendCommandsEvent.java │ └── io │ └── papermc │ └── paper │ └── command │ └── brigadier │ ├── CommandSourceStack.java │ └── Commands.java ├── qodana.yaml ├── settings.gradle └── velocity ├── build.gradle └── src └── main └── java └── dev └── velix └── imperat ├── InternalVelocityCommand.java ├── VelocityConfigBuilder.java ├── VelocityImperat.java ├── VelocitySource.java ├── exception └── UnknownPlayerException.java └── types └── ParameterPlayer.java /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | release: 5 | types: [ created ] 6 | 7 | jobs: 8 | cleanup: 9 | runs-on: ubuntu-latest 10 | permissions: write-all 11 | steps: 12 | - name: 🗑 Delete deployment 13 | uses: strumwolf/delete-deployment-environment@v2 14 | with: 15 | token: ${{ secrets.GITHUB_TOKEN }} 16 | environment: Maven Central 17 | onlyRemoveDeployments: true 18 | release: 19 | needs: cleanup 20 | runs-on: ubuntu-latest 21 | environment: Maven Central 22 | permissions: 23 | contents: read 24 | packages: write 25 | steps: 26 | - name: ✅ Checkout Project 27 | uses: actions/checkout@v4 28 | 29 | - name: 📐 Setup Java 30 | uses: actions/setup-java@v4 31 | with: 32 | distribution: corretto 33 | java-version: 17 34 | 35 | - name: 📐 Setup Gradle 36 | uses: gradle/actions/setup-gradle@v4 37 | 38 | - name: 👷 Gradle Build 39 | run: | 40 | chmod +x ./gradlew 41 | ./gradlew build 42 | 43 | - name: 🚀 Deploy 44 | run: ./gradlew publishAndReleaseToMavenCentral 45 | env: 46 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 47 | ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_USERNAME }} 48 | ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_PASSWORD }} 49 | ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.MAVEN_SIGNING_KEY }} 50 | # ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.MAVEN_SIGNING_KEY_ID }} 51 | ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.MAVEN_SIGNING_PASSWORD }} 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### IntelliJ IDEA ### 8 | .idea 9 | *.iws 10 | *.iml 11 | *.ipr 12 | out/ 13 | !**/src/main/**/out/ 14 | !**/src/test/**/out/ 15 | 16 | ### Eclipse ### 17 | .apt_generated 18 | .classpath 19 | .factorypath 20 | .project 21 | .settings 22 | .springBeans 23 | .sts4-cache 24 | bin/ 25 | !**/src/main/**/bin/ 26 | !**/src/test/**/bin/ 27 | 28 | ### NetBeans ### 29 | /nbproject/private/ 30 | /nbbuild/ 31 | /dist/ 32 | /nbdist/ 33 | /.nb-gradle/ 34 | 35 | ### VS Code ### 36 | .vscode/ 37 | 38 | ### Mac OS ### 39 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Attribution License for Imperat 2 | 3 | Copyright (c) 2024 Velix Developments 4 | 5 | 1. Grant of License: 6 | Permission is granted to any person obtaining a copy of this software and associated documentation files (the "Software") 7 | to use, copy, and distribute the Software, subject to the following conditions: 8 | 9 | 2. Attribution: 10 | All copies or substantial portions of the Software must include the original copyright notice, 11 | and this permission notice. Any public use, distribution, or derivative works must credit the original authors clearly and prominently. 12 | 13 | 3. Forks: 14 | Public forks of this project are allowed, provided that proper attribution is maintained as outlined in Section 2. 15 | Any modifications must also be clearly labeled as such and must not claim original authorship. 16 | 17 | Private forks of this project are not permitted without explicit written permission from the original authors. 18 | 19 | 4. No Claim of Ownership: You may not claim the Software or any modifications thereof as your own. 20 | Attribution to the original authors must be maintained in any distribution or public presentation. 21 | 22 | 5. Warranty Disclaimer: 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 26 | DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, 27 | OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | -------------------------------------------------------------------------------- /adventure/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "java-library" 3 | } 4 | 5 | repositories { 6 | mavenCentral() 7 | } 8 | 9 | dependencies { 10 | compileOnly project(":core") 11 | compileOnlyApi("${kyori(KyoriModule.API)}") 12 | 13 | compileOnly("org.projectlombok:lombok:1.18.30") 14 | annotationProcessor("org.projectlombok:lombok:1.18.30") 15 | 16 | compileOnly 'org.jetbrains:annotations:24.1.0' 17 | annotationProcessor('org.jetbrains:annotations:24.1.0') 18 | } 19 | 20 | java { 21 | toolchain.languageVersion.set(JavaLanguageVersion.of(17)) 22 | } -------------------------------------------------------------------------------- /adventure/src/main/java/dev/velix/imperat/adventure/CastingAdventure.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.adventure; 2 | 3 | import net.kyori.adventure.audience.Audience; 4 | 5 | public class CastingAdventure implements AdventureProvider { 6 | 7 | @Override 8 | public final Audience audience(final S sender) { 9 | return (Audience) sender; 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /adventure/src/main/java/dev/velix/imperat/adventure/EmptyAdventure.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.adventure; 2 | 3 | import dev.velix.imperat.context.Source; 4 | import net.kyori.adventure.audience.Audience; 5 | import net.kyori.adventure.text.ComponentLike; 6 | 7 | public class EmptyAdventure implements AdventureProvider { 8 | 9 | @Override 10 | public Audience audience(final S sender) { 11 | return null; 12 | } 13 | 14 | @Override 15 | public Audience audience(final Source source) { 16 | return null; 17 | } 18 | 19 | @Override 20 | public void send(final S sender, final ComponentLike component) { 21 | // do nothing 22 | } 23 | 24 | @Override 25 | public void send(final Source source, final ComponentLike component) { 26 | // do nothing 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /brigadier/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "java" 3 | } 4 | 5 | repositories { 6 | mavenCentral() 7 | maven { 8 | url "https://libraries.minecraft.net" 9 | } 10 | } 11 | 12 | dependencies { 13 | compileOnly project(":core") 14 | compileOnly("com.mojang:brigadier:1.0.18") 15 | 16 | compileOnly 'org.jetbrains:annotations:24.1.0' 17 | annotationProcessor('org.jetbrains:annotations:24.1.0') 18 | } 19 | 20 | java { 21 | toolchain.languageVersion.set(JavaLanguageVersion.of(17)) 22 | } -------------------------------------------------------------------------------- /bukkit/build.gradle: -------------------------------------------------------------------------------- 1 | repositories { 2 | mavenCentral() 3 | maven { 4 | url = "https://repo.codemc.io/repository/nms/" 5 | } 6 | maven { 7 | url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' 8 | 9 | content { 10 | includeGroup 'org.bukkit' 11 | includeGroup 'org.spigotmc' 12 | } 13 | } 14 | maven { url = 'https://oss.sonatype.org/content/repositories/snapshots' } 15 | maven { url = 'https://oss.sonatype.org/content/repositories/central' } 16 | maven { 17 | name = "papermc" 18 | url = uri("https://repo.papermc.io/repository/maven-public/") 19 | } 20 | maven { 21 | url = "https://libraries.minecraft.net" 22 | } 23 | } 24 | 25 | dependencies { 26 | api project(":adventure") 27 | api project(":brigadier") 28 | 29 | compileOnly project(":core") 30 | compileOnly project(":paper") 31 | 32 | compileOnly("com.mojang:brigadier:1.0.18") 33 | compileOnly("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT") 34 | compileOnly 'org.spigotmc:spigot:1.13.2-R0.1-SNAPSHOT' 35 | 36 | compileOnly "${kyoriPlatform(KyoriModule.BUKKIT)}" 37 | } 38 | 39 | processTestResources { 40 | duplicatesStrategy = DuplicatesStrategy.EXCLUDE 41 | } 42 | 43 | java { 44 | toolchain.languageVersion.set(JavaLanguageVersion.of(17)) 45 | } -------------------------------------------------------------------------------- /bukkit/src/main/java/dev/velix/imperat/BukkitAdventure.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat; 2 | 3 | import dev.velix.imperat.adventure.AdventureProvider; 4 | import net.kyori.adventure.audience.Audience; 5 | import net.kyori.adventure.platform.bukkit.BukkitAudiences; 6 | import org.bukkit.command.CommandSender; 7 | import org.bukkit.plugin.Plugin; 8 | 9 | public final class BukkitAdventure implements AdventureProvider { 10 | 11 | private final BukkitAudiences audiences; 12 | 13 | public BukkitAdventure(final Plugin plugin) { 14 | this.audiences = BukkitAudiences.create(plugin); 15 | } 16 | 17 | @Override 18 | public Audience audience(final CommandSender sender) { 19 | return audiences.sender(sender); 20 | } 21 | 22 | @Override 23 | public void close() { 24 | this.audiences.close(); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /bukkit/src/main/java/dev/velix/imperat/BukkitPermissionResolver.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat; 2 | 3 | import dev.velix.imperat.resolvers.PermissionResolver; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | public final class BukkitPermissionResolver implements PermissionResolver { 8 | 9 | 10 | /** 11 | * @param source the source of the command (console or other) 12 | * @param permission the permission 13 | * @return whether this command source/sender has a specific permission 14 | */ 15 | @Override 16 | public boolean hasPermission( 17 | @NotNull BukkitSource source, 18 | @Nullable String permission 19 | ) { 20 | if (permission == null || permission.isEmpty()) return true; 21 | return source.origin().hasPermission(permission); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /bukkit/src/main/java/dev/velix/imperat/commodore/BrigadierUnsupportedException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of commodore, licensed under the MIT License. 3 | * 4 | * Copyright (c) lucko (Luck) 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in all 15 | * copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | * SOFTWARE. 24 | */ 25 | 26 | package dev.velix.imperat.commodore; 27 | 28 | /** 29 | * Exception thrown when the server does not support Brigadier. 30 | */ 31 | public final class BrigadierUnsupportedException extends UnsupportedOperationException { 32 | 33 | BrigadierUnsupportedException(String message) { 34 | super(message); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /bukkit/src/main/java/dev/velix/imperat/exception/UnknownOfflinePlayerException.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.exception; 2 | 3 | public class UnknownOfflinePlayerException extends ImperatException { 4 | 5 | private final String name; 6 | 7 | public UnknownOfflinePlayerException(final String name) { 8 | this.name = name; 9 | } 10 | 11 | public String getName() { 12 | return name; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /bukkit/src/main/java/dev/velix/imperat/exception/UnknownPlayerException.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.exception; 2 | 3 | public class UnknownPlayerException extends ImperatException { 4 | 5 | private final String name; 6 | 7 | public UnknownPlayerException(final String name) { 8 | this.name = name; 9 | } 10 | 11 | public String getName() { 12 | return name; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /bukkit/src/main/java/dev/velix/imperat/exception/UnknownWorldException.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.exception; 2 | 3 | public class UnknownWorldException extends ImperatException { 4 | 5 | private final String name; 6 | 7 | public UnknownWorldException(final String name) { 8 | this.name = name; 9 | } 10 | 11 | public String getName() { 12 | return name; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /bukkit/src/main/java/dev/velix/imperat/selector/EntityCondition.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.selector; 2 | 3 | import dev.velix.imperat.BukkitSource; 4 | import dev.velix.imperat.exception.ImperatException; 5 | import org.bukkit.entity.Entity; 6 | 7 | /** 8 | * This interface represents a condition to be evaluated on an entity based on certain criteria. 9 | * It is a functional interface and can be used as a lambda expression or method reference. 10 | */ 11 | @FunctionalInterface 12 | public interface EntityCondition { 13 | 14 | /** 15 | * Evaluates the given condition on an entity based on the source. 16 | * 17 | * @param sender the source initiating the test, typically a command sender 18 | * @param entity the entity to be evaluated against the condition 19 | * @return true if the entity meets the condition based on the source, false otherwise 20 | * @throws ImperatException if there is an error during the evaluation 21 | */ 22 | boolean test(BukkitSource sender, Entity entity) throws ImperatException; 23 | 24 | default EntityCondition and(EntityCondition other) { 25 | return (sender, entity) -> EntityCondition.this.test(sender, entity) && other.test(sender, entity); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /bukkit/src/main/java/dev/velix/imperat/selector/field/SelectionField.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.selector.field; 2 | 3 | import dev.velix.imperat.exception.ImperatException; 4 | 5 | import java.lang.reflect.Type; 6 | import java.util.List; 7 | 8 | //selection field for mojang entity/target-selectors in minecraft 9 | 10 | /** 11 | * The SelectionField interface defines methods for handling selection fields 12 | * with generic types. It provides functionalities to get the type of the value 13 | * and to parse a string representation into the field's value type. 14 | * 15 | * @param The type of the value that the selection field handles. 16 | */ 17 | public interface SelectionField extends SelectionFields { 18 | char VALUE_EQUALS = '=', SEPARATOR = ','; 19 | 20 | /** 21 | * Retrieves the name of the selection field. 22 | * 23 | * @return The name of the selection field. 24 | */ 25 | String getName(); 26 | 27 | /** 28 | * Retrieves the type of the value handled by the selection field. 29 | * 30 | * @return The type information wrapped in a TypeWrap object, which represents 31 | * the value type of the selection field. 32 | */ 33 | Type getValueType(); 34 | 35 | /** 36 | * Parses the given string representation of the value and converts it into the field's value type. 37 | * 38 | * @param value the string representation of the value to be parsed 39 | * @return the parsed value of the field's type 40 | * @throws ImperatException if the parsing fails 41 | */ 42 | V parseFieldValue(String value) throws ImperatException; 43 | 44 | 45 | /** 46 | * Retrieves a list of suggestions related to this selection field's value 47 | * 48 | * @return A list of suggestion strings. 49 | */ 50 | List getSuggestions(); 51 | } 52 | -------------------------------------------------------------------------------- /bukkit/src/main/java/dev/velix/imperat/selector/field/SelectionFields.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.selector.field; 2 | 3 | import dev.velix.imperat.selector.field.filters.PredicateField; 4 | import dev.velix.imperat.selector.field.operators.OperatorField; 5 | import dev.velix.imperat.BukkitUtil; 6 | 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | 10 | //minestom style ;D 11 | interface SelectionFields { 12 | 13 | //TODO add constants for each type of field 14 | 15 | Set> ALL = BukkitUtil.mergedSet( 16 | new HashSet<>(PredicateField.ALL_PREDICATES), 17 | new HashSet<>(OperatorField.ALL_OPERATORS), 18 | HashSet::new 19 | ); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /bukkit/src/main/java/dev/velix/imperat/selector/field/filters/GamemodeField.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.selector.field.filters; 2 | 3 | import dev.velix.imperat.BukkitSource; 4 | import dev.velix.imperat.context.internal.CommandInputStream; 5 | import dev.velix.imperat.exception.ImperatException; 6 | import dev.velix.imperat.exception.SourceException; 7 | import dev.velix.imperat.selector.EntityCondition; 8 | import dev.velix.imperat.util.TypeWrap; 9 | import org.bukkit.GameMode; 10 | import org.bukkit.entity.HumanEntity; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import java.util.Arrays; 14 | 15 | final class GamemodeField extends PredicateField { 16 | 17 | GamemodeField(String name) { 18 | super(name, TypeWrap.of(GameMode.class)); 19 | Arrays.stream(GameMode.values()) 20 | .map(GameMode::name) 21 | .map(String::toLowerCase) 22 | .forEach(suggestions::add); 23 | } 24 | 25 | @Override 26 | protected @NotNull EntityCondition getCondition(GameMode value, CommandInputStream commandInputStream) { 27 | return ((sender, entity) -> { 28 | if (!(entity instanceof HumanEntity humanEntity)) return false; 29 | return humanEntity.getGameMode() == value; 30 | }); 31 | } 32 | 33 | /** 34 | * Parses the given string representation of the value and converts it into the field's value type. 35 | * 36 | * @param value the string representation of the value to be parsed 37 | * @return the parsed value of the field's type 38 | * @throws ImperatException if the parsing fails 39 | */ 40 | @Override 41 | public GameMode parseFieldValue(String value) throws ImperatException { 42 | try { 43 | return GameMode.valueOf(value); 44 | } catch (EnumConstantNotPresentException ex) { 45 | throw new SourceException("Unknown gamemode '%s'", value); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /bukkit/src/main/java/dev/velix/imperat/selector/field/filters/LevelField.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.selector.field.filters; 2 | 3 | import dev.velix.imperat.BukkitSource; 4 | import dev.velix.imperat.context.internal.CommandInputStream; 5 | import dev.velix.imperat.exception.ImperatException; 6 | import dev.velix.imperat.selector.EntityCondition; 7 | import dev.velix.imperat.selector.field.NumericField; 8 | import dev.velix.imperat.selector.field.Range; 9 | import dev.velix.imperat.selector.field.RangedNumericField; 10 | import dev.velix.imperat.util.TypeWrap; 11 | import org.bukkit.entity.Player; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | final class LevelField extends PredicateField> { 15 | 16 | private final RangedNumericField numericField; 17 | 18 | LevelField(String name) { 19 | super(name, new TypeWrap<>() { 20 | }); 21 | this.numericField = RangedNumericField.of(NumericField.integerField(name)); 22 | } 23 | 24 | 25 | @Override 26 | protected @NotNull EntityCondition getCondition(Range value, CommandInputStream commandInputStream) { 27 | return ((sender, entity) -> { 28 | if (!(entity instanceof Player humanEntity)) return false; 29 | return value.isInRange(humanEntity.getLevel()); 30 | }); 31 | } 32 | 33 | /** 34 | * Parses the given string representation of the value and converts it into the field's value type. 35 | * 36 | * @param value the string representation of the value to be parsed 37 | * @return the parsed value of the field's type 38 | * @throws ImperatException if the parsing fails 39 | */ 40 | @Override 41 | public Range parseFieldValue(String value) throws ImperatException { 42 | return numericField.parseFieldValue(value); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /bukkit/src/main/java/dev/velix/imperat/selector/field/filters/NameField.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.selector.field.filters; 2 | 3 | import dev.velix.imperat.BukkitSource; 4 | import dev.velix.imperat.context.internal.CommandInputStream; 5 | import dev.velix.imperat.selector.EntityCondition; 6 | import dev.velix.imperat.util.TypeWrap; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | final class NameField extends PredicateField { 10 | /** 11 | * Constructs an AbstractField instance with the specified name and type. 12 | * 13 | * @param name The name of the selection field. 14 | */ 15 | NameField(String name) { 16 | super(name, TypeWrap.of(String.class)); 17 | } 18 | 19 | @Override 20 | protected @NotNull EntityCondition getCondition(String value, CommandInputStream commandInputStream) { 21 | return (sender, entity) -> entity.getName().equalsIgnoreCase(value); 22 | } 23 | 24 | /** 25 | * Parses the given string representation of the value and converts it into the field's value type. 26 | * 27 | * @param value the string representation of the value to be parsed 28 | * @return the parsed value of the field's type 29 | */ 30 | @Override 31 | public String parseFieldValue(String value) { 32 | return value; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /bukkit/src/main/java/dev/velix/imperat/selector/field/filters/PredicateFields.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.selector.field.filters; 2 | 3 | import dev.velix.imperat.selector.field.Range; 4 | import org.bukkit.GameMode; 5 | import org.bukkit.entity.EntityType; 6 | 7 | import java.util.Set; 8 | 9 | /** 10 | * The PredicateFields interface defines a set of named PredicateField objects 11 | * used for filtering entities based on various criteria. 12 | * These predicates can be used to generate filtering conditions 13 | * which can test entities against specified values. 14 | */ 15 | interface PredicateFields { 16 | 17 | 18 | //TODO predicates here 19 | PredicateField NAME = new NameField("name"); 20 | 21 | PredicateField TYPE = new TypeField("type"); 22 | 23 | PredicateField> LEVEL = new LevelField("level"); 24 | 25 | PredicateField> DISTANCE = new DistanceField("distance"); 26 | 27 | PredicateField GAMEMODE = new GamemodeField("gamemode"); 28 | 29 | PredicateField TAG = new TagField("tag"); 30 | 31 | 32 | Set> ALL_PREDICATES = Set.of( 33 | NAME, 34 | TYPE, 35 | LEVEL, 36 | DISTANCE, 37 | GAMEMODE, 38 | TAG 39 | ); 40 | //TODO add more 41 | 42 | } 43 | -------------------------------------------------------------------------------- /bukkit/src/main/java/dev/velix/imperat/selector/field/filters/TagField.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.selector.field.filters; 2 | 3 | import dev.velix.imperat.BukkitSource; 4 | import dev.velix.imperat.context.internal.CommandInputStream; 5 | import dev.velix.imperat.selector.EntityCondition; 6 | import dev.velix.imperat.util.TypeWrap; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | final class TagField extends PredicateField { 10 | 11 | TagField(String name) { 12 | super(name, TypeWrap.of(String.class)); 13 | } 14 | 15 | 16 | @Override 17 | protected @NotNull EntityCondition getCondition(String value, CommandInputStream commandInputStream) { 18 | return ((sender, entity) -> entity.hasMetadata(value)); 19 | } 20 | 21 | /** 22 | * Parses the given string representation of the value and converts it into the field's value type. 23 | * 24 | * @param value the string representation of the value to be parsed 25 | * @return the parsed value of the field's type 26 | */ 27 | @Override 28 | public String parseFieldValue(String value) { 29 | return value; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /bukkit/src/main/java/dev/velix/imperat/selector/field/filters/TypeField.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.selector.field.filters; 2 | 3 | import dev.velix.imperat.BukkitSource; 4 | import dev.velix.imperat.context.internal.CommandInputStream; 5 | import dev.velix.imperat.exception.ImperatException; 6 | import dev.velix.imperat.exception.SourceException; 7 | import dev.velix.imperat.selector.EntityCondition; 8 | import dev.velix.imperat.util.TypeWrap; 9 | import org.bukkit.entity.EntityType; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | import java.util.Arrays; 13 | 14 | final class TypeField extends PredicateField { 15 | /** 16 | * Constructs an AbstractField instance with the specified name and type. 17 | * 18 | * @param name The name of the selection field. 19 | */ 20 | TypeField(String name) { 21 | super(name, TypeWrap.of(EntityType.class)); 22 | Arrays.stream(EntityType.values()) 23 | .map(EntityType::name) 24 | .map(String::toLowerCase) 25 | .forEach(suggestions::add); 26 | } 27 | 28 | @Override 29 | protected @NotNull EntityCondition getCondition(EntityType value, CommandInputStream commandInputStream) { 30 | return (sender, entity) -> entity.getType() == value; 31 | } 32 | 33 | /** 34 | * Parses the given string representation of the value and converts it into the field's value type. 35 | * 36 | * @param value the string representation of the value to be parsed 37 | * @return the parsed value of the field's type 38 | * @throws ImperatException if the parsing fails 39 | */ 40 | @Override 41 | public EntityType parseFieldValue(String value) throws ImperatException { 42 | try { 43 | return EntityType.valueOf(value.toUpperCase()); 44 | } catch (EnumConstantNotPresentException ex) { 45 | throw new SourceException("Unknown type '%s'", value); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /bukkit/src/main/java/dev/velix/imperat/selector/field/operators/LimitOperatorField.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.selector.field.operators; 2 | 3 | import dev.velix.imperat.exception.ImperatException; 4 | import dev.velix.imperat.selector.field.NumericField; 5 | import dev.velix.imperat.util.TypeWrap; 6 | import org.bukkit.entity.Entity; 7 | 8 | import java.util.List; 9 | 10 | public final class LimitOperatorField extends OperatorField { 11 | 12 | private final NumericField numericField; 13 | 14 | LimitOperatorField(String name) { 15 | super(name, TypeWrap.of(Integer.class)); 16 | this.numericField = NumericField.integerField(name); 17 | } 18 | 19 | /** 20 | * Parses the given string representation of the value and converts it into the field's value type. 21 | * 22 | * @param value the string representation of the value to be parsed 23 | * @return the parsed value of the field's type 24 | * @throws ImperatException if the parsing fails 25 | */ 26 | @Override 27 | public Integer parseFieldValue(String value) throws ImperatException { 28 | return numericField.parseFieldValue(value); 29 | } 30 | 31 | /** 32 | * Performs an operation on the specified value and a list of entities. 33 | * 34 | * @param value the value to be operated on 35 | * @param entities the list of entities to be processed 36 | */ 37 | @Override 38 | public void operate(Integer value, List entities) { 39 | if (entities.size() < value) return; 40 | 41 | int diff = entities.size() - value; 42 | for (int i = 0; i < diff; i++) 43 | entities.remove(entities.size() - 1); 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /bukkit/src/main/java/dev/velix/imperat/selector/field/operators/OperatorField.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.selector.field.operators; 2 | 3 | 4 | import dev.velix.imperat.exception.ImperatException; 5 | import dev.velix.imperat.selector.field.AbstractField; 6 | import dev.velix.imperat.util.TypeWrap; 7 | import org.bukkit.entity.Entity; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * OperatorField is an abstract base class that extends AbstractField. 13 | * It is designed to add additional operational functionality to the field it wraps. 14 | * 15 | * @param The type of the value that this field handles. 16 | */ 17 | public abstract class OperatorField extends AbstractField implements OperatorFields { 18 | 19 | protected OperatorField(String name, TypeWrap type) { 20 | super(name, type); 21 | } 22 | 23 | /** 24 | * Parses the given string representation of the value and converts it into the field's value type. 25 | * 26 | * @param value the string representation of the value to be parsed 27 | * @return the parsed value of the field's type 28 | * @throws ImperatException if the parsing fails 29 | */ 30 | @Override 31 | public abstract V parseFieldValue(String value) throws ImperatException; 32 | 33 | /** 34 | * Performs an operation on the specified value and a list of entities. 35 | * 36 | * @param value the value to be operated on 37 | * @param entities the list of entities to be processed 38 | */ 39 | public abstract void operate(V value, List entities); 40 | } 41 | -------------------------------------------------------------------------------- /bukkit/src/main/java/dev/velix/imperat/selector/field/operators/OperatorFields.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.selector.field.operators; 2 | 3 | import java.util.Set; 4 | 5 | interface OperatorFields { 6 | 7 | OperatorField SORT = new SortOperatorField("sort"); 8 | 9 | OperatorField LIMIT = new LimitOperatorField("limit"); 10 | 11 | Set> ALL_OPERATORS = Set.of( 12 | SORT, 13 | LIMIT 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /bukkit/src/main/java/dev/velix/imperat/selector/field/operators/SortOperatorField.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.selector.field.operators; 2 | 3 | import dev.velix.imperat.exception.ImperatException; 4 | import dev.velix.imperat.exception.SourceException; 5 | import dev.velix.imperat.util.TypeWrap; 6 | import org.bukkit.entity.Entity; 7 | 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | final class SortOperatorField extends OperatorField { 12 | 13 | SortOperatorField(String name) { 14 | super(name, TypeWrap.of(SortOption.class)); 15 | Arrays.stream(SortOption.values()) 16 | .map(SortOption::name) 17 | .map(String::toLowerCase) 18 | .forEach(suggestions::add); 19 | } 20 | 21 | /** 22 | * Parses the given string representation of the value and converts it into the field's value type. 23 | * 24 | * @param value the string representation of the value to be parsed 25 | * @return the parsed value of the field's type 26 | * @throws ImperatException if the parsing fails 27 | */ 28 | @Override 29 | public SortOption parseFieldValue(String value) throws ImperatException { 30 | for (SortOption option : SortOption.values()) { 31 | if (option.name().equalsIgnoreCase(name)) { 32 | return option; 33 | } 34 | } 35 | throw new SourceException("Unknown sort option '%s'", name); 36 | } 37 | 38 | /** 39 | * Performs an operation on the specified value and a list of entities. 40 | * 41 | * @param value the value to be operated on 42 | * @param entities the list of entities to be processed 43 | */ 44 | @Override 45 | public void operate(SortOption value, List entities) { 46 | value.sort(entities); 47 | } 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /bukkit/src/main/java/dev/velix/imperat/selector/field/operators/SortOption.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.selector.field.operators; 2 | 3 | import org.bukkit.entity.Entity; 4 | 5 | import java.util.Collections; 6 | import java.util.Comparator; 7 | import java.util.List; 8 | import java.util.Random; 9 | 10 | public enum SortOption { 11 | 12 | NEAREST(Comparator.comparingDouble(e -> e.getLocation().distanceSquared(e.getLocation()))), 13 | 14 | FURTHEST((e1, e2) -> 15 | Double.compare(e2.getLocation().distanceSquared(e2.getLocation()), e1.getLocation().distanceSquared(e1.getLocation()))), 16 | 17 | RANDOM(Comparator.comparing(e -> Math.random())) /*not sure if it works*/, 18 | 19 | ARBITRARY(null); 20 | 21 | private final static Random RANDOM_GENERATOR = new Random(); 22 | 23 | private final Comparator comparator; 24 | 25 | SortOption(Comparator comparator) { 26 | this.comparator = comparator; 27 | } 28 | 29 | void sort(List entities) { 30 | if (entities.isEmpty()) return; 31 | if (this == RANDOM) { 32 | Collections.shuffle(entities, RANDOM_GENERATOR); 33 | return; 34 | } 35 | if (comparator != null) { 36 | entities.sort(comparator); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /bukkit/src/main/java/dev/velix/imperat/selector/field/provider/FieldProvider.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.selector.field.provider; 2 | 3 | import dev.velix.imperat.BukkitSource; 4 | import dev.velix.imperat.context.internal.CommandInputStream; 5 | import dev.velix.imperat.selector.field.SelectionField; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | /** 9 | *

10 | * The FieldProvider interface is designed to define a method for providing 11 | * selection fields based on their names. Implementations of this interface 12 | * are responsible for defining the logic to retrieve a field corresponding 13 | * to the provided name. 14 | * The purpose is to allow dynamic retrieval of fields, which may be used 15 | * for various purposes such as filtering or selecting specific data 16 | * attributes. 17 | *

18 | */ 19 | public sealed interface FieldProvider permits FieldProviderImpl { 20 | 21 | FieldProvider INSTANCE = new FieldProviderImpl(); 22 | 23 | /** 24 | * Provides a selection field based on the given name. 25 | * 26 | * @param The type of the value that the selection field handles. 27 | * @param name The name of the selection field to retrieve. 28 | * @return The selection field corresponding to the provided name, or null if no such field exists. 29 | */ 30 | @Nullable SelectionField provideField(String name, CommandInputStream commandInputStream); 31 | //TODO sync this with the old criteria parsing system. 32 | 33 | } 34 | -------------------------------------------------------------------------------- /bukkit/src/main/java/dev/velix/imperat/selector/field/provider/FieldProviderImpl.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.selector.field.provider; 2 | 3 | import dev.velix.imperat.BukkitSource; 4 | import dev.velix.imperat.context.internal.CommandInputStream; 5 | import dev.velix.imperat.selector.field.SelectionField; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | final class FieldProviderImpl implements FieldProvider { 9 | 10 | @Override 11 | @SuppressWarnings("unchecked") 12 | public @Nullable SelectionField provideField(String name, CommandInputStream commandInputStream) { 13 | for (var field : SelectionField.ALL) { 14 | if (field.getName().equalsIgnoreCase(name)) { 15 | return (SelectionField) field; 16 | } 17 | } 18 | return null; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /bungee/build.gradle: -------------------------------------------------------------------------------- 1 | repositories { 2 | mavenCentral() 3 | maven { 4 | url "https://libraries.minecraft.net" 5 | } 6 | } 7 | 8 | dependencies { 9 | api project(":adventure") 10 | 11 | compileOnly project(":core") 12 | compileOnly "${kyoriPlatform(KyoriModule.BUNGEE)}" 13 | compileOnly("net.md-5:bungeecord-api:1.21-R0.2") 14 | } 15 | -------------------------------------------------------------------------------- /bungee/src/main/java/dev/velix/imperat/InternalBungeeCommand.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat; 2 | 3 | import dev.velix.imperat.command.Command; 4 | import net.md_5.bungee.api.CommandSender; 5 | import net.md_5.bungee.api.plugin.TabExecutor; 6 | 7 | 8 | final class InternalBungeeCommand extends net.md_5.bungee.api.plugin.Command implements TabExecutor { 9 | 10 | private final BungeeImperat bungeeCommandDispatcher; 11 | private final Command bungeeCommand; 12 | 13 | InternalBungeeCommand( 14 | BungeeImperat commandDispatcher, 15 | Command bungeeCommand 16 | ) { 17 | super( 18 | bungeeCommand.name(), 19 | bungeeCommand.permission(), 20 | bungeeCommand.aliases().toArray(new String[0]) 21 | ); 22 | this.bungeeCommandDispatcher = commandDispatcher; 23 | this.bungeeCommand = bungeeCommand; 24 | } 25 | 26 | @Override 27 | public void execute(CommandSender sender, String[] args) { 28 | bungeeCommandDispatcher.dispatch( 29 | bungeeCommandDispatcher.wrapSender(sender), 30 | bungeeCommand.name(), 31 | args 32 | ); 33 | } 34 | 35 | 36 | @Override 37 | public Iterable onTabComplete( 38 | CommandSender sender, 39 | String[] args 40 | ) { 41 | return bungeeCommandDispatcher.autoComplete( 42 | bungeeCommand, 43 | bungeeCommandDispatcher.wrapSender(sender), 44 | bungeeCommand.name(), 45 | args 46 | ).join(); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /bungee/src/main/java/dev/velix/imperat/adventure/BungeeAdventure.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.adventure; 2 | 3 | import net.kyori.adventure.audience.Audience; 4 | import net.kyori.adventure.platform.bungeecord.BungeeAudiences; 5 | import net.md_5.bungee.api.CommandSender; 6 | import net.md_5.bungee.api.plugin.Plugin; 7 | 8 | public final class BungeeAdventure implements AdventureProvider { 9 | 10 | private final BungeeAudiences audiences; 11 | 12 | public BungeeAdventure(final Plugin plugin) { 13 | this.audiences = BungeeAudiences.create(plugin); 14 | } 15 | 16 | @Override 17 | public Audience audience(final CommandSender sender) { 18 | return audiences.sender(sender); 19 | } 20 | 21 | @Override 22 | public void close() { 23 | this.audiences.close(); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /bungee/src/main/java/dev/velix/imperat/exception/UnknownPlayerException.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.exception; 2 | 3 | public class UnknownPlayerException extends ImperatException { 4 | 5 | private final String name; 6 | 7 | public UnknownPlayerException(final String name) { 8 | this.name = name; 9 | } 10 | 11 | public String getName() { 12 | return name; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /bungee/src/main/java/dev/velix/imperat/resolvers/BungeePermissionResolver.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.resolvers; 2 | 3 | import dev.velix.imperat.BungeeSource; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | public class BungeePermissionResolver implements PermissionResolver { 8 | 9 | @Override 10 | public boolean hasPermission( 11 | @NotNull BungeeSource source, 12 | @Nullable String permission 13 | ) { 14 | return source.origin().hasPermission(permission); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /cli/build.gradle: -------------------------------------------------------------------------------- 1 | repositories { 2 | mavenCentral() 3 | } 4 | 5 | dependencies { 6 | compileOnly project(":core") 7 | compileOnly 'org.jetbrains:annotations:24.0.0' 8 | } 9 | -------------------------------------------------------------------------------- /cli/src/main/java/dev/velix/imperat/CommandLineConfigBuilder.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.io.InputStream; 6 | import java.io.PrintStream; 7 | 8 | public final class CommandLineConfigBuilder extends ConfigBuilder { 9 | 10 | private final InputStream inputStream; 11 | 12 | CommandLineConfigBuilder(InputStream inputStream) { 13 | this.inputStream = inputStream; 14 | config.registerSourceResolver(PrintStream.class, ConsoleSource::origin); 15 | } 16 | 17 | /** 18 | * Builds and returns a configured CommandLineImperat instance. 19 | * 20 | * @return the CommandLineImperat instance 21 | */ 22 | @Override 23 | public @NotNull CommandLineImperat build() { 24 | return new CommandLineImperat(inputStream, this.config); 25 | } 26 | } -------------------------------------------------------------------------------- /cli/src/main/java/dev/velix/imperat/ConsoleSource.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat; 2 | 3 | import dev.velix.imperat.context.Source; 4 | 5 | import java.io.PrintStream; 6 | 7 | public class ConsoleSource implements Source { 8 | 9 | private final PrintStream outputStream; 10 | 11 | public ConsoleSource(final PrintStream outputStream) { 12 | this.outputStream = outputStream; 13 | } 14 | 15 | @Override 16 | public String name() { 17 | return "CONSOLE"; 18 | } 19 | 20 | @Override 21 | public PrintStream origin() { 22 | return outputStream; 23 | } 24 | 25 | @Override 26 | public void reply(final String message) { 27 | outputStream.println(message); 28 | } 29 | 30 | @Override 31 | public void warn(final String message) { 32 | outputStream.println("[WARN] " + message); 33 | } 34 | 35 | @Override 36 | public void error(final String message) { 37 | outputStream.println("[ERROR] " + message); 38 | } 39 | 40 | @Override 41 | public boolean isConsole() { 42 | return true; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /core/build.gradle: -------------------------------------------------------------------------------- 1 | repositories { 2 | mavenCentral() 3 | } 4 | 5 | dependencies { 6 | compileOnly "org.jetbrains:annotations:24.1.0" 7 | annotationProcessor "org.jetbrains:annotations:24.1.0" 8 | 9 | api 'org.ow2.asm:asm:9.6' 10 | 11 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' 12 | testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.8.1' 13 | 14 | testImplementation("org.jetbrains:annotations:24.1.0") 15 | testAnnotationProcessor("org.jetbrains:annotations:24.1.0") 16 | } 17 | 18 | test { 19 | useJUnitPlatform() 20 | } 21 | 22 | java { 23 | toolchain.languageVersion.set(JavaLanguageVersion.of(17)) 24 | } -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/CommandHelpHandler.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat; 2 | 3 | import dev.velix.imperat.context.Source; 4 | import dev.velix.imperat.help.HelpProvider; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | public sealed interface CommandHelpHandler permits ImperatConfig { 8 | 9 | 10 | /** 11 | * @return The template for showing help 12 | */ 13 | @Nullable 14 | HelpProvider getHelpProvider(); 15 | 16 | /** 17 | * Set the help template to use 18 | * 19 | * @param template the help template 20 | */ 21 | void setHelpProvider(@Nullable HelpProvider template); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/FlagRegistrar.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat; 2 | 3 | import dev.velix.imperat.context.FlagData; 4 | import dev.velix.imperat.context.Source; 5 | 6 | import java.util.Optional; 7 | import java.util.Set; 8 | 9 | /** 10 | * The {@code FlagRegistrar} interface is responsible for managing the registration and retrieval 11 | * of free flags in the Imperat command framework. Free flags are flags that can be used anywhere 12 | * in the command syntax, without being tied to a specific position or index. 13 | * 14 | * @param the type of source that extends the {@code Source} class, representing the origin 15 | * or context of the command (e.g., a user, a system, etc.). 16 | */ 17 | public interface FlagRegistrar { 18 | 19 | /** 20 | * Registers a new free flag in the system. 21 | * 22 | * @param flagBuilder the {@link FlagData} object containing the data required to define 23 | * and register the flag. This includes the flag's name, aliases, 24 | * description, and any associated logic or behavior. 25 | */ 26 | void registerFlag(FlagData flagBuilder); 27 | 28 | /** 29 | * Retrieves a registered flag based on its raw input (e.g., a string provided by the user 30 | * in a command). 31 | * 32 | * @param raw the raw input string that may represent a flag (e.g., {@code --help}, {@code -h}). 33 | * @return an {@link Optional} containing the {@link FlagData} object if a matching flag is found. 34 | * If no match is found, the {@link Optional} will be empty. 35 | */ 36 | Optional> getFlagFromRaw(String raw); 37 | 38 | /** 39 | * Retrieves all registered flags in the system. 40 | * 41 | * @return a {@link Set} containing all {@link FlagData} objects that have been registered. 42 | */ 43 | Set> getRegisteredFlags(); 44 | } 45 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/ProcessorRegistrar.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat; 2 | 3 | import dev.velix.imperat.command.processors.CommandPostProcessor; 4 | import dev.velix.imperat.command.processors.CommandPreProcessor; 5 | import dev.velix.imperat.command.processors.CommandProcessingChain; 6 | import dev.velix.imperat.context.Source; 7 | 8 | /** 9 | * Represents a registrar that allows the configuration and management of pre-processing 10 | * and post-processing chains of command processors for a given source type. 11 | * 12 | * @param the type of the source associated with command processing 13 | */ 14 | public sealed interface ProcessorRegistrar permits ImperatConfig { 15 | 16 | /** 17 | * Sets the whole pre-processing chain 18 | * @param chain the chain to set 19 | */ 20 | void setPreProcessorsChain(CommandProcessingChain> chain); 21 | 22 | /** 23 | * Sets the whole post-processing chain 24 | * @param chain the chain to set 25 | */ 26 | void setPostProcessorsChain(CommandProcessingChain> chain); 27 | 28 | /** 29 | * @return gets the pre-processors in the chain of execution 30 | * @see CommandPreProcessor 31 | */ 32 | CommandProcessingChain> getPreProcessors(); 33 | 34 | /** 35 | * @return gets the post-processors in the chain of execution 36 | * @see CommandPostProcessor 37 | */ 38 | CommandProcessingChain> getPostProcessors(); 39 | 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/SourceWrapper.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat; 2 | 3 | import dev.velix.imperat.context.Source; 4 | 5 | import java.lang.reflect.Type; 6 | 7 | /** 8 | * A sealed interface representing the abstraction for wrapping 9 | * and handling sources or senders of commands, and validating 10 | * the compatibility of a type as a sender. 11 | * 12 | * @param the type that extends the {@link Source}, representing the command sender 13 | */ 14 | public sealed interface SourceWrapper permits Imperat { 15 | /** 16 | * Wraps the sender into a built-in command-sender valueType 17 | * 18 | * @param sender the sender's actual value 19 | * @return the wrapped command-sender valueType 20 | */ 21 | S wrapSender(Object sender); 22 | 23 | /** 24 | * Checks whether the valueType can be a command sender 25 | * 26 | * @param type the valueType 27 | * @return whether the valueType can be a command sender 28 | */ 29 | boolean canBeSender(Type type); 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/Async.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 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) 10 | public @interface Async { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/Command.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 2 | 3 | import org.jetbrains.annotations.NotNull; 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 | @Retention(RetentionPolicy.RUNTIME) 11 | @Target({ElementType.TYPE, ElementType.METHOD}) 12 | public @interface Command { 13 | 14 | /** 15 | * @return The names of this command 16 | * The first element is the unique name of the command 17 | * others are going to be considered the aliases 18 | */ 19 | @NotNull String[] value(); 20 | 21 | /** 22 | * @return Whether to ignore the permission checks 23 | * while auto-completing/suggesting or not 24 | */ 25 | boolean skipSuggestionsChecks() default false; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/ContextResolved.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 2 | 3 | import org.jetbrains.annotations.ApiStatus; 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 | @Retention(RetentionPolicy.RUNTIME) 11 | @Target({ElementType.TYPE, ElementType.PARAMETER}) 12 | @ApiStatus.AvailableSince("1.9.0") 13 | public @interface ContextResolved { 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/Cooldown.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 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 | import java.util.concurrent.TimeUnit; 8 | 9 | @Retention(RetentionPolicy.RUNTIME) 10 | @Target(ElementType.METHOD) 11 | public @interface Cooldown { 12 | 13 | long value(); 14 | 15 | TimeUnit unit(); 16 | 17 | String permission() default ""; 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/Default.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 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, ElementType.PARAMETER}) 10 | public @interface Default { 11 | 12 | String value(); 13 | } 14 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/DefaultProvider.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 2 | 3 | import dev.velix.imperat.command.parameters.OptionalValueSupplier; 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 | @Retention(RetentionPolicy.RUNTIME) 11 | @Target({ElementType.TYPE, ElementType.PARAMETER}) 12 | public @interface DefaultProvider { 13 | 14 | Class value(); 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/Dependency.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 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.FIELD}) 10 | public @interface Dependency { 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/Description.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 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, ElementType.METHOD, ElementType.PARAMETER}) 10 | public @interface Description { 11 | 12 | /** 13 | * @return the description of the command 14 | */ 15 | String value(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/Flag.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 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, ElementType.PARAMETER}) 10 | public @interface Flag { 11 | 12 | String[] value(); 13 | 14 | boolean free() default false; 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/GlobalAttachmentMode.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 2 | 3 | import dev.velix.imperat.command.AttachmentMode; 4 | import org.jetbrains.annotations.ApiStatus; 5 | 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | @Retention(RetentionPolicy.RUNTIME) 12 | @Target({ElementType.TYPE, ElementType.METHOD}) 13 | @ApiStatus.AvailableSince("1.9.0") 14 | public @interface GlobalAttachmentMode { 15 | 16 | /** 17 | * Retrieves the {@link AttachmentMode} associated with this annotation. 18 | * @return the attachment mode that defines how a subcommand integrates with the usages of a command. 19 | */ 20 | AttachmentMode value(); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/Greedy.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 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, ElementType.PARAMETER}) 10 | public @interface Greedy { 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/Help.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 2 | 3 | import dev.velix.imperat.help.HelpProvider; 4 | import org.jetbrains.annotations.ApiStatus; 5 | 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | @Retention(RetentionPolicy.RUNTIME) 12 | @Target({ElementType.TYPE, ElementType.METHOD}) 13 | @ApiStatus.AvailableSince("1.9.0") 14 | public @interface Help { 15 | 16 | /** 17 | * Specifies the class to be used as the help provider. 18 | * 19 | * @return a class extending {@code HelpProvider} that defines the help functionality. 20 | */ 21 | Class> value(); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/Inherit.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 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 Inherit { 11 | 12 | /** 13 | * @return the children subcommands 14 | */ 15 | Class[] value(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/Named.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 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, ElementType.PARAMETER}) 10 | public @interface Named { 11 | 12 | String value() default ""; 13 | } 14 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/Optional.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 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, ElementType.PARAMETER}) 10 | public @interface Optional { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/Permission.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 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, ElementType.METHOD, ElementType.PARAMETER}) 10 | public @interface Permission { 11 | 12 | /** 13 | * @return the permission 14 | */ 15 | String value(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/PostProcessor.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 2 | 3 | import dev.velix.imperat.command.processors.CommandPostProcessor; 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 | @Retention(RetentionPolicy.RUNTIME) 11 | @Target({ElementType.TYPE, ElementType.METHOD}) 12 | public @interface PostProcessor { 13 | 14 | Class>[] value(); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/PreProcessor.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 2 | 3 | import dev.velix.imperat.command.processors.CommandPreProcessor; 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 | @Retention(RetentionPolicy.RUNTIME) 11 | @Target({ElementType.TYPE, ElementType.METHOD}) 12 | public @interface PreProcessor { 13 | 14 | Class>[] value(); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/Range.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 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, ElementType.PARAMETER}) 10 | public @interface Range { 11 | 12 | double min() default Double.MIN_VALUE; 13 | 14 | double max() default Double.MAX_VALUE; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/SubCommand.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 2 | 3 | import dev.velix.imperat.command.AttachmentMode; 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 | @Retention(RetentionPolicy.RUNTIME) 11 | @Target({ElementType.TYPE, ElementType.METHOD}) 12 | public @interface SubCommand { 13 | 14 | String[] value(); 15 | 16 | boolean skipSuggestionsChecks() default false; 17 | 18 | AttachmentMode attachment() default AttachmentMode.MAIN; 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/Suggest.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 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 Suggest { 11 | 12 | String[] value(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/SuggestionProvider.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 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 SuggestionProvider { 11 | 12 | String value(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/Switch.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 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 | String[] value(); 13 | 14 | boolean free() default false; 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/Usage.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 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) 10 | public @interface Usage { 11 | 12 | //empty 13 | 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/Values.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations; 2 | 3 | import org.jetbrains.annotations.ApiStatus; 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 | @Retention(RetentionPolicy.RUNTIME) 11 | @Target(ElementType.PARAMETER) 12 | @ApiStatus.AvailableSince("1.7.6") 13 | public @interface Values { 14 | 15 | /** 16 | * Specific allowed values 17 | * @return the only allowed input values. 18 | */ 19 | String[] value(); 20 | 21 | /** 22 | * Whether the values should be checked upon for 23 | * being exactly the SAME as the input (including upper and lower case) 24 | *

25 | * example: 26 | * values: ["Hello"] 27 | * input: "hello" 28 | *

29 | * if this method returns true, then the input wouldn't be allowed and vice versa. 30 | * 31 | * @return if checks on values should allow difference in lower/upper cases between input and the values... 32 | */ 33 | boolean caseSensitive() default true; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/base/AnnotationReader.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations.base; 2 | 3 | import dev.velix.imperat.Imperat; 4 | import dev.velix.imperat.annotations.base.element.ClassElement; 5 | import dev.velix.imperat.annotations.base.element.CommandClassVisitor; 6 | import dev.velix.imperat.annotations.base.element.MethodElement; 7 | import dev.velix.imperat.annotations.base.element.RootCommandClass; 8 | import dev.velix.imperat.annotations.base.element.selector.ElementSelector; 9 | import dev.velix.imperat.context.Source; 10 | import org.jetbrains.annotations.ApiStatus; 11 | 12 | /** 13 | * Represents a class that has a single responsibility of 14 | * reading the annotation components of an annotated command class 15 | * 16 | * @author Mqzen 17 | */ 18 | @ApiStatus.AvailableSince("1.0.0") 19 | public interface AnnotationReader { 20 | 21 | static AnnotationReader read( 22 | Imperat imperat, 23 | ElementSelector methodSelector, 24 | AnnotationParser parser, 25 | Object target 26 | ) { 27 | return new AnnotationReaderImpl<>(imperat, methodSelector, parser, target); 28 | } 29 | 30 | RootCommandClass getRootClass(); 31 | 32 | ClassElement getParsedClass(); 33 | 34 | void accept(CommandClassVisitor visitor); 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/base/AnnotationReplacer.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations.base; 2 | 3 | import dev.velix.imperat.annotations.Command; 4 | import dev.velix.imperat.annotations.base.element.ParseElement; 5 | import org.jetbrains.annotations.ApiStatus; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.lang.annotation.Annotation; 9 | import java.util.Collection; 10 | 11 | /** 12 | * Replaces a custom annotation made by the user, with annotations 13 | * made from the basic pre-made annotations such as {@link Command} 14 | * 15 | * @param the valueType of annotation to replace with other annotations 16 | */ 17 | @ApiStatus.AvailableSince("1.0.0") 18 | public interface AnnotationReplacer { 19 | 20 | /** 21 | * The annotation to replace 22 | * 23 | * @param element the loaded element holding this annotation 24 | * @param annotation the annotation 25 | * @return the annotations replaced by this annotation 26 | */ 27 | @NotNull 28 | Collection replace(@NotNull ParseElement element, A annotation); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/base/element/CommandClassVisitor.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations.base.element; 2 | 3 | import dev.velix.imperat.Imperat; 4 | import dev.velix.imperat.annotations.base.AnnotationParser; 5 | import dev.velix.imperat.annotations.base.element.selector.ElementSelector; 6 | import dev.velix.imperat.annotations.base.element.selector.MethodRules; 7 | import dev.velix.imperat.command.Command; 8 | import dev.velix.imperat.context.Source; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | import java.util.Set; 12 | 13 | /** 14 | * Visits each element in a {@link ClassElement} 15 | * 16 | * @param the command source 17 | */ 18 | public abstract class CommandClassVisitor { 19 | 20 | protected final Imperat imperat; 21 | protected final AnnotationParser parser; 22 | protected final ElementSelector methodSelector; 23 | 24 | protected CommandClassVisitor( 25 | Imperat imperat, 26 | AnnotationParser parser, 27 | ElementSelector methodSelector 28 | ) { 29 | this.imperat = imperat; 30 | this.parser = parser; 31 | this.methodSelector = methodSelector; 32 | } 33 | 34 | public abstract Set> visitCommandClass( 35 | @NotNull ClassElement clazz 36 | ); 37 | 38 | public static CommandClassVisitor newSimpleVisitor( 39 | Imperat imperat, 40 | AnnotationParser parser 41 | ) { 42 | return new SimpleCommandClassVisitor<>( 43 | imperat, 44 | parser, 45 | ElementSelector.create() 46 | .addRule(MethodRules.HAS_KNOWN_SENDER) 47 | //.addRule(MethodRules.HAS_LEAST_ONLY_ONE_MAIN_ANNOTATION) 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/base/element/RootCommandClass.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations.base.element; 2 | 3 | import dev.velix.imperat.command.Command; 4 | import dev.velix.imperat.context.Source; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import java.util.Objects; 8 | 9 | /** 10 | * Represents a root command class information 11 | * 12 | * @param the command source valueType 13 | */ 14 | public final class RootCommandClass { 15 | 16 | private final Class proxyClass; 17 | private final Object proxyInstance; 18 | private @Nullable Command commandLoaded; 19 | 20 | public RootCommandClass(Class proxyClass, Object proxyInstance) { 21 | this.proxyClass = proxyClass; 22 | this.proxyInstance = proxyInstance; 23 | } 24 | 25 | public void setRootCommand(Command commandLoaded) { 26 | this.commandLoaded = commandLoaded; 27 | } 28 | 29 | public boolean isCommand() { 30 | return commandLoaded != null; 31 | } 32 | 33 | public Class proxyClass() { 34 | return proxyClass; 35 | } 36 | 37 | public Object proxyInstance() { 38 | return proxyInstance; 39 | } 40 | 41 | 42 | @Override 43 | public boolean equals(Object obj) { 44 | if (obj == this) return true; 45 | if (obj == null || obj.getClass() != this.getClass()) return false; 46 | var that = (RootCommandClass) obj; 47 | return Objects.equals(this.proxyClass, that.proxyClass) && 48 | Objects.equals(this.proxyInstance, that.proxyInstance); 49 | } 50 | 51 | @Override 52 | public int hashCode() { 53 | return Objects.hash(proxyClass, proxyInstance); 54 | } 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/base/element/selector/ElementSelector.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations.base.element.selector; 2 | 3 | import dev.velix.imperat.Imperat; 4 | import dev.velix.imperat.annotations.base.AnnotationParser; 5 | import dev.velix.imperat.annotations.base.element.ParseElement; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Selects a {@link ParseElement} based on a specific list of {@link Rule} 12 | * 13 | * @param the valueType of {@link ParseElement} to select 14 | */ 15 | public sealed interface ElementSelector> permits SimpleElementSelector { 16 | 17 | /** 18 | * @return the rules that all must be followed to select a single {@link ParseElement} 19 | * * during annotation parsing. 20 | * @see Rule 21 | */ 22 | @NotNull 23 | List> getRules(); 24 | 25 | default void verifyWithFail(Rule rule, Imperat imperat, AnnotationParser registry, E element) { 26 | if (!rule.test(imperat, registry, element)) { 27 | rule.onFailure(registry, element); 28 | } 29 | } 30 | 31 | default ElementSelector addRule(Rule rule) { 32 | getRules().add(rule); 33 | return this; 34 | } 35 | 36 | default void removeRule(Rule rule) { 37 | getRules().remove(rule); 38 | } 39 | 40 | default boolean canBeSelected(Imperat imperat, AnnotationParser parse, E element, boolean fail) { 41 | for (var rule : getRules()) { 42 | if (!rule.test(imperat, parse, element)) { 43 | if (fail) { 44 | rule.onFailure(parse, element); 45 | } 46 | return false; 47 | } 48 | } 49 | return true; 50 | } 51 | 52 | static > ElementSelector create() { 53 | return new SimpleElementSelector<>(); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/base/element/selector/RuleCondition.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations.base.element.selector; 2 | 3 | import dev.velix.imperat.Imperat; 4 | import dev.velix.imperat.annotations.base.AnnotationParser; 5 | 6 | @FunctionalInterface 7 | @SuppressWarnings("rawtypes") 8 | public interface RuleCondition { 9 | boolean test(Imperat imperat, AnnotationParser parser, E element); 10 | 11 | default RuleCondition and(RuleCondition other) { 12 | if (other == null) return this; 13 | return (imperat, registry, element) -> this.test(imperat, registry, element) 14 | && other.test(imperat, registry, element); 15 | } 16 | 17 | default RuleCondition or(RuleCondition other) { 18 | if (other == null) return this; 19 | return (imperat, registry, element) -> this.test(imperat, registry, element) 20 | || other.test(imperat, registry, element); 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/base/element/selector/SimpleElementSelector.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations.base.element.selector; 2 | 3 | import dev.velix.imperat.annotations.base.element.ParseElement; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | final class SimpleElementSelector> implements ElementSelector { 10 | 11 | private final List> rules = new ArrayList<>(); 12 | 13 | SimpleElementSelector() { 14 | 15 | } 16 | 17 | @Override 18 | public @NotNull List> getRules() { 19 | return rules; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/annotations/parameters/AnnotatedParameter.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.annotations.parameters; 2 | 3 | import dev.velix.imperat.command.parameters.CommandParameter; 4 | import dev.velix.imperat.context.Source; 5 | import org.jetbrains.annotations.ApiStatus; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import java.lang.annotation.Annotation; 9 | import java.util.Collection; 10 | 11 | /** 12 | * Represents an annotated parameter 13 | * from using annotation parser 14 | */ 15 | @ApiStatus.AvailableSince("1.0.0") 16 | public interface AnnotatedParameter extends CommandParameter { 17 | 18 | /** 19 | * Get the instance of specific annotation 20 | * 21 | * @param clazz the valueType of annotation 22 | * @param the valueType of the annotation 23 | * @return the specific instance of an annotation of a certain valueType {@linkplain A} 24 | */ 25 | @Nullable 26 | A getAnnotation(Class clazz); 27 | 28 | default boolean hasAnnotation(Class clazz) { 29 | return getAnnotation(clazz) != null; 30 | } 31 | 32 | /** 33 | * @return the annotations associated with this parameter 34 | */ 35 | Collection getAnnotations(); 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/AttachmentMode.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command; 2 | 3 | import org.jetbrains.annotations.ApiStatus; 4 | 5 | /** 6 | * Defines the modes of attachment for a subcommand within a {@link Command}. 7 | * This enum determines how a subcommand integrates with the usages of a command, 8 | * based on the specified attachment mode. 9 | */ 10 | @ApiStatus.AvailableSince("1.9.0") 11 | public enum AttachmentMode { 12 | 13 | 14 | /** 15 | * Attaches the subcommand to an empty usage of the {@link Command} 16 | */ 17 | EMPTY(false), 18 | 19 | /** 20 | * Attaches the subcommand to the default usage of a {@link Command}, 21 | * which may be empty or contain optional arguments/parameters. 22 | */ 23 | DEFAULT(true), 24 | 25 | /** 26 | * Attaches the sub command to the main {@link CommandUsage} of a {@link Command} 27 | * A main usage is a usage that contains required arguments/parameters. 28 | */ 29 | MAIN(true), 30 | 31 | /** 32 | * Behaves exactly like {@link AttachmentMode#MAIN}, 33 | * It's used internally to know when to prefer the global attachment mode over the per subcommand one. 34 | */ 35 | UNSET(true), 36 | ; 37 | 38 | private final boolean requiresParameterInheritance; 39 | 40 | AttachmentMode(boolean requiresParameterInheritance) { 41 | this.requiresParameterInheritance = requiresParameterInheritance; 42 | } 43 | 44 | 45 | public boolean requiresParameterInheritance() { 46 | return requiresParameterInheritance; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/CommandExecution.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command; 2 | 3 | import dev.velix.imperat.context.ExecutionContext; 4 | import dev.velix.imperat.context.Source; 5 | import dev.velix.imperat.exception.ImperatException; 6 | import org.jetbrains.annotations.ApiStatus; 7 | 8 | /** 9 | * This class represents the execution/action of this command that's triggered when 10 | * the sender asks for this command to be executed. 11 | * 12 | * @param the command sender valueType 13 | */ 14 | @ApiStatus.AvailableSince("1.0.0") 15 | public interface CommandExecution { 16 | 17 | static CommandExecution empty() { 18 | return (source, context) -> { 19 | }; 20 | } 21 | 22 | /** 23 | * Executes the command's actions 24 | * 25 | * @param source the source/sender of this command 26 | * @param context the context of the command 27 | */ 28 | void execute(final S source, final ExecutionContext context) throws ImperatException; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/ContextResolverFactory.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command; 2 | 3 | import dev.velix.imperat.annotations.base.element.ParameterElement; 4 | import dev.velix.imperat.context.Source; 5 | import dev.velix.imperat.resolvers.ContextResolver; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import java.lang.reflect.Type; 9 | 10 | /** 11 | * Represents a context resolver factory 12 | * that is responsible for creating {@link ContextResolver} 13 | * 14 | * @param the command-sender valueType 15 | */ 16 | public interface ContextResolverFactory { 17 | 18 | /** 19 | * Creates a context resolver based on the parameter 20 | * 21 | * @param parameter the parameter (null if used classic way) 22 | * @return the {@link ContextResolver} specific for that parameter 23 | */ 24 | @Nullable 25 | ContextResolver create(Type type, @Nullable ParameterElement parameter); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/CooldownHolder.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command; 2 | 3 | 4 | import dev.velix.imperat.command.cooldown.UsageCooldown; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import java.util.concurrent.TimeUnit; 8 | 9 | public interface CooldownHolder { 10 | 11 | @Nullable 12 | UsageCooldown getCooldown(); 13 | 14 | void setCooldown(UsageCooldown cooldown); 15 | 16 | default void setCooldown(long value, TimeUnit unit) { 17 | setCooldown(value, unit, null); 18 | } 19 | 20 | default void setCooldown(long value, TimeUnit unit, @Nullable String permission) { 21 | setCooldown(new UsageCooldown(value, unit, permission)); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/Description.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command; 2 | 3 | public final class Description { 4 | 5 | private final static String NON_APPLICABLE = "N/A"; 6 | public final static Description EMPTY = Description.of(NON_APPLICABLE); 7 | private final String value; 8 | 9 | Description(String value) { 10 | this.value = value; 11 | } 12 | 13 | public static Description of(String value) { 14 | return new Description(value); 15 | } 16 | 17 | @Override 18 | public String toString() { 19 | return value; 20 | } 21 | 22 | public boolean isEmpty() { 23 | return this == EMPTY || this.value.isBlank() || this.value.equals(NON_APPLICABLE); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/DescriptionHolder.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command; 2 | 3 | /** 4 | * Represents an entity that can have a description. 5 | * This interface provides methods to get and set a description. 6 | */ 7 | public interface DescriptionHolder { 8 | 9 | /** 10 | * Retrieves the current description associated with this entity. 11 | * 12 | * @return the current {@link Description}. 13 | */ 14 | Description description(); 15 | 16 | /** 17 | * Sets the description for this entity. 18 | * 19 | * @param description the {@link Description} to set. 20 | */ 21 | void describe(final Description description); 22 | 23 | /** 24 | * Sets the description for this entity using a string. 25 | * 26 | * @param description the string to create a {@link Description} from and set. 27 | */ 28 | default void describe(final String description) { 29 | describe(Description.of(description)); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/NumericComparator.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command; 2 | 3 | import dev.velix.imperat.command.parameters.NumericRange; 4 | import dev.velix.imperat.util.Preconditions; 5 | 6 | import java.util.Map; 7 | 8 | public interface NumericComparator { 9 | 10 | NumericComparator DOUBLE_NUMERIC_COMPARATOR = (number, range) -> range.matches(number); 11 | 12 | NumericComparator FLOAT_NUMERIC_COMPARATOR = (number, range) -> range.matches(number); 13 | 14 | NumericComparator INTEGER_NUMERIC_COMPARATOR = (number, range) -> range.matches(number); 15 | 16 | NumericComparator LONG_NUMERIC_COMPARATOR = (number, range) -> range.matches((double) ((long) number)); 17 | 18 | 19 | Map, NumericComparator> COMPARATORS = Map.of(Double.class, DOUBLE_NUMERIC_COMPARATOR, 20 | Float.class, FLOAT_NUMERIC_COMPARATOR, 21 | Integer.class, INTEGER_NUMERIC_COMPARATOR, 22 | Long.class, LONG_NUMERIC_COMPARATOR 23 | ); 24 | 25 | @SuppressWarnings("unchecked") 26 | static NumericComparator of(N value) { 27 | Preconditions.notNull(value, "Value"); 28 | return (NumericComparator) COMPARATORS.getOrDefault(value.getClass(), DOUBLE_NUMERIC_COMPARATOR); 29 | } 30 | 31 | boolean isWithin(N number, NumericRange range); 32 | } 33 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/PermissionHolder.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | /** 6 | * Represents an entity that can hold a permission. 7 | * This interface allows getting and setting a permission string. 8 | */ 9 | public interface PermissionHolder { 10 | 11 | /** 12 | * Retrieves the permission associated with this holder. 13 | * 14 | * @return the permission string, or {@code null} if no permission is set. 15 | */ 16 | @Nullable 17 | String permission(); 18 | 19 | /** 20 | * Sets the permission for this holder. 21 | * 22 | * @param permission the permission string to set, can be {@code null}. 23 | */ 24 | void permission(String permission); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/ReturnResolverRegistry.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command; 2 | 3 | import dev.velix.imperat.command.returns.ReturnResolver; 4 | import dev.velix.imperat.context.Source; 5 | import dev.velix.imperat.util.Registry; 6 | import org.jetbrains.annotations.ApiStatus; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import java.lang.reflect.Type; 10 | 11 | @ApiStatus.AvailableSince("1.9.1") 12 | public final class ReturnResolverRegistry extends Registry> { 13 | 14 | public static ReturnResolverRegistry createDefault() { 15 | return new ReturnResolverRegistry<>(); 16 | } 17 | 18 | @SuppressWarnings("unchecked") 19 | public @Nullable ReturnResolver getReturnResolver(Type type) { 20 | return (ReturnResolver) getData(type).orElse(null); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/SourceResolverRegistry.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command; 2 | 3 | import dev.velix.imperat.context.Source; 4 | import dev.velix.imperat.resolvers.SourceResolver; 5 | import dev.velix.imperat.util.Registry; 6 | 7 | import java.lang.reflect.Type; 8 | 9 | public final class SourceResolverRegistry extends Registry> { 10 | 11 | SourceResolverRegistry() { 12 | 13 | } 14 | 15 | public static SourceResolverRegistry createDefault() { 16 | return new SourceResolverRegistry<>(); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/UsageComparator.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command; 2 | 3 | import dev.velix.imperat.command.parameters.CommandParameter; 4 | 5 | import java.util.Comparator; 6 | 7 | final class UsageComparator implements Comparator> { 8 | 9 | private static UsageComparator instance; 10 | 11 | static UsageComparator getInstance() { 12 | if (instance == null) { 13 | instance = new UsageComparator(); 14 | return instance; 15 | } 16 | return instance; 17 | } 18 | 19 | 20 | @Override 21 | public int compare(CommandUsage firstUsage, CommandUsage secondUsage) { 22 | if (firstUsage.size() == secondUsage.size()) { 23 | 24 | for (int i = 0; i < firstUsage.size(); i++) { 25 | CommandParameter p1 = firstUsage.getParameter(i); 26 | CommandParameter p2 = secondUsage.getParameter(i); 27 | if (p1 == null || p2 == null) break; 28 | if (p1.isCommand() && !p2.isCommand()) { 29 | return -1; 30 | } else if (!p1.isCommand() && p2.isCommand()) { 31 | return 1; 32 | } 33 | } 34 | 35 | } 36 | 37 | return firstUsage.size() - secondUsage.size(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/UsageMap.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command; 2 | 3 | import dev.velix.imperat.command.parameters.CommandParameter; 4 | import dev.velix.imperat.context.Source; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.util.HashMap; 8 | import java.util.Iterator; 9 | import java.util.LinkedHashSet; 10 | import java.util.List; 11 | import java.util.Set; 12 | 13 | final class UsageMap extends HashMap>, CommandUsage> implements Iterable> { 14 | 15 | private final LinkedHashSet> sort = new LinkedHashSet<>(); 16 | 17 | UsageMap() { 18 | super(); 19 | } 20 | 21 | @Override 22 | public CommandUsage put(List> key, CommandUsage value) { 23 | sort.add(value); 24 | return super.put(key, value); 25 | } 26 | 27 | public Set> asSortedSet() { 28 | return sort; 29 | } 30 | 31 | @Override 32 | public @NotNull Iterator> iterator() { 33 | return sort.iterator(); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/cooldown/UsageCooldown.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.cooldown; 2 | 3 | import org.jetbrains.annotations.ApiStatus; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | import java.time.Duration; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | /** 10 | * Represents a required cooldown of a {@link dev.velix.imperat.command.CommandUsage} 11 | * 12 | * @param value the value for this 13 | * @param unit the unit for the time 14 | * @see TimeUnit 15 | * @see Duration 16 | */ 17 | @ApiStatus.AvailableSince("1.0.0") 18 | public record UsageCooldown(long value, TimeUnit unit, @Nullable String permission) { 19 | 20 | public long toMillis() { 21 | return unit.toMillis(value); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/parameters/FlagParameter.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.parameters; 2 | 3 | import dev.velix.imperat.context.FlagData; 4 | import dev.velix.imperat.context.Source; 5 | import dev.velix.imperat.resolvers.SuggestionResolver; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import java.lang.reflect.Type; 10 | 11 | public interface FlagParameter extends CommandParameter { 12 | 13 | /** 14 | * @return The flag's data 15 | */ 16 | @NotNull 17 | FlagData flagData(); 18 | 19 | /** 20 | * @return The valueType of input value 21 | */ 22 | default Type inputValueType() { 23 | var type = flagData().inputType(); 24 | if (type == null) 25 | return Boolean.class; 26 | return type.type(); 27 | } 28 | 29 | /** 30 | * @return the {@link SuggestionResolver} for input value of this flag 31 | * null if the flag is switch, check using {@link FlagParameter#isSwitch()} 32 | */ 33 | @Nullable 34 | SuggestionResolver inputSuggestionResolver(); 35 | 36 | /** 37 | * @return checks whether this parameter is a flag 38 | */ 39 | @Override 40 | default boolean isFlag() { 41 | return true; 42 | } 43 | 44 | default boolean isSwitch() { 45 | return flagData().inputType() == null; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/parameters/NormalCommandParameter.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.parameters; 2 | 3 | import dev.velix.imperat.command.Description; 4 | import dev.velix.imperat.command.parameters.type.ParameterType; 5 | import dev.velix.imperat.context.Source; 6 | import dev.velix.imperat.resolvers.SuggestionResolver; 7 | import dev.velix.imperat.util.StringUtils; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | class NormalCommandParameter extends InputParameter { 12 | 13 | NormalCommandParameter(String name, 14 | ParameterType type, 15 | @Nullable String permission, 16 | Description description, 17 | boolean optional, 18 | boolean greedy, 19 | @NotNull OptionalValueSupplier valueSupplier, 20 | @Nullable SuggestionResolver suggestionResolver) { 21 | super( 22 | name, type, permission, description, optional, 23 | false, greedy, valueSupplier, suggestionResolver 24 | ); 25 | } 26 | 27 | /** 28 | * Formats the usage parameter 29 | * 30 | * @return the formatted parameter 31 | */ 32 | @Override 33 | public String format() { 34 | var content = name(); 35 | if (isGreedy()) 36 | content += "..."; 37 | return StringUtils.normalizedParameterFormatting(content, isOptional()); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/parameters/NumericParameter.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.parameters; 2 | 3 | import dev.velix.imperat.command.NumericComparator; 4 | import dev.velix.imperat.context.Source; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | /** 8 | * Represents a behavior that deals with numeric 9 | * inputs if they are ranged from min to max using {@link NumericRange} 10 | */ 11 | public interface NumericParameter extends CommandParameter { 12 | 13 | /** 14 | * @return The actual range of the numeric parameter 15 | * returns null if no range is specified! 16 | */ 17 | @Nullable 18 | NumericRange getRange(); 19 | 20 | default boolean hasRange() { 21 | return getRange() != null; 22 | } 23 | 24 | default boolean matchesRange(N value) { 25 | var range = getRange(); 26 | return range != null && NumericComparator.of(value).isWithin(value, range); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/parameters/NumericRange.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.parameters; 2 | 3 | public final class NumericRange { 4 | 5 | private final double min, max; 6 | 7 | NumericRange(double min, double max) { 8 | this.min = min; 9 | this.max = max; 10 | } 11 | 12 | public static NumericRange of(double min, double max) { 13 | return new NumericRange(min, max); 14 | } 15 | 16 | public static NumericRange min(double min) { 17 | return new NumericRange(min, Double.MAX_VALUE); 18 | } 19 | 20 | public static NumericRange max(double max) { 21 | return new NumericRange(Double.MIN_VALUE, max); 22 | } 23 | 24 | public static NumericRange empty() { 25 | return of(Double.MIN_VALUE, Double.MAX_VALUE); 26 | } 27 | 28 | public boolean matches(double value) { 29 | return value >= min && value <= max; 30 | } 31 | 32 | public double getMin() { 33 | return min; 34 | } 35 | 36 | public double getMax() { 37 | return max; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/parameters/OptionalValueSupplier.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.parameters; 2 | 3 | import dev.velix.imperat.context.Source; 4 | import dev.velix.imperat.util.Preconditions; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | public interface OptionalValueSupplier { 9 | 10 | OptionalValueSupplier EMPTY = new OptionalValueSupplier() { 11 | @Override 12 | public @Nullable String supply(S source, CommandParameter parameter) { 13 | return null; 14 | } 15 | }; 16 | 17 | static OptionalValueSupplier of(@NotNull String value) { 18 | Preconditions.notNull(value, "default cannot be null, use `OptionalValueSupplier#empty` instead"); 19 | return new OptionalValueSupplier() { 20 | @Override 21 | public @NotNull String supply(S source, CommandParameter parameter) { 22 | return value; 23 | } 24 | }; 25 | } 26 | 27 | static @NotNull OptionalValueSupplier empty() { 28 | return EMPTY; 29 | } 30 | 31 | default boolean isEmpty() { 32 | return this == EMPTY; 33 | } 34 | 35 | /** 36 | * Supplies a default-value for optional 37 | * usage parameters {@link CommandParameter} 38 | * 39 | * @param source the context 40 | * @param parameter the parameter 41 | * @return the resolved default value 42 | */ 43 | @Nullable 44 | String supply(S source, CommandParameter parameter); 45 | 46 | } 47 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/parameters/StrictParameterList.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.parameters; 2 | 3 | import dev.velix.imperat.context.Source; 4 | 5 | import java.util.Collection; 6 | import java.util.LinkedList; 7 | 8 | public final class StrictParameterList extends LinkedList> { 9 | 10 | @Override 11 | public void addFirst(CommandParameter parameter) { 12 | if (containsSimilar(parameter)) 13 | return; 14 | 15 | super.addFirst(parameter); 16 | } 17 | 18 | @Override 19 | public boolean add(CommandParameter parameter) { 20 | 21 | if (containsSimilar(parameter)) 22 | return false; 23 | 24 | return super.add(parameter); 25 | } 26 | 27 | @Override 28 | public boolean addAll(Collection> c) { 29 | for (var e : c) { 30 | add(e); 31 | } 32 | return true; 33 | } 34 | 35 | 36 | @Override 37 | public boolean contains(Object o) { 38 | if (!(o instanceof CommandParameter parameter)) return false; 39 | return super.contains(parameter) || containsSimilar(parameter); 40 | } 41 | 42 | public boolean containsSimilar(CommandParameter parameter) { 43 | for (var p : this) { 44 | if (p.similarTo(parameter)) 45 | return true; 46 | } 47 | return false; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/parameters/type/ParameterCommand.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.parameters.type; 2 | 3 | import dev.velix.imperat.command.Command; 4 | import dev.velix.imperat.command.parameters.CommandParameter; 5 | import dev.velix.imperat.context.ExecutionContext; 6 | import dev.velix.imperat.context.Source; 7 | import dev.velix.imperat.context.internal.CommandInputStream; 8 | import dev.velix.imperat.exception.ImperatException; 9 | import dev.velix.imperat.util.TypeWrap; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | import java.util.List; 14 | 15 | public final class ParameterCommand extends BaseParameterType> { 16 | private final String name; 17 | ParameterCommand(String name, List aliases) { 18 | super(new TypeWrap<>() { 19 | }); 20 | this.name = name; 21 | suggestions.add(name); 22 | suggestions.addAll(aliases); 23 | } 24 | 25 | @Override 26 | public @Nullable Command resolve(@NotNull ExecutionContext context, @NotNull CommandInputStream commandInputStream, String input) throws ImperatException { 27 | return commandInputStream.currentParameter() 28 | .map(CommandParameter::asCommand).orElse(null); 29 | } 30 | 31 | @Override 32 | public boolean matchesInput(String input, CommandParameter parameter) { 33 | return parameter.isCommand() && 34 | parameter.asCommand().hasName(input.toLowerCase()); 35 | } 36 | 37 | public String getName() { 38 | return name; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/parameters/type/ParameterTypes.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.parameters.type; 2 | 3 | import dev.velix.imperat.context.FlagData; 4 | import dev.velix.imperat.context.Source; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.util.List; 8 | 9 | public final class ParameterTypes { 10 | 11 | 12 | private ParameterTypes() { 13 | } 14 | 15 | public static ParameterWord word() { 16 | return new ParameterWord<>(); 17 | } 18 | 19 | public static ParameterString string() { 20 | return new ParameterString<>(); 21 | } 22 | 23 | public static ParameterNumber numeric(Class numType) { 24 | return ParameterNumber.from(numType); 25 | } 26 | 27 | public static ParameterBoolean bool() { 28 | return new ParameterBoolean<>(); 29 | } 30 | 31 | public static ParameterFlag flag(FlagData flagData) { 32 | return new ParameterFlag<>(flagData); 33 | } 34 | 35 | 36 | public static @NotNull ParameterCommand command(String name, List aliases) { 37 | return new ParameterCommand<>(name, aliases); 38 | } 39 | 40 | public static ParameterUUID uuid() { 41 | return new ParameterUUID<>(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/parameters/type/ParameterUUID.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.parameters.type; 2 | 3 | import dev.velix.imperat.command.parameters.CommandParameter; 4 | import dev.velix.imperat.context.ExecutionContext; 5 | import dev.velix.imperat.context.Source; 6 | import dev.velix.imperat.context.internal.CommandInputStream; 7 | import dev.velix.imperat.exception.ImperatException; 8 | import dev.velix.imperat.exception.InvalidUUIDException; 9 | import dev.velix.imperat.util.TypeWrap; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | import java.util.UUID; 13 | 14 | public final class ParameterUUID extends BaseParameterType { 15 | public ParameterUUID() { 16 | super(TypeWrap.of(UUID.class)); 17 | } 18 | 19 | @Override 20 | public @NotNull UUID resolve( 21 | @NotNull ExecutionContext context, 22 | @NotNull CommandInputStream commandInputStream, 23 | String input) throws ImperatException { 24 | 25 | try { 26 | return UUID.fromString(input); 27 | } catch (Exception ex) { 28 | throw new InvalidUUIDException(input); 29 | } 30 | } 31 | 32 | @Override 33 | public boolean matchesInput(String input, CommandParameter parameter) { 34 | try { 35 | UUID.fromString(input); 36 | return true; 37 | } catch (Exception ex) { 38 | return false; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/processors/ChainImpl.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.processors; 2 | 3 | import dev.velix.imperat.context.Source; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | import java.util.Iterator; 7 | import java.util.Queue; 8 | 9 | record ChainImpl>(Queue

processors) implements CommandProcessingChain { 10 | @Override 11 | public @NotNull Queue

getProcessors() { 12 | return processors; 13 | } 14 | 15 | @Override 16 | public void reset() { 17 | processors.clear(); 18 | } 19 | 20 | @Override 21 | public void add(P processor) { 22 | processors.add(processor); 23 | } 24 | 25 | /** 26 | * Returns an iterator over elements of type {@code T}. 27 | * 28 | * @return an Iterator. 29 | */ 30 | @Override 31 | public @NotNull Iterator

iterator() { 32 | return processors.iterator(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/processors/CommandPostProcessor.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.processors; 2 | 3 | import dev.velix.imperat.Imperat; 4 | import dev.velix.imperat.context.Context; 5 | import dev.velix.imperat.context.ResolvedContext; 6 | import dev.velix.imperat.context.Source; 7 | import dev.velix.imperat.exception.ImperatException; 8 | 9 | /** 10 | * Defines a functional interface that processes a {@link Context} 11 | * AFTER the resolving of the arguments into values. 12 | * 13 | * @param the command sender valueType 14 | */ 15 | public interface CommandPostProcessor extends CommandProcessor { 16 | 17 | /** 18 | * Processes context AFTER the resolving operation. 19 | * 20 | * @param imperat the api 21 | * @param context the context 22 | * @throws ImperatException the exception to throw if something happens 23 | */ 24 | void process( 25 | Imperat imperat, 26 | ResolvedContext context 27 | ) throws ImperatException; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/processors/CommandPreProcessor.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.processors; 2 | 3 | import dev.velix.imperat.Imperat; 4 | import dev.velix.imperat.command.CommandUsage; 5 | import dev.velix.imperat.context.Context; 6 | import dev.velix.imperat.context.Source; 7 | import dev.velix.imperat.exception.ImperatException; 8 | 9 | /** 10 | * Defines a functional interface that processes a {@link Context} 11 | * BEFORE the resolving of the arguments into values. 12 | * 13 | * @param the command sender valueType 14 | */ 15 | @FunctionalInterface 16 | public interface CommandPreProcessor extends CommandProcessor { 17 | 18 | /** 19 | * Processes context BEFORE the resolving operation. 20 | * 21 | * @param imperat the api 22 | * @param context the context 23 | * @param usage The usage detected 24 | * @throws ImperatException the exception to throw if something happens 25 | */ 26 | void process( 27 | Imperat imperat, 28 | Context context, 29 | CommandUsage usage 30 | ) throws ImperatException; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/processors/CommandProcessingChain.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.processors; 2 | 3 | import dev.velix.imperat.context.Source; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | import java.util.Comparator; 7 | import java.util.PriorityQueue; 8 | import java.util.Queue; 9 | 10 | public interface CommandProcessingChain> extends Iterable

{ 11 | 12 | @NotNull 13 | Queue

getProcessors(); 14 | 15 | void reset(); 16 | 17 | void add(P preProcessor); 18 | 19 | final class Builder> { 20 | private final PriorityQueue

processors; 21 | 22 | public Builder() { 23 | // Create a priority queue that sorts by the priority of the processors 24 | this.processors = new PriorityQueue<>(Comparator.comparingInt(CommandProcessor::priority)); 25 | } 26 | 27 | public Builder then(P processor) { 28 | // Add the processor to the queue 29 | processors.offer(processor); 30 | return this; 31 | } 32 | 33 | public CommandProcessingChain build() { 34 | return new ChainImpl<>(processors); 35 | } 36 | } 37 | 38 | 39 | static Builder> preProcessors() { 40 | return new Builder<>(); 41 | } 42 | 43 | static Builder> postProcessors() { 44 | return new Builder<>(); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/processors/CommandProcessor.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.processors; 2 | 3 | import dev.velix.imperat.context.Source; 4 | 5 | public interface CommandProcessor { 6 | 7 | /** 8 | * Returns the priority of the command processor. 9 | * Processors with lower priority values are executed first. 10 | * 11 | *

12 | * The default priority is 50. Implementations may override this 13 | * method to specify a custom priority value. 14 | *

15 | * 16 | * @return the priority of this command processor 17 | */ 18 | default int priority() { 19 | return 50; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/processors/impl/DefaultProcessors.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.processors.impl; 2 | 3 | import dev.velix.imperat.context.Source; 4 | 5 | public final class DefaultProcessors { 6 | 7 | public static UsageCooldownProcessor preUsageCooldown() { 8 | return new UsageCooldownProcessor<>(); 9 | } 10 | 11 | public static UsagePermissionProcessor preUsagePermission() { 12 | return new UsagePermissionProcessor<>(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/processors/impl/UsageCooldownProcessor.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.processors.impl; 2 | 3 | import dev.velix.imperat.Imperat; 4 | import dev.velix.imperat.command.CommandUsage; 5 | import dev.velix.imperat.command.processors.CommandPreProcessor; 6 | import dev.velix.imperat.context.Context; 7 | import dev.velix.imperat.context.Source; 8 | import dev.velix.imperat.exception.CooldownException; 9 | import dev.velix.imperat.exception.ImperatException; 10 | 11 | public final class UsageCooldownProcessor implements CommandPreProcessor { 12 | 13 | UsageCooldownProcessor() { 14 | 15 | } 16 | /** 17 | * Processes context BEFORE the resolving operation. 18 | * 19 | * @param imperat the api 20 | * @param context the context 21 | * @param usage The usage detected 22 | * @throws ImperatException the exception to throw if something happens 23 | */ 24 | @Override 25 | public void process( 26 | Imperat imperat, 27 | Context context, 28 | CommandUsage usage 29 | ) throws ImperatException { 30 | var source = context.source(); 31 | var handler = usage.getCooldownHandler(); 32 | var cooldown = usage.getCooldown(); 33 | 34 | if (handler.hasCooldown(source)) { 35 | assert cooldown != null; 36 | if(cooldown.permission() == null 37 | || cooldown.permission().isEmpty() 38 | || !imperat.config().getPermissionResolver().hasPermission(source, cooldown.permission())) { 39 | throw new CooldownException( 40 | cooldown.toMillis(), 41 | handler.getLastTimeExecuted(source).orElse(0L) 42 | ); 43 | } 44 | } 45 | handler.registerExecutionMoment(source); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/processors/impl/UsagePermissionProcessor.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.processors.impl; 2 | 3 | import dev.velix.imperat.Imperat; 4 | import dev.velix.imperat.command.CommandUsage; 5 | import dev.velix.imperat.command.processors.CommandPreProcessor; 6 | import dev.velix.imperat.context.Context; 7 | import dev.velix.imperat.context.Source; 8 | import dev.velix.imperat.exception.ImperatException; 9 | import dev.velix.imperat.exception.PermissionDeniedException; 10 | 11 | public final class UsagePermissionProcessor implements CommandPreProcessor { 12 | 13 | UsagePermissionProcessor() { 14 | 15 | } 16 | /** 17 | * Processes context BEFORE the resolving operation. 18 | * 19 | * @param context the context 20 | * @param usage The usage detected 21 | * @throws ImperatException the exception to throw if something happens 22 | */ 23 | @Override 24 | public void process( 25 | Imperat imperat, 26 | Context context, 27 | CommandUsage usage 28 | ) throws ImperatException { 29 | var source = context.source(); 30 | 31 | if (!imperat.config().getPermissionResolver().hasUsagePermission(source, usage)) { 32 | throw new PermissionDeniedException(); 33 | } 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/returns/BaseReturnResolver.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.returns; 2 | 3 | import dev.velix.imperat.context.Source; 4 | import dev.velix.imperat.util.TypeWrap; 5 | 6 | import java.lang.reflect.Type; 7 | 8 | public abstract class BaseReturnResolver implements ReturnResolver { 9 | 10 | private final Type type; 11 | 12 | public BaseReturnResolver(Type type) { 13 | this.type = type; 14 | } 15 | 16 | public BaseReturnResolver(TypeWrap type) { 17 | this.type = type.getType(); 18 | } 19 | 20 | @Override 21 | public Type getType() { 22 | return type; 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | return "BaseReturnResolver{" + 28 | "type=" + type + 29 | '}'; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/returns/ReturnResolver.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.returns; 2 | 3 | import dev.velix.imperat.annotations.base.element.MethodElement; 4 | import dev.velix.imperat.context.ExecutionContext; 5 | import dev.velix.imperat.context.Source; 6 | 7 | import java.lang.reflect.Type; 8 | 9 | public interface ReturnResolver { 10 | 11 | /** 12 | * Handles the return value of a command. 13 | * 14 | * @param context The execution contex of the command. 15 | * @param method The method element of the command. 16 | * @param value The return value of the command. 17 | */ 18 | void handle(ExecutionContext context, MethodElement method, T value); 19 | 20 | /** 21 | * Returns the type of the return value. 22 | * 23 | * @return The type of the return value. 24 | */ 25 | Type getType(); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/suggestions/CompletionArg.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.suggestions; 2 | 3 | import org.jetbrains.annotations.ApiStatus; 4 | 5 | /** 6 | * Represents an argument that's being completed 7 | * or an argument requested by the command-sender 8 | * to be completed 9 | * 10 | * @param value the argument input half-complete or empty to be completed 11 | * @param index the index of this argument 12 | */ 13 | @ApiStatus.AvailableSince("1.0.0") 14 | public record CompletionArg(String value, int index) { 15 | 16 | public boolean isEmpty() { 17 | return value.isEmpty() || value.isBlank(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/suggestions/NativeAutoCompleter.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.suggestions; 2 | 3 | import dev.velix.imperat.Imperat; 4 | import dev.velix.imperat.command.Command; 5 | import dev.velix.imperat.context.Source; 6 | import dev.velix.imperat.context.SuggestionContext; 7 | 8 | import java.util.List; 9 | import java.util.concurrent.CompletableFuture; 10 | 11 | final class NativeAutoCompleter extends AutoCompleter { 12 | 13 | NativeAutoCompleter(Command command) { 14 | super(command); 15 | } 16 | 17 | /** 18 | * Autocompletes an argument from the whole position of the 19 | * argument-raw input 20 | * 21 | * @param imperat the command dispatcher 22 | * @param context the context for suggestions 23 | * @return the auto-completed results 24 | */ 25 | @Override 26 | public CompletableFuture> autoComplete(Imperat imperat, SuggestionContext context) { 27 | var tree = command.tree(); 28 | return tree.tabComplete(imperat, context).thenApply((results)-> { 29 | var toComplete = context.getArgToComplete(); 30 | String input = context.getArgToComplete().value().toLowerCase(); // Lowercase input for case-insensitive comparison 31 | return results.stream() 32 | .distinct() 33 | .filter((str)-> toComplete.isEmpty() || str.toLowerCase().startsWith(input)) 34 | .toList(); 35 | }); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/tree/ArgumentNode.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.tree; 2 | 3 | import dev.velix.imperat.command.CommandUsage; 4 | import dev.velix.imperat.command.parameters.CommandParameter; 5 | import dev.velix.imperat.context.Source; 6 | import org.jetbrains.annotations.ApiStatus; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | @ApiStatus.Internal 11 | public final class ArgumentNode extends ParameterNode> { 12 | 13 | ArgumentNode(@NotNull CommandParameter data, @Nullable CommandUsage usage) { 14 | super(data, usage); 15 | } 16 | 17 | @Override 18 | public boolean matchesInput(String input) { 19 | var type = data.type(); 20 | return type.matchesInput(input, data); 21 | } 22 | 23 | @Override 24 | public String format() { 25 | return data.format(); 26 | } 27 | 28 | @Override 29 | public int priority() { 30 | return 1; 31 | } 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/tree/CommandNode.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.tree; 2 | 3 | import dev.velix.imperat.command.Command; 4 | import dev.velix.imperat.command.CommandUsage; 5 | import dev.velix.imperat.context.Source; 6 | import org.jetbrains.annotations.ApiStatus; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | @ApiStatus.Internal 11 | public final class CommandNode extends ParameterNode> { 12 | 13 | CommandNode(@NotNull Command data, @Nullable CommandUsage usage) { 14 | super(data, usage); 15 | } 16 | 17 | boolean isSubCommand() { 18 | return data.hasParent(); 19 | } 20 | 21 | boolean isRoot() { 22 | return !isSubCommand(); 23 | } 24 | 25 | @Override 26 | public boolean matchesInput(String raw) { 27 | return data.hasName(raw); 28 | } 29 | 30 | 31 | @Override 32 | public String format() { 33 | return data.format(); 34 | } 35 | 36 | @Override 37 | public int priority() { 38 | return -1; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/command/tree/CommandTreeVisualizer.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.command.tree; 2 | 3 | import dev.velix.imperat.context.Source; 4 | import dev.velix.imperat.util.ImperatDebugger; 5 | import org.jetbrains.annotations.ApiStatus; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | @ApiStatus.Internal 9 | public final class CommandTreeVisualizer { 10 | 11 | private final @Nullable CommandTree tree; 12 | 13 | CommandTreeVisualizer(@Nullable CommandTree tree) { 14 | this.tree = tree; 15 | } 16 | 17 | public static CommandTreeVisualizer of(@Nullable CommandTree tree) { 18 | return new CommandTreeVisualizer<>(tree); 19 | } 20 | 21 | 22 | public void visualize() { 23 | if (tree == null || !ImperatDebugger.isEnabled()) return; 24 | StringBuilder builder = new StringBuilder(); 25 | visualizeNode(tree.root, builder, 0); 26 | ImperatDebugger.debug(builder.toString()); 27 | } 28 | 29 | private void visualizeNode(ParameterNode node, StringBuilder builder, int depth) { 30 | if (node == null) { 31 | return; 32 | } 33 | builder.append(" ".repeat(Math.max(0, depth))); 34 | builder.append(node.format()).append("\n"); 35 | for (ParameterNode child : node.getChildren()) { 36 | visualizeNode(child, builder, depth + 1); 37 | } 38 | 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/context/Context.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.context; 2 | 3 | import dev.velix.imperat.Imperat; 4 | import dev.velix.imperat.ImperatConfig; 5 | import dev.velix.imperat.command.Command; 6 | import org.jetbrains.annotations.ApiStatus; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * Represents the processes context of a command 11 | * entered by {@link Source} 12 | * 13 | * @param the command sender valueType 14 | */ 15 | @ApiStatus.AvailableSince("1.0.0") 16 | public interface Context { 17 | 18 | /** 19 | * @return imperat's instance 20 | */ 21 | Imperat imperat(); 22 | 23 | /** 24 | * @return the config for imperat 25 | */ 26 | ImperatConfig imperatConfig(); 27 | 28 | /** 29 | * @return The {@link Command} owning this context. 30 | */ 31 | @NotNull Command command(); 32 | 33 | /** 34 | * @return the {@link Source} of the command 35 | * @see Source 36 | */ 37 | @NotNull S source(); 38 | 39 | /** 40 | * @return the root command entered by the {@link Source} 41 | */ 42 | @NotNull 43 | String label(); 44 | 45 | /** 46 | * @return the arguments entered by the {@link Source} 47 | * @see ArgumentQueue 48 | */ 49 | ArgumentQueue arguments(); 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/context/Source.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.context; 2 | 3 | import org.jetbrains.annotations.ApiStatus; 4 | 5 | import java.util.UUID; 6 | 7 | /** 8 | * Represents the sender/source 9 | * of a command being executed 10 | * may be a console, a player in a game, etc... 11 | */ 12 | @ApiStatus.AvailableSince("1.0.0") 13 | public interface Source { 14 | 15 | UUID CONSOLE_UUID = UUID.nameUUIDFromBytes("imperat-console".getBytes()); 16 | 17 | /** 18 | * @return name of a command source 19 | */ 20 | String name(); 21 | 22 | /** 23 | * @return The original command sender valueType instance 24 | */ 25 | Object origin(); 26 | 27 | /** 28 | * Replies to the command sender with a string message 29 | * 30 | * @param message the message 31 | */ 32 | void reply(String message); 33 | 34 | /** 35 | * Replies to the command sender with a string message 36 | * formatted specifically for error messages 37 | * 38 | * @param message the message 39 | */ 40 | void warn(String message); 41 | 42 | /** 43 | * Replies to the command sender with a string message 44 | * formatted specifically for error messages 45 | * 46 | * @param message the message 47 | */ 48 | void error(String message); 49 | 50 | /** 51 | * @return Whether the command source is from the console 52 | */ 53 | boolean isConsole(); 54 | 55 | /** 56 | * @return returns the UUID of the command source 57 | */ 58 | default UUID uuid() { 59 | return CONSOLE_UUID; 60 | } 61 | 62 | @SuppressWarnings("unchecked") 63 | default T as(Class clazz) { 64 | return (T) this.origin(); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/context/SuggestionContext.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.context; 2 | 3 | import dev.velix.imperat.command.suggestions.CompletionArg; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | /** 7 | * Represents the context for auto-completion while providing suggestions 8 | * 9 | * @param the command-source 10 | */ 11 | public interface SuggestionContext extends Context { 12 | 13 | /** 14 | * @return The info about the argument being completed 15 | */ 16 | @NotNull 17 | CompletionArg getArgToComplete(); 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/context/internal/Argument.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.context.internal; 2 | 3 | import dev.velix.imperat.command.parameters.CommandParameter; 4 | import dev.velix.imperat.context.Source; 5 | import org.jetbrains.annotations.ApiStatus; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | @ApiStatus.Internal 9 | public record Argument( 10 | @Nullable String raw, 11 | CommandParameter parameter, 12 | int index, @Nullable Object value 13 | ) { 14 | @Override 15 | public String toString() { 16 | return "Argument{" + 17 | "raw='" + raw + '\'' + 18 | ", parameter=" + parameter.format() + 19 | ", index=" + index + 20 | ", value=" + value + 21 | '}'; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/context/internal/ExtractedInputFlag.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.context.internal; 2 | 3 | import dev.velix.imperat.context.FlagData; 4 | 5 | public record ExtractedInputFlag(FlagData flag, String flagRaw, 6 | String flagRawInput, Object value) { 7 | 8 | public boolean isSwitch() { 9 | return flag.inputType() == null; 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/context/internal/PositionShiftCondition.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.context.internal; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | @FunctionalInterface 6 | interface PositionShiftCondition { 7 | boolean canContinue(@NotNull Cursor cursor); 8 | } -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/context/internal/ShiftOperation.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.context.internal; 2 | 3 | import java.util.function.IntUnaryOperator; 4 | 5 | enum ShiftOperation { 6 | RIGHT(value -> value + 1), 7 | 8 | LEFT(value -> value - 1); 9 | 10 | final IntUnaryOperator operator; 11 | 12 | ShiftOperation(IntUnaryOperator operator) { 13 | this.operator = operator; 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/context/internal/ShiftTarget.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.context.internal; 2 | 3 | enum ShiftTarget { 4 | 5 | RAW_ONLY((pos) -> pos.raw < pos.maxRaws()), 6 | 7 | PARAMETER_ONLY((pos) -> pos.parameter < pos.maxParameters()), 8 | 9 | ALL((pos) -> 10 | pos.raw < pos.maxRaws() && pos.parameter < pos.maxParameters()); 11 | 12 | private final PositionShiftCondition canContinueCheck; 13 | 14 | ShiftTarget(PositionShiftCondition canContinueCheck) { 15 | this.canContinueCheck = canContinueCheck; 16 | } 17 | 18 | boolean canContinue(Cursor cursor) { 19 | return canContinueCheck.canContinue(cursor); 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/context/internal/SuggestionContextImpl.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.context.internal; 2 | 3 | import dev.velix.imperat.Imperat; 4 | import dev.velix.imperat.command.Command; 5 | import dev.velix.imperat.command.suggestions.CompletionArg; 6 | import dev.velix.imperat.context.ArgumentQueue; 7 | import dev.velix.imperat.context.Source; 8 | import dev.velix.imperat.context.SuggestionContext; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | final class SuggestionContextImpl extends ContextImpl implements SuggestionContext { 12 | private final CompletionArg completionArg; 13 | 14 | SuggestionContextImpl( 15 | Imperat dispatcher, 16 | Command command, 17 | S source, 18 | String label, 19 | ArgumentQueue args, 20 | CompletionArg completionArg 21 | ) { 22 | super(dispatcher, command, source, label, args); 23 | this.completionArg = completionArg; 24 | } 25 | 26 | @Override 27 | public @NotNull CompletionArg getArgToComplete() { 28 | return completionArg; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/exception/AmbiguousUsageAdditionException.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.exception; 2 | 3 | import dev.velix.imperat.command.Command; 4 | import dev.velix.imperat.command.CommandUsage; 5 | import dev.velix.imperat.context.Source; 6 | 7 | public final class AmbiguousUsageAdditionException extends RuntimeException { 8 | 9 | public AmbiguousUsageAdditionException( 10 | final Command command, 11 | final CommandUsage first, 12 | final CommandUsage second 13 | ) { 14 | super( 15 | String.format( 16 | "Failed to add usage '%s' because it's ambiguous along with other usage '%s'", 17 | CommandUsage.format(command, first), 18 | CommandUsage.format(command, second) 19 | ) 20 | ); 21 | } 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/exception/CooldownException.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.exception; 2 | 3 | public final class CooldownException extends ImperatException { 4 | 5 | private final long defaultCooldown, cooldown; 6 | 7 | public CooldownException(final long cooldown, final long defaultCooldown) { 8 | this.defaultCooldown = defaultCooldown; 9 | this.cooldown = cooldown; 10 | } 11 | 12 | public long getCooldown() { 13 | return cooldown; 14 | } 15 | 16 | public long getDefaultCooldown() { 17 | return defaultCooldown; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/exception/ImperatException.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.exception; 2 | 3 | import org.jetbrains.annotations.ApiStatus; 4 | 5 | @ApiStatus.AvailableSince("1.0.0") 6 | public class ImperatException extends Exception { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/exception/InvalidCommandUsageException.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.exception; 2 | 3 | import dev.velix.imperat.command.Command; 4 | import dev.velix.imperat.command.CommandUsage; 5 | import dev.velix.imperat.context.Source; 6 | 7 | public final class InvalidCommandUsageException extends RuntimeException { 8 | 9 | /** 10 | * Constructs a new runtime exception with the specified detail message. 11 | * The cause is not initialized, and may subsequently be initialized by a 12 | * call to {@link #initCause}. 13 | */ 14 | public InvalidCommandUsageException( 15 | final Command command, 16 | final CommandUsage usage 17 | ) { 18 | super( 19 | String.format("Invalid command usage: '%s'", CommandUsage.format(command, usage)) 20 | ); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/exception/InvalidSourceException.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.exception; 2 | 3 | import java.lang.reflect.Type; 4 | 5 | public class InvalidSourceException extends ImperatException { 6 | 7 | private final Type targetType; 8 | 9 | public InvalidSourceException(Type targetType) { 10 | super(); 11 | this.targetType = targetType; 12 | } 13 | 14 | public Type getTargetType() { 15 | return targetType; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/exception/InvalidSyntaxException.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.exception; 2 | 3 | public final class InvalidSyntaxException extends ImperatException { 4 | } 5 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/exception/InvalidUUIDException.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.exception; 2 | 3 | public class InvalidUUIDException extends ImperatException { 4 | 5 | private final String raw; 6 | 7 | public InvalidUUIDException(final String raw) { 8 | this.raw = raw; 9 | } 10 | 11 | public String getRaw() { 12 | return raw; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/exception/NoHelpException.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.exception; 2 | 3 | public final class NoHelpException extends ImperatException { 4 | } 5 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/exception/NoHelpPageException.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.exception; 2 | 3 | public final class NoHelpPageException extends ImperatException { 4 | } 5 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/exception/NumberOutOfRangeException.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.exception; 2 | 3 | import dev.velix.imperat.command.parameters.NumericParameter; 4 | import dev.velix.imperat.command.parameters.NumericRange; 5 | 6 | public class NumberOutOfRangeException extends SourceException { 7 | 8 | public NumberOutOfRangeException( 9 | final NumericParameter parameter, 10 | final Number value, 11 | final NumericRange range 12 | ) { 13 | super("Value '" + value + "' entered for parameter '" + parameter.format() + "' must be " + formatRange(range)); 14 | } 15 | 16 | private static String formatRange(final NumericRange range) { 17 | final StringBuilder builder = new StringBuilder(); 18 | if (range.getMin() != Double.MIN_VALUE && range.getMax() != Double.MAX_VALUE) 19 | builder.append("within ").append(range.getMin()).append('-').append(range.getMax()); 20 | else if (range.getMin() != Double.MIN_VALUE) 21 | builder.append("at least '").append(range.getMin()).append("'"); 22 | else if (range.getMax() != Double.MAX_VALUE) 23 | builder.append("at most '").append(range.getMax()).append("'"); 24 | else 25 | builder.append("(Open range)"); 26 | 27 | return builder.toString(); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/exception/PermissionDeniedException.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.exception; 2 | 3 | public final class PermissionDeniedException extends ImperatException { 4 | } 5 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/exception/SelfHandledException.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.exception; 2 | 3 | import dev.velix.imperat.ImperatConfig; 4 | import dev.velix.imperat.context.Context; 5 | import dev.velix.imperat.context.Source; 6 | import org.jetbrains.annotations.ApiStatus; 7 | 8 | @ApiStatus.AvailableSince("1.0.0") 9 | public abstract class SelfHandledException extends ImperatException { 10 | 11 | /** 12 | * Handles the exception 13 | * 14 | * @param the command-source valueType 15 | * @param imperat the api 16 | * @param context the context 17 | */ 18 | public abstract void handle(ImperatConfig imperat, Context context); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/exception/SourceException.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.exception; 2 | 3 | public class SourceException extends ImperatException { 4 | 5 | private final String message; 6 | private final ErrorLevel type; 7 | 8 | public SourceException( 9 | final String msg, 10 | final Object... args 11 | ) { 12 | this.type = ErrorLevel.SEVERE; 13 | this.message = String.format(msg, args); 14 | } 15 | 16 | public SourceException( 17 | final ErrorLevel type, 18 | final String msg, 19 | final Object... args 20 | ) { 21 | this.type = type; 22 | this.message = String.format(msg, args); 23 | } 24 | 25 | public ErrorLevel getType() { 26 | return type; 27 | } 28 | 29 | @Override 30 | public String getMessage() { 31 | return this.message; 32 | } 33 | 34 | public enum ErrorLevel { 35 | REPLY, 36 | WARN, 37 | SEVERE 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/exception/ThrowableResolver.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.exception; 2 | 3 | import dev.velix.imperat.ImperatConfig; 4 | import dev.velix.imperat.context.Context; 5 | import dev.velix.imperat.context.Source; 6 | 7 | public interface ThrowableResolver { 8 | 9 | void resolve(final E exception, ImperatConfig imperat, Context context); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/help/CommandHelp.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.help; 2 | 3 | import dev.velix.imperat.ImperatConfig; 4 | import dev.velix.imperat.context.Context; 5 | import dev.velix.imperat.context.ExecutionContext; 6 | import dev.velix.imperat.context.Source; 7 | import dev.velix.imperat.annotations.ContextResolved; 8 | import org.jetbrains.annotations.ApiStatus; 9 | 10 | @ApiStatus.AvailableSince("1.0.0") 11 | @SuppressWarnings("unchecked") 12 | @ContextResolved 13 | public final class CommandHelp { 14 | 15 | private final ImperatConfig dispatcher; 16 | private final ExecutionContext context; 17 | 18 | public CommandHelp( 19 | ImperatConfig dispatcher, 20 | ExecutionContext context 21 | ) { 22 | this.dispatcher = dispatcher; 23 | this.context = context; 24 | } 25 | 26 | public void display(S source) { 27 | try { 28 | 29 | HelpProvider provider = (HelpProvider) 30 | (context.command().hasHelpProvider() ? context.command().getHelpProvider() : dispatcher.getHelpProvider()); 31 | if (provider != null) { 32 | provider.provide((ExecutionContext) context, source); 33 | } 34 | } catch (Throwable ex) { 35 | ((ImperatConfig) dispatcher).handleExecutionThrowable(ex, (Context) context, this.getClass(), "display(source)"); 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/help/DefaultFormatter.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.help; 2 | 3 | import dev.velix.imperat.command.Command; 4 | import dev.velix.imperat.command.CommandUsage; 5 | import dev.velix.imperat.context.Source; 6 | import org.jetbrains.annotations.ApiStatus; 7 | 8 | @ApiStatus.Internal 9 | final class DefaultFormatter implements UsageFormatter { 10 | 11 | final static DefaultFormatter INSTANCE = new DefaultFormatter(); 12 | 13 | private DefaultFormatter() { 14 | 15 | } 16 | 17 | @Override 18 | public String format(Command command, CommandUsage usage, int index) { 19 | String format = "/" + CommandUsage.format(command, usage); 20 | return "&8&l[&3+&8]&r &a" + format + " &r&l-&r&e " + usage.description(); 21 | } 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/help/HelpHyphen.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.help; 2 | 3 | import dev.velix.imperat.context.Source; 4 | import org.jetbrains.annotations.ApiStatus; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | /** 8 | * Represents dynamic function returning a hyphen string 9 | * used in headers and footers for {@link HelpHyphen} 10 | * 11 | * @param 12 | */ 13 | @ApiStatus.AvailableSince("1.0.0") 14 | @FunctionalInterface 15 | public interface HelpHyphen { 16 | 17 | /** 18 | * @param content the content of this hyphen 19 | * @return the value of the {@link HyphenContent} 20 | */ 21 | @NotNull 22 | String value(HyphenContent content); 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/help/HyphenContent.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.help; 2 | 3 | import dev.velix.imperat.command.Command; 4 | import dev.velix.imperat.context.Source; 5 | 6 | public record HyphenContent(Command command, int currentPage, int maxPages) { 7 | public static HyphenContent of(Command cmd, int currentPage, int maxPages) { 8 | return new HyphenContent<>(cmd, currentPage, maxPages); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/help/TemplatePagination.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.help; 2 | 3 | import dev.velix.imperat.command.Command; 4 | import dev.velix.imperat.context.Source; 5 | 6 | final class TemplatePagination extends PaginatedHelpTemplate { 7 | private final HelpTemplate template; 8 | 9 | TemplatePagination( 10 | HelpTemplate template, 11 | int syntaxesPerPage 12 | ) { 13 | super(template.formatter, syntaxesPerPage); 14 | this.template = template; 15 | } 16 | 17 | 18 | @Override 19 | public String getHeader(Command command, int currentPage, int maxPages) { 20 | return template.getHeader(command, currentPage, maxPages); 21 | } 22 | 23 | @Override 24 | public String getFooter(Command command, int currentPage, int maxPages) { 25 | return template.getFooter(command, currentPage, maxPages); 26 | } 27 | 28 | @Override 29 | public void displayHeaderHyphen(Command command, Source source, int page, int maxPages) { 30 | source.reply(getHeader(command, page, maxPages)); 31 | } 32 | 33 | @Override 34 | public void displayFooterHyphen(Command command, Source source, int page, int maxPages) { 35 | source.reply(getHeader(command, page, maxPages)); 36 | } 37 | } -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/help/UsageDisplayer.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.help; 2 | 3 | import dev.velix.imperat.command.CommandUsage; 4 | import dev.velix.imperat.context.ExecutionContext; 5 | import dev.velix.imperat.context.Source; 6 | 7 | import java.util.Collection; 8 | 9 | @FunctionalInterface 10 | public interface UsageDisplayer { 11 | 12 | void accept(ExecutionContext ctx, S source, Collection> commandUsages); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/help/UsageFormatter.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.help; 2 | 3 | import dev.velix.imperat.command.Command; 4 | import dev.velix.imperat.command.CommandUsage; 5 | import dev.velix.imperat.context.Source; 6 | import org.jetbrains.annotations.ApiStatus; 7 | 8 | /** 9 | * Represents a class responsible for formatting 10 | * each single {@link CommandUsage} 11 | */ 12 | @ApiStatus.AvailableSince("1.0.0") 13 | public interface UsageFormatter { 14 | 15 | /** 16 | * Displays the usage by converting it into 17 | * an adventure component 18 | * 19 | * @param command the command 20 | * @param usage the usage to display 21 | * @param index the index of the usage 22 | * @param the sender-valueType 23 | * @return the usage component 24 | */ 25 | String format( 26 | Command command, 27 | CommandUsage usage, 28 | int index 29 | ); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/placeholders/Placeholder.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.placeholders; 2 | 3 | import dev.velix.imperat.ImperatConfig; 4 | import dev.velix.imperat.context.Source; 5 | import dev.velix.imperat.util.Preconditions; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | public sealed interface Placeholder permits PlaceholderImpl { 9 | 10 | /** 11 | * The unique name for this placeholder 12 | * 13 | * @return the name for this placeholder 14 | */ 15 | @NotNull 16 | String id(); 17 | 18 | /** 19 | * The dynamic resolver for this placeholder 20 | * 21 | * @return the {@link PlaceholderResolver} resolver 22 | */ 23 | @NotNull 24 | PlaceholderResolver resolver(); 25 | 26 | boolean isUsedIn(String input); 27 | 28 | default String resolveInput(String id, ImperatConfig imperat) { 29 | return resolver().resolve(id, imperat); 30 | } 31 | 32 | String replaceResolved(ImperatConfig imperat, String id, String input); 33 | 34 | static Builder builder(String id) { 35 | return new Builder<>(id); 36 | } 37 | 38 | 39 | final class Builder { 40 | private final String id; 41 | private PlaceholderResolver resolver = null; 42 | 43 | Builder(String id) { 44 | this.id = id; 45 | } 46 | 47 | public Builder resolver(PlaceholderResolver resolver) { 48 | this.resolver = resolver; 49 | return this; 50 | } 51 | 52 | public Placeholder build() { 53 | Preconditions.notNull(resolver, "resolver is not set in the placeholder-builder"); 54 | return new PlaceholderImpl<>(id, resolver); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/placeholders/PlaceholderImpl.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.placeholders; 2 | 3 | import dev.velix.imperat.ImperatConfig; 4 | import dev.velix.imperat.context.Source; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import java.util.Objects; 8 | import java.util.regex.Pattern; 9 | 10 | final class PlaceholderImpl implements Placeholder { 11 | private final String id; 12 | private final PlaceholderResolver resolver; 13 | 14 | private final Pattern pattern; 15 | 16 | PlaceholderImpl(String id, PlaceholderResolver resolver) { 17 | this.id = id; 18 | this.resolver = resolver; 19 | this.pattern = Pattern.compile(id); 20 | } 21 | 22 | @Override 23 | public @NotNull String id() { 24 | return id; 25 | } 26 | 27 | @Override 28 | public @NotNull PlaceholderResolver resolver() { 29 | return resolver; 30 | } 31 | 32 | @Override 33 | public boolean isUsedIn(String input) { 34 | return pattern.matcher(input).find(); 35 | } 36 | 37 | @Override 38 | public String replaceResolved(ImperatConfig imperat, String id, String input) { 39 | assert isUsedIn(input); 40 | return pattern.matcher(input).replaceAll( 41 | resolveInput(id, imperat) 42 | ); 43 | } 44 | 45 | 46 | @Override 47 | public boolean equals(Object obj) { 48 | if (obj == this) return true; 49 | if (obj == null || obj.getClass() != this.getClass()) return false; 50 | var that = (PlaceholderImpl) obj; 51 | return Objects.equals(this.id, that.id); 52 | } 53 | 54 | @Override 55 | public int hashCode() { 56 | return Objects.hash(id); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/placeholders/PlaceholderRegistry.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.placeholders; 2 | 3 | import dev.velix.imperat.ImperatConfig; 4 | import dev.velix.imperat.context.Source; 5 | import dev.velix.imperat.util.Registry; 6 | 7 | public final class PlaceholderRegistry extends Registry> { 8 | 9 | private final ImperatConfig imperat; 10 | 11 | PlaceholderRegistry(ImperatConfig imperat) { 12 | this.imperat = imperat; 13 | } 14 | 15 | public static PlaceholderRegistry createDefault(ImperatConfig imperat) { 16 | return new PlaceholderRegistry<>(imperat); 17 | } 18 | 19 | public String resolvedString(String input) { 20 | 21 | String result = input; 22 | for (var placeHolder : getAll()) { 23 | 24 | if (placeHolder.isUsedIn(result)) { 25 | String id = placeHolder.id(); 26 | result = placeHolder.replaceResolved(imperat, id, result); 27 | } 28 | 29 | } 30 | return result; 31 | } 32 | 33 | public String[] resolvedArray(String[] array) { 34 | String[] arr = new String[array.length]; 35 | for (int i = 0; i < array.length; i++) { 36 | arr[i] = resolvedString(array[i]); 37 | } 38 | return arr; 39 | } 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/placeholders/PlaceholderResolver.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.placeholders; 2 | 3 | import dev.velix.imperat.ImperatConfig; 4 | import dev.velix.imperat.context.Source; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | public interface PlaceholderResolver { 8 | 9 | /** 10 | * Resolves a placeholder 11 | * 12 | * @param placeHolderId the id for the placeholder 13 | * @param imperat the imperat 14 | * @return the placeholder to return 15 | */ 16 | @NotNull 17 | String resolve(String placeHolderId, ImperatConfig imperat); 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/resolvers/ContextResolver.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.resolvers; 2 | 3 | import dev.velix.imperat.annotations.base.element.ParameterElement; 4 | import dev.velix.imperat.context.ExecutionContext; 5 | import dev.velix.imperat.context.Source; 6 | import dev.velix.imperat.exception.ImperatException; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | import java.util.function.Supplier; 11 | 12 | /** 13 | * Resolves a default non-overridable value for 14 | * missing required arguments 15 | * 16 | * @param the valueType of resolver's parameter 17 | */ 18 | @FunctionalInterface 19 | public interface ContextResolver { 20 | 21 | 22 | static ContextResolver of(T value) { 23 | return (c, p) -> value; 24 | } 25 | 26 | static ContextResolver of(Supplier supplier) { 27 | return of(supplier.get()); 28 | } 29 | 30 | /** 31 | * Resolves a parameter's default value 32 | * if it has been not input by the user 33 | * 34 | * @param context the context 35 | * @param parameter the parameter (null if used the classic way) 36 | * @return the resolved default-value 37 | */ 38 | @Nullable 39 | T resolve( 40 | @NotNull ExecutionContext context, 41 | @Nullable ParameterElement parameter 42 | ) throws ImperatException; 43 | 44 | } 45 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/resolvers/DependencySupplier.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.resolvers; 2 | 3 | import java.util.function.Supplier; 4 | 5 | /** 6 | * A functional interface that supplies a generic dependency 7 | * object. This interface extends the {@link Supplier} interface 8 | * from the Java standard library, allowing it to be used 9 | * directly in scenarios where a {@code Supplier} is expected. 10 | *

11 | * This interface is designed to act as a generic provider 12 | * of dependency objects in various contexts, enabling dependency 13 | * resolution or injection frameworks to handle or supply appropriate 14 | * objects dynamically at runtime. 15 | *

16 | * Being a functional interface, it can be implemented using 17 | * a lambda expression or a method reference to streamline 18 | * the handling of such suppliers. 19 | */ 20 | @FunctionalInterface 21 | public interface DependencySupplier extends Supplier { 22 | 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/resolvers/PermissionResolver.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.resolvers; 2 | 3 | import dev.velix.imperat.command.CommandUsage; 4 | import dev.velix.imperat.command.parameters.CommandParameter; 5 | import dev.velix.imperat.context.Source; 6 | import org.jetbrains.annotations.ApiStatus; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | /** 11 | * Represents a functional way of checking for the permissions 12 | * of the command source/sender. 13 | */ 14 | @ApiStatus.AvailableSince("1.0.0") 15 | public interface PermissionResolver { 16 | 17 | /** 18 | * @param source the source of the command (console or other) 19 | * @param permission the permission 20 | * @return whether this command source/sender has a specific permission 21 | */ 22 | boolean hasPermission(@NotNull S source, @Nullable String permission); 23 | 24 | default boolean hasUsagePermission(S source, @Nullable CommandUsage usage) { 25 | if (usage == null || usage.permission() == null) { 26 | return true; 27 | } 28 | if (!hasPermission(source, usage.permission())) return false; 29 | for (CommandParameter parameter : usage.getParameters()) { 30 | //if (!parameter.isCommand()) continue; 31 | if (parameter.permission() == null) continue; 32 | if (!hasPermission(source, parameter.permission())) 33 | return false; 34 | } 35 | return true; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/resolvers/SourceResolver.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.resolvers; 2 | 3 | import dev.velix.imperat.context.Source; 4 | import dev.velix.imperat.exception.ImperatException; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | /** 8 | * An interface whose single responsibility is to resolve {@link S} 9 | * into {@link R} to allow custom command sources 10 | * 11 | * @param the default platform source 12 | * @param the resulting source 13 | */ 14 | public interface SourceResolver { 15 | 16 | /** 17 | * Resolves {@link S} into {@link R} 18 | * 19 | * @param source the default source within the platform 20 | * @return the resolved source 21 | */ 22 | @NotNull 23 | R resolve(S source) throws ImperatException; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/resolvers/SuggestionResolver.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.resolvers; 2 | 3 | import dev.velix.imperat.command.Command; 4 | import dev.velix.imperat.command.parameters.CommandParameter; 5 | import dev.velix.imperat.context.Source; 6 | import dev.velix.imperat.context.SuggestionContext; 7 | import org.jetbrains.annotations.ApiStatus; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | import java.util.concurrent.CompletableFuture; 13 | 14 | /** 15 | * Represents a suggestion providing interface 16 | * for an argument/parameter 17 | * 18 | * @param the command-sender valueType 19 | * @see CommandParameter 20 | */ 21 | @ApiStatus.AvailableSince("1.0.0") 22 | public interface SuggestionResolver { 23 | 24 | static SuggestionResolver plain(List results) { 25 | return ((context, parameterToComplete) -> results); 26 | } 27 | 28 | static SuggestionResolver plain(String... results) { 29 | return plain(Arrays.asList(results)); 30 | } 31 | 32 | static SuggestionResolver forCommand(Command command) { 33 | List list = new ArrayList<>(); 34 | list.add(command.name()); 35 | list.addAll(command.aliases()); 36 | return plain(list); 37 | } 38 | 39 | 40 | /** 41 | * @param context the context for suggestions 42 | * @param parameter the parameter of the value to complete 43 | * @return the auto-completed suggestions of the current argument 44 | */ 45 | List autoComplete(SuggestionContext context, CommandParameter parameter); 46 | 47 | default CompletableFuture> asyncAutoComplete(SuggestionContext context, CommandParameter parameter) { 48 | return CompletableFuture.supplyAsync(() -> autoComplete(context, parameter)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/util/AnnotationMap.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.util; 2 | 3 | import java.lang.annotation.Annotation; 4 | import java.util.HashMap; 5 | 6 | public final class AnnotationMap extends HashMap, Annotation> { 7 | 8 | public void set(final Annotation annotation) { 9 | this.put(annotation.annotationType(), annotation); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/util/Patterns.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.util; 2 | 3 | import java.util.regex.Pattern; 4 | 5 | public final class Patterns { 6 | 7 | public final static Pattern SINGLE_FLAG = Pattern.compile("-([a-zA-Z]+)"); 8 | public final static Pattern DOUBLE_FLAG = Pattern.compile("--([a-zA-Z]+)"); 9 | 10 | private Patterns() { 11 | throw new AssertionError(); 12 | } 13 | 14 | public static boolean isSingleFlag(String input) { 15 | return SINGLE_FLAG.matcher(input).matches(); 16 | } 17 | 18 | public static boolean isDoubleFlag(String input) { 19 | return DOUBLE_FLAG.matcher(input).matches(); 20 | } 21 | 22 | public static boolean isInputFlag(String input) { 23 | return isSingleFlag(input) || isDoubleFlag(input); 24 | } 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/util/asm/DefaultMethodCallerFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of lamp, licensed under the MIT License. 3 | * 4 | * Copyright (c) Revxrsal 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | package dev.velix.imperat.util.asm; 25 | 26 | import org.jetbrains.annotations.NotNull; 27 | 28 | import java.lang.reflect.Method; 29 | 30 | public final class DefaultMethodCallerFactory implements MethodCallerFactory { 31 | 32 | public static final DefaultMethodCallerFactory INSTANCE = new DefaultMethodCallerFactory(); 33 | 34 | @Override 35 | public @NotNull MethodCaller createFor(@NotNull Method method) throws Throwable { 36 | return MethodCallerFactory.methodHandles().createFor(method); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/util/reflection/FieldAccessor.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.util.reflection; 2 | 3 | /** 4 | * An interface for retrieving the field content. (Credits: TinyProtocol) 5 | * 6 | * @param - field valueType. 7 | * @author Kristian 8 | */ 9 | public interface FieldAccessor { 10 | 11 | /** 12 | * Retrieve the content of a field. 13 | * 14 | * @param target - the targetToLoad object, or NULL for a static field. 15 | * @return The value of the field. 16 | */ 17 | T get(final Object target); 18 | 19 | /** 20 | * Set the content of a field. 21 | * 22 | * @param target - the targetToLoad object, or NULL for a static field. 23 | * @param value - the new value of the field. 24 | */ 25 | void set(final Object target, final Object value); 26 | 27 | /** 28 | * Determine if the given object has this field. 29 | * 30 | * @param target - the object to test. 31 | * @return TRUE if it does, FALSE otherwise. 32 | */ 33 | @SuppressWarnings("unused") 34 | boolean hasField(final Object target); 35 | 36 | } -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/util/text/PaginatedText.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.util.text; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | import java.util.ArrayList; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | public final class PaginatedText { 12 | 13 | private final List objects = new ArrayList<>(); 14 | private final int itemsPerPage; 15 | 16 | @NotNull 17 | private final Map> pages = new HashMap<>(); 18 | 19 | public PaginatedText(int itemsPerPage) { 20 | this.itemsPerPage = itemsPerPage; 21 | } 22 | 23 | public int getMaxPages() { 24 | return pages.size(); 25 | } 26 | 27 | public @Nullable TextPage getPage(int index) { 28 | return pages.get(index); 29 | } 30 | 31 | public void add(T convertible) { 32 | objects.add(convertible); 33 | } 34 | 35 | public void paginate() { 36 | for (int i = 1; i <= objects.size(); i++) { 37 | T obj = objects.get(i - 1); 38 | //calculate the page from its index and the items per page 39 | int page = (int) Math.ceil((double) (i) / (itemsPerPage)); 40 | 41 | pages.compute(page, (index, existingPage) -> { 42 | if (existingPage == null) { 43 | List list = new ArrayList<>(itemsPerPage); 44 | list.add(obj); 45 | return new TextPage<>(page, itemsPerPage, list); 46 | } 47 | 48 | existingPage.add(obj); 49 | return existingPage; 50 | }); 51 | 52 | } 53 | } 54 | 55 | public void clear() { 56 | objects.clear(); 57 | pages.clear(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/util/text/TextPage.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.util.text; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.util.Iterator; 6 | import java.util.List; 7 | 8 | public record TextPage(int index, int capacity, List items) implements Iterable { 9 | 10 | public void add(T obj) { 11 | if (items.size() + 1 > capacity) return; 12 | items.add(obj); 13 | } 14 | 15 | public void remove(T obj) { 16 | items.remove(obj); 17 | } 18 | 19 | public void addAll(List otherItems) { 20 | otherItems.forEach(this::add); 21 | } 22 | 23 | 24 | @Override 25 | public @NotNull Iterator iterator() { 26 | return items.iterator(); 27 | } 28 | 29 | public List asList() { 30 | return items; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /core/src/main/java/dev/velix/imperat/verification/UsageVerifier.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.verification; 2 | 3 | import dev.velix.imperat.command.CommandUsage; 4 | import dev.velix.imperat.context.Source; 5 | import org.jetbrains.annotations.ApiStatus; 6 | 7 | /** 8 | * Verifies that the {@link CommandUsage} is suitable 9 | * to be added to the command 10 | * 11 | *

12 | * Rule(s) recommended following when creating a usage: 13 | *

14 | * 1) A usage MUST have AT LEAST ONE required parameter at the beginning before any other 15 | * parameters whether optional or required 16 | *

17 | * 2) If the usage doesn't have any subcommands, 18 | * it must not be duplicated in a similar parameters pattern 19 | *

20 | * 3) It MUSTN'T have a greedy argument in the middle of the parameters, 21 | * therefore, if you want to add any greedy arguments, 22 | * you should put A SINGLE (not multiple) greedy argument 23 | * at the END of the usage parameters list ONLY 24 | */ 25 | @ApiStatus.AvailableSince("1.0.0") 26 | public interface UsageVerifier { 27 | 28 | static UsageVerifier simpleVerifier() { 29 | return new SimpleVerifier<>(); 30 | } 31 | 32 | static UsageVerifier typeTolerantVerifier() { 33 | return new TypeTolerantVerifier<>(); 34 | } 35 | 36 | /** 37 | * @param usage the usage 38 | * @return Verifies the usage to be acceptable 39 | */ 40 | boolean verify(CommandUsage usage); 41 | 42 | /** 43 | * @param firstUsage the first usage 44 | * @param secondUsage the second usage 45 | * @return whether both usages are similar 46 | * and/or share similar indistinguishable parameters or syntax 47 | */ 48 | boolean areAmbiguous(CommandUsage firstUsage, CommandUsage secondUsage); 49 | } 50 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/advanced/GuildMOTDCommand.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.advanced; 2 | 3 | import dev.velix.imperat.annotations.Command; 4 | 5 | import dev.velix.imperat.annotations.Default; 6 | import dev.velix.imperat.annotations.Flag; 7 | import dev.velix.imperat.annotations.Greedy; 8 | import dev.velix.imperat.annotations.Named; 9 | import dev.velix.imperat.annotations.Usage; 10 | import dev.velix.imperat.components.TestSource; 11 | 12 | import java.time.Duration; 13 | 14 | @Command("motd") 15 | public class GuildMOTDCommand { 16 | 17 | @Usage 18 | public void def(TestSource source) { 19 | source.reply("Default motd execution"); 20 | } 21 | 22 | /*@Usage 23 | public void mainUsage(TestSource source, @Named("message") String msg, @Named("duration") @Default("24h")Duration duration) { 24 | source.reply("Message: '" + msg + "'"); 25 | source.reply("Duration: '" + DurationParser.formatDuration(duration) + "'"); 26 | }*/ 27 | 28 | @Usage 29 | public void mainUsage( 30 | TestSource source, 31 | @Flag("time") @Default("24h") Duration time, 32 | @Named("message") @Greedy String message 33 | ) { 34 | // /motd [-time ] 35 | source.reply("Message: '" + message + "'"); 36 | source.reply("Duration: '" + DurationParser.formatDuration(time) + "'"); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/commands/EmptyCmd.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.commands; 2 | 3 | import dev.velix.imperat.annotations.Command; 4 | 5 | @Command("empty") 6 | public class EmptyCmd { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/commands/KingdomChatCommand.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.commands; 2 | 3 | import dev.velix.imperat.components.TestSource; 4 | import dev.velix.imperat.annotations.Command; 5 | import dev.velix.imperat.annotations.Greedy; 6 | import dev.velix.imperat.annotations.Usage; 7 | 8 | @Command("kingdomchat") 9 | public class KingdomChatCommand { 10 | 11 | @Usage 12 | public void def(TestSource source) { 13 | source.reply("This is the default usage of the kingdomchat command."); 14 | } 15 | 16 | @Usage 17 | public void mainUsage(TestSource source, @Greedy String message) { 18 | source.reply("Your message: " + message); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/commands/MyCustomAnnotation.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.commands; 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, ElementType.METHOD}) 10 | public @interface MyCustomAnnotation { 11 | 12 | String name(); 13 | } 14 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/commands/ParameterDuration.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.commands; 2 | 3 | import dev.velix.imperat.command.parameters.type.BaseParameterType; 4 | import dev.velix.imperat.context.ExecutionContext; 5 | import dev.velix.imperat.context.Source; 6 | import dev.velix.imperat.context.internal.CommandInputStream; 7 | import dev.velix.imperat.exception.ImperatException; 8 | import dev.velix.imperat.exception.SourceException; 9 | import dev.velix.imperat.resolvers.SuggestionResolver; 10 | import dev.velix.imperat.util.TypeWrap; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | 15 | public class ParameterDuration extends BaseParameterType { 16 | 17 | private final SuggestionResolver resolver = SuggestionResolver.plain( 18 | "permanent", "30d", "1y", "5y" 19 | ); 20 | 21 | public ParameterDuration() { 22 | super(TypeWrap.of(Duration.class)); 23 | } 24 | 25 | @Override 26 | public @Nullable Duration resolve( 27 | @NotNull ExecutionContext context, 28 | @NotNull CommandInputStream cis, 29 | String input 30 | ) throws ImperatException { 31 | final long ms = Utilities.convertDurationToMs(input); 32 | if (ms == 0) { 33 | throw new SourceException("Bad duration input"); 34 | } 35 | return new Duration(ms); 36 | } 37 | 38 | @Override 39 | public SuggestionResolver getSuggestionResolver() { 40 | return resolver; 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/commands/RankCommand.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.commands; 2 | 3 | import dev.velix.imperat.annotations.Command; 4 | import dev.velix.imperat.annotations.Default; 5 | import dev.velix.imperat.annotations.Description; 6 | import dev.velix.imperat.annotations.Flag; 7 | import dev.velix.imperat.annotations.Named; 8 | import dev.velix.imperat.annotations.SubCommand; 9 | import dev.velix.imperat.annotations.Switch; 10 | import dev.velix.imperat.components.TestSource; 11 | 12 | @Command("rank") 13 | public class RankCommand { 14 | 15 | @SubCommand("addperm") 16 | @Description("Adds a permission") 17 | public void addPerm( 18 | final TestSource actor, 19 | @Named("rank") final String rank, 20 | @Named("permission") String permission, 21 | @Flag("duration") @Default("permanent") Duration duration, 22 | @Switch("force") final boolean force 23 | ) { 24 | actor.reply("rank= " + rank); 25 | actor.reply("perm= " + permission); 26 | actor.reply("duration= " + duration.toString()); 27 | actor.reply("forced= " + force); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/commands/Test2Command.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.commands; 2 | 3 | import dev.velix.imperat.components.TestSource; 4 | import dev.velix.imperat.annotations.Command; 5 | import dev.velix.imperat.annotations.Named; 6 | import dev.velix.imperat.annotations.SubCommand; 7 | import dev.velix.imperat.annotations.Usage; 8 | 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | @Command("test2") 13 | public class Test2Command { 14 | 15 | @Usage 16 | @SubCommand("help") 17 | public void def(TestSource source) { 18 | source.reply("Send help input"); 19 | // /test2 20 | } 21 | 22 | @SubCommand("array") 23 | public void def(TestSource source, @Named("myArray") String[] array) { 24 | source.reply("SIZE= " + array.length); 25 | for(var entry : array) { 26 | source.reply("-> " + entry); 27 | } 28 | // /test2 array hi hello how are you 29 | } 30 | 31 | @SubCommand("collection") 32 | public void def(TestSource source, @Named("myCollection") List collection) { 33 | source.reply("SIZE= " + collection.size()); 34 | for(var entry : collection) { 35 | source.reply("-> " + entry); 36 | } 37 | } 38 | 39 | @SubCommand("map") 40 | public void def(TestSource source, @Named("myMap") Map map) { 41 | source.reply("SIZE= " + map.size()); 42 | for(var entry : map.entrySet()) { 43 | source.reply("-> " + entry.getKey() + ":" + entry.getValue()); 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/commands/Test3Command.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.commands; 2 | 3 | import dev.velix.imperat.components.TestSource; 4 | import dev.velix.imperat.annotations.Command; 5 | import dev.velix.imperat.annotations.Default; 6 | import dev.velix.imperat.annotations.Named; 7 | import dev.velix.imperat.annotations.SubCommand; 8 | import dev.velix.imperat.annotations.Usage; 9 | import dev.velix.imperat.command.AttachmentMode; 10 | 11 | @Command("test3") 12 | public class Test3Command { 13 | 14 | @Usage 15 | public void def(TestSource source, @Named("input") @Default("hello") String input) { 16 | source.reply("input=" + input); 17 | } 18 | 19 | @SubCommand(value = "sub", attachment = AttachmentMode.EMPTY) 20 | public void subDefaultExecution(TestSource source) { 21 | source.reply("subcommand - default execution !"); 22 | } 23 | 24 | @SubCommand(value = "sub", attachment = AttachmentMode.EMPTY) 25 | public void subMainExecution(TestSource source, @Named("sub-input") String subInput) { 26 | source.reply("sub command input= " + subInput); 27 | } 28 | /* 29 | @SubCommand(value = "sub", attachment = AttachmentMode.EMPTY) 30 | public static class Sub { 31 | @Usage 32 | public void def(TestSource source) { 33 | source.reply("sub command - default execution"); 34 | } 35 | @Usage 36 | public void sub(TestSource source, @Named("sub-input") String subInput) { 37 | source.reply("sub command input= " + subInput); 38 | } 39 | }*/ 40 | } -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/commands/TestCustomAnnotationCmd.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.commands; 2 | 3 | import dev.velix.imperat.components.TestSource; 4 | import dev.velix.imperat.annotations.SubCommand; 5 | import dev.velix.imperat.annotations.Usage; 6 | 7 | @MyCustomAnnotation(name = "testreplacer") 8 | public class TestCustomAnnotationCmd { 9 | 10 | @Usage 11 | public void def(TestSource source) { 12 | source.reply("DEF"); 13 | } 14 | @SubCommand("teto") 15 | interface Teto { 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/commands/annotations/FirstOptionalArgumentCmd.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.commands.annotations; 2 | 3 | import dev.velix.imperat.components.TestSource; 4 | import dev.velix.imperat.annotations.Command; 5 | import dev.velix.imperat.annotations.Default; 6 | import dev.velix.imperat.annotations.Named; 7 | import dev.velix.imperat.annotations.Optional; 8 | import dev.velix.imperat.annotations.SubCommand; 9 | import dev.velix.imperat.annotations.Usage; 10 | 11 | @Command("foa") 12 | public final class FirstOptionalArgumentCmd { 13 | 14 | @Usage 15 | public void def(TestSource source, @Named("num") @Optional @Default("1") Integer num) { 16 | source.reply("Num=" + num); 17 | } 18 | 19 | @SubCommand("sub") 20 | public static class MySub { 21 | 22 | 23 | @Usage 24 | public void defaultUsage(TestSource source, @Named("num") Integer num) { 25 | source.reply("Default execution of sub-command, inherited num='" + num + "'"); 26 | } 27 | 28 | @Usage 29 | public void mainUsage(TestSource source, @Named("num") Integer num, @Named("num2") Integer num2) { 30 | source.reply("Main execution of sub-command, inherited num='" + num + "', num2='" + num2 + "'"); 31 | } 32 | 33 | } 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/commands/annotations/FirstSub.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.commands.annotations; 2 | 3 | import dev.velix.imperat.components.TestSource; 4 | import dev.velix.imperat.annotations.Inherit; 5 | import dev.velix.imperat.annotations.Named; 6 | import dev.velix.imperat.annotations.SubCommand; 7 | import dev.velix.imperat.annotations.Suggest; 8 | import dev.velix.imperat.annotations.Usage; 9 | 10 | @SubCommand("first") 11 | @Inherit(SecondSub.class) 12 | public final class FirstSub { 13 | 14 | @Usage 15 | public void defaultUsage(TestSource source, @Named("otherText") String otherText, @Named("otherText2") String otherText2) { 16 | source.reply("Default execution of first sub-command"); 17 | } 18 | 19 | @Usage 20 | public void cmdUsage(TestSource source, @Named("otherText") String otherText, @Named("otherText2") String otherText2, @Named("arg1") @Suggest({"x", "y", "z", "sexy"}) String arg1) { 21 | source.reply("Executing usage in first's main usage, otherText=" + otherText + ", arg1= " + arg1); 22 | } 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/commands/annotations/KitCommand.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.commands.annotations; 2 | 3 | import dev.velix.imperat.components.TestSource; 4 | import dev.velix.imperat.annotations.Command; 5 | import dev.velix.imperat.annotations.Default; 6 | import dev.velix.imperat.annotations.Named; 7 | import dev.velix.imperat.annotations.Optional; 8 | import dev.velix.imperat.annotations.SubCommand; 9 | import dev.velix.imperat.util.ImperatDebugger; 10 | 11 | @Command("kit") 12 | public final class KitCommand { 13 | 14 | @SubCommand( 15 | "create" 16 | ) 17 | public void createKit(TestSource source, @Named("kit") String kit, @Named("weight") @Optional @Default("1") Integer weight) { 18 | ImperatDebugger.debug("kit=%s, weight=%s", kit, weight); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/commands/annotations/SecondSub.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.commands.annotations; 2 | 3 | import dev.velix.imperat.components.TestSource; 4 | import dev.velix.imperat.annotations.Named; 5 | import dev.velix.imperat.annotations.SubCommand; 6 | import dev.velix.imperat.annotations.Usage; 7 | 8 | @SubCommand("second") 9 | public class SecondSub { 10 | 11 | @Usage 12 | public void defaultUsage(TestSource source, 13 | @Named("otherText") String otherText, 14 | @Named("otherText2") String otherText2, 15 | @Named("arg1") String arg1 16 | ) { 17 | source.reply("Default execution of second sub-command"); 18 | } 19 | 20 | @Usage 21 | public void cmdUsage(TestSource source, 22 | @Named("otherText") String otherText, 23 | @Named("otherText2") String otherText2, 24 | @Named("arg1") String arg1, 25 | @Named("arg1") String arg2) { 26 | source.reply("Executing usage in first's main usage," + 27 | " otherText=" + otherText + ", arg1= " + arg1 + ", arg2= " + arg2); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/commands/annotations/contextresolver/ContextResolvingCmd.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.commands.annotations.contextresolver; 2 | 3 | 4 | import dev.velix.imperat.components.TestSource; 5 | import dev.velix.imperat.annotations.Command; 6 | import dev.velix.imperat.annotations.ContextResolved; 7 | import dev.velix.imperat.annotations.SubCommand; 8 | import dev.velix.imperat.annotations.Usage; 9 | import dev.velix.imperat.commands.annotations.examples.Group; 10 | import org.junit.jupiter.api.Assertions; 11 | 12 | @Command("ctx") 13 | public final class ContextResolvingCmd { 14 | 15 | @Usage 16 | public void def(TestSource source, @ContextResolved PlayerData data) { 17 | Assertions.assertEquals(source.name(), data.name()); 18 | } 19 | 20 | @SubCommand("sub") 21 | public void defSub(TestSource source, @ContextResolved Group group) { 22 | //throws an error 23 | System.out.println("DEFAULT SUBCMD EXECUTION, CONTEXT RESOLVED GROUP=" + group.name()); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/commands/annotations/contextresolver/PlayerData.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.commands.annotations.contextresolver; 2 | 3 | import java.util.UUID; 4 | 5 | public record PlayerData(String name, UUID uuid) { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/commands/annotations/contextresolver/PlayerDataContextResolver.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.commands.annotations.contextresolver; 2 | 3 | import dev.velix.imperat.components.TestSource; 4 | import dev.velix.imperat.annotations.base.element.ParameterElement; 5 | import dev.velix.imperat.context.ExecutionContext; 6 | import dev.velix.imperat.exception.ImperatException; 7 | import dev.velix.imperat.resolvers.ContextResolver; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | public class PlayerDataContextResolver implements ContextResolver { 12 | 13 | /** 14 | * Resolves a parameter's default value 15 | * if it has been not input by the user 16 | * 17 | * @param context the context 18 | * @param parameter the parameter (null if used the classic way) 19 | * @return the resolved default-value 20 | */ 21 | @Override 22 | public @Nullable PlayerData resolve( 23 | @NotNull ExecutionContext context, 24 | @Nullable ParameterElement parameter 25 | ) throws ImperatException { 26 | TestSource source = context.source(); 27 | return new PlayerData(source.name(), source.uuid()); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/commands/annotations/examples/GitCommand.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.commands.annotations.examples; 2 | 3 | import dev.velix.imperat.components.TestSource; 4 | import dev.velix.imperat.annotations.Command; 5 | import dev.velix.imperat.annotations.Flag; 6 | import dev.velix.imperat.annotations.SubCommand; 7 | import dev.velix.imperat.annotations.Usage; 8 | 9 | @Command("git") 10 | public class GitCommand { 11 | 12 | @Usage 13 | public void def(TestSource source) { 14 | source.reply("default usage"); 15 | } 16 | 17 | @SubCommand("commit") 18 | public void commit(TestSource source, @Flag({"message", "m"}) String msg) { 19 | 20 | // /git commit -m 21 | System.out.println("Commiting with msg: " + msg); 22 | 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/commands/annotations/examples/Group.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.commands.annotations.examples; 2 | 3 | public record Group(String name) { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/commands/annotations/examples/GroupRegistry.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.commands.annotations.examples; 2 | 3 | import dev.velix.imperat.util.Registry; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | public final class GroupRegistry extends Registry { 9 | 10 | private final static GroupRegistry instance = new GroupRegistry(); 11 | private final Map userGroups = new HashMap<>(); 12 | 13 | GroupRegistry() { 14 | Group g = new Group("member"); 15 | setData("member", g); 16 | setData("mod", new Group("mod")); 17 | setData("srmod", new Group("srmod")); 18 | setData("owner", new Group("owner")); 19 | 20 | setGroup("mqzen", g); 21 | } 22 | 23 | public static GroupRegistry getInstance() { 24 | return instance; 25 | } 26 | 27 | public void setGroup(String name, Group group) { 28 | userGroups.put(name, group); 29 | } 30 | 31 | public Group getGroup(String username) { 32 | return userGroups.get(username); 33 | } 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/commands/annotations/examples/GroupSuggestionResolver.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.commands.annotations.examples; 2 | 3 | import dev.velix.imperat.components.TestSource; 4 | import dev.velix.imperat.command.parameters.CommandParameter; 5 | import dev.velix.imperat.context.SuggestionContext; 6 | import dev.velix.imperat.resolvers.SuggestionResolver; 7 | 8 | import java.util.List; 9 | 10 | public class GroupSuggestionResolver implements SuggestionResolver { 11 | 12 | 13 | @Override 14 | public List autoComplete( 15 | SuggestionContext context, 16 | CommandParameter parameter 17 | ) { 18 | return GroupRegistry.getInstance().getAll() 19 | .stream().map(Group::name) 20 | .toList(); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/commands/annotations/examples/MessageCmd.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.commands.annotations.examples; 2 | 3 | import dev.velix.imperat.components.TestSource; 4 | import dev.velix.imperat.annotations.Command; 5 | import dev.velix.imperat.annotations.Greedy; 6 | import dev.velix.imperat.annotations.Named; 7 | import dev.velix.imperat.annotations.Suggest; 8 | import dev.velix.imperat.annotations.Usage; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | @Command({"message"}) 12 | public class MessageCmd { 13 | 14 | @Usage 15 | public void exec(@NotNull TestSource sender, 16 | @Named("target") @NotNull String target, 17 | @Named("message") @Suggest({"this is a long greedy", "some sentence", "idk"}) @Greedy String message) { 18 | sender.reply("sending to '" + target + 19 | "' the message '" + message + "'"); 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/commands/annotations/examples/OptionalArgCommand.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.commands.annotations.examples; 2 | 3 | import dev.velix.imperat.components.TestSource; 4 | import dev.velix.imperat.annotations.Command; 5 | import dev.velix.imperat.annotations.Default; 6 | import dev.velix.imperat.annotations.Named; 7 | import dev.velix.imperat.annotations.Usage; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | @Command("opt") 11 | public class OptionalArgCommand { 12 | 13 | @Usage 14 | public void mainUsage(TestSource source, @Named("a") String arg1, @Named("b") @Default("hello-world") @NotNull String b) { 15 | source.reply("A=" + arg1 + ", B= " + b); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/components/TestImperat.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.components; 2 | 3 | import dev.velix.imperat.BaseImperat; 4 | import dev.velix.imperat.ImperatConfig; 5 | import dev.velix.imperat.command.Command; 6 | 7 | import java.io.PrintStream; 8 | 9 | public final class TestImperat extends BaseImperat { 10 | 11 | TestImperat(ImperatConfig config) { 12 | super(config); 13 | } 14 | 15 | /** 16 | * Wraps the sender into a built-in command-sender valueType 17 | * 18 | * @param sender the sender's actual value 19 | * @return the wrapped command-sender valueType 20 | */ 21 | @Override 22 | public TestSource wrapSender(Object sender) { 23 | return new TestSource((PrintStream) sender); 24 | } 25 | 26 | /** 27 | * @return the platform of the module 28 | */ 29 | @Override 30 | public Object getPlatform() { 31 | return null; 32 | } 33 | 34 | 35 | /** 36 | * 37 | */ 38 | @Override 39 | public void shutdownPlatform() { 40 | 41 | } 42 | 43 | 44 | @Override 45 | public void registerCommand(Command command) { 46 | super.registerCommand(command); 47 | command.visualizeTree(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/components/TestImperatConfig.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.components; 2 | 3 | import dev.velix.imperat.ConfigBuilder; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public final class TestImperatConfig extends ConfigBuilder { 7 | 8 | public TestImperatConfig() { 9 | super(); 10 | } 11 | 12 | @Override 13 | public @NotNull TestImperat build() { 14 | return new TestImperat(config); 15 | } 16 | 17 | public static TestImperatConfig builder() { 18 | return new TestImperatConfig(); 19 | } 20 | 21 | 22 | } 23 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/components/TestPlaceholder.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.components; 2 | 3 | import static dev.velix.imperat.TestRun.IMPERAT; 4 | 5 | import dev.velix.imperat.placeholders.Placeholder; 6 | import dev.velix.imperat.placeholders.PlaceholderRegistry; 7 | import org.junit.jupiter.api.Assertions; 8 | import org.junit.jupiter.api.Test; 9 | 10 | public class TestPlaceholder { 11 | PlaceholderRegistry placeholderRegistry = PlaceholderRegistry.createDefault(IMPERAT.config()); 12 | 13 | public TestPlaceholder() { 14 | placeholderRegistry.setData("%description%", Placeholder.builder("%description%") 15 | .resolver(((placeHolderId, imperat) -> "My desc")).build()); 16 | } 17 | 18 | @Test 19 | public void test1() { 20 | String str = "This is my desc: %description%"; 21 | String value = placeholderRegistry.resolvedString(str); 22 | //System.out.println(value.replaceAll("%description%", "My desc")); 23 | Assertions.assertEquals("This is my desc: My desc", value); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/components/TestSource.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.components; 2 | 3 | import dev.velix.imperat.context.Source; 4 | 5 | import java.io.PrintStream; 6 | 7 | public record TestSource(PrintStream origin) implements Source { 8 | 9 | public void sendMsg(String msg) { 10 | origin.println(msg); 11 | } 12 | 13 | @Override 14 | public String name() { 15 | return "mqzen"; 16 | } 17 | 18 | @Override 19 | public void reply(String message) { 20 | sendMsg(message); 21 | } 22 | 23 | @Override 24 | public void warn(String message) { 25 | sendMsg(message); 26 | } 27 | 28 | @Override 29 | public void error(String message) { 30 | sendMsg(message); 31 | } 32 | 33 | @Override 34 | public boolean isConsole() { 35 | return true; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/misc/CustomCooldownProcessor.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.misc; 2 | 3 | import dev.velix.imperat.Imperat; 4 | import dev.velix.imperat.command.CommandUsage; 5 | import dev.velix.imperat.command.cooldown.CooldownHandler; 6 | import dev.velix.imperat.command.processors.CommandPreProcessor; 7 | import dev.velix.imperat.context.Context; 8 | import dev.velix.imperat.context.Source; 9 | import dev.velix.imperat.exception.CooldownException; 10 | import dev.velix.imperat.exception.ImperatException; 11 | 12 | public final class CustomCooldownProcessor implements CommandPreProcessor { 13 | /** 14 | * Processes context BEFORE the resolving operation. 15 | * 16 | * @param imperat the api 17 | * @param context the context 18 | * @param usage The usage detected 19 | * @throws ImperatException the exception to throw if something happens 20 | */ 21 | @Override 22 | public void process(Imperat imperat, Context context, CommandUsage usage) throws ImperatException { 23 | CooldownHandler cooldownHandler = usage.getCooldownHandler(); 24 | S source = context.source(); 25 | 26 | if (cooldownHandler.hasCooldown(source) && !imperat.config().getPermissionResolver().hasPermission(source, "yourpermission")) { 27 | //if there's a cooldown and the source doesn't have a specific permission, let's send him the cooldown message through the exception below 28 | throw new CooldownException( 29 | cooldownHandler.getUsageCooldown().orElseThrow().toMillis(), 30 | cooldownHandler.getLastTimeExecuted(source).orElse(0L) 31 | ); 32 | } 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/misc/CustomEnum.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.misc; 2 | 3 | public enum CustomEnum { 4 | 5 | VALUE_1, VALUE_2, VALUE_3; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/misc/CustomEnumParameterType.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.misc; 2 | 3 | import dev.velix.imperat.components.TestSource; 4 | import dev.velix.imperat.command.parameters.type.BaseParameterType; 5 | import dev.velix.imperat.context.ExecutionContext; 6 | import dev.velix.imperat.context.internal.CommandInputStream; 7 | import dev.velix.imperat.exception.ImperatException; 8 | import dev.velix.imperat.util.ImperatDebugger; 9 | import org.jetbrains.annotations.NotNull; 10 | import org.jetbrains.annotations.Nullable; 11 | 12 | public class CustomEnumParameterType extends BaseParameterType { 13 | 14 | public CustomEnumParameterType() { 15 | super(CustomEnum.class); 16 | } 17 | 18 | @Override public @Nullable CustomEnum resolve( 19 | @NotNull ExecutionContext context, 20 | @NotNull CommandInputStream inputStream, 21 | @NotNull String input 22 | ) throws ImperatException { 23 | ImperatDebugger.debug("Running resolve for CustomEnumParameterType"); 24 | return CustomEnum.valueOf(input); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/misc/ParameterGroup.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.misc; 2 | 3 | import dev.velix.imperat.components.TestSource; 4 | import dev.velix.imperat.command.parameters.type.BaseParameterType; 5 | import dev.velix.imperat.commands.annotations.examples.Group; 6 | import dev.velix.imperat.commands.annotations.examples.GroupRegistry; 7 | import dev.velix.imperat.commands.annotations.examples.GroupSuggestionResolver; 8 | import dev.velix.imperat.context.ExecutionContext; 9 | import dev.velix.imperat.context.internal.CommandInputStream; 10 | import dev.velix.imperat.exception.ImperatException; 11 | import dev.velix.imperat.exception.SourceException; 12 | import dev.velix.imperat.resolvers.SuggestionResolver; 13 | import dev.velix.imperat.util.TypeWrap; 14 | import org.jetbrains.annotations.NotNull; 15 | import org.jetbrains.annotations.Nullable; 16 | 17 | public final class ParameterGroup extends BaseParameterType { 18 | private final GroupSuggestionResolver suggestionResolver = new GroupSuggestionResolver(); 19 | 20 | public ParameterGroup() { 21 | super(TypeWrap.of(Group.class)); 22 | //static plain suggestions 23 | } 24 | 25 | @Override 26 | public @Nullable Group resolve( 27 | @NotNull ExecutionContext context, 28 | @NotNull CommandInputStream commandInputStream, 29 | String input) throws ImperatException { 30 | String raw = commandInputStream.currentRaw().orElse(null); 31 | if (raw == null) { 32 | return null; 33 | } 34 | return GroupRegistry.getInstance().getData(raw) 35 | .orElseThrow(() -> new SourceException("Unknown group '%s'", raw)); 36 | } 37 | 38 | @Override 39 | public SuggestionResolver getSuggestionResolver() { 40 | return suggestionResolver; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/misc/Test4Cmd.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.misc; 2 | 3 | import dev.velix.imperat.components.TestSource; 4 | import dev.velix.imperat.annotations.Command; 5 | import dev.velix.imperat.annotations.Named; 6 | import dev.velix.imperat.annotations.Usage; 7 | 8 | @Command("test4") 9 | public class Test4Cmd { 10 | 11 | @Usage 12 | public void exec(TestSource source, @Named("enumHere") CustomEnum customEnum) { 13 | source.reply("Custom enum input: " + customEnum.name()); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/processors/CustomPostProcessor.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.processors; 2 | 3 | import dev.velix.imperat.Imperat; 4 | import dev.velix.imperat.TestRun; 5 | import dev.velix.imperat.components.TestSource; 6 | import dev.velix.imperat.command.processors.CommandPostProcessor; 7 | import dev.velix.imperat.context.ResolvedContext; 8 | import dev.velix.imperat.exception.ImperatException; 9 | 10 | public class CustomPostProcessor implements CommandPostProcessor { 11 | @Override 12 | public void process( 13 | Imperat imperat, 14 | ResolvedContext context 15 | ) throws ImperatException { 16 | TestRun.POST_PROCESSOR_INT++; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /core/src/test/java/dev/velix/imperat/processors/CustomPreProcessor.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.processors; 2 | 3 | import dev.velix.imperat.Imperat; 4 | import dev.velix.imperat.TestRun; 5 | import dev.velix.imperat.components.TestSource; 6 | import dev.velix.imperat.command.CommandUsage; 7 | import dev.velix.imperat.command.processors.CommandPreProcessor; 8 | import dev.velix.imperat.context.Context; 9 | import dev.velix.imperat.exception.ImperatException; 10 | 11 | public final class CustomPreProcessor implements CommandPreProcessor { 12 | @Override 13 | public void process( 14 | Imperat imperat, 15 | Context context, 16 | CommandUsage usage 17 | ) throws ImperatException { 18 | TestRun.PRE_PROCESSOR_INT++; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VelixDevelopments/Imperat/3f780b74ac57566141f63d1164bdfa3ebbdab6b3/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VelixDevelopments/Imperat/3f780b74ac57566141f63d1164bdfa3ebbdab6b3/logo.png -------------------------------------------------------------------------------- /minestom/build.gradle: -------------------------------------------------------------------------------- 1 | repositories { 2 | mavenCentral() 3 | maven { url "https://jitpack.io" } 4 | } 5 | 6 | compileJava.sourceCompatibility = '21' 7 | compileJava.targetCompatibility = '21' 8 | 9 | dependencies { 10 | compileOnly project(":core") 11 | compileOnly 'net.minestom:minestom-snapshots:620ebe5d6b' 12 | } 13 | 14 | java { 15 | toolchain { 16 | languageVersion = JavaLanguageVersion.of(21) 17 | } 18 | } -------------------------------------------------------------------------------- /minestom/src/main/java/dev/velix/imperat/ArgumentDecorator.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat; 2 | 3 | import dev.velix.imperat.command.parameters.CommandParameter; 4 | import net.minestom.server.command.ArgumentParserType; 5 | import net.minestom.server.command.CommandSender; 6 | import net.minestom.server.command.builder.arguments.Argument; 7 | import net.minestom.server.command.builder.exception.ArgumentSyntaxException; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | final class ArgumentDecorator extends Argument { 11 | 12 | private final CommandParameter parameter; 13 | private final Argument argument; 14 | 15 | ArgumentDecorator(CommandParameter parameter, Argument argument) { 16 | super(argument.getId(), argument.allowSpace(), argument.useRemaining()); 17 | this.parameter = parameter; 18 | this.argument = argument; 19 | } 20 | 21 | 22 | @Override 23 | public @NotNull T parse(@NotNull CommandSender sender, @NotNull String input) throws ArgumentSyntaxException { 24 | return argument.parse(sender, input); 25 | } 26 | 27 | @Override 28 | public ArgumentParserType parser() { 29 | return argument.parser(); 30 | } 31 | 32 | 33 | @Override 34 | public boolean isOptional() { 35 | return parameter.isOptional() && super.isOptional(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /minestom/src/main/java/dev/velix/imperat/InternalMinestomCommand.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat; 2 | 3 | import static dev.velix.imperat.SyntaxDataLoader.loadArguments; 4 | import static dev.velix.imperat.SyntaxDataLoader.loadCondition; 5 | import static dev.velix.imperat.SyntaxDataLoader.loadExecutor; 6 | 7 | import net.minestom.server.command.builder.Command; 8 | 9 | final class InternalMinestomCommand extends Command { 10 | 11 | MinestomImperat imperat; 12 | dev.velix.imperat.command.Command imperatCommand; 13 | 14 | InternalMinestomCommand(MinestomImperat imperat, dev.velix.imperat.command.Command imperatCommand) { 15 | super(imperatCommand.name(), imperatCommand.aliases().toArray(new String[0])); 16 | this.imperat = imperat; 17 | this.imperatCommand = imperatCommand; 18 | 19 | this.setCondition( 20 | (sender, commandString) -> imperat.config().getPermissionResolver().hasPermission( 21 | imperat.wrapSender(sender), imperatCommand.permission() 22 | ) 23 | ); 24 | 25 | this.setDefaultExecutor( 26 | (commandSender, commandContext) -> 27 | imperat.dispatch(imperat.wrapSender(commandSender), 28 | commandContext.getCommandName(), commandContext.getInput()) 29 | ); 30 | 31 | for (var usage : imperatCommand.usages()) { 32 | addConditionalSyntax( 33 | loadCondition(imperat, usage), 34 | loadExecutor(imperat), 35 | loadArguments(imperat, imperatCommand, usage) 36 | ); 37 | } 38 | 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /minestom/src/main/java/dev/velix/imperat/MinestomConfigBuilder.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat; 2 | 3 | import dev.velix.imperat.exception.SourceException; 4 | import dev.velix.imperat.exception.UnknownPlayerException; 5 | import net.minestom.server.ServerProcess; 6 | import net.minestom.server.command.CommandSender; 7 | import net.minestom.server.entity.Player; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | public final class MinestomConfigBuilder extends ConfigBuilder { 11 | 12 | private final ServerProcess serverProcess; 13 | 14 | MinestomConfigBuilder(@NotNull ServerProcess serverProcess) { 15 | this.serverProcess = serverProcess; 16 | registerDefaultResolvers(); 17 | addThrowableHandlers(); 18 | } 19 | 20 | private void registerDefaultResolvers() { 21 | config.registerSourceResolver(CommandSender.class, MinestomSource::origin); 22 | 23 | config.registerSourceResolver(Player.class, source -> { 24 | if (source.isConsole()) { 25 | throw new SourceException("Only players are allowed to do this!"); 26 | } 27 | return source.asPlayer(); 28 | }); 29 | } 30 | 31 | private void addThrowableHandlers() { 32 | config.setThrowableResolver( 33 | UnknownPlayerException.class, (exception, imperat, context) -> 34 | context.source().error("A player with the name '" + exception.getName() + "' is not online.") 35 | ); 36 | } 37 | 38 | @Override 39 | public @NotNull MinestomImperat build() { 40 | return new MinestomImperat(serverProcess, config); 41 | } 42 | } -------------------------------------------------------------------------------- /minestom/src/main/java/dev/velix/imperat/exception/UnknownPlayerException.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.exception; 2 | 3 | public class UnknownPlayerException extends ImperatException { 4 | 5 | private final String name; 6 | 7 | public UnknownPlayerException(final String name) { 8 | this.name = name; 9 | } 10 | 11 | public String getName() { 12 | return name; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /paper/build.gradle: -------------------------------------------------------------------------------- 1 | repositories { 2 | mavenCentral() 3 | maven { url = 'https://oss.sonatype.org/content/repositories/snapshots' } 4 | maven { url = 'https://oss.sonatype.org/content/repositories/central' } 5 | maven { 6 | url = "https://repo.papermc.io/repository/maven-public/" 7 | } 8 | maven { 9 | url = "https://libraries.minecraft.net" 10 | } 11 | } 12 | 13 | dependencies { 14 | compileOnly("com.mojang:brigadier:1.0.18") 15 | compileOnlyApi("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT") 16 | } 17 | 18 | processTestResources { 19 | duplicatesStrategy = DuplicatesStrategy.EXCLUDE 20 | } 21 | 22 | java { 23 | toolchain.languageVersion.set(JavaLanguageVersion.of(17)) 24 | } -------------------------------------------------------------------------------- /paper/src/main/java/com/destroystokyo/paper/event/brigadier/AsyncPlayerSendCommandsEvent.java: -------------------------------------------------------------------------------- 1 | package com.destroystokyo.paper.event.brigadier; 2 | 3 | import com.mojang.brigadier.tree.RootCommandNode; 4 | import io.papermc.paper.command.brigadier.CommandSourceStack; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.entity.Player; 7 | import org.bukkit.event.HandlerList; 8 | import org.bukkit.event.player.PlayerEvent; 9 | import org.jetbrains.annotations.ApiStatus; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | public class AsyncPlayerSendCommandsEvent extends PlayerEvent { 13 | 14 | private static final HandlerList HANDLER_LIST = new HandlerList(); 15 | private final RootCommandNode node; 16 | private final boolean hasFiredAsync; 17 | 18 | @ApiStatus.Internal 19 | public AsyncPlayerSendCommandsEvent(Player player, RootCommandNode node, boolean hasFiredAsync) { 20 | super(player, !Bukkit.isPrimaryThread()); 21 | this.node = node; 22 | this.hasFiredAsync = hasFiredAsync; 23 | } 24 | 25 | public RootCommandNode getCommandNode() { 26 | return this.node; 27 | } 28 | 29 | public boolean hasFiredAsync() { 30 | return this.hasFiredAsync; 31 | } 32 | 33 | public @NotNull HandlerList getHandlers() { 34 | return HANDLER_LIST; 35 | } 36 | 37 | public static HandlerList getHandlerList() { 38 | return HANDLER_LIST; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /paper/src/main/java/io/papermc/paper/command/brigadier/CommandSourceStack.java: -------------------------------------------------------------------------------- 1 | package io.papermc.paper.command.brigadier; 2 | 3 | import org.bukkit.Location; 4 | import org.bukkit.command.CommandSender; 5 | import org.bukkit.entity.Entity; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | public interface CommandSourceStack { 9 | Location getLocation(); 10 | 11 | CommandSender getSender(); 12 | 13 | @Nullable 14 | Entity getExecutor(); 15 | } 16 | -------------------------------------------------------------------------------- /paper/src/main/java/io/papermc/paper/command/brigadier/Commands.java: -------------------------------------------------------------------------------- 1 | package io.papermc.paper.command.brigadier; 2 | 3 | import com.mojang.brigadier.CommandDispatcher; 4 | import com.mojang.brigadier.arguments.ArgumentType; 5 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 6 | import com.mojang.brigadier.builder.RequiredArgumentBuilder; 7 | import com.mojang.brigadier.tree.LiteralCommandNode; 8 | import io.papermc.paper.plugin.configuration.PluginMeta; 9 | import io.papermc.paper.plugin.lifecycle.event.registrar.Registrar; 10 | import org.jetbrains.annotations.Nullable; 11 | import org.jetbrains.annotations.Unmodifiable; 12 | 13 | import java.util.Collection; 14 | import java.util.Set; 15 | 16 | public interface Commands extends Registrar { 17 | 18 | static LiteralArgumentBuilder literal(String literal) { 19 | return LiteralArgumentBuilder.literal(literal); 20 | } 21 | 22 | static RequiredArgumentBuilder argument(String name, ArgumentType argumentType) { 23 | return RequiredArgumentBuilder.argument(name, argumentType); 24 | } 25 | 26 | CommandDispatcher getDispatcher(); 27 | 28 | default @Unmodifiable Set register(LiteralCommandNode node) { 29 | return null; 30 | } 31 | 32 | default @Unmodifiable Set register(LiteralCommandNode node, @Nullable String description) { 33 | return null; 34 | } 35 | 36 | default @Unmodifiable Set register(LiteralCommandNode node, Collection aliases) { 37 | return null; 38 | } 39 | 40 | @Unmodifiable 41 | Set register(LiteralCommandNode var1, @Nullable String var2, Collection var3); 42 | 43 | @Unmodifiable 44 | Set register(PluginMeta var1, LiteralCommandNode var2, @Nullable String var3, Collection var4); 45 | 46 | } 47 | -------------------------------------------------------------------------------- /qodana.yaml: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------------------------------------# 2 | # Qodana analysis is configured by qodana.yaml file # 3 | # https://www.jetbrains.com/help/qodana/qodana-yaml.html # 4 | #-------------------------------------------------------------------------------# 5 | version: "1.0" 6 | 7 | #Specify inspection profile for code analysis 8 | profile: 9 | name: qodana.starter 10 | 11 | #Enable inspections 12 | #include: 13 | # - name: 14 | 15 | #Disable inspections 16 | #exclude: 17 | # - name: 18 | # paths: 19 | # - 20 | 21 | projectJDK: 17 #(Applied in CI/CD pipeline) 22 | 23 | #Execute shell command before Qodana execution (Applied in CI/CD pipeline) 24 | #bootstrap: sh ./prepare-qodana.sh 25 | 26 | #Install IDE plugins before Qodana execution (Applied in CI/CD pipeline) 27 | #plugins: 28 | # - id: #(plugin id can be found at https://plugins.jetbrains.com) 29 | 30 | #Specify Qodana linter for analysis (Applied in CI/CD pipeline) 31 | linter: jetbrains/qodana-jvm-community:latest 32 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'Imperat' 2 | 3 | include 'core' 4 | include 'bukkit' 5 | include 'paper' 6 | include 'bungee' 7 | include 'velocity' 8 | include 'brigadier' 9 | include 'adventure' 10 | include 'cli' 11 | include 'velocity' 12 | include 'minestom' 13 | 14 | -------------------------------------------------------------------------------- /velocity/build.gradle: -------------------------------------------------------------------------------- 1 | repositories { 2 | mavenCentral() 3 | maven { 4 | name = 'papermc' 5 | url = 'https://repo.papermc.io/repository/maven-public/' 6 | } 7 | maven { 8 | url = "https://libraries.minecraft.net" 9 | } 10 | } 11 | 12 | dependencies { 13 | compileOnly project(":core") 14 | compileOnly("com.mojang:brigadier:1.0.18") 15 | api project(":brigadier") 16 | 17 | compileOnly 'com.velocitypowered:velocity-api:3.4.0-SNAPSHOT' 18 | annotationProcessor 'com.velocitypowered:velocity-api:3.4.0-SNAPSHOT' 19 | } 20 | -------------------------------------------------------------------------------- /velocity/src/main/java/dev/velix/imperat/VelocitySource.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat; 2 | 3 | import com.velocitypowered.api.command.CommandSource; 4 | import com.velocitypowered.api.proxy.ConsoleCommandSource; 5 | import com.velocitypowered.api.proxy.Player; 6 | import dev.velix.imperat.context.Source; 7 | import net.kyori.adventure.text.ComponentLike; 8 | 9 | import java.util.UUID; 10 | 11 | public class VelocitySource implements Source { 12 | 13 | private final CommandSource origin; 14 | 15 | VelocitySource(CommandSource origin) { 16 | this.origin = origin; 17 | } 18 | 19 | @Override 20 | public String name() { 21 | return origin instanceof Player pl ? pl.getUsername() : "CONSOLE"; 22 | } 23 | 24 | @Override 25 | public CommandSource origin() { 26 | return origin; 27 | } 28 | 29 | @Override 30 | public void reply(String message) { 31 | origin.sendRichMessage(message); 32 | } 33 | 34 | public void reply(ComponentLike component) { 35 | origin.sendMessage(component); 36 | } 37 | 38 | @Override 39 | public void warn(String message) { 40 | origin.sendRichMessage("" + message + ""); 41 | } 42 | 43 | @Override 44 | public void error(String message) { 45 | origin.sendRichMessage("" + message + ""); 46 | } 47 | 48 | @Override 49 | public boolean isConsole() { 50 | return origin instanceof ConsoleCommandSource; 51 | } 52 | 53 | @Override 54 | public UUID uuid() { 55 | return this.isConsole() ? CONSOLE_UUID : this.asPlayer().getUniqueId(); 56 | } 57 | 58 | public ConsoleCommandSource asConsole() { 59 | return (ConsoleCommandSource) origin; 60 | } 61 | 62 | public Player asPlayer() { 63 | return (Player) origin; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /velocity/src/main/java/dev/velix/imperat/exception/UnknownPlayerException.java: -------------------------------------------------------------------------------- 1 | package dev.velix.imperat.exception; 2 | 3 | public class UnknownPlayerException extends ImperatException { 4 | 5 | private final String name; 6 | 7 | public UnknownPlayerException(final String name) { 8 | this.name = name; 9 | } 10 | 11 | public String getName() { 12 | return name; 13 | } 14 | 15 | } 16 | --------------------------------------------------------------------------------