├── .gitignore ├── .idea ├── .gitignore ├── codeStyles │ └── codeStyleConfig.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── kotlinc.xml ├── misc.xml ├── uiDesigner.xml └── vcs.xml ├── README.MD ├── bungee ├── build.gradle └── src │ └── main │ └── java │ └── io │ └── github │ └── mqzn │ └── commands │ ├── BungeeCaption.java │ ├── BungeeCommandManager.java │ ├── BungeeCommandRequirement.java │ ├── BungeeCommandSyntaxBuilder.java │ ├── BungeeSenderWrapper.java │ ├── BungeeSubCommandBuilder.java │ ├── InternalBungeeCommand.java │ ├── arguments │ ├── ArgumentOnlinePlayer.java │ └── BungeeArgument.java │ └── help │ └── BungeeCommandHelpProvider.java ├── common ├── build.gradle └── src │ ├── main │ └── java │ │ └── io │ │ └── github │ │ └── mqzn │ │ └── commands │ │ ├── annotations │ │ ├── AnnotationParser.java │ │ ├── base │ │ │ ├── Arg.java │ │ │ ├── Command.java │ │ │ ├── CommandsGroup.java │ │ │ ├── Cooldown.java │ │ │ ├── Default.java │ │ │ ├── ExecutionMeta.java │ │ │ ├── Flag.java │ │ │ ├── Greedy.java │ │ │ ├── Range.java │ │ │ └── Suggest.java │ │ └── subcommands │ │ │ ├── SubCommand.java │ │ │ ├── SubCommandExecution.java │ │ │ ├── SubCommandInfo.java │ │ │ └── SubCommands.java │ │ ├── arguments │ │ ├── AbstractArgument.java │ │ ├── Argument.java │ │ ├── ArgumentBoolean.java │ │ ├── ArgumentData.java │ │ ├── ArgumentDouble.java │ │ ├── ArgumentEnum.java │ │ ├── ArgumentFloat.java │ │ ├── ArgumentInteger.java │ │ ├── ArgumentLiteral.java │ │ ├── ArgumentLong.java │ │ ├── ArgumentNumber.java │ │ ├── ArgumentStringArray.java │ │ └── ArgumentWord.java │ │ ├── base │ │ ├── Command.java │ │ ├── CommandInfo.java │ │ ├── CommandRequirement.java │ │ ├── Information.java │ │ ├── SenderProvider.java │ │ ├── SenderWrapper.java │ │ ├── SuggestionProvider.java │ │ ├── caption │ │ │ ├── Caption.java │ │ │ ├── CaptionKey.java │ │ │ ├── CaptionRegistry.java │ │ │ └── Message.java │ │ ├── context │ │ │ ├── CommandArgs.java │ │ │ ├── CommandContext.java │ │ │ ├── Context.java │ │ │ └── DelegateCommandContext.java │ │ ├── cooldown │ │ │ ├── CommandCooldown.java │ │ │ └── CooldownCaption.java │ │ ├── manager │ │ │ ├── AbstractCommandManager.java │ │ │ ├── AmbiguityChecker.java │ │ │ ├── ArgumentNumberComparator.java │ │ │ ├── ArgumentNumberSuggestionProcessor.java │ │ │ ├── ArgumentTypeRegistry.java │ │ │ ├── CommandExecutionCoordinator.java │ │ │ ├── CommandManager.java │ │ │ ├── CommandSuggestionEngine.java │ │ │ ├── FlagRegistry.java │ │ │ ├── SenderProviderRegistry.java │ │ │ ├── SuggestionProviderRegistry.java │ │ │ └── flags │ │ │ │ ├── CommandFlag.java │ │ │ │ ├── ContextFlagRegistry.java │ │ │ │ └── FlagInfo.java │ │ └── syntax │ │ │ ├── CommandAliases.java │ │ │ ├── CommandExecution.java │ │ │ ├── CommandSyntax.java │ │ │ ├── CommandSyntaxBuilder.java │ │ │ ├── SubCommandBuilder.java │ │ │ ├── SubCommandSyntax.java │ │ │ ├── SyntaxFlags.java │ │ │ └── tree │ │ │ ├── CommandTree.java │ │ │ └── SubCommandArgumentTree.java │ │ ├── exceptions │ │ ├── CommandException.java │ │ ├── CommandExceptionHandler.java │ │ ├── UnknownCommandSenderType.java │ │ └── types │ │ │ ├── ArgumentParseException.java │ │ │ └── SyntaxAmbiguityException.java │ │ ├── help │ │ ├── CommandHelpProvider.java │ │ ├── CommandHelpStyle.java │ │ ├── CommandSyntaxPageDisplayer.java │ │ ├── SubCommandHelp.java │ │ └── UnknownPageCaption.java │ │ └── utilities │ │ ├── ArgumentSyntaxUtility.java │ │ ├── Pair.java │ │ ├── TimeParser.java │ │ └── text │ │ ├── ItemPageTextDisplayer.java │ │ ├── PaginatedText.java │ │ ├── TextConvertible.java │ │ └── TextPage.java │ └── test │ └── java │ └── io │ └── github │ └── mqzn │ └── commands │ └── test │ ├── ClientSender.java │ ├── ClientSenderWrapper.java │ ├── CustomException.java │ ├── TestBootstrap.java │ ├── TestCommandManager.java │ └── annotations │ └── TestAnnotatedCommand.java ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── img.png ├── jcord ├── build.gradle └── src │ └── main │ └── java │ └── io │ └── github │ └── mqzn │ └── commands │ ├── JCordCommandManager.java │ ├── JCordCommandSyntaxBuilder.java │ └── JCordSubCommandBuilder.java ├── settings.gradle ├── spigot ├── build.gradle └── src │ └── main │ ├── java │ └── io │ │ └── github │ │ └── mqzn │ │ └── commands │ │ ├── InternalSpigotCommand.java │ │ ├── SpigotCaption.java │ │ ├── SpigotCommandManager.java │ │ ├── SpigotCommandRequirement.java │ │ ├── SpigotCommandSyntaxBuilder.java │ │ ├── SpigotSenderWrapper.java │ │ ├── SpigotSubCommandBuilder.java │ │ ├── arguments │ │ ├── ArgumentLocation.java │ │ ├── ArgumentOfflinePlayer.java │ │ ├── ArgumentOnlinePlayer.java │ │ ├── ArgumentUUID.java │ │ ├── ArgumentWorld.java │ │ └── SpigotArgument.java │ │ └── help │ │ └── SpigotCommandHelpProvider.java │ └── resources │ └── plugin.yml └── velocity ├── build.gradle └── src └── main └── java └── io └── github └── commands ├── InternalVelocityCommand.java ├── VelocityCaption.java ├── VelocityCommandManager.java ├── VelocityCommandRequirement.java ├── VelocitySenderWrapper.java ├── VelocitySubCommandBuilder.java ├── arguments ├── ArgumentOnlinePlayer.java └── VelocityArgument.java └── help └── VelocityCommandHelpProvider.java /.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/modules.xml 9 | .idea/jarRepositories.xml 10 | .idea/compiler.xml 11 | .idea/libraries/ 12 | *.iws 13 | *.iml 14 | *.ipr 15 | out/ 16 | !**/src/main/**/out/ 17 | !**/src/test/**/out/ 18 | 19 | ### Eclipse ### 20 | .apt_generated 21 | .classpath 22 | .factorypath 23 | .project 24 | .settings 25 | .springBeans 26 | .sts4-cache 27 | bin/ 28 | !**/src/main/**/bin/ 29 | !**/src/test/**/bin/ 30 | 31 | ### NetBeans ### 32 | /nbproject/private/ 33 | /nbbuild/ 34 | /dist/ 35 | /nbdist/ 36 | /.nb-gradle/ 37 | 38 | ### VS Code ### 39 | .vscode/ 40 | 41 | ### Mac OS ### 42 | .DS_Store -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 22 | 23 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 26 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 |
2 | mCommands logo 3 |
4 | 5 | # mCommands (NO LONGER MAINTAINED, DONT RECOMMEND TO USE !) 6 | **I Recommend Going for [Imperat](https://github.com/VelixDevelopments/Imperat)** 7 | 8 | An advanced general purpose command dispatching framework 9 | designed using OOP concepts.The library is user-friendly and provides 10 | high performance along with a high quality of production code. 11 | 12 | This library utilizes [Kyori Adventure](https://github.com/KyoriPowered/adventure) for 13 | messages and text styles 14 | 15 | so in the installation you must include the dependencies of Kyori in your project's dependencies control tool file 16 | here's an example using build.gradle: 17 | 18 | ```gradle 19 | 20 | dependencies { 21 | compileOnly "net.kyori:adventure-api:4.13.1" 22 | compileOnly "net.kyori:adventure-platform-bukkit:4.3.0" 23 | } 24 | 25 | ``` 26 | 27 | ## Installation 28 | 29 | mCommands has its own repo in maven central 30 | so all you have to do is like this: 31 | 32 | ```gradle 33 | repositories { 34 | mavenCentral() 35 | } 36 | 37 | dependencies { 38 | implementation 'io.github.mqzn:mCommands-:' 39 | } 40 | ``` 41 | 42 | ## Platforms 43 | 44 | ### Common 45 | 46 | The main platform that contain the core of the library 47 | 48 | ```gradle 49 | implementation 'io.github.mqzn:mCommands-common:' 50 | ``` 51 | 52 | ### Spigot 53 | 54 | The spigot platform is for minecraft spigot api development 55 | 56 | ```gradle 57 | implementation 'io.github.mqzn:mCommands-spigot:' 58 | ``` 59 | 60 | ### Bungee 61 | 62 | This bungeecord platform is for minecraft bungeecord proxy api development, allows you 63 | to declare and register bungeecord commands. 64 | 65 | ```gradle 66 | implementation 'io.github.mqzn:mCommands-bungee:' 67 | ``` 68 | 69 | ## Wiki 70 | If you want to learn how to fully utilize the amazing potential of this library. 71 | you must read the wiki pages starting from [here](https://github.com/Mqzn/mCommands/wiki) 72 | You will also need the wiki in order to make something cool like the example below: 73 | 74 | ### Code Example 75 | Here's a quick example on how to create a command using mCommands. 76 | ```java 77 | public final class SpigotPluginTest extends JavaPlugin { 78 | 79 | private SpigotCommandManager commandManager; 80 | 81 | @Override 82 | public void onEnable() { 83 | var cmd = Command.builder(commandManager, "test") 84 | .info(new CommandInfo("test.perm", "Test cmd", "testis")) 85 | .requirement(SpigotCommandRequirement.ONLY_PLAYER_EXECUTABLE) 86 | .cooldown(new CommandCooldown(5, TimeUnit.MINUTES)) 87 | .executionMeta( 88 | SpigotCommandSyntaxBuilder.builder(commandManager, "test") 89 | .argument(Argument.literal("testsub")) 90 | .execute((sender, context) -> sender.sendMessage("Test sub works !")) 91 | .build() 92 | ) 93 | .defaultExecutor((s, context) -> s.sendMessage("OMG NO ARGS !")) 94 | .build(); 95 | 96 | commandManager.register(cmd); 97 | } 98 | 99 | } 100 | ``` 101 | 102 | 103 | -------------------------------------------------------------------------------- /bungee/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'com.github.johnrengelman.shadow' version '7.1.0' 4 | id 'maven-publish' 5 | id 'signing' 6 | } 7 | 8 | group 'io.github.mqzn' 9 | version '1.1.7' 10 | 11 | repositories { 12 | mavenCentral() 13 | maven { 14 | url 'https://oss.sonatype.org/content/repositories/snapshots' 15 | } 16 | } 17 | 18 | dependencies { 19 | implementation project(":common") 20 | compileOnly 'org.jetbrains:annotations:24.0.1' 21 | compileOnly 'net.md-5:bungeecord-api:1.16-R0.3' 22 | compileOnly "net.kyori:adventure-api:4.13.1" 23 | compileOnly "net.kyori:adventure-platform-bungeecord:4.3.0" 24 | } 25 | 26 | def targetJavaVersion = 17 27 | 28 | java { 29 | def javaVersion = JavaVersion.toVersion(targetJavaVersion) 30 | sourceCompatibility = javaVersion 31 | targetCompatibility = javaVersion 32 | if (JavaVersion.current() < javaVersion) { 33 | toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) 34 | } 35 | withSourcesJar() 36 | withJavadocJar() 37 | 38 | } 39 | 40 | compileJava { 41 | options.encoding = "UTF-8" 42 | } 43 | 44 | shadowJar { 45 | setArchiveName("mCommands-Bungee-${project.version}.jar") 46 | } 47 | 48 | publishing { 49 | 50 | publications { 51 | 52 | mavenJava(MavenPublication) { 53 | groupId project.group 54 | artifactId 'mCommands-bungee' 55 | version project.version 56 | from components.java 57 | 58 | 59 | pom { 60 | name = 'mCommands' 61 | description = 'Advanced command dispatching java library' 62 | url = 'https://github.com/Mqzn/mCommands' 63 | inceptionYear = '2023' 64 | 65 | licenses { 66 | license { 67 | name = 'MIT License' 68 | url = 'http://www.opensource.org/licenses/mit-license.php' 69 | } 70 | } 71 | developers { 72 | developer { 73 | id = 'mqzn' 74 | name = 'Mqzen' 75 | email = 'mezoahmed2507@gmail.com' 76 | } 77 | } 78 | scm { 79 | connection = 'scm:git:git:github.com/Mqzn/mCommands.git' 80 | developerConnection = 'scm:git:ssh://github.com/Mqzn/mCommands.git' 81 | url = 'https://github.com/Mqzn/mCommands' 82 | } 83 | } 84 | 85 | } 86 | 87 | } 88 | repositories { 89 | maven { 90 | name = "OSSRH" 91 | url = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" 92 | 93 | credentials { 94 | username = project.properties["ossrhUsername"] 95 | password = project.properties["ossrhPassword"] 96 | } 97 | 98 | } 99 | 100 | } 101 | } 102 | 103 | signing { 104 | sign publishing.publications.mavenJava 105 | } 106 | 107 | apply plugin: 'java' 108 | apply plugin: 'com.github.johnrengelman.shadow' 109 | apply plugin: 'maven-publish' 110 | apply plugin: 'signing' 111 | 112 | -------------------------------------------------------------------------------- /bungee/src/main/java/io/github/mqzn/commands/BungeeCaption.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands; 2 | 3 | import io.github.mqzn.commands.base.caption.Caption; 4 | import io.github.mqzn.commands.base.caption.CaptionKey; 5 | import io.github.mqzn.commands.base.caption.Message; 6 | import net.kyori.adventure.text.Component; 7 | import net.kyori.adventure.text.format.NamedTextColor; 8 | import net.md_5.bungee.api.CommandSender; 9 | 10 | public interface BungeeCaption { 11 | 12 | Caption UNKNOWN_COMMAND = Caption.builder(CaptionKey.UNKNOWN_COMMAND) 13 | .withMessage((sender, context, ex) -> Message.prefixed(Message.EXECUTION_ERROR) 14 | .append(Component.text(String.format("Unknown Command In ExecutionMeta '%s'", context.rawFormat())))) 15 | .build(); 16 | 17 | Caption NO_PERMISSION = Caption.builder(CaptionKey.NO_PERMISSION) 18 | .withMessage((sender, context, ex) -> Message.prefixed(Message.EXECUTION_ERROR) 19 | .append(Component.text("You don't have the permission to do this !", NamedTextColor.GRAY))) 20 | .build(); 21 | 22 | 23 | Caption ONLY_PLAYER_EXECUTABLE = Caption.builder(CaptionKey.ONLY_PLAYER_EXECUTABLE) 24 | .withMessage((sender, context, ex) -> Message.prefixed(Message.EXECUTION_ERROR).append(Component.text("Only a player can execute this !", NamedTextColor.RED))) 25 | .build(); 26 | 27 | Caption INVALID_ARGUMENT = Caption.builder(CaptionKey.INVALID_ARGUMENT) 28 | .withMessage((sender, context, ex) -> { 29 | String msg = ex == null ? "Invalid argument used" : ex.getMessage(); 30 | return Message.prefixed(Message.INVALID_ARGUMENT_ERROR).append(Component.text(msg, NamedTextColor.DARK_GRAY)); 31 | }) 32 | .build(); 33 | 34 | Caption NO_HELP_TOPIC_AVAILABLE = Caption.builder(CaptionKey.NO_HELP_TOPIC_AVAILABLE) 35 | .withMessage((sender, context, ex) -> Message.prefixed(Message.EXECUTION_ERROR).append(Component.text("There's no help topic for this command '/" + context.commandUsed().name() + "'", NamedTextColor.RED))) 36 | .build(); 37 | } 38 | -------------------------------------------------------------------------------- /bungee/src/main/java/io/github/mqzn/commands/BungeeCommandManager.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands; 2 | 3 | import io.github.mqzn.commands.arguments.ArgumentOnlinePlayer; 4 | import io.github.mqzn.commands.base.Command; 5 | import io.github.mqzn.commands.base.manager.AbstractCommandManager; 6 | import net.md_5.bungee.api.CommandSender; 7 | import net.md_5.bungee.api.connection.ProxiedPlayer; 8 | import net.md_5.bungee.api.plugin.Plugin; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | public final class BungeeCommandManager extends AbstractCommandManager { 12 | 13 | 14 | public BungeeCommandManager(@NotNull Plugin plugin) { 15 | super(plugin, new BungeeSenderWrapper(plugin)); 16 | 17 | captionRegistry.registerCaption(BungeeCaption.UNKNOWN_COMMAND); 18 | captionRegistry.registerCaption(BungeeCaption.INVALID_ARGUMENT); 19 | captionRegistry.registerCaption(BungeeCaption.NO_PERMISSION); 20 | captionRegistry.registerCaption(BungeeCaption.ONLY_PLAYER_EXECUTABLE); 21 | captionRegistry.registerCaption(BungeeCaption.NO_HELP_TOPIC_AVAILABLE); 22 | 23 | typeRegistry().registerArgumentConverter(ProxiedPlayer.class, ArgumentOnlinePlayer::new); 24 | } 25 | 26 | @Override 27 | public char commandPrefix() { 28 | return '/'; 29 | } 30 | 31 | @Override 32 | public > void registerCommand(C command) { 33 | super.registerCommand(command); 34 | bootstrap.getProxy().getPluginManager().registerCommand(bootstrap, new InternalBungeeCommand(this, command)); 35 | } 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /bungee/src/main/java/io/github/mqzn/commands/BungeeCommandRequirement.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands; 2 | 3 | import io.github.mqzn.commands.base.CommandRequirement; 4 | import io.github.mqzn.commands.base.caption.CaptionKey; 5 | import io.github.mqzn.commands.base.context.Context; 6 | import net.md_5.bungee.api.CommandSender; 7 | import net.md_5.bungee.api.connection.ProxiedPlayer; 8 | 9 | public interface BungeeCommandRequirement extends CommandRequirement { 10 | 11 | BungeeCommandRequirement ONLY_PLAYER_EXECUTABLE = new BungeeCommandRequirement() { 12 | @Override 13 | public boolean accepts(CommandSender sender, Context commandContext) { 14 | return !(sender instanceof ProxiedPlayer); 15 | } 16 | 17 | @Override 18 | public CaptionKey caption() { 19 | return CaptionKey.ONLY_PLAYER_EXECUTABLE; 20 | } 21 | 22 | }; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /bungee/src/main/java/io/github/mqzn/commands/BungeeCommandSyntaxBuilder.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands; 2 | 3 | import io.github.mqzn.commands.base.syntax.CommandSyntaxBuilder; 4 | import net.md_5.bungee.api.CommandSender; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | public final class BungeeCommandSyntaxBuilder extends CommandSyntaxBuilder { 8 | 9 | private BungeeCommandSyntaxBuilder(BungeeCommandManager commandManager, 10 | @NotNull Class senderClass, @NotNull String label) { 11 | super(commandManager, senderClass, label); 12 | } 13 | 14 | public static BungeeCommandSyntaxBuilder builder(BungeeCommandManager manager, @NotNull Class senderClass, @NotNull String label) { 15 | return new BungeeCommandSyntaxBuilder<>(manager, senderClass, label); 16 | } 17 | 18 | public static BungeeCommandSyntaxBuilder builder(BungeeCommandManager manager, @NotNull String label) { 19 | return builder(manager, CommandSender.class, label); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /bungee/src/main/java/io/github/mqzn/commands/BungeeSenderWrapper.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands; 2 | 3 | import io.github.mqzn.commands.base.SenderWrapper; 4 | import net.kyori.adventure.platform.bungeecord.BungeeAudiences; 5 | import net.kyori.adventure.text.TextComponent; 6 | import net.md_5.bungee.api.ChatColor; 7 | import net.md_5.bungee.api.CommandSender; 8 | import net.md_5.bungee.api.connection.ProxiedPlayer; 9 | import net.md_5.bungee.api.plugin.Plugin; 10 | import org.jetbrains.annotations.NotNull; 11 | import org.jetbrains.annotations.Nullable; 12 | 13 | final class BungeeSenderWrapper implements SenderWrapper { 14 | 15 | @NotNull 16 | private final BungeeAudiences audiences; 17 | 18 | public BungeeSenderWrapper(Plugin plugin) { 19 | audiences = BungeeAudiences.create(plugin); 20 | } 21 | 22 | @Override 23 | public Class senderType() { 24 | return CommandSender.class; 25 | } 26 | 27 | @Override 28 | public boolean isConsole(CommandSender sender) { 29 | return !(sender instanceof ProxiedPlayer); 30 | } 31 | 32 | @Override 33 | public void sendMessage(CommandSender sender, String msg) { 34 | sender.sendMessage(net.md_5.bungee.api.chat. 35 | TextComponent.fromLegacyText(ChatColor.translateAlternateColorCodes('&', msg))); 36 | } 37 | 38 | @Override 39 | public boolean canBeSender(Class type) { 40 | return CommandSender.class.isAssignableFrom(type); 41 | } 42 | 43 | @Override 44 | public boolean hasPermission(CommandSender sender, @Nullable String name) { 45 | if (name == null || name.isEmpty()) 46 | return true; 47 | 48 | return sender.hasPermission(name); 49 | } 50 | 51 | @Override 52 | public String senderName(CommandSender sender) { 53 | return sender.getName(); 54 | } 55 | 56 | @Override 57 | public void sendMessage(CommandSender sender, TextComponent component) { 58 | audiences.sender(sender) 59 | .sendMessage(component); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /bungee/src/main/java/io/github/mqzn/commands/BungeeSubCommandBuilder.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands; 2 | 3 | import io.github.mqzn.commands.base.syntax.SubCommandBuilder; 4 | import net.md_5.bungee.api.CommandSender; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | public final class BungeeSubCommandBuilder extends SubCommandBuilder { 8 | private BungeeSubCommandBuilder(BungeeCommandManager manager, @NotNull Class senderClass, @NotNull String label, @NotNull String name) { 9 | super(manager, senderClass, label, name); 10 | } 11 | 12 | /** 13 | * @param manager the bungee command-manager 14 | * @param senderClass the type of the sender to use 15 | * @param label the command label 16 | * @param name the name of the subcommand 17 | * @param the type of the sender to use while building the subcommand's syntax 18 | * @return the builder of subcommands in the bungee-cord platform 19 | */ 20 | public static BungeeSubCommandBuilder builder(BungeeCommandManager manager, 21 | @NotNull Class senderClass, 22 | @NotNull String label, 23 | @NotNull String name) { 24 | return new BungeeSubCommandBuilder<>(manager, senderClass, label, name); 25 | } 26 | 27 | public static BungeeSubCommandBuilder builder(BungeeCommandManager manager, @NotNull String label, @NotNull String name) { 28 | return builder(manager, CommandSender.class, label, name); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /bungee/src/main/java/io/github/mqzn/commands/InternalBungeeCommand.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands; 2 | 3 | import io.github.mqzn.commands.base.Command; 4 | import net.md_5.bungee.api.CommandSender; 5 | import net.md_5.bungee.api.plugin.TabExecutor; 6 | import org.jetbrains.annotations.ApiStatus; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | @ApiStatus.Internal 10 | final class InternalBungeeCommand extends net.md_5.bungee.api.plugin.Command implements TabExecutor { 11 | 12 | @NotNull 13 | private final BungeeCommandManager manager; 14 | 15 | @NotNull 16 | private final Command command; 17 | 18 | public InternalBungeeCommand(@NotNull BungeeCommandManager manager, @NotNull Command command) { 19 | super(command.name(), command.info().permission(), command.info().aliases()); 20 | this.manager = manager; 21 | this.command = command; 22 | } 23 | 24 | @Override 25 | public void execute(CommandSender commandSender, String[] args) { 26 | manager.executeCommand(command, commandSender, args); 27 | } 28 | 29 | 30 | @Override 31 | public Iterable onTabComplete(CommandSender commandSender, String[] args) { 32 | return manager.suggest(command, commandSender, args); 33 | } 34 | 35 | 36 | } 37 | -------------------------------------------------------------------------------- /bungee/src/main/java/io/github/mqzn/commands/arguments/ArgumentOnlinePlayer.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.arguments; 2 | 3 | import io.github.mqzn.commands.exceptions.types.ArgumentParseException; 4 | import net.md_5.bungee.api.ProxyServer; 5 | import net.md_5.bungee.api.connection.ProxiedPlayer; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.UnknownNullability; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | public final class ArgumentOnlinePlayer extends AbstractArgument { 13 | 14 | 15 | public ArgumentOnlinePlayer(@NotNull String id) { 16 | super(id, ProxiedPlayer.class); 17 | } 18 | 19 | public ArgumentOnlinePlayer(@NotNull String id, boolean optional, boolean useRemainingSpace) { 20 | super(id, ProxiedPlayer.class, optional, useRemainingSpace); 21 | } 22 | 23 | public ArgumentOnlinePlayer(String id, boolean useRemainingSpace) { 24 | super(id, ProxiedPlayer.class, useRemainingSpace); 25 | } 26 | 27 | public ArgumentOnlinePlayer(@NotNull ArgumentData data) { 28 | super(data, ProxiedPlayer.class); 29 | } 30 | 31 | @Override 32 | public ProxiedPlayer parse(@UnknownNullability S sender, @NotNull String command, @NotNull String input) throws ArgumentParseException { 33 | 34 | ProxiedPlayer player = ProxyServer.getInstance().getPlayer(input); 35 | if (player == null || !player.isConnected()) { 36 | throw new ArgumentParseException(String.format("Player %s is offline or doesn't exist", input), input, command); 37 | } 38 | 39 | return player; 40 | } 41 | 42 | @Override 43 | public @NotNull List suggestions() { 44 | return new ArrayList<>(ProxyServer.getInstance().getPlayers()); 45 | } 46 | 47 | @Override 48 | public boolean isSuggestionDynamic() { 49 | return true; 50 | } 51 | 52 | @Override 53 | public String toString(ProxiedPlayer obj) { 54 | return obj.getName(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /bungee/src/main/java/io/github/mqzn/commands/arguments/BungeeArgument.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.arguments; 2 | 3 | public interface BungeeArgument { 4 | 5 | static ArgumentOnlinePlayer onlinePlayer(String id) { 6 | return new ArgumentOnlinePlayer(id); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /bungee/src/main/java/io/github/mqzn/commands/help/BungeeCommandHelpProvider.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.help; 2 | 3 | import net.md_5.bungee.api.CommandSender; 4 | 5 | public interface BungeeCommandHelpProvider extends CommandHelpProvider { 6 | } 7 | -------------------------------------------------------------------------------- /common/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.8.21' 3 | repositories { 4 | mavenCentral() 5 | } 6 | dependencies { 7 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 8 | } 9 | } 10 | 11 | 12 | plugins { 13 | id 'java' 14 | id 'maven-publish' 15 | id 'signing' 16 | id "org.jetbrains.kotlin.jvm" version "1.8.21" 17 | } 18 | 19 | 20 | group 'io.github.mqzn' 21 | version '1.1.7' 22 | 23 | repositories { 24 | mavenCentral() 25 | } 26 | 27 | dependencies { 28 | testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' 29 | testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' 30 | 31 | testImplementation 'org.projectlombok:lombok:1.18.22' 32 | testImplementation 'org.jetbrains:annotations:23.0.0' 33 | testImplementation "net.kyori:adventure-api:4.13.1" 34 | 35 | compileOnly 'org.jetbrains:annotations:24.0.1' 36 | compileOnly "net.kyori:adventure-api:4.13.1" 37 | 38 | } 39 | 40 | java { 41 | withSourcesJar() 42 | withJavadocJar() 43 | } 44 | 45 | def targetJavaVersion = 17 46 | 47 | java { 48 | def javaVersion = JavaVersion.toVersion(targetJavaVersion) 49 | sourceCompatibility = javaVersion 50 | targetCompatibility = javaVersion 51 | if (JavaVersion.current() < javaVersion) { 52 | toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) 53 | } 54 | 55 | withSourcesJar() 56 | withJavadocJar() 57 | 58 | } 59 | 60 | kotlin { 61 | sourceCompatibility = targetJavaVersion 62 | targetCompatibility = targetJavaVersion 63 | } 64 | 65 | 66 | publishing { 67 | publications { 68 | mavenJava(MavenPublication) { 69 | groupId project.group 70 | artifactId 'mCommands-common' 71 | version project.version 72 | from components.java 73 | 74 | 75 | pom { 76 | name = 'mCommands' 77 | description = 'Advanced command dispatching java library' 78 | url = 'https://github.com/Mqzn/mCommands' 79 | inceptionYear = '2023' 80 | 81 | licenses { 82 | license { 83 | name = 'MIT License' 84 | url = 'http://www.opensource.org/licenses/mit-license.php' 85 | } 86 | } 87 | developers { 88 | developer { 89 | id = 'mqzn' 90 | name = 'Mqzen' 91 | email = 'mezoahmed2507@gmail.com' 92 | } 93 | } 94 | scm { 95 | connection = 'scm:git:git:github.com/Mqzn/mCommands.git' 96 | developerConnection = 'scm:git:ssh://github.com/Mqzn/mCommands.git' 97 | url = 'https://github.com/Mqzn/mCommands' 98 | } 99 | } 100 | 101 | } 102 | 103 | } 104 | repositories { 105 | maven { 106 | name = "OSSRH" 107 | url = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" 108 | 109 | credentials { 110 | username = project.properties["ossrhUsername"] 111 | password = project.properties["ossrhPassword"] 112 | } 113 | 114 | } 115 | 116 | } 117 | } 118 | 119 | signing { 120 | sign publishing.publications.mavenJava 121 | } 122 | 123 | test { 124 | useJUnitPlatform() 125 | } 126 | 127 | apply plugin: 'java' 128 | apply plugin: 'kotlin' 129 | apply plugin: 'maven-publish' 130 | apply plugin: 'signing' 131 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/annotations/base/Arg.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.annotations.base; 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 Arg { 11 | 12 | String id(); 13 | 14 | boolean optional() default false; 15 | 16 | String defaultValue() default ""; 17 | 18 | String description() default ""; 19 | } 20 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/annotations/base/Command.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.annotations.base; 2 | 3 | import io.github.mqzn.commands.base.CommandRequirement; 4 | import io.github.mqzn.commands.base.manager.CommandExecutionCoordinator; 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) 13 | public @interface Command { 14 | String name(); 15 | 16 | CommandExecutionCoordinator.Type executionType() default CommandExecutionCoordinator.Type.SYNC; 17 | 18 | String permission() default ""; 19 | 20 | String description() default ""; 21 | 22 | String[] aliases() default {}; 23 | 24 | Class>[] requirements() default {}; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/annotations/base/CommandsGroup.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.annotations.base; 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 CommandsGroup { 11 | 12 | 13 | } 14 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/annotations/base/Cooldown.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.annotations.base; 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.TYPE) 11 | public @interface Cooldown { 12 | 13 | long value(); 14 | 15 | TimeUnit unit() default TimeUnit.SECONDS; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/annotations/base/Default.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.annotations.base; 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 Default { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/annotations/base/ExecutionMeta.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.annotations.base; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | 9 | @Retention(RetentionPolicy.RUNTIME) 10 | @Target({ElementType.METHOD, ElementType.TYPE}) 11 | public @interface ExecutionMeta { 12 | 13 | String syntax() default ""; 14 | 15 | Class senderType() default Object.class; 16 | 17 | String description() default ""; 18 | 19 | String permission() default ""; 20 | 21 | 22 | } 23 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/annotations/base/Flag.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.annotations.base; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Target(ElementType.PARAMETER) 10 | public @interface Flag { 11 | 12 | String name(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/annotations/base/Greedy.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.annotations.base; 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 Greedy { 11 | } 12 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/annotations/base/Range.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.annotations.base; 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 Range { 11 | 12 | String min() default ""; 13 | 14 | String max() default ""; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/annotations/base/Suggest.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.annotations.base; 2 | 3 | import io.github.mqzn.commands.base.SuggestionProvider; 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 | public @interface Suggest { 13 | 14 | String[] value() default {}; 15 | 16 | Class provider() default SuggestionProvider.class; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/annotations/subcommands/SubCommand.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.annotations.subcommands; 2 | 3 | import java.lang.annotation.*; 4 | 5 | @Retention(RetentionPolicy.RUNTIME) 6 | @Target({ElementType.TYPE}) 7 | @Repeatable(SubCommands.class) 8 | public @interface SubCommand { 9 | 10 | Class value(); 11 | } -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/annotations/subcommands/SubCommandExecution.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.annotations.subcommands; 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 SubCommandExecution { 11 | 12 | 13 | } 14 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/annotations/subcommands/SubCommandInfo.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.annotations.subcommands; 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 SubCommandInfo { 11 | 12 | String name(); 13 | 14 | Class parent() default Object.class; 15 | 16 | Class[] children() default {}; 17 | 18 | String[] aliases() default {}; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/annotations/subcommands/SubCommands.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.annotations.subcommands; 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 SubCommands { 11 | 12 | SubCommand[] value() default {}; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/arguments/ArgumentBoolean.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.arguments; 2 | 3 | import io.github.mqzn.commands.exceptions.types.ArgumentParseException; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.UnknownNullability; 6 | 7 | import java.util.Arrays; 8 | import java.util.List; 9 | 10 | public final class ArgumentBoolean extends AbstractArgument { 11 | ArgumentBoolean(String id) { 12 | super(id, Boolean.class); 13 | } 14 | 15 | ArgumentBoolean(ArgumentData data) { 16 | super(data, Boolean.class); 17 | } 18 | 19 | @Override 20 | public Boolean parse(@UnknownNullability S sender, @NotNull String command, @NotNull String input) throws ArgumentParseException { 21 | 22 | if (!input.equalsIgnoreCase("true") && !input.equalsIgnoreCase("false") && !input.equalsIgnoreCase("no") && !input.equalsIgnoreCase("yes")) 23 | throw new ArgumentParseException("Argument '" + id() + "' should be boolean, the input '" + input + "' is not a valid boolean", input, command); 24 | 25 | return input.equalsIgnoreCase("true") 26 | || input.equalsIgnoreCase("yes"); 27 | } 28 | 29 | @Override 30 | public @NotNull List suggestions() { 31 | return Arrays.asList(true, false); 32 | } 33 | 34 | @Override 35 | public Class[] alternativeTypes() { 36 | return new Class[]{boolean.class}; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/arguments/ArgumentData.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.arguments; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | public final class ArgumentData { 6 | 7 | @NotNull 8 | private final String id; 9 | 10 | private final boolean optional; 11 | 12 | private final boolean useRemainingSpace; 13 | 14 | private ArgumentData(@NotNull String id, boolean optional, boolean useRemainingSpace) { 15 | this.id = id; 16 | this.optional = optional; 17 | this.useRemainingSpace = useRemainingSpace; 18 | } 19 | 20 | private ArgumentData(@NotNull String id) { 21 | this(id, false, false); 22 | } 23 | 24 | public static ArgumentData of(@NotNull String id, boolean optional, boolean useRemainingSpace) { 25 | return new ArgumentData(id, optional, useRemainingSpace); 26 | } 27 | 28 | public static ArgumentData of(@NotNull String id) { 29 | return new ArgumentData(id); 30 | } 31 | 32 | public @NotNull String getId() { 33 | return id; 34 | } 35 | 36 | public boolean isOptional() { 37 | return optional; 38 | } 39 | 40 | public boolean isUseRemainingSpace() { 41 | return useRemainingSpace; 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/arguments/ArgumentDouble.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.arguments; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | public final class ArgumentDouble extends ArgumentNumber { 6 | 7 | 8 | ArgumentDouble(@NotNull String id) { 9 | super(id, Double.class, Double::parseDouble, 10 | ((s, radix) -> (double) Long.parseLong(s, radix)), Double::compare); 11 | } 12 | 13 | ArgumentDouble(@NotNull ArgumentData data) { 14 | super(data, Double.class, Double::parseDouble, 15 | ((s, radix) -> (double) Long.parseLong(s, radix)), Double::compare); 16 | } 17 | 18 | @Override 19 | public Class[] alternativeTypes() { 20 | return new Class[]{double.class}; 21 | } 22 | 23 | 24 | @Override 25 | public Double increment(Double num) { 26 | return num + 1D; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/arguments/ArgumentEnum.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.arguments; 2 | 3 | import io.github.mqzn.commands.exceptions.types.ArgumentParseException; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | import java.util.Arrays; 7 | import java.util.List; 8 | import java.util.Locale; 9 | import java.util.function.UnaryOperator; 10 | 11 | public final class ArgumentEnum> extends AbstractArgument { 12 | 13 | private final Class enumClass; 14 | private final E[] values; 15 | private Format format = Format.DEFAULT; 16 | 17 | public ArgumentEnum(@NotNull String id, Class enumClass) { 18 | super(id, enumClass); 19 | this.enumClass = enumClass; 20 | this.values = enumClass.getEnumConstants(); 21 | } 22 | 23 | public ArgumentEnum(@NotNull ArgumentData data, Class enumClass) { 24 | super(data, enumClass); 25 | this.enumClass = enumClass; 26 | this.values = enumClass.getEnumConstants(); 27 | } 28 | 29 | public ArgumentEnum setFormat(@NotNull Format format) { 30 | this.format = format; 31 | return this; 32 | } 33 | 34 | @NotNull 35 | @Override 36 | public E parse(@NotNull S sender, 37 | @NotNull String command, 38 | @NotNull String input) throws ArgumentParseException { 39 | for (E value : this.values) { 40 | if (this.format.formatter.apply(value.name()).equals(input)) { 41 | return value; 42 | } 43 | } 44 | throw new ArgumentParseException("Not a " + this.enumClass.getSimpleName() + " value", input, command); 45 | } 46 | 47 | public List entries() { 48 | return Arrays.stream(values).map(x -> format.formatter.apply(x.name())).toList(); 49 | } 50 | 51 | @Override 52 | public String toString(E obj) { 53 | return obj.name(); 54 | } 55 | 56 | public enum Format { 57 | DEFAULT(name -> name), 58 | LOWER_CASED(name -> name.toLowerCase(Locale.ROOT)), 59 | UPPER_CASED(name -> name.toUpperCase(Locale.ROOT)); 60 | 61 | private final UnaryOperator formatter; 62 | 63 | Format(@NotNull UnaryOperator formatter) { 64 | this.formatter = formatter; 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/arguments/ArgumentFloat.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.arguments; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | public final class ArgumentFloat extends ArgumentNumber { 6 | 7 | ArgumentFloat(@NotNull String id) { 8 | super(id, Float.class, Float::parseFloat, 9 | ((s, radix) -> (float) Long.parseLong(s, radix)), Float::compare); 10 | } 11 | 12 | ArgumentFloat(@NotNull ArgumentData data) { 13 | super(data, Float.class, Float::parseFloat, 14 | ((s, radix) -> (float) Long.parseLong(s, radix)), Float::compare); 15 | } 16 | 17 | 18 | @Override 19 | public Class[] alternativeTypes() { 20 | return new Class[]{float.class}; 21 | } 22 | 23 | @Override 24 | public Float increment(Float num) { 25 | return num + 1F; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/arguments/ArgumentInteger.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.arguments; 2 | 3 | public final class ArgumentInteger extends ArgumentNumber { 4 | 5 | ArgumentInteger(String id) { 6 | super(id, Integer.class, 7 | Integer::parseInt, 8 | Integer::parseInt, 9 | Integer::compare); 10 | } 11 | 12 | ArgumentInteger(ArgumentData data) { 13 | super(data, Integer.class, 14 | Integer::parseInt, 15 | Integer::parseInt, 16 | Integer::compare); 17 | } 18 | 19 | @Override 20 | public Class[] alternativeTypes() { 21 | return new Class[]{int.class}; 22 | } 23 | 24 | @Override 25 | public Integer increment(Integer num) { 26 | return num + 1; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/arguments/ArgumentLiteral.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.arguments; 2 | 3 | import io.github.mqzn.commands.base.syntax.CommandAliases; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | import org.jetbrains.annotations.UnknownNullability; 7 | 8 | public final class ArgumentLiteral extends AbstractArgument { 9 | 10 | @NotNull 11 | private CommandAliases commandAliases = CommandAliases.of(); 12 | 13 | ArgumentLiteral(String id) { 14 | super(id, String.class); 15 | suggestions.add(id); 16 | } 17 | 18 | ArgumentLiteral(ArgumentData data) { 19 | super(data, String.class); 20 | suggestions.add(data.getId()); 21 | } 22 | 23 | @Override 24 | public String parse(@UnknownNullability S sender, @NotNull String command, @NotNull String input) { 25 | return input; 26 | } 27 | 28 | @Override 29 | public @Nullable String defaultValue() { 30 | return id(); 31 | } 32 | 33 | @Override 34 | public @NotNull Argument setDefaultValue(@Nullable String value) { 35 | throw new UnsupportedOperationException("Argument is a literal, it cannot have a default value !"); 36 | } 37 | 38 | @Override 39 | public boolean isOptional() { 40 | return false; 41 | } 42 | 43 | public ArgumentLiteral aliases(String... aliases) { 44 | this.commandAliases = CommandAliases.of(aliases); 45 | for (String aliase : aliases) { 46 | if (!suggestions.contains(aliase)) { 47 | suggestions.add(aliase); 48 | } 49 | } 50 | return this; 51 | } 52 | 53 | @Override 54 | public Class[] alternativeTypes() { 55 | return super.alternativeTypes(); 56 | } 57 | 58 | @Override 59 | public Argument suggest(@NotNull String suggestion) { 60 | throw new UnsupportedOperationException("You cannot do that for a literal argument"); 61 | } 62 | 63 | public @NotNull CommandAliases getAliases() { 64 | return commandAliases; 65 | } 66 | 67 | 68 | } 69 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/arguments/ArgumentLong.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.arguments; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | public final class ArgumentLong extends ArgumentNumber { 6 | 7 | ArgumentLong(@NotNull String id) { 8 | super(id, Long.class, Long::parseLong, Long::parseLong, Long::compare); 9 | } 10 | 11 | ArgumentLong(@NotNull ArgumentData data) { 12 | super(data, Long.class, Long::parseLong, Long::parseLong, Long::compare); 13 | } 14 | 15 | 16 | @Override 17 | public Class[] alternativeTypes() { 18 | return new Class[]{long.class}; 19 | } 20 | 21 | @Override 22 | public Long increment(Long num) { 23 | return num + 1; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/arguments/ArgumentNumber.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.arguments; 2 | 3 | import io.github.mqzn.commands.exceptions.types.ArgumentParseException; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | import org.jetbrains.annotations.UnknownNullability; 7 | 8 | import java.math.BigDecimal; 9 | import java.util.Comparator; 10 | import java.util.function.BiFunction; 11 | import java.util.function.Function; 12 | import java.util.regex.Pattern; 13 | 14 | /** 15 | * A class that represents any numeric argument 16 | * 17 | * @param the type of the number 18 | */ 19 | public abstract class ArgumentNumber extends AbstractArgument { 20 | 21 | protected final BiFunction radixParser; 22 | 23 | protected final Function parser; 24 | 25 | protected final Comparator comparator; 26 | 27 | 28 | protected boolean hasMin, hasMax; 29 | 30 | @Nullable 31 | protected T min, max; 32 | 33 | ArgumentNumber(@NotNull String id, Class type, 34 | Function parser, 35 | BiFunction radixParser, 36 | Comparator comparator) { 37 | super(id, type); 38 | this.radixParser = radixParser; 39 | this.parser = parser; 40 | this.comparator = comparator; 41 | 42 | } 43 | 44 | ArgumentNumber(@NotNull ArgumentData data, Class type, 45 | Function parser, 46 | BiFunction radixParser, 47 | Comparator comparator) { 48 | super(data, type); 49 | this.radixParser = radixParser; 50 | this.parser = parser; 51 | this.comparator = comparator; 52 | } 53 | 54 | @Override 55 | public @NotNull T parse(@UnknownNullability S sender, 56 | @NotNull String command, 57 | @NotNull String input) throws ArgumentParseException { 58 | try { 59 | final T value; 60 | final int radix = getRadix(input); 61 | if (radix == 10) { 62 | value = parser.apply(parseValue(input)); 63 | } else { 64 | value = radixParser.apply(parseValue(input), radix); 65 | } 66 | 67 | // Check range 68 | if (hasMin && comparator.compare(value, min) < 0) { 69 | throw new ArgumentParseException(String.format("Input '%s' is lower than the minimum allowed value", input), input, command); 70 | } 71 | if (hasMax && comparator.compare(value, max) > 0) { 72 | throw new ArgumentParseException(String.format("Input '%s' is higher than the maximum allowed value", input), input, command); 73 | } 74 | 75 | return value; 76 | } catch (NumberFormatException | NullPointerException e) { 77 | throw new ArgumentParseException(String.format("Input '%s' is not a number, or it's invalid for the given type", input), input, command); 78 | } 79 | } 80 | 81 | 82 | @NotNull 83 | public ArgumentNumber min(@NotNull T value) { 84 | this.min = value; 85 | this.hasMin = true; 86 | return this; 87 | } 88 | 89 | @NotNull 90 | @SuppressWarnings("UnusedReturnValue") 91 | public ArgumentNumber max(@NotNull T value) { 92 | this.max = value; 93 | this.hasMax = true; 94 | 95 | return this; 96 | } 97 | 98 | @NotNull 99 | public ArgumentNumber between(@NotNull T min, @NotNull T max) { 100 | this.min = min; 101 | this.max = max; 102 | this.hasMin = true; 103 | this.hasMax = true; 104 | return this; 105 | } 106 | 107 | 108 | /** 109 | * Gets if the argument has a minimum. 110 | * 111 | * @return true if the argument has a minimum 112 | */ 113 | public boolean hasMin() { 114 | return hasMin; 115 | } 116 | 117 | /** 118 | * Gets the minimum value for this argument. 119 | * 120 | * @return the minimum of this argument 121 | */ 122 | public @Nullable T getMin() { 123 | return min; 124 | } 125 | 126 | /** 127 | * Gets if the argument has a maximum. 128 | * 129 | * @return true if the argument has a maximum 130 | */ 131 | public boolean hasMax() { 132 | return hasMax; 133 | } 134 | 135 | /** 136 | * Gets the maximum value for this argument. 137 | * 138 | * @return the maximum of this argument 139 | */ 140 | public @Nullable T getMax() { 141 | return max; 142 | } 143 | 144 | @NotNull 145 | protected String parseValue(@NotNull String value) { 146 | if (value.startsWith("0b")) { 147 | value = value.replaceFirst(Pattern.quote("0b"), ""); 148 | } else if (value.startsWith("0x")) { 149 | value = value.replaceFirst(Pattern.quote("0x"), ""); 150 | } else if (value.toLowerCase().contains("e")) { 151 | value = removeScientificNotation(value); 152 | } 153 | // TODO number suffix support (k,m,b,t) 154 | assert value != null; 155 | return value; 156 | } 157 | 158 | protected int getRadix(@NotNull String value) { 159 | if (value.startsWith("0b")) { 160 | return 2; 161 | } else if (value.startsWith("0x")) { 162 | return 16; 163 | } 164 | return 10; 165 | } 166 | 167 | @Nullable 168 | protected String removeScientificNotation(@NotNull String value) { 169 | try { 170 | return new BigDecimal(value).toPlainString(); 171 | } catch (NumberFormatException e) { 172 | return null; 173 | } 174 | } 175 | 176 | 177 | public abstract T increment(T num); 178 | 179 | public Function getParser() { 180 | return parser; 181 | } 182 | 183 | } -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/arguments/ArgumentStringArray.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.arguments; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | import org.jetbrains.annotations.UnknownNullability; 5 | 6 | import java.util.regex.Pattern; 7 | 8 | public final class ArgumentStringArray extends AbstractArgument { 9 | 10 | ArgumentStringArray(String id) { 11 | super(id, String[].class, false, true); 12 | } 13 | 14 | @Override 15 | public String[] parse(@UnknownNullability S sender, 16 | @NotNull String command, 17 | @NotNull String input) { 18 | return input.split(Pattern.quote(" ")); 19 | } 20 | 21 | @Override 22 | public String toString(String[] obj) { 23 | return String.join(" ", obj); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/arguments/ArgumentWord.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.arguments; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | import org.jetbrains.annotations.UnknownNullability; 5 | 6 | public final class ArgumentWord extends AbstractArgument { 7 | 8 | ArgumentWord(String id) { 9 | super(id, String.class); 10 | } 11 | 12 | ArgumentWord(ArgumentData data) { 13 | super(data, String.class); 14 | } 15 | 16 | @Override 17 | public String parse(@UnknownNullability S sender, 18 | @NotNull String command, 19 | @NotNull String input) { 20 | return input; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/CommandInfo.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base; 2 | 3 | /** 4 | * A record that holds some information to it 5 | * such as the permission of the command, it's description 6 | * and it's aliases ! 7 | * 8 | * @param permission the permission of the command 9 | * @param description the description explaining the purpose of the command 10 | * @param aliases the aliases of the command 11 | */ 12 | public record CommandInfo(String permission, 13 | String description, 14 | String... aliases) { 15 | 16 | 17 | public static final CommandInfo EMPTY_INFO = new CommandInfo(null, ""); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/CommandRequirement.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base; 2 | 3 | import io.github.mqzn.commands.base.caption.CaptionKey; 4 | import io.github.mqzn.commands.base.context.Context; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | /** 8 | * Represents a requirement/criteria 9 | * that must be true for the command to be executed 10 | * 11 | * @param the sender type 12 | */ 13 | public interface CommandRequirement { 14 | 15 | /** 16 | * Whether the criteria are true or not 17 | * 18 | * @param sender the command sender 19 | * @param commandContext the context used 20 | * @return Whether the criteria are true or not 21 | */ 22 | boolean accepts(S sender, Context commandContext); 23 | 24 | /** 25 | * The caption that will be sent 26 | * to the user if the criteria is false 27 | * 28 | * @return null if no caption to send, 29 | * otherwise it will return the key of the caption to send 30 | */ 31 | @Nullable CaptionKey caption(); 32 | 33 | } 34 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/Information.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base; 2 | 3 | public record Information(String permission, String description) { 4 | } -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/SenderProvider.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | public interface SenderProvider { 6 | 7 | 8 | static SenderProvider self() { 9 | 10 | return (s) -> s; 11 | } 12 | 13 | C mapSender(@NotNull S sender); 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/SenderWrapper.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base; 2 | 3 | import net.kyori.adventure.text.TextComponent; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | public interface SenderWrapper { 7 | 8 | Class senderType(); 9 | 10 | boolean isConsole(S sender); 11 | 12 | void sendMessage(S sender, String msg); 13 | 14 | void sendMessage(S sender, TextComponent component); 15 | 16 | boolean canBeSender(Class type); 17 | 18 | boolean hasPermission(S sender, @Nullable String name); 19 | 20 | String senderName(S sender); 21 | } 22 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/SuggestionProvider.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * An interface that represents 9 | * a function to provide suggestions for 10 | * an argument for annotation parsing purposes 11 | * 12 | * @author Mqzen 13 | */ 14 | public interface SuggestionProvider { 15 | 16 | /** 17 | * The suggestions to be used for the argument 18 | * 19 | * @return the suggestions 20 | */ 21 | @NotNull List suggestions(); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/caption/Caption.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.caption; 2 | 3 | import io.github.mqzn.commands.base.context.Context; 4 | import net.kyori.adventure.text.Component; 5 | import net.kyori.adventure.text.TextComponent; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | public interface Caption { 10 | 11 | 12 | static @NotNull Builder builder(CaptionKey key) { 13 | return new Builder<>(key); 14 | } 15 | 16 | @NotNull CaptionKey key(); 17 | 18 | @NotNull TextComponent message(S sender, Context context, Throwable exception); 19 | 20 | interface CaptionResult { 21 | 22 | @NotNull TextComponent messageResult(@NotNull S sender, 23 | @NotNull Context context, 24 | @Nullable Throwable exception); 25 | 26 | } 27 | 28 | final class Builder { 29 | 30 | @NotNull 31 | private final CaptionKey key; 32 | 33 | @NotNull 34 | private CaptionResult messageCreator = (cmd, s, ex) -> Component.empty(); 35 | 36 | Builder(@NotNull CaptionKey key) { 37 | this.key = key; 38 | } 39 | 40 | public @NotNull Builder withMessage(@NotNull CaptionResult messageCreator) { 41 | this.messageCreator = messageCreator; 42 | return this; 43 | } 44 | 45 | 46 | public Caption build() { 47 | return new ImmutableCaption<>(key, messageCreator); 48 | } 49 | 50 | record ImmutableCaption(CaptionKey key, CaptionResult messageCreator) implements Caption { 51 | 52 | 53 | @Override 54 | public @NotNull TextComponent message(@NotNull S sender, 55 | @NotNull Context context, 56 | @Nullable Throwable exception) { 57 | return messageCreator.messageResult(sender, context, exception); 58 | } 59 | 60 | } 61 | 62 | 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/caption/CaptionKey.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.caption; 2 | 3 | 4 | import java.util.Objects; 5 | 6 | public final class CaptionKey { 7 | 8 | 9 | public final static CaptionKey UNKNOWN_COMMAND = CaptionKey.of("execution.unknown-command"); 10 | public final static CaptionKey NO_PERMISSION = CaptionKey.of("execution.no-permission"); 11 | public final static CaptionKey ONLY_PLAYER_EXECUTABLE = CaptionKey.of("execution.only-player-allowed"); 12 | public final static CaptionKey INVALID_ARGUMENT = CaptionKey.of("argument.parsing-invalid"); 13 | public static final CaptionKey NO_HELP_TOPIC_AVAILABLE = CaptionKey.of("execution.unknown-help-topic"); 14 | public static final CaptionKey UNKNOWN_HELP_PAGE = CaptionKey.of("execution.unknown-help-page"); 15 | public static final CaptionKey COMMAND_IN_COOLDOWN = CaptionKey.of("execution.command-cooldown"); 16 | 17 | private final String key; 18 | 19 | private CaptionKey(String key) { 20 | this.key = key; 21 | } 22 | 23 | public static CaptionKey of(String key) { 24 | return new CaptionKey(key); 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return key; 30 | } 31 | 32 | @Override 33 | public boolean equals(Object o) { 34 | if (this == o) return true; 35 | if (!(o instanceof CaptionKey key1)) return false; 36 | return Objects.equals(key, key1.key); 37 | } 38 | 39 | @Override 40 | public int hashCode() { 41 | return Objects.hash(key); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/caption/CaptionRegistry.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.caption; 2 | 3 | import io.github.mqzn.commands.base.context.Context; 4 | import io.github.mqzn.commands.base.manager.CommandManager; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | public class CaptionRegistry { 12 | 13 | private final CommandManager manager; 14 | 15 | @NotNull 16 | private final Map> captions; 17 | 18 | public CaptionRegistry(CommandManager manager) { 19 | this.manager = manager; 20 | this.captions = new HashMap<>(); 21 | } 22 | 23 | public void registerCaption(Caption caption) { 24 | captions.put(caption.key(), caption); 25 | } 26 | 27 | public void unregisterCaption(Caption caption) { 28 | captions.remove(caption.key()); 29 | } 30 | 31 | @Nullable 32 | public Caption getCaption(CaptionKey key) { 33 | return captions.get(key); 34 | } 35 | 36 | public void sendCaption(@NotNull S sender, 37 | @NotNull Context commandContext, 38 | @Nullable Throwable exception, 39 | @NotNull CaptionKey key) { 40 | var caption = getCaption(key); 41 | 42 | if (caption == null) return; 43 | sendCaption(sender, commandContext, exception, caption); 44 | } 45 | 46 | public void sendCaption(S sender, Context commandContext, CaptionKey key) { 47 | this.sendCaption(sender, commandContext, null, key); 48 | } 49 | 50 | public void sendCaption(@NotNull S sender, 51 | @NotNull Context commandContext, 52 | @Nullable Throwable exception, 53 | @NotNull Caption caption) { 54 | var text = caption.message(sender, commandContext, exception); 55 | manager.getSenderWrapper().sendMessage(sender, text); 56 | } 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/caption/Message.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.caption; 2 | 3 | import net.kyori.adventure.text.Component; 4 | import net.kyori.adventure.text.TextComponent; 5 | import net.kyori.adventure.text.format.NamedTextColor; 6 | 7 | public interface Message { 8 | 9 | TextComponent PREFIX = Component.text("[", NamedTextColor.DARK_GRAY) 10 | .append(Component.text("!", NamedTextColor.DARK_RED)) 11 | .append(Component.text("]", NamedTextColor.DARK_GRAY)) 12 | .append(Component.space()); 13 | 14 | TextComponent EXECUTION_ERROR = Component.text("Execution Error: ", NamedTextColor.RED); 15 | 16 | TextComponent INVALID_ARGUMENT_ERROR = Component.text("Invalid Argument: ", NamedTextColor.RED); 17 | 18 | 19 | static TextComponent prefixed(TextComponent component) { 20 | return PREFIX.append(component); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/context/CommandArgs.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.context; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Iterator; 8 | import java.util.List; 9 | 10 | public final class CommandArgs implements Iterable { 11 | 12 | private final List args = new ArrayList<>(); 13 | 14 | private CommandArgs(Context context) { 15 | args.addAll(context.getRawArguments()); 16 | } 17 | 18 | public static CommandArgs create(Context context) { 19 | return new CommandArgs(context); 20 | } 21 | 22 | @Nullable 23 | public String getRaw(int index) { 24 | if (!verifyIndex(index)) return null; 25 | return args.get(index); 26 | } 27 | 28 | @Nullable 29 | public String getAfterRaw(String raw) { 30 | int index = args.indexOf(raw); 31 | if (!verifyIndex(index)) return null; 32 | return args.get(index); 33 | } 34 | 35 | @Override 36 | public String toString() { 37 | return "Raw-arguments => " + String.join(" ", this); 38 | } 39 | 40 | /** 41 | * Returns an iterator over elements of type {@code T}. 42 | * 43 | * @return an Iterator. 44 | */ 45 | @NotNull 46 | @Override 47 | public Iterator iterator() { 48 | return args.iterator(); 49 | } 50 | 51 | private boolean verifyIndex(int index) { 52 | return index >= 0 && index < args.size(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/context/Context.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.context; 2 | 3 | import io.github.mqzn.commands.arguments.Argument; 4 | import io.github.mqzn.commands.base.Command; 5 | import io.github.mqzn.commands.base.manager.flags.ContextFlagRegistry; 6 | import io.github.mqzn.commands.base.syntax.CommandSyntax; 7 | import io.github.mqzn.commands.exceptions.types.ArgumentParseException; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import java.util.List; 12 | 13 | public interface Context { 14 | 15 | 16 | /** 17 | * Fetches the number of flags used in the raw arguments 18 | * 19 | * @return the count of flags used in raw args 20 | */ 21 | int flagsUsed(); 22 | 23 | /** 24 | * Fetches the sender for this context 25 | * 26 | * @return the context command sender 27 | */ 28 | @NotNull S sender(); 29 | 30 | /** 31 | * The command found and used in the context 32 | * made by the command sender 33 | * 34 | * @return the command used ! 35 | * @see Command 36 | */ 37 | @NotNull Command commandUsed(); 38 | 39 | /** 40 | * The raw arguments used in the context 41 | * made by the command sender 42 | * 43 | * @return The raw arguments 44 | */ 45 | @NotNull List getRawArguments(); 46 | 47 | /** 48 | * The raw arguments formatted using the 49 | * command used 50 | * 51 | * @return the raw format used in the context 52 | * @see Command 53 | * @see CommandContext 54 | */ 55 | @NotNull String rawFormat(); 56 | 57 | /** 58 | * Fetches the raw argument from the input in the 59 | * constructor 60 | * 61 | * @param index the index of the raw argument to fetch 62 | * @return the raw argument at a specific position 63 | */ 64 | @Nullable String getRawArgument(int index); 65 | 66 | 67 | /** 68 | * Fetches the parsed argument value 69 | * may return null if the value parsed is not valid 70 | * some cases of failed argument parsing may be like this one: 71 | * an integer argument with min of 1 and max of 10, however the input was "one" 72 | * or may be "-1" which is not a valid value . 73 | * 74 | * @param id the argument name/id 75 | * @param the type of arg value 76 | * @return the parsed value of the argument 77 | */ 78 | @Nullable T getArgument(String id); 79 | 80 | 81 | /** 82 | * Fetches the parsed argument value 83 | * may return null if the value parsed is not valid 84 | * some cases of failed argument parsing may be like this one: 85 | * an integer argument with min of 1 and max of 10, however the input was "one" 86 | * or may be "-1" which is not a valid value . 87 | * 88 | * @param index the argument index/position 89 | * @param the type of arg value 90 | * @return the parsed value of the argument 91 | */ 92 | @Nullable T getArgument(int index); 93 | 94 | 95 | /** 96 | * Fetches the original required argument 97 | * stated by the syntax executed 98 | * 99 | * @param index the index of the arg 100 | * @return the original required argument 101 | */ 102 | @Nullable Argument getRequiredArgument(int index); 103 | 104 | /** 105 | * Parses the arguments into the used syntax 106 | * this algorithm should provide good reasonable performance 107 | */ 108 | void parse() throws ArgumentParseException; 109 | 110 | /** 111 | * The number of parsed args 112 | * 113 | * @return the number of arguments parsed in the context 114 | */ 115 | int parsedArguments(); 116 | 117 | /** 118 | * The flags used in the command 119 | * 120 | * @return the flag registry for the flags used in the context 121 | * of the command executed by the command sender 122 | */ 123 | @NotNull ContextFlagRegistry flags(); 124 | 125 | /** 126 | * @return The syntax used in the context of the command 127 | */ 128 | @NotNull CommandSyntax syntaxUsed(); 129 | 130 | /** 131 | * The length of the args used in the raw context 132 | * 133 | * @return The length of the args used in the raw context 134 | */ 135 | int length(); 136 | 137 | default int getLastIndex() { 138 | return length() - 1; 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/cooldown/CommandCooldown.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.cooldown; 2 | 3 | import java.time.Duration; 4 | import java.util.Objects; 5 | import java.util.concurrent.TimeUnit; 6 | 7 | public class CommandCooldown { 8 | 9 | public static final CommandCooldown EMPTY = new CommandCooldown(0, TimeUnit.SECONDS); 10 | private final long value; 11 | private final TimeUnit unit; 12 | private final Duration duration; 13 | 14 | public CommandCooldown(long value, TimeUnit unit) { 15 | this.value = value; 16 | this.unit = unit; 17 | this.duration = Duration.of(value, unit.toChronoUnit()); 18 | } 19 | 20 | public boolean isEmpty() { 21 | return value <= 0; 22 | } 23 | 24 | public long toMillis() { 25 | return duration.toMillis(); 26 | } 27 | 28 | public long getValue() { 29 | return value; 30 | } 31 | 32 | public TimeUnit getUnit() { 33 | return unit; 34 | } 35 | 36 | @Override 37 | public boolean equals(Object other) { 38 | if (this == other) return true; 39 | if (!(other instanceof CommandCooldown that)) return false; 40 | return value == that.value && unit == that.unit; 41 | } 42 | 43 | @Override 44 | public int hashCode() { 45 | return Objects.hash(value, unit); 46 | } 47 | } 48 | 49 | 50 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/cooldown/CooldownCaption.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.cooldown; 2 | 3 | import io.github.mqzn.commands.base.caption.Caption; 4 | import io.github.mqzn.commands.base.caption.CaptionKey; 5 | import io.github.mqzn.commands.base.caption.Message; 6 | import io.github.mqzn.commands.base.context.Context; 7 | import io.github.mqzn.commands.utilities.TimeParser; 8 | import net.kyori.adventure.text.Component; 9 | import net.kyori.adventure.text.TextComponent; 10 | import net.kyori.adventure.text.format.NamedTextColor; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | import java.util.Locale; 14 | import java.util.concurrent.TimeUnit; 15 | 16 | public final class CooldownCaption implements Caption { 17 | 18 | public static String formatUnit(TimeUnit unit) { 19 | var unitName = unit.name().toLowerCase(Locale.getDefault()); 20 | unitName = unitName.substring(0, unitName.length() - 1); 21 | return unitName + "(s)"; 22 | } 23 | 24 | public static long calculateRemainingTime(long lastTime, CommandCooldown commandCooldown) { 25 | var diff = System.currentTimeMillis() - lastTime; 26 | return commandCooldown.toMillis() - diff; 27 | } 28 | 29 | @Override 30 | public @NotNull CaptionKey key() { 31 | return CaptionKey.COMMAND_IN_COOLDOWN; 32 | } 33 | 34 | @Override 35 | public @NotNull TextComponent message(S sender, Context context, Throwable exception) { 36 | var command = context.commandUsed(); 37 | var manager = command.manager(); 38 | var cooldown = command.cooldown(); 39 | var lastTimeCommandExecuted = manager.getCommandCooldown(manager.getSenderWrapper().senderName(sender)); 40 | if (lastTimeCommandExecuted == null) lastTimeCommandExecuted = 0L; 41 | // Send a caption telling the user that he's in a cooldown 42 | // Calculating remaining time 43 | var parser = TimeParser.parse(calculateRemainingTime(lastTimeCommandExecuted, cooldown)); 44 | var timeData = parser.highestLogicalUnitValue(); 45 | 46 | return cooldownMessage(timeData.getLeft(), timeData.getRight(), context); 47 | } 48 | 49 | public TextComponent cooldownMessage(long time, TimeUnit unit, Context context) { 50 | return Message.prefixed(Message.EXECUTION_ERROR) 51 | .append( 52 | Component.text( 53 | String.format( 54 | "Command '" + context.commandUsed().name() 55 | + "' is in cooldown for %d %s", time, formatUnit(unit) 56 | ), NamedTextColor.YELLOW 57 | ) 58 | ); 59 | } 60 | } 61 | 62 | 63 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/manager/AmbiguityChecker.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.manager; 2 | 3 | import io.github.mqzn.commands.arguments.ArgumentLiteral; 4 | import io.github.mqzn.commands.base.Command; 5 | import io.github.mqzn.commands.base.syntax.CommandSyntax; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class AmbiguityChecker { 11 | private final Command command; 12 | private final List> syntaxes; 13 | 14 | private AmbiguityChecker(Command command, List> syntaxes) { 15 | this.command = command; 16 | this.syntaxes = syntaxes; 17 | } 18 | 19 | public static AmbiguityChecker of(Command command) { 20 | return new AmbiguityChecker<>(command, command.syntaxes()); 21 | } 22 | 23 | public static boolean hasLiteralArgs(CommandSyntax syntax) { 24 | if (syntax.isSubCommand()) return true; 25 | for (Object arg : syntax.getArguments()) { 26 | if (arg instanceof ArgumentLiteral) { 27 | return true; 28 | } 29 | } 30 | return false; 31 | } 32 | 33 | public List> findAmbiguity() { 34 | for (CommandSyntax syntax : syntaxes) { 35 | if (syntax.useSpace() && !hasLiteralArgs(syntax) && syntaxes.size() > 1) { 36 | return syntaxes; 37 | } 38 | } 39 | 40 | List> ambiguous = new ArrayList<>(); 41 | for (int first = 0; first < syntaxes.size(); first++) { 42 | CommandSyntax firstSyntax = syntaxes.get(first); 43 | for (int second = 0; second < syntaxes.size(); second++) { 44 | if (first == second) continue; 45 | CommandSyntax secondSyntax = syntaxes.get(second); 46 | if (areAmbiguous(firstSyntax, secondSyntax)) { 47 | ambiguous.add(firstSyntax); 48 | ambiguous.add(secondSyntax); 49 | } 50 | } 51 | } 52 | return ambiguous; 53 | } 54 | 55 | private boolean areAmbiguous(CommandSyntax s1, CommandSyntax s2) { 56 | int s1Length = CommandSyntax.getArguments(command.tree(), s1).size(); 57 | int s2Length = CommandSyntax.getArguments(command.tree(), s2).size(); 58 | 59 | return (!hasLiteralArgs(s1) && !hasLiteralArgs(s2) && s1Length == s2Length) || 60 | (s1Length == s2Length && s1.equals(s2)); 61 | } 62 | } 63 | 64 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/manager/ArgumentNumberComparator.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.manager; 2 | 3 | import io.github.mqzn.commands.arguments.*; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | public final class ArgumentNumberComparator { 9 | 10 | private final Map>, ArgumentComparator> comparators; 11 | 12 | public ArgumentNumberComparator() { 13 | comparators = new HashMap<>(); 14 | registerComparator(ArgumentInteger.class, new IntegerComparator()); 15 | registerComparator(ArgumentDouble.class, new DoubleComparator()); 16 | registerComparator(ArgumentFloat.class, new FloatComparator()); 17 | registerComparator(ArgumentLong.class, new LongComparator()); 18 | } 19 | 20 | public void registerComparator(Class> argClass, ArgumentComparator comparator) { 21 | comparators.put(argClass, comparator); 22 | } 23 | 24 | @SuppressWarnings("unchecked") 25 | public ArgumentComparator comparatorOfArg(Class> clazzArg) { 26 | return (ArgumentComparator) comparators.get(clazzArg); 27 | } 28 | 29 | public interface ArgumentComparator { 30 | boolean greaterThan(N n1, N n2); 31 | 32 | boolean greaterThanOrEqual(N n1, N n2); 33 | 34 | boolean lessThan(N n1, N n2); 35 | 36 | boolean lessThanOrEqual(N n1, N n2); 37 | } 38 | 39 | private static class IntegerComparator implements ArgumentComparator { 40 | @Override 41 | public boolean greaterThan(Integer n1, Integer n2) { 42 | return n1 > n2; 43 | } 44 | 45 | @Override 46 | public boolean greaterThanOrEqual(Integer n1, Integer n2) { 47 | return n1 >= n2; 48 | } 49 | 50 | @Override 51 | public boolean lessThan(Integer n1, Integer n2) { 52 | return n1 < n2; 53 | } 54 | 55 | @Override 56 | public boolean lessThanOrEqual(Integer n1, Integer n2) { 57 | return n1 <= n2; 58 | } 59 | } 60 | 61 | private static class DoubleComparator implements ArgumentComparator { 62 | @Override 63 | public boolean greaterThan(Double n1, Double n2) { 64 | return n1 > n2; 65 | } 66 | 67 | @Override 68 | public boolean greaterThanOrEqual(Double n1, Double n2) { 69 | return n1 >= n2; 70 | } 71 | 72 | @Override 73 | public boolean lessThan(Double n1, Double n2) { 74 | return n1 < n2; 75 | } 76 | 77 | @Override 78 | public boolean lessThanOrEqual(Double n1, Double n2) { 79 | return n1 <= n2; 80 | } 81 | } 82 | 83 | private static class FloatComparator implements ArgumentComparator { 84 | @Override 85 | public boolean greaterThan(Float n1, Float n2) { 86 | return n1 > n2; 87 | } 88 | 89 | @Override 90 | public boolean greaterThanOrEqual(Float n1, Float n2) { 91 | return n1 >= n2; 92 | } 93 | 94 | @Override 95 | public boolean lessThan(Float n1, Float n2) { 96 | return n1 < n2; 97 | } 98 | 99 | @Override 100 | public boolean lessThanOrEqual(Float n1, Float n2) { 101 | return n1 <= n2; 102 | } 103 | } 104 | 105 | private static class LongComparator implements ArgumentComparator { 106 | @Override 107 | public boolean greaterThan(Long n1, Long n2) { 108 | return n1 > n2; 109 | } 110 | 111 | @Override 112 | public boolean greaterThanOrEqual(Long n1, Long n2) { 113 | return n1 >= n2; 114 | } 115 | 116 | @Override 117 | public boolean lessThan(Long n1, Long n2) { 118 | return n1 < n2; 119 | } 120 | 121 | @Override 122 | public boolean lessThanOrEqual(Long n1, Long n2) { 123 | return n1 <= n2; 124 | } 125 | } 126 | } -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/manager/ArgumentNumberSuggestionProcessor.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.manager; 2 | 3 | import io.github.mqzn.commands.arguments.ArgumentNumber; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | public final class ArgumentNumberSuggestionProcessor { 8 | 9 | @NotNull 10 | private final CommandManager manager; 11 | 12 | private ArgumentNumberSuggestionProcessor(@NotNull CommandManager manager) { 13 | this.manager = manager; 14 | } 15 | 16 | public static ArgumentNumberSuggestionProcessor create(@NotNull CommandManager manager) { 17 | return new ArgumentNumberSuggestionProcessor(manager); 18 | } 19 | 20 | @SuppressWarnings("unchecked") 21 | public void provide(@NotNull ArgumentNumber argumentNumber) { 22 | 23 | ArgumentNumberComparator.ArgumentComparator comparator 24 | = manager.typeRegistry().getComparator((Class>) argumentNumber.getClass()); 25 | 26 | @Nullable N end = argumentNumber.getMax(); 27 | @Nullable N start = argumentNumber.getMin(); 28 | 29 | if (start == null && end == null) { 30 | return; 31 | } else if (end == null) { 32 | argumentNumber.suggest(start); 33 | return; 34 | } else if (start == null) { 35 | argumentNumber.suggest(end); 36 | return; 37 | } 38 | 39 | while (comparator.lessThanOrEqual(start, end)) { 40 | argumentNumber.suggest(start); 41 | start = argumentNumber.increment(start); 42 | } 43 | 44 | } 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/manager/ArgumentTypeRegistry.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.manager; 2 | 3 | import io.github.mqzn.commands.arguments.Argument; 4 | import io.github.mqzn.commands.arguments.ArgumentData; 5 | import io.github.mqzn.commands.arguments.ArgumentNumber; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | import java.util.function.Function; 12 | 13 | public final class ArgumentTypeRegistry { 14 | 15 | @NotNull 16 | private final ArgumentNumberComparator argumentNumberComparator; 17 | 18 | @NotNull 19 | private final Map, Function>> argumentCreatorMapper = new HashMap<>(); 20 | 21 | 22 | ArgumentTypeRegistry() { 23 | 24 | this.argumentNumberComparator = new ArgumentNumberComparator(); 25 | 26 | argumentCreatorMapper.put(Integer.class, Argument::integer); 27 | argumentCreatorMapper.put(int.class, Argument::integer); 28 | 29 | argumentCreatorMapper.put(Double.class, Argument::Double); 30 | argumentCreatorMapper.put(double.class, Argument::Double); 31 | 32 | argumentCreatorMapper.put(Float.class, Argument::Float); 33 | argumentCreatorMapper.put(float.class, Argument::Float); 34 | 35 | argumentCreatorMapper.put(Long.class, Argument::Long); 36 | argumentCreatorMapper.put(long.class, Argument::Long); 37 | 38 | argumentCreatorMapper.put(Boolean.class, Argument::Boolean); 39 | argumentCreatorMapper.put(boolean.class, Argument::Boolean); 40 | 41 | argumentCreatorMapper.put(String[].class, (data) -> Argument.Array(data.getId())); 42 | } 43 | 44 | 45 | @Nullable 46 | public Argument convertArgument(@NotNull ArgumentData data, @NotNull Class clazz) { 47 | var mapper = argumentCreatorMapper.get(clazz); 48 | if (mapper == null) { 49 | return null; 50 | } 51 | return mapper.apply(data); 52 | } 53 | 54 | public void registerArgumentConverter(Class type, Function> mapper) { 55 | argumentCreatorMapper.put(type, mapper); 56 | } 57 | 58 | 59 | public ArgumentNumberComparator.ArgumentComparator getComparator(Class> argNumClass) { 60 | return argumentNumberComparator.comparatorOfArg(argNumClass); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/manager/CommandExecutionCoordinator.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.manager; 2 | 3 | import io.github.mqzn.commands.base.Command; 4 | import io.github.mqzn.commands.base.context.CommandContext; 5 | import io.github.mqzn.commands.base.syntax.CommandSyntax; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.concurrent.CompletableFuture; 9 | 10 | public sealed abstract class CommandExecutionCoordinator { 11 | 12 | @NotNull 13 | protected final Command command; 14 | 15 | private CommandExecutionCoordinator(@NotNull Command manager) { 16 | this.command = manager; 17 | } 18 | 19 | static CommandExecutionCoordinator async(@NotNull Command manager) { 20 | return new AsyncCommandCoordinator<>(manager); 21 | } 22 | 23 | static CommandExecutionCoordinator sync(@NotNull Command manager) { 24 | return new SyncCommandCoordinator<>(manager); 25 | } 26 | 27 | public static CommandExecutionCoordinator fromType(@NotNull Command manager, Type type) { 28 | return type == Type.SYNC ? sync(manager) : async(manager); 29 | } 30 | 31 | public Command command() { 32 | return command; 33 | } 34 | 35 | public abstract Type type(); 36 | 37 | public abstract CompletableFuture coordinateExecution(@NotNull C sender, 38 | @NotNull CommandSyntax syntax, 39 | @NotNull CommandContext context); 40 | 41 | public enum ExecutionResult { 42 | SUCCESS, 43 | FAILED 44 | } 45 | 46 | 47 | public enum Type { 48 | ASYNC, 49 | SYNC 50 | } 51 | 52 | final static class AsyncCommandCoordinator extends CommandExecutionCoordinator { 53 | 54 | private AsyncCommandCoordinator(@NotNull Command manager) { 55 | super(manager); 56 | } 57 | 58 | @Override 59 | public Type type() { 60 | return Type.ASYNC; 61 | } 62 | 63 | @Override 64 | public CompletableFuture coordinateExecution(@NotNull C sender, 65 | @NotNull CommandSyntax syntax, 66 | @NotNull CommandContext context) { 67 | return CompletableFuture.supplyAsync(() -> { 68 | try { 69 | syntax.execute(sender, context); 70 | return ExecutionResult.SUCCESS; 71 | } catch (Exception ex) { 72 | ex.printStackTrace(); 73 | return ExecutionResult.FAILED; 74 | } 75 | 76 | }); 77 | 78 | } 79 | } 80 | 81 | 82 | final static class SyncCommandCoordinator extends CommandExecutionCoordinator { 83 | public SyncCommandCoordinator(Command manager) { 84 | super(manager); 85 | } 86 | 87 | @Override 88 | public Type type() { 89 | return Type.SYNC; 90 | } 91 | 92 | @Override 93 | public CompletableFuture coordinateExecution(@NotNull C sender, 94 | @NotNull CommandSyntax syntax, 95 | @NotNull CommandContext context) { 96 | try { 97 | syntax.execute(sender, context); 98 | return CompletableFuture.completedFuture(ExecutionResult.SUCCESS); 99 | } catch (Exception ex) { 100 | ex.printStackTrace(); 101 | return CompletableFuture.completedFuture(ExecutionResult.FAILED); 102 | } 103 | 104 | } 105 | 106 | 107 | } 108 | 109 | 110 | } 111 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/manager/FlagRegistry.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.manager; 2 | 3 | import io.github.mqzn.commands.base.manager.flags.FlagInfo; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import java.util.Collection; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | import java.util.concurrent.atomic.AtomicBoolean; 11 | 12 | public final class FlagRegistry { 13 | 14 | @NotNull 15 | public final static String FLAG_IDENTIFIER = "-"; 16 | @NotNull 17 | private static final AtomicBoolean flagRegistryCreated = new AtomicBoolean(false); 18 | @NotNull 19 | private final Map flags = new HashMap<>(); 20 | 21 | private FlagRegistry() { 22 | flagRegistryCreated.set(true); 23 | } 24 | 25 | static FlagRegistry create() throws IllegalAccessException { 26 | 27 | if (flagRegistryCreated.get()) { 28 | throw new IllegalAccessException("Error trying to create another instance of flag registry !"); 29 | } 30 | 31 | return new FlagRegistry(); 32 | } 33 | 34 | public void registerFlag(@NotNull FlagInfo flag) { 35 | flags.put(flag.getName(), flag); 36 | } 37 | 38 | 39 | public void unregisterFlag(String flag) { 40 | flags.remove(flag); 41 | 42 | for (var f : flags.values()) { 43 | if (f.hasAliase(flag)) { 44 | flags.remove(flag); 45 | break; 46 | } 47 | 48 | } 49 | 50 | } 51 | 52 | public @Nullable FlagInfo getFlag(String flag) { 53 | for (FlagInfo f : flags.values()) { 54 | if (f.getName().equalsIgnoreCase(flag) || f.hasAliase(flag)) 55 | return f; 56 | } 57 | 58 | return null; 59 | } 60 | 61 | 62 | public @NotNull Collection flags() { 63 | return flags.values(); 64 | } 65 | 66 | public boolean flagExists(String flagAlias) { 67 | return getFlag(flagAlias) != null; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/manager/SenderProviderRegistry.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.manager; 2 | 3 | import io.github.mqzn.commands.base.SenderProvider; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | public final class SenderProviderRegistry { 11 | 12 | @NotNull 13 | private final Map, SenderProvider> senderProviders = new HashMap<>(); 14 | 15 | 16 | @SuppressWarnings("unchecked") 17 | private @Nullable SenderProvider getSenderProvider(Class clazz) { 18 | return (SenderProvider) senderProviders.get(clazz); 19 | } 20 | 21 | public void registerSenderProvider(Class clazz, SenderProvider provider) { 22 | senderProviders.put(clazz, provider); 23 | } 24 | 25 | public boolean hasProviderFor(Class clazz) { 26 | return senderProviders.get(clazz) != null; 27 | } 28 | 29 | public @Nullable C provideSender(@NotNull S sender, Class clazz) { 30 | 31 | SenderProvider provider = getSenderProvider(clazz); 32 | if (provider != null) 33 | return provider.mapSender(sender); 34 | 35 | return null; 36 | } 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/manager/SuggestionProviderRegistry.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.manager; 2 | 3 | import io.github.mqzn.commands.base.SuggestionProvider; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public final class SuggestionProviderRegistry { 10 | 11 | private final Map, SuggestionProvider> providers = new HashMap<>(); 12 | 13 | SuggestionProviderRegistry() { 14 | 15 | } 16 | 17 | public void register(SuggestionProvider provider) { 18 | providers.put(provider.getClass(), provider); 19 | } 20 | 21 | public void unregister(Class clazz) { 22 | providers.remove(clazz); 23 | } 24 | 25 | @SuppressWarnings("unchecked") 26 | public @Nullable SP getProvider(Class clazz) { 27 | return (SP) providers.get(clazz); 28 | } 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/manager/flags/CommandFlag.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.manager.flags; 2 | 3 | import io.github.mqzn.commands.base.Information; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | public sealed interface CommandFlag permits CommandFlag.Builder.SimpleCommandFlag { 8 | 9 | static @NotNull CommandFlag from(FlagInfo info) { 10 | Builder builder = CommandFlag.builder(info.getName()) 11 | .withAliases(info.getAliases()); 12 | 13 | if (info.getInformation() != null) { 14 | 15 | var flagInformation = info.getInformation(); 16 | if (flagInformation.permission() != null) 17 | builder.withPermission(flagInformation.permission()); 18 | 19 | if (flagInformation.description() != null) 20 | builder.withDescription(flagInformation.description()); 21 | 22 | } 23 | 24 | return builder.build(); 25 | } 26 | 27 | static @NotNull Builder builder(String flagName) { 28 | return new Builder(flagName); 29 | } 30 | 31 | @NotNull String name(); 32 | 33 | @Nullable Information info(); 34 | 35 | @NotNull String[] aliases(); 36 | 37 | default boolean hasAliase(String aliase) { 38 | for (var a : aliases()) 39 | if (a.equalsIgnoreCase(aliase)) return true; 40 | 41 | return false; 42 | } 43 | 44 | final class Builder { 45 | 46 | @NotNull 47 | private final String name; 48 | 49 | @Nullable 50 | private String permission = null; 51 | 52 | @Nullable 53 | private String description = null; 54 | 55 | @NotNull 56 | private String[] aliases = new String[0]; 57 | 58 | Builder(@NotNull String name) { 59 | this.name = name; 60 | } 61 | 62 | 63 | public @NotNull Builder withAliases(@NotNull String... aliases) { 64 | this.aliases = aliases; 65 | return this; 66 | } 67 | 68 | @SuppressWarnings("UnusedReturnValue") 69 | public @NotNull Builder withPermission(@NotNull String permission) { 70 | this.permission = permission; 71 | return this; 72 | } 73 | 74 | @SuppressWarnings("UnusedReturnValue") 75 | public @NotNull Builder withDescription(@NotNull String description) { 76 | this.description = description; 77 | return this; 78 | } 79 | 80 | public CommandFlag build() { 81 | Information info = new Information(permission, description); 82 | return new SimpleCommandFlag(name, info, aliases); 83 | } 84 | 85 | 86 | record SimpleCommandFlag(@NotNull String name, 87 | @Nullable Information info, 88 | @NotNull String... aliases) implements CommandFlag { 89 | } 90 | 91 | } 92 | 93 | 94 | } 95 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/manager/flags/ContextFlagRegistry.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.manager.flags; 2 | 3 | import io.github.mqzn.commands.arguments.Argument; 4 | import io.github.mqzn.commands.base.Command; 5 | import io.github.mqzn.commands.base.Information; 6 | import io.github.mqzn.commands.base.context.CommandContext; 7 | import io.github.mqzn.commands.base.context.Context; 8 | import io.github.mqzn.commands.base.manager.CommandManager; 9 | import io.github.mqzn.commands.base.manager.FlagRegistry; 10 | import io.github.mqzn.commands.base.syntax.CommandSyntax; 11 | import io.github.mqzn.commands.base.syntax.SubCommandSyntax; 12 | import org.jetbrains.annotations.NotNull; 13 | import org.jetbrains.annotations.Nullable; 14 | 15 | import java.util.*; 16 | import java.util.regex.Matcher; 17 | import java.util.regex.Pattern; 18 | 19 | public final class ContextFlagRegistry { 20 | 21 | @NotNull 22 | public static final Pattern FLAG_PATTERN = Pattern.compile(FlagRegistry.FLAG_IDENTIFIER + "[a-z]+", Pattern.CASE_INSENSITIVE); 23 | 24 | @NotNull 25 | private final CommandManager manager; 26 | 27 | @NotNull 28 | private final Context commandContext; 29 | 30 | @NotNull 31 | private final Map flagsUsed = new HashMap<>(); 32 | 33 | 34 | private ContextFlagRegistry(@NotNull CommandManager manager, 35 | @NotNull Context commandContext) { 36 | this.manager = manager; 37 | this.commandContext = commandContext; 38 | } 39 | 40 | public static @NotNull ContextFlagRegistry create( 41 | @NotNull CommandManager manager, 42 | @NotNull CommandContext commandContext 43 | ) { 44 | return new ContextFlagRegistry<>(manager, commandContext); 45 | } 46 | 47 | public static boolean isRawArgumentFlag(String rawArg) { 48 | Matcher matcher = FLAG_PATTERN.matcher(rawArg); 49 | return matcher.matches(); 50 | } 51 | 52 | public FlagExtractionResult extractFlags(@NotNull S sender, @NotNull Command command, @NotNull CommandSyntax syntax) { 53 | 54 | List> argumentList = (syntax instanceof SubCommandSyntax sub) ? command.tree().getParentalArguments(sub.key()) 55 | : syntax.getArguments(); 56 | 57 | for (int r = 0; r < argumentList.size()+commandContext.flagsUsed(); r++) { 58 | String raw = commandContext.getRawArgument(r); 59 | 60 | if (raw == null) 61 | break; 62 | 63 | if (isRawArgumentFlag(raw)) { 64 | var extracted = extractFlagsUsed(sender, raw); 65 | boolean foundOneAtLeast = !extracted.isEmpty(); 66 | 67 | if (!foundOneAtLeast) { 68 | manager.getSenderWrapper().sendMessage(sender, "The flag(s) used are unknown to the command flags registry !"); 69 | return FlagExtractionResult.FAILED; 70 | } 71 | 72 | for (CommandFlag flag : extracted) 73 | this.flagsUsed.put(flag.name(), flag); 74 | } 75 | 76 | } 77 | 78 | 79 | for (var flag : flagsUsed.values()) { 80 | 81 | Information flagInfo = flag.info(); 82 | if (!syntax.getFlags().hasFlag(flag.name())) { 83 | manager.getSenderWrapper().sendMessage(sender, "The flag '" + FlagRegistry.FLAG_IDENTIFIER + flag.name() + "' is not allowed in this syntax"); 84 | return FlagExtractionResult.FAILED; 85 | } else if (flagInfo != null && flagInfo.permission() != null && !manager.getSenderWrapper().hasPermission(sender, flagInfo.permission())) { 86 | manager.getSenderWrapper().sendMessage(sender, "No permission to use the flag '" + FlagRegistry.FLAG_IDENTIFIER + flag.name() + "'"); 87 | return FlagExtractionResult.FAILED; 88 | } 89 | 90 | } 91 | 92 | if (flagsUsed.isEmpty()) return FlagExtractionResult.FOUND_NONE; 93 | else return FlagExtractionResult.SUCCESS; 94 | 95 | } 96 | 97 | @NotNull 98 | private Set extractFlagsUsed(@NotNull S sender, @NotNull String flagRaw) { 99 | final Set flags = new LinkedHashSet<>(); 100 | 101 | //first check if it's single 102 | 103 | //there's more ! 104 | //examples: -x , -xyz 105 | 106 | for (FlagInfo flagInfo : manager.flagRegistry().flags()) { 107 | 108 | boolean foundFlagAliase = false; 109 | for (String aliase : flagInfo.getAliases()) { 110 | if (flagRaw.contains(aliase)) { 111 | 112 | if (foundFlagAliase) { 113 | manager.getSenderWrapper().sendMessage(sender, "Warning: you used the same flag '" + flagRaw + "' twice !"); 114 | break; 115 | } 116 | 117 | flags.add(CommandFlag.from(flagInfo)); 118 | foundFlagAliase = true; 119 | } 120 | 121 | } 122 | 123 | } 124 | 125 | return flags; 126 | } 127 | 128 | public boolean isPresent(String flagName) { 129 | return getFlag(flagName) != null; 130 | } 131 | 132 | public @Nullable CommandFlag getFlag(String flagName) { 133 | return flagsUsed.get(flagName); 134 | } 135 | 136 | public int count() { 137 | return flagsUsed.size(); 138 | } 139 | 140 | public enum FlagExtractionResult { 141 | 142 | SUCCESS, 143 | 144 | FOUND_NONE, 145 | 146 | FAILED 147 | 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/manager/flags/FlagInfo.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.manager.flags; 2 | 3 | import io.github.mqzn.commands.base.Information; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | public final class FlagInfo { 8 | 9 | @NotNull 10 | private final String name; 11 | 12 | @Nullable 13 | private final Information information; 14 | 15 | private final String[] aliases; 16 | 17 | 18 | private FlagInfo(@NotNull String name, @Nullable Information information, String... aliases) { 19 | this.name = name; 20 | this.information = information; 21 | this.aliases = aliases; 22 | } 23 | 24 | public static @NotNull Builder builder(String name) { 25 | return new Builder(name); 26 | } 27 | 28 | public boolean hasAliase(String aliase) { 29 | for (var a : aliases) 30 | if (a.equalsIgnoreCase(aliase)) return true; 31 | 32 | return false; 33 | 34 | } 35 | 36 | public @NotNull String getName() { 37 | return name; 38 | } 39 | 40 | public @Nullable Information getInformation() { 41 | return information; 42 | } 43 | 44 | public String[] getAliases() { 45 | return aliases; 46 | } 47 | 48 | public static final class Builder { 49 | private final String name; 50 | 51 | @Nullable 52 | private Information information = null; 53 | 54 | private String[] aliases = new String[0]; 55 | 56 | Builder(String name) { 57 | this.name = name; 58 | } 59 | 60 | public @NotNull Builder info(@Nullable Information information) { 61 | this.information = information; 62 | return this; 63 | } 64 | 65 | public @NotNull Builder aliases(@NotNull String... aliases) { 66 | this.aliases = aliases; 67 | return this; 68 | } 69 | 70 | 71 | public @NotNull FlagInfo build() { 72 | return new FlagInfo(name, information, aliases); 73 | } 74 | 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/syntax/CommandAliases.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.syntax; 2 | 3 | 4 | public final class CommandAliases { 5 | 6 | private final String[] array; 7 | 8 | CommandAliases(String[] array) { 9 | this.array = array; 10 | } 11 | 12 | public static CommandAliases of(String... arr) { 13 | return new CommandAliases(arr); 14 | } 15 | 16 | public String[] getArray() { 17 | return array; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/syntax/CommandExecution.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.syntax; 2 | 3 | import io.github.mqzn.commands.base.context.Context; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public interface CommandExecution { 7 | 8 | void execute(@NotNull C sender, Context commandContext); 9 | } 10 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/syntax/CommandSyntaxBuilder.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.syntax; 2 | 3 | import io.github.mqzn.commands.arguments.Argument; 4 | import io.github.mqzn.commands.base.Information; 5 | import io.github.mqzn.commands.base.manager.CommandManager; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | public class CommandSyntaxBuilder { 13 | @NotNull 14 | protected final CommandManager manager; 15 | 16 | @NotNull 17 | protected final String commandLabel; 18 | 19 | @NotNull 20 | protected final List> arguments = new ArrayList<>(); 21 | 22 | @Nullable 23 | protected Class senderClass; 24 | 25 | @Nullable 26 | protected CommandExecution execution; 27 | 28 | @NotNull 29 | protected SyntaxFlags flags = SyntaxFlags.of(); 30 | 31 | @Nullable 32 | protected Information info = null; 33 | 34 | protected CommandSyntaxBuilder(@NotNull CommandManager manager, 35 | @NotNull Class senderClass, 36 | @NotNull String label) { 37 | this.manager = manager; 38 | this.senderClass = senderClass; 39 | this.commandLabel = label; 40 | } 41 | 42 | public static CommandSyntaxBuilder genericBuilder(@NotNull CommandManager manager, 43 | @NotNull Class senderClass, 44 | @NotNull String label) { 45 | return new CommandSyntaxBuilder<>(manager, senderClass, label); 46 | } 47 | 48 | public CommandSyntaxBuilder info(@Nullable Information info) { 49 | this.info = info; 50 | return this; 51 | } 52 | 53 | public CommandSyntaxBuilder flags(String... flags) { 54 | this.flags = SyntaxFlags.of(flags); 55 | return this; 56 | } 57 | 58 | public CommandSyntaxBuilder flags(SyntaxFlags flags) { 59 | this.flags = flags; 60 | return this; 61 | } 62 | 63 | public CommandSyntaxBuilder senderType(@Nullable Class senderClass) { 64 | this.senderClass = senderClass; 65 | return this; 66 | } 67 | 68 | public CommandSyntaxBuilder argument(@NotNull Argument argument) { 69 | if (!arguments.contains(argument)) { 70 | arguments.add(argument); 71 | } 72 | return this; 73 | } 74 | 75 | public CommandSyntaxBuilder execute(@NotNull CommandExecution execution) { 76 | this.execution = execution; 77 | return this; 78 | } 79 | 80 | public CommandSyntax build() { 81 | assert senderClass != null; 82 | CommandSyntax syntax = new CommandSyntax<>(manager, senderClass, commandLabel, 83 | execution == null ? (s, c) -> { 84 | } : execution, flags, arguments); 85 | syntax.setInfo(info); 86 | return syntax; 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/syntax/SubCommandBuilder.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.syntax; 2 | 3 | import io.github.mqzn.commands.arguments.Argument; 4 | import io.github.mqzn.commands.base.Information; 5 | import io.github.mqzn.commands.base.manager.CommandManager; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | import java.util.Arrays; 10 | import java.util.LinkedHashSet; 11 | 12 | public class SubCommandBuilder extends CommandSyntaxBuilder { 13 | 14 | @NotNull 15 | private final String name; 16 | @NotNull 17 | private final LinkedHashSet children = new LinkedHashSet<>(); 18 | @Nullable 19 | private CommandAliases commandAliases = CommandAliases.of(); 20 | @Nullable 21 | private String parent = null; 22 | 23 | @Nullable 24 | private CommandExecution defaultExecution = null; 25 | 26 | protected SubCommandBuilder(@NotNull CommandManager manager, 27 | @NotNull Class senderClass, 28 | @NotNull String label, 29 | @NotNull String name) { 30 | super(manager, senderClass, label); 31 | this.name = name; 32 | } 33 | 34 | public static SubCommandBuilder genericBuilder(CommandManager manager, 35 | @NotNull Class senderClass, 36 | @NotNull String label, 37 | @NotNull String name) { 38 | return new SubCommandBuilder<>(manager, senderClass, label, name); 39 | } 40 | 41 | public SubCommandBuilder aliases(String... aliases) { 42 | this.commandAliases = CommandAliases.of(aliases); 43 | return this; 44 | } 45 | 46 | public SubCommandBuilder children(String... children) { 47 | this.children.addAll(Arrays.asList(children)); 48 | return this; 49 | } 50 | 51 | public SubCommandBuilder defaultExecution(CommandExecution defaultExecution) { 52 | this.defaultExecution = defaultExecution; 53 | return this; 54 | } 55 | 56 | @Override 57 | public SubCommandBuilder info(@Nullable Information info) { 58 | return (SubCommandBuilder) super.info(info); 59 | } 60 | 61 | @Override 62 | public SubCommandBuilder flags(String... flags) { 63 | return (SubCommandBuilder) super.flags(flags); 64 | } 65 | 66 | @Override 67 | public SubCommandBuilder flags(SyntaxFlags flags) { 68 | return (SubCommandBuilder) super.flags(flags); 69 | } 70 | 71 | @Override 72 | public SubCommandBuilder senderType(@Nullable Class senderClass) { 73 | return (SubCommandBuilder) super.senderType(senderClass); 74 | } 75 | 76 | @Override 77 | public SubCommandBuilder argument(@NotNull Argument argument) { 78 | if (arguments.contains(argument)) return this; 79 | return (SubCommandBuilder) super.argument(argument); 80 | } 81 | 82 | @Override 83 | public SubCommandBuilder execute(@NotNull CommandExecution execution) { 84 | return (SubCommandBuilder) super.execute(execution); 85 | } 86 | 87 | public SubCommandBuilder parent(@Nullable String parent) { 88 | this.parent = parent; 89 | return this; 90 | } 91 | 92 | @Override 93 | public SubCommandSyntax build() { 94 | assert commandAliases != null; 95 | assert senderClass != null; 96 | 97 | 98 | SubCommandSyntax subCommandSyntax = new SubCommandSyntax<>(manager, senderClass, commandLabel, parent, name, 99 | commandAliases, execution, flags, arguments, defaultExecution); 100 | 101 | subCommandSyntax.setInfo(info); 102 | 103 | for (var child : children) 104 | subCommandSyntax.addChild(child); 105 | 106 | return subCommandSyntax; 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/syntax/SubCommandSyntax.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.syntax; 2 | 3 | import io.github.mqzn.commands.arguments.Argument; 4 | import io.github.mqzn.commands.base.Command; 5 | import io.github.mqzn.commands.base.context.CommandContext; 6 | import io.github.mqzn.commands.base.context.DelegateCommandContext; 7 | import io.github.mqzn.commands.base.manager.CommandManager; 8 | import io.github.mqzn.commands.base.syntax.tree.CommandTree; 9 | import io.github.mqzn.commands.utilities.ArgumentSyntaxUtility; 10 | import net.kyori.adventure.text.Component; 11 | import net.kyori.adventure.text.TextComponent; 12 | import org.jetbrains.annotations.NotNull; 13 | import org.jetbrains.annotations.Nullable; 14 | 15 | import java.util.LinkedHashSet; 16 | import java.util.List; 17 | import java.util.Objects; 18 | 19 | 20 | @SuppressWarnings("unused") 21 | public class SubCommandSyntax extends CommandSyntax { 22 | 23 | @NotNull 24 | private final String name; 25 | 26 | @NotNull 27 | private final CommandAliases commandAliases; 28 | 29 | @NotNull 30 | private final LinkedHashSet<@NotNull String> children = new LinkedHashSet<>(); 31 | 32 | @Nullable 33 | private final CommandExecution defaultExecution; 34 | 35 | @Nullable 36 | private String parent; 37 | 38 | protected SubCommandSyntax( 39 | @NotNull CommandManager manager, 40 | @NotNull Class senderClass, 41 | @NotNull String commandLabel, 42 | @Nullable String parent, 43 | @NotNull String name, 44 | @NotNull CommandAliases commandAliases, 45 | @Nullable CommandExecution execution, 46 | @NotNull SyntaxFlags flags, 47 | @NotNull List> arguments, 48 | @Nullable CommandExecution defaultExecution 49 | ) { 50 | 51 | super(manager, senderClass, commandLabel, execution, flags, arguments); 52 | this.name = name; 53 | this.parent = parent; 54 | this.commandAliases = commandAliases; 55 | this.defaultExecution = defaultExecution; 56 | 57 | } 58 | 59 | public void addChild(SubCommandSyntax subCommand) { 60 | addChild(subCommand.getName()); 61 | } 62 | 63 | public void addChild(String subCommand) { 64 | children.add(subCommand); 65 | } 66 | 67 | public boolean hasChild(String name) { 68 | return children.contains(name); 69 | } 70 | 71 | public boolean hasChild(SubCommandSyntax subCommandSyntax) { 72 | return hasChild(subCommandSyntax.getName()); 73 | } 74 | 75 | public void removeChild(SubCommandSyntax subCommandSyntax) { 76 | removeChild(subCommandSyntax.getName()); 77 | } 78 | 79 | public void removeChild(String name) { 80 | children.remove(name); 81 | } 82 | 83 | public boolean isLeafChild() { 84 | return !hasChildren() && !isOrphan(); 85 | } 86 | 87 | public boolean hasChildren() { 88 | return !children.isEmpty(); 89 | } 90 | 91 | public boolean isOrphan() { 92 | return parent == null; 93 | } 94 | 95 | @SuppressWarnings("unchecked") 96 | public void defaultExecution(C sender, DelegateCommandContext context) { 97 | if (defaultExecution == null) return; 98 | ((CommandExecution) defaultExecution).execute(sender, context); 99 | } 100 | 101 | @Override 102 | @SuppressWarnings("unchecked") 103 | public void execute(C sender, CommandContext commandContext) { 104 | if (execution == null) { 105 | if (defaultExecution == null) { 106 | throw new IllegalStateException( 107 | String.format("Failed to execute subcommand `%s`, as it doesn't have an execution or even a default execution", name) 108 | ); 109 | } 110 | 111 | ((CommandExecution) defaultExecution).execute(sender, commandContext); 112 | } else { 113 | ((CommandExecution) execution).execute(sender, commandContext); 114 | } 115 | 116 | } 117 | 118 | public boolean matches(String rawArgument) { 119 | if (rawArgument == null) { 120 | return false; 121 | } 122 | return this.name.equalsIgnoreCase(rawArgument) 123 | || ArgumentSyntaxUtility.aliasesIncludes(commandAliases, rawArgument); 124 | } 125 | 126 | @Override 127 | public String toString() { 128 | return name; 129 | } 130 | 131 | public @NotNull String getName() { 132 | return name; 133 | } 134 | 135 | public @NotNull LinkedHashSet getChildren() { 136 | return children; 137 | } 138 | 139 | public @Nullable String getParent() { 140 | return parent; 141 | } 142 | 143 | public void setParent(@Nullable String parentName) { 144 | this.parent = parentName; 145 | } 146 | 147 | public @NotNull CommandAliases getAliases() { 148 | return commandAliases; 149 | } 150 | 151 | 152 | @Override 153 | public @NotNull TextComponent toText(@NotNull CommandManager manager, @NotNull S sender) { 154 | Command command = manager.getCommand(commandLabel); 155 | if (command == null) { 156 | return Component.empty(); 157 | } 158 | 159 | CommandTree tree = command.tree(); 160 | 161 | return Component.text(ArgumentSyntaxUtility.format(manager, commandLabel, tree.getParentalArguments(key()))); 162 | } 163 | 164 | public CommandTree.SubCommandKey key() { 165 | return new CommandTree.SubCommandKey<>(parent, name); 166 | } 167 | 168 | @Override 169 | public boolean equals(Object o) { 170 | if (this == o) return true; 171 | if (!(o instanceof SubCommandSyntax that)) return false; 172 | if (!super.equals(o)) return false; 173 | 174 | return Objects.equals(name, that.name) && arguments.equals(that.arguments); 175 | } 176 | 177 | @Override 178 | public int hashCode() { 179 | return Objects.hash(super.hashCode(), parent, name); 180 | } 181 | 182 | 183 | } 184 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/syntax/SyntaxFlags.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.syntax; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | import java.util.*; 6 | 7 | public final class SyntaxFlags implements Iterable { 8 | 9 | @NotNull 10 | private final Set flags = new HashSet<>(); 11 | 12 | private SyntaxFlags(String... flags) { 13 | Collections.addAll(this.flags, flags); 14 | } 15 | 16 | public static SyntaxFlags of(String... flags) { 17 | return new SyntaxFlags(flags); 18 | } 19 | 20 | 21 | public void addFlag(String flag) { 22 | if(flag == null) return; 23 | flags.add(flag); 24 | } 25 | 26 | public void removeFlag(String flag) { 27 | flags.remove(flag); 28 | } 29 | 30 | public boolean hasFlag(String flag) { 31 | return flag != null && flags.contains(flag); 32 | } 33 | 34 | /** 35 | * Returns an iterator over elements of type {@code T}. 36 | * 37 | * @return an Iterator. 38 | */ 39 | @NotNull 40 | @Override 41 | public Iterator iterator() { 42 | return flags.iterator(); 43 | } 44 | 45 | public int count() { 46 | return flags.size(); 47 | } 48 | 49 | @Override 50 | public boolean equals(Object o) { 51 | if (this == o) return true; 52 | if (!(o instanceof SyntaxFlags strings)) return false; 53 | return flags.equals(strings.flags); 54 | } 55 | 56 | @Override 57 | public int hashCode() { 58 | return Objects.hash(flags); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/base/syntax/tree/SubCommandArgumentTree.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.base.syntax.tree; 2 | 3 | import io.github.mqzn.commands.arguments.Argument; 4 | import io.github.mqzn.commands.base.Command; 5 | import io.github.mqzn.commands.base.syntax.SubCommandSyntax; 6 | 7 | import java.util.*; 8 | 9 | /** 10 | * This class represents the tree of subcommands arguments relations 11 | * it's very beneficial in cases of complex inheritance between subcommands; 12 | * while loading/parsing a single subcommand , it's essential to know 2 sets of arguments, which are 13 | * the arguments of the subcommand being loaded specifically AND the all arguments related to the subcommand being loaded. 14 | * All arguments related to the subcommand are loaded using the parents of this subcommand 15 | * 16 | * @author Mqzen 17 | * @see CommandTree 18 | */ 19 | @SuppressWarnings("unused") 20 | public final class SubCommandArgumentTree { 21 | private final Command command; 22 | private final CommandTree tree; 23 | private final Map, Map, Pathway>> data = new HashMap<>(); 24 | 25 | private SubCommandArgumentTree(Command command, CommandTree tree) { 26 | this.command = command; 27 | this.tree = tree; 28 | initPathways(); 29 | loadParentalPathway(); 30 | } 31 | 32 | public static SubCommandArgumentTree wrap(Command command, CommandTree tree) { 33 | return new SubCommandArgumentTree<>(command, tree); 34 | } 35 | 36 | private void initPathways() { 37 | for (CommandTree.CommandNode root : tree.getRoots().values()) 38 | loadRootArguments(root, root); 39 | } 40 | 41 | private void loadRootArguments(CommandTree.CommandNode root, CommandTree.CommandNode node) { 42 | CommandTree.SubCommandKey rootKey = new CommandTree.SubCommandKey<>(root.data.getParent(), root.data.getName()); 43 | 44 | data.compute(rootKey, (key, oldMap) -> { 45 | if (oldMap == null) { 46 | LinkedList> list = new LinkedList<>(); 47 | list.addLast(Argument.literal(node.data.getName()).aliases(node.data.getAliases().getArray())); 48 | for (Argument arg : node.data.getArguments()) list.addLast(arg); 49 | 50 | Map, Pathway> newMap = new HashMap<>(); 51 | newMap.put(node.data.key(), new Pathway(list)); 52 | return newMap; 53 | } else { 54 | CommandTree.SubCommandKey nodeKey = node.data.key(); 55 | if (!oldMap.containsKey(nodeKey)) { 56 | LinkedList> list = new LinkedList<>(); 57 | list.addLast(Argument.literal(node.data.getName()).aliases(node.data.getAliases().getArray())); 58 | for (Argument arg : node.data.getArguments()) list.addLast(arg); 59 | oldMap.put(nodeKey, new Pathway(list)); 60 | } else { 61 | Pathway oldPathway = oldMap.get(nodeKey); 62 | oldPathway.addArg(Argument.literal(node.data.getName()).aliases(node.data.getAliases().getArray())); 63 | for (Argument arg : node.data.getArguments()) oldPathway.addArg(arg); 64 | } 65 | return oldMap; 66 | } 67 | }); 68 | 69 | for (String child : node.data.getChildren()) { 70 | CommandTree.SubCommandKey childKey = CommandTree.SubCommandKey.create(node.data.getName(), child); 71 | SubCommandSyntax childSub = tree.getSubCommand(childKey); 72 | if (childSub == null) 73 | throw new IllegalStateException("Unknown child sub command `" + childKey.name() + "` make sure you registered this subcommand in the main class "); 74 | loadRootArguments(root, new CommandTree.CommandNode<>(command, childSub)); 75 | } 76 | } 77 | 78 | private void loadParentalPathway() { 79 | for (Map, Pathway> nodePathwayMapping : data.values()) { 80 | for (Map.Entry, Pathway> entry : nodePathwayMapping.entrySet()) { 81 | CommandTree.SubCommandKey key = entry.getKey(); 82 | Pathway pathway = entry.getValue(); 83 | SubCommandSyntax subCommand = tree.getSubCommand(key); 84 | if (subCommand == null || subCommand.isOrphan()) continue; 85 | 86 | String parentName = key.parent(); 87 | SubCommandSyntax parentSub = tree.searchForSub(parentName); 88 | while (parentSub != null) { 89 | List> reversed = new ArrayList<>(parentSub.getArguments()); 90 | Collections.reverse(reversed); 91 | for (Argument arg : reversed) pathway.addFirstArg(arg); 92 | 93 | pathway.addFirstArg(Argument.literal(parentName).aliases(parentSub.getAliases().getArray())); 94 | 95 | parentName = parentSub.getParent(); 96 | if (parentName == null) break; 97 | parentSub = tree.searchForSub(parentName); 98 | } 99 | } 100 | } 101 | } 102 | 103 | public LinkedList> getSubCommandArguments(CommandTree.SubCommandKey key) { 104 | for (Map, Pathway> mapping : data.values()) { 105 | Pathway pathway = mapping.get(key); 106 | if (pathway != null) return pathway.arguments; 107 | } 108 | return null; 109 | } 110 | 111 | private record Pathway(LinkedList> arguments) { 112 | 113 | void addFirstArg(Argument arg) { 114 | arguments.addFirst(arg); 115 | } 116 | 117 | void addArg(Argument arg) { 118 | arguments.addLast(arg); 119 | } 120 | 121 | void removeArg(Argument arg) { 122 | arguments.remove(arg); 123 | } 124 | 125 | Pathway addPathway(Pathway pathway) { 126 | for (Argument arg : pathway.arguments) addArg(arg); 127 | return this; 128 | } 129 | 130 | @Override 131 | public String toString() { 132 | return arguments.toString(); 133 | } 134 | } 135 | } 136 | 137 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/exceptions/CommandException.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.exceptions; 2 | 3 | public abstract class CommandException extends Exception { 4 | 5 | protected final String command; 6 | 7 | public CommandException(String message, 8 | String command) { 9 | super(message); 10 | this.command = command; 11 | } 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/exceptions/CommandExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.exceptions; 2 | 3 | import io.github.mqzn.commands.base.caption.CaptionKey; 4 | import io.github.mqzn.commands.base.context.Context; 5 | import io.github.mqzn.commands.base.manager.CommandManager; 6 | import io.github.mqzn.commands.exceptions.types.ArgumentParseException; 7 | import io.github.mqzn.commands.utilities.Pair; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | public final class CommandExceptionHandler { 14 | 15 | @NotNull 16 | private final Map, ExceptionCallback> callBacks = new HashMap<>(); 17 | 18 | public CommandExceptionHandler(@NotNull CommandManager manager) { 19 | 20 | registerCallback(ArgumentParseException.class, ((exception, sender, commandContext) -> manager.captionRegistry().sendCaption(sender, commandContext, exception, CaptionKey.INVALID_ARGUMENT))); 21 | } 22 | 23 | public void registerCallback(@NotNull Class exception, @NotNull ExceptionCallback callback) { 24 | callBacks.put(exception, callback); 25 | } 26 | 27 | public void handleException(@NotNull Throwable exception, 28 | @NotNull S sender, 29 | @NotNull Context commandContext) { 30 | 31 | var pair = this.getHandleData(exception); 32 | ExceptionCallback handle = pair.getRight(); 33 | if (handle != null) 34 | handle.callback(pair.getLeft(), sender, commandContext); 35 | else 36 | exception.printStackTrace(); 37 | } 38 | 39 | private Pair> getHandleData(@NotNull Throwable exception) { 40 | ExceptionCallback handle = callBacks.get(exception.getClass()); 41 | if (handle != null) 42 | return Pair.of(exception, handle); 43 | 44 | Throwable deepestCause = exception.getCause(); 45 | if (deepestCause == null) 46 | return Pair.empty(); 47 | 48 | Throwable lastCause = deepestCause; 49 | handle = callBacks.get(deepestCause.getClass()); 50 | while (handle == null) { 51 | deepestCause = deepestCause.getCause(); 52 | if (deepestCause == null) break; 53 | lastCause = deepestCause; 54 | handle = callBacks.get(deepestCause.getClass()); 55 | } 56 | 57 | 58 | return Pair.of(lastCause, handle); 59 | } 60 | 61 | @FunctionalInterface 62 | public interface ExceptionCallback { 63 | 64 | void callback(Throwable exception, S sender, Context commandContext); 65 | 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/exceptions/UnknownCommandSenderType.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.exceptions; 2 | 3 | public class UnknownCommandSenderType extends RuntimeException { 4 | 5 | public UnknownCommandSenderType(Class senderClass) { 6 | super("Unknown sender type : " + senderClass.getName()); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/exceptions/types/ArgumentParseException.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.exceptions.types; 2 | 3 | import io.github.mqzn.commands.exceptions.CommandException; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public final class ArgumentParseException extends CommandException { 7 | 8 | private final String input; 9 | 10 | public ArgumentParseException(@NotNull String message, 11 | @NotNull String input, 12 | @NotNull String command) { 13 | super(message, command); 14 | this.input = input; 15 | } 16 | 17 | @Override 18 | public Throwable fillInStackTrace() { 19 | // Stacktrace is useless to the parser 20 | return this; 21 | } 22 | 23 | /** 24 | * Gets the problematic command input. 25 | * 26 | * @return the command input which triggered the exception 27 | */ 28 | @NotNull 29 | public String getInput() { 30 | return input; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/exceptions/types/SyntaxAmbiguityException.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.exceptions.types; 2 | 3 | import io.github.mqzn.commands.base.Command; 4 | import io.github.mqzn.commands.base.manager.CommandManager; 5 | import io.github.mqzn.commands.base.syntax.CommandSyntax; 6 | import io.github.mqzn.commands.exceptions.CommandException; 7 | import io.github.mqzn.commands.utilities.ArgumentSyntaxUtility; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import java.util.Collection; 11 | import java.util.stream.Collectors; 12 | 13 | public final class SyntaxAmbiguityException extends CommandException { 14 | 15 | public SyntaxAmbiguityException(@NotNull CommandManager manager, 16 | Command command, 17 | Collection> syntaxes) { 18 | 19 | super("Similar syntaxes detected (duplicate execution logic) : " + String.join("\n", 20 | syntaxes.stream().map((syntax) -> ArgumentSyntaxUtility.format(manager, command.name(), CommandSyntax.getArguments(command.tree(), syntax))) 21 | .collect(Collectors.toSet())), command.name()); 22 | 23 | } 24 | 25 | public SyntaxAmbiguityException(String message, Command command) { 26 | 27 | super(message, command.name()); 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/help/CommandHelpProvider.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.help; 2 | 3 | import io.github.mqzn.commands.base.Command; 4 | import io.github.mqzn.commands.base.manager.CommandManager; 5 | import io.github.mqzn.commands.base.syntax.CommandSyntax; 6 | import io.github.mqzn.commands.utilities.text.ItemPageTextDisplayer; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public interface CommandHelpProvider { 10 | 11 | CommandHelpStyle menuStyle(); 12 | 13 | default ItemPageTextDisplayer> syntaxDisplayer(@NotNull CommandManager manager, 14 | @NotNull Command command, 15 | @NotNull CommandHelpStyle provider) { 16 | return new CommandSyntaxPageDisplayer<>(manager, command, provider); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/help/CommandHelpStyle.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.help; 2 | 3 | import io.github.mqzn.commands.base.syntax.CommandSyntax; 4 | import net.kyori.adventure.text.TextComponent; 5 | import net.kyori.adventure.text.format.Style; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | import java.util.function.Function; 9 | 10 | public interface CommandHelpStyle { 11 | 12 | static Builder builder() { 13 | return new Builder<>(); 14 | } 15 | 16 | /** 17 | * The line style of the help topic 18 | * 19 | * @return how the line is displayed in the help topic 20 | * e.g: "[style]========== Help Menu [style]============ 21 | * @see Style 22 | */ 23 | @NotNull Style lineStyle(); 24 | 25 | /** 26 | * The header of the help topic 27 | * 28 | * @param label the command label (name) 29 | * @return the header of the help topic 30 | */ 31 | @NotNull TextComponent header(String label); 32 | 33 | /** 34 | * The style of the syntax of a command 35 | * 36 | * @param syntax the syntax of a command 37 | * @return The style of the syntax of a command 38 | */ 39 | @NotNull Style syntaxStyle(@NotNull CommandSyntax syntax); 40 | 41 | final class Builder { 42 | 43 | private Style lineStyle; 44 | private Function, Style> syntaxStyleMapper; 45 | private Function header; 46 | 47 | Builder() { 48 | } 49 | 50 | public Builder lineStyle(Style lineStyle) { 51 | this.lineStyle = lineStyle; 52 | return this; 53 | } 54 | 55 | public Builder syntaxStyle(Function, Style> syntaxStyleMapper) { 56 | this.syntaxStyleMapper = syntaxStyleMapper; 57 | return this; 58 | } 59 | 60 | public Builder header(Function header) { 61 | this.header = header; 62 | return this; 63 | } 64 | 65 | public CommandHelpStyle build() { 66 | return new ImmutableHelpStyle<>(lineStyle, syntaxStyleMapper, header); 67 | } 68 | 69 | 70 | } 71 | 72 | record ImmutableHelpStyle(Style lineStyle, 73 | Function, Style> syntaxStyle, 74 | Function header) implements CommandHelpStyle { 75 | 76 | /** 77 | * The header of the help topic 78 | * 79 | * @param label the command label (name) 80 | * @return the header of the help topic 81 | */ 82 | @Override 83 | public @NotNull TextComponent header(String label) { 84 | return header.apply(label); 85 | } 86 | 87 | /** 88 | * The style of the syntax of a command 89 | * 90 | * @param syntax the syntax of a command 91 | * @return The style of the syntax of a command 92 | */ 93 | @Override 94 | public @NotNull Style syntaxStyle(@NotNull CommandSyntax syntax) { 95 | return syntaxStyle.apply(syntax); 96 | } 97 | 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/help/CommandSyntaxPageDisplayer.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.help; 2 | 3 | import io.github.mqzn.commands.base.Command; 4 | import io.github.mqzn.commands.base.Information; 5 | import io.github.mqzn.commands.base.manager.CommandManager; 6 | import io.github.mqzn.commands.base.syntax.CommandSyntax; 7 | import io.github.mqzn.commands.base.syntax.SubCommandSyntax; 8 | import io.github.mqzn.commands.utilities.ArgumentSyntaxUtility; 9 | import io.github.mqzn.commands.utilities.text.ItemPageTextDisplayer; 10 | import net.kyori.adventure.text.Component; 11 | import net.kyori.adventure.text.TextComponent; 12 | import net.kyori.adventure.text.event.ClickEvent; 13 | import net.kyori.adventure.text.format.NamedTextColor; 14 | import net.kyori.adventure.text.format.Style; 15 | import org.jetbrains.annotations.NotNull; 16 | 17 | /** 18 | * The class to display each syntax of a command 19 | * it defines how it displays a single syntax 20 | * 21 | * @see CommandSyntax 22 | * @see ItemPageTextDisplayer 23 | */ 24 | public class CommandSyntaxPageDisplayer implements ItemPageTextDisplayer> { 25 | 26 | @NotNull 27 | protected final CommandManager manager; 28 | 29 | @NotNull 30 | protected final CommandHelpStyle provider; 31 | 32 | @NotNull 33 | protected final Command command; 34 | 35 | public CommandSyntaxPageDisplayer(@NotNull CommandManager manager, 36 | @NotNull Command command, 37 | @NotNull CommandHelpStyle provider) { 38 | this.manager = manager; 39 | this.command = command; 40 | this.provider = provider; 41 | } 42 | 43 | 44 | @Override 45 | public TextComponent displayPageItem(@NotNull S sender, 46 | @NotNull CommandSyntax convertible, 47 | int index) { 48 | 49 | Information syntaxInfo = convertible.getInfo(); 50 | 51 | TextComponent comp = convertible.toText(manager, sender); 52 | TextComponent description = (TextComponent) Component.text("-") 53 | .style(Style.style(NamedTextColor.GOLD)) 54 | .appendSpace() 55 | .append(Component.text((syntaxInfo == null || syntaxInfo.description().isEmpty() 56 | || syntaxInfo.description().isBlank() ? "N/A" : syntaxInfo.description())) 57 | .style(Style.style(NamedTextColor.WHITE))); 58 | 59 | 60 | TextComponent result = Component.text("+ ", NamedTextColor.BLUE) 61 | .append(comp.style(provider.syntaxStyle(convertible))); 62 | 63 | String format = ArgumentSyntaxUtility.format(manager, command.name(), CommandSyntax.getArguments(command.tree(), convertible)); 64 | if (convertible instanceof SubCommandSyntax sub && sub.hasChildren()) format = format + " help"; 65 | result = result.clickEvent(ClickEvent.suggestCommand(format)); 66 | 67 | return (TextComponent) result.appendSpace() 68 | .append(description); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/help/SubCommandHelp.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.help; 2 | 3 | import io.github.mqzn.commands.arguments.Argument; 4 | import io.github.mqzn.commands.base.caption.CaptionKey; 5 | import io.github.mqzn.commands.base.context.Context; 6 | import io.github.mqzn.commands.base.manager.CommandManager; 7 | import io.github.mqzn.commands.base.syntax.CommandAliases; 8 | import io.github.mqzn.commands.base.syntax.CommandSyntax; 9 | import io.github.mqzn.commands.base.syntax.SubCommandSyntax; 10 | import io.github.mqzn.commands.base.syntax.SyntaxFlags; 11 | import io.github.mqzn.commands.base.syntax.tree.CommandTree; 12 | 13 | import java.util.List; 14 | import java.util.Optional; 15 | import java.util.stream.Collectors; 16 | 17 | public class SubCommandHelp extends SubCommandSyntax { 18 | 19 | public SubCommandHelp(CommandManager manager, String commandLabel, SubCommandSyntax parent) { 20 | super(manager, manager.getSenderWrapper().senderType(), 21 | commandLabel, parent.getName(), "help", CommandAliases.of(), 22 | ((sender, commandContext) -> execute(manager, parent, sender, commandContext)), 23 | SyntaxFlags.of(), 24 | List.of(Argument.integer("page").min(1).asOptional().setDefaultValue(1)), 25 | ((sender, context) -> helpExecution(sender, manager, context, 1, parent))); 26 | } 27 | 28 | private static void helpExecution(S sender, CommandManager manager, Context context, int page, SubCommandSyntax parent) { 29 | List> children = parent.getChildren().stream() 30 | .map(childName -> { 31 | CommandTree.SubCommandKey key = CommandTree.SubCommandKey.create(parent.getName(), childName); 32 | return context.commandUsed().tree().getSubCommand(key); 33 | }) 34 | .collect(Collectors.toList()); 35 | 36 | try { 37 | manager.handleHelpRequest(sender, context, parent.getName(), page, children); 38 | } catch (IllegalArgumentException ex) { 39 | manager.captionRegistry().sendCaption(sender, context, CaptionKey.UNKNOWN_HELP_PAGE); 40 | } 41 | } 42 | 43 | private static void execute(CommandManager manager, 44 | SubCommandSyntax parent, 45 | S sender, Context context) { 46 | int page = (int) Optional.ofNullable(context.getArgument("page")).orElse(1); 47 | helpExecution(sender, manager, context, page, parent); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/help/UnknownPageCaption.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.help; 2 | 3 | import io.github.mqzn.commands.base.caption.Caption; 4 | import io.github.mqzn.commands.base.caption.CaptionKey; 5 | import io.github.mqzn.commands.base.caption.Message; 6 | import io.github.mqzn.commands.base.context.Context; 7 | import net.kyori.adventure.text.Component; 8 | import net.kyori.adventure.text.TextComponent; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | public final class UnknownPageCaption implements Caption { 12 | 13 | 14 | @Override 15 | public @NotNull CaptionKey key() { 16 | return CaptionKey.UNKNOWN_HELP_PAGE; 17 | } 18 | 19 | @Override 20 | public @NotNull TextComponent message(S sender, Context context, Throwable exception) { 21 | Integer page = context.getArgument("page"); 22 | if (page == null) page = 1; 23 | return Message.prefixed(Message.EXECUTION_ERROR).append(Component.text("Unknown page '" + page + "' make sure it's within boundaries")); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/utilities/ArgumentSyntaxUtility.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.utilities; 2 | 3 | import io.github.mqzn.commands.arguments.Argument; 4 | import io.github.mqzn.commands.arguments.ArgumentLiteral; 5 | import io.github.mqzn.commands.base.manager.CommandManager; 6 | import io.github.mqzn.commands.base.syntax.CommandAliases; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.util.List; 10 | 11 | public final class ArgumentSyntaxUtility { 12 | 13 | @NotNull 14 | public final static String[] ARGUMENT_FORMAT_PREFIX_SUFFIX = { 15 | "<", ">", "[", "]" 16 | }; 17 | 18 | private ArgumentSyntaxUtility() { 19 | } 20 | 21 | 22 | public static boolean isArgRequired(String argSyntax) { 23 | return argSyntax.startsWith(ARGUMENT_FORMAT_PREFIX_SUFFIX[0]) && argSyntax.endsWith(ARGUMENT_FORMAT_PREFIX_SUFFIX[1]); 24 | } 25 | 26 | public static boolean isArgOptional(String argSyntax) { 27 | return argSyntax.startsWith(ARGUMENT_FORMAT_PREFIX_SUFFIX[2]) && argSyntax.endsWith(ARGUMENT_FORMAT_PREFIX_SUFFIX[3]); 28 | } 29 | 30 | public static boolean isArgLiteral(String argSyntax) { 31 | return !isArgRequired(argSyntax) && !isArgOptional(argSyntax); 32 | } 33 | 34 | public static String fetchArgId(String argSyntax) { 35 | StringBuilder builder = new StringBuilder(argSyntax); 36 | builder.deleteCharAt(argSyntax.length() - 1); 37 | builder.deleteCharAt(0); 38 | 39 | return builder.toString(); 40 | } 41 | 42 | public static boolean aliasesIncludes(CommandAliases commandAliases, String name) { 43 | return aliasesIncludes(commandAliases.getArray(), name); 44 | } 45 | 46 | public static boolean aliasesIncludes(String[] aliases, String name) { 47 | for (String aliase : aliases) { 48 | if (aliase.equalsIgnoreCase(name)) 49 | return true; 50 | } 51 | 52 | return false; 53 | } 54 | 55 | public static String format( 56 | @NotNull CommandManager commandManager, 57 | @NotNull String commandLabel, 58 | @NotNull List<@NotNull Argument> arguments) { 59 | 60 | String start = commandManager.commandPrefix() == ' ' ? "" : String.valueOf(commandManager.commandPrefix()); 61 | StringBuilder builder = new StringBuilder(start).append(commandLabel).append(" "); 62 | 63 | for (int i = 0; i < arguments.size(); i++) { 64 | 65 | var arg = arguments.get(i); 66 | 67 | if (arg instanceof ArgumentLiteral) { 68 | builder.append(arg.id()).append(" "); 69 | continue; 70 | } 71 | 72 | String format = formatArg(arg); 73 | builder.append(format); 74 | 75 | if (i != arguments.size() - 1) builder.append(" "); 76 | 77 | } 78 | 79 | return builder.toString(); 80 | } 81 | 82 | 83 | public static String formatArg(@NotNull Argument argument) { 84 | if (argument instanceof ArgumentLiteral) return ""; 85 | String prefix, suffix; 86 | if (argument.isOptional()) { 87 | prefix = ARGUMENT_FORMAT_PREFIX_SUFFIX[2]; 88 | suffix = ARGUMENT_FORMAT_PREFIX_SUFFIX[3]; 89 | } else { 90 | prefix = ARGUMENT_FORMAT_PREFIX_SUFFIX[0]; 91 | suffix = ARGUMENT_FORMAT_PREFIX_SUFFIX[1]; 92 | } 93 | 94 | return prefix + argument.id() + suffix; 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/utilities/Pair.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.utilities; 2 | 3 | import java.util.Objects; 4 | 5 | public class Pair { 6 | 7 | private final L left; 8 | private final R right; 9 | 10 | Pair(L left, R right) { 11 | this.left = left; 12 | this.right = right; 13 | } 14 | 15 | public static Pair of(L left, R right) { 16 | return new Pair<>(left, right); 17 | } 18 | 19 | public static Pair empty() { 20 | return new Pair<>(null, null); 21 | } 22 | 23 | public L getLeft() { 24 | return left; 25 | } 26 | 27 | public R getRight() { 28 | return right; 29 | } 30 | 31 | @Override 32 | public boolean equals(Object o) { 33 | if (this == o) return true; 34 | if (o == null || getClass() != o.getClass()) return false; 35 | Pair pair = (Pair) o; 36 | return Objects.equals(left, pair.left) && Objects.equals(right, pair.right); 37 | } 38 | 39 | @Override 40 | public int hashCode() { 41 | return Objects.hash(left, right); 42 | } 43 | } -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/utilities/TimeParser.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.utilities; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | /** 6 | * The main class that is used to parse time strings inputs 7 | * The algorithm is fully written by me (aka Mqzen) 8 | * Make sure to know just when to use it properly :D ! 9 | * 10 | * @author Mqzen 11 | */ 12 | public class TimeParser { 13 | private long days; 14 | private long hours; 15 | private long minutes; 16 | private long seconds; 17 | 18 | private TimeParser(long millis) { 19 | this.days = millis / 86400000L; 20 | this.hours = (millis / 3600000L) % 24L; 21 | this.minutes = (millis / 60000L) % 60L; 22 | this.seconds = (millis / 1000L) % 60L; 23 | } 24 | 25 | private TimeParser(String timePeriod) { 26 | char[] chars = timePeriod.toCharArray(); 27 | int i = 0; 28 | while (i < timePeriod.length()) { 29 | if (Character.isDigit(chars[i])) { 30 | StringBuilder digitToCollect = new StringBuilder(); 31 | int start = i; 32 | while (Character.isDigit(chars[start])) { 33 | digitToCollect.append(chars[start]); 34 | start++; 35 | } 36 | 37 | char unit = chars[start]; 38 | int digit = Integer.parseInt(digitToCollect.toString()); 39 | switch (unit) { 40 | case 'd', 'D' -> this.days += digit; 41 | case 'h', 'H' -> this.hours += digit; 42 | case 'm', 'M' -> this.minutes += digit; 43 | case 's', 'S' -> this.seconds += digit; 44 | } 45 | i = start; 46 | } 47 | i++; 48 | } 49 | } 50 | 51 | public static TimeParser parse(String timePeriod) { 52 | return new TimeParser(timePeriod); 53 | } 54 | 55 | public static TimeParser parse(long millis) { 56 | return new TimeParser(millis); 57 | } 58 | 59 | public Pair highestLogicalUnitValue() { 60 | if (this.days != 0L) { 61 | return Pair.of(this.days, TimeUnit.DAYS); 62 | } 63 | if (this.hours != 0L) { 64 | return Pair.of(this.hours, TimeUnit.HOURS); 65 | } 66 | if (this.minutes != 0L) { 67 | return Pair.of(this.minutes, TimeUnit.MINUTES); 68 | } 69 | return Pair.of(this.seconds, TimeUnit.SECONDS); 70 | } 71 | } -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/utilities/text/ItemPageTextDisplayer.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.utilities.text; 2 | 3 | 4 | import io.github.mqzn.commands.base.SenderWrapper; 5 | import net.kyori.adventure.text.TextComponent; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | public interface ItemPageTextDisplayer> { 9 | 10 | 11 | TextComponent displayPageItem(@NotNull S sender, @NotNull T convertible, int index); 12 | 13 | default void display(SenderWrapper wrapper, @NotNull S sender, @NotNull TextPage page) { 14 | 15 | int i = 1; 16 | for (T pageItem : page) { 17 | TextComponent toDisplay = displayPageItem(sender, pageItem, i); 18 | wrapper.sendMessage(sender, toDisplay); 19 | i++; 20 | } 21 | 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/utilities/text/PaginatedText.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.utilities.text; 2 | 3 | import io.github.mqzn.commands.base.SenderWrapper; 4 | import io.github.mqzn.commands.help.CommandHelpStyle; 5 | import net.kyori.adventure.text.Component; 6 | import net.kyori.adventure.text.TextComponent; 7 | import net.kyori.adventure.text.format.NamedTextColor; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.Nullable; 10 | 11 | import java.util.ArrayList; 12 | import java.util.HashMap; 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | public final class PaginatedText> { 17 | 18 | public final static int DEFAULT_ITEMS_PER_PAGE = 10; 19 | 20 | @NotNull 21 | private final SenderWrapper wrapper; 22 | 23 | private final int itemsPerPage; 24 | 25 | @NotNull 26 | private final List textObjects = new ArrayList<>(); 27 | 28 | @NotNull 29 | private final Map> pages = new HashMap<>(); 30 | 31 | @NotNull 32 | private final CommandHelpStyle style; 33 | 34 | @NotNull 35 | private TextComponent headerLine = Component.text("==========="); 36 | 37 | @Nullable 38 | private ItemPageTextDisplayer displayer; 39 | 40 | @NotNull 41 | private NamedTextColor primaryColor = NamedTextColor.YELLOW, secondaryColor = NamedTextColor.GOLD; 42 | 43 | private PaginatedText(@NotNull CommandHelpStyle provider, 44 | @NotNull SenderWrapper wrapper) { 45 | this(provider, wrapper, DEFAULT_ITEMS_PER_PAGE); 46 | } 47 | 48 | private PaginatedText(@NotNull CommandHelpStyle style, 49 | @NotNull SenderWrapper wrapper, 50 | int itemsPerPage) { 51 | this.style = style; 52 | this.wrapper = wrapper; 53 | this.itemsPerPage = itemsPerPage; 54 | } 55 | 56 | public static > PaginatedText create(@NotNull CommandHelpStyle provider, 57 | @NotNull SenderWrapper wrapper) { 58 | return new PaginatedText<>(provider, wrapper); 59 | } 60 | 61 | public static > PaginatedText create(@NotNull CommandHelpStyle provider, 62 | @NotNull SenderWrapper wrapper, 63 | int itemsPerPage) { 64 | return new PaginatedText<>(provider, wrapper, itemsPerPage); 65 | } 66 | 67 | public int getItemsPerPage() { 68 | return itemsPerPage; 69 | } 70 | 71 | public void add(T object) { 72 | textObjects.add(object); 73 | } 74 | 75 | public PaginatedText remove(@NotNull T object) { 76 | textObjects.remove(object); 77 | return this; 78 | } 79 | 80 | public PaginatedText withHeaderLine(@NotNull String line) { 81 | this.headerLine = Component.text(line); 82 | return this; 83 | } 84 | 85 | 86 | public PaginatedText withPrimaryColor(@NotNull NamedTextColor primaryColor) { 87 | this.primaryColor = primaryColor; 88 | return this; 89 | } 90 | 91 | public PaginatedText withSecondaryColor(@NotNull NamedTextColor secondaryColor) { 92 | this.secondaryColor = secondaryColor; 93 | return this; 94 | } 95 | 96 | public PaginatedText withDisplayer(@NotNull ItemPageTextDisplayer displayer) { 97 | this.displayer = displayer; 98 | return this; 99 | } 100 | 101 | public void paginate() { 102 | for (int i = 1; i <= textObjects.size(); i++) { 103 | T obj = textObjects.get(i - 1); 104 | //calculate the page from it's index and the items per page 105 | int page = (int) Math.ceil((double) (i) / (itemsPerPage)); 106 | 107 | pages.compute(page, (index, existingPage) -> { 108 | if (existingPage == null) { 109 | List list = new ArrayList<>(itemsPerPage); 110 | list.add(obj); 111 | return new TextPage<>(page, itemsPerPage, list); 112 | } 113 | 114 | existingPage.add(obj); 115 | return existingPage; 116 | }); 117 | 118 | } 119 | 120 | } 121 | 122 | public @Nullable TextPage getPage(int index) { 123 | return pages.get(index); 124 | } 125 | 126 | public int maxPages() { 127 | return pages.size(); 128 | } 129 | 130 | public void displayPage(@NotNull String label, @NotNull S sender, int page) { 131 | 132 | int maxPages = pages.size(); 133 | if (page > maxPages || page < 1) { 134 | throw new IllegalArgumentException("Page must be in range 1-" + maxPages); 135 | } 136 | 137 | if (displayer == null) { 138 | throw new IllegalStateException("The text menu is not fully ready yet (early access)!!"); 139 | } 140 | 141 | TextPage textPage = getPage(page); 142 | if (textPage == null) return; 143 | 144 | 145 | TextComponent line = headerLine.style(style.lineStyle()); 146 | TextComponent firstLine = Component.empty().append(line) 147 | .append(Component.space()).append(Component.space().decorations(new HashMap<>())) 148 | .append(style.header(label)) 149 | .append(Component.space()).append(Component.space()) 150 | .append(Component.text("(", secondaryColor)) 151 | .append(Component.text(page, primaryColor)) 152 | .append(Component.text("/", secondaryColor)) 153 | .append(Component.text(maxPages, primaryColor)) 154 | .append(Component.text(")", secondaryColor)) 155 | .append(Component.text(" ", NamedTextColor.WHITE)) 156 | .append(line); 157 | 158 | wrapper.sendMessage(sender, firstLine); 159 | displayer.display(wrapper, sender, textPage); 160 | } 161 | 162 | 163 | } 164 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/utilities/text/TextConvertible.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.utilities.text; 2 | 3 | import io.github.mqzn.commands.base.manager.CommandManager; 4 | import net.kyori.adventure.text.TextComponent; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | public interface TextConvertible { 8 | 9 | @NotNull TextComponent toText(@NotNull CommandManager manager, @NotNull S sender); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /common/src/main/java/io/github/mqzn/commands/utilities/text/TextPage.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.utilities.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 pageIndex, int capacity, 9 | List pageItems) implements Iterable { 10 | 11 | public void add(T obj) { 12 | if (pageItems.size() + 1 > capacity) return; 13 | pageItems.add(obj); 14 | } 15 | 16 | public void remove(T obj) { 17 | pageItems.remove(obj); 18 | } 19 | 20 | public void addAll(List otherItems) { 21 | otherItems.forEach(this::add); 22 | } 23 | 24 | 25 | @Override 26 | public @NotNull Iterator iterator() { 27 | return pageItems.iterator(); 28 | } 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /common/src/test/java/io/github/mqzn/commands/test/ClientSender.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.test; 2 | 3 | 4 | public record ClientSender(String name) { 5 | public void sendMessage(String msg) { 6 | System.out.println("To " + name + " : " + msg); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /common/src/test/java/io/github/mqzn/commands/test/ClientSenderWrapper.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.test; 2 | 3 | import io.github.mqzn.commands.base.SenderWrapper; 4 | import net.kyori.adventure.text.TextComponent; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | public final class ClientSenderWrapper implements SenderWrapper { 8 | 9 | @Override 10 | public Class senderType() { 11 | return ClientSender.class; 12 | } 13 | 14 | @Override 15 | public boolean isConsole(ClientSender sender) { 16 | return false; 17 | } 18 | 19 | @Override 20 | public void sendMessage(ClientSender sender, String msg) { 21 | System.out.println("Message to " + sender.name() + ": " + msg); 22 | } 23 | 24 | @Override 25 | public void sendMessage(ClientSender sender, TextComponent component) { 26 | System.out.println("Sending msg to " + sender.name()); 27 | sendMessage(sender, component.content()); 28 | } 29 | 30 | @Override 31 | public boolean canBeSender(Class type) { 32 | return type.getName().equals(senderType().getName()); 33 | } 34 | 35 | @Override 36 | public boolean hasPermission(ClientSender sender, @Nullable String name) { 37 | return true; 38 | } 39 | 40 | @Override 41 | public String senderName(ClientSender sender) { 42 | return sender.name(); 43 | } 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /common/src/test/java/io/github/mqzn/commands/test/CustomException.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.test; 2 | 3 | public final class CustomException extends Exception { 4 | 5 | public CustomException(String msg) { 6 | super(msg); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /common/src/test/java/io/github/mqzn/commands/test/TestBootstrap.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.test; 2 | 3 | import io.github.mqzn.commands.annotations.AnnotationParser; 4 | import io.github.mqzn.commands.base.manager.flags.FlagInfo; 5 | import io.github.mqzn.commands.test.annotations.TestAnnotatedCommand; 6 | import org.jetbrains.annotations.TestOnly; 7 | import org.junit.jupiter.api.Assertions; 8 | import org.junit.jupiter.api.Test; 9 | 10 | @TestOnly 11 | public final class TestBootstrap { 12 | 13 | private final TestCommandManager commandManager; 14 | 15 | private final AnnotationParser parser; 16 | private final ClientSender sender = new ClientSender("mqzen"); 17 | 18 | public TestBootstrap() { 19 | commandManager = new TestCommandManager(this); 20 | commandManager.flagRegistry().registerFlag(FlagInfo.builder("silent") 21 | .aliases("s").build()); 22 | commandManager.exceptionHandler().registerCallback(CustomException.class, (exception, commandSender, context) -> System.out.println("Handling exception: " + exception.getClass().getName())); 23 | //commandManager.senderProviderRegistry().registerSenderProvider(ClientSender.class, (provider) -> provider); 24 | parser = new AnnotationParser<>(commandManager); 25 | } 26 | 27 | @Test 28 | public void firstTest() { 29 | 30 | parser.parse(new TestAnnotatedCommand()); 31 | String[] args = new String[]{ 32 | "mqzen", 33 | "-s" 34 | }; 35 | 36 | var cmd = commandManager.getCommand("punish"); 37 | Assertions.assertNotNull(cmd); 38 | commandManager.executeCommand(cmd, sender, args); 39 | } 40 | 41 | @Test 42 | public void testCooldown() { 43 | parser.parse(new TestAnnotatedCommand()); 44 | String[] args = new String[]{ 45 | "disband" 46 | }; 47 | 48 | var cmd = commandManager.getCommand("testa"); 49 | Assertions.assertNotNull(cmd); 50 | 51 | commandManager.executeCommand(cmd, sender, args); 52 | commandManager.executeCommand(cmd, sender, args); 53 | } 54 | 55 | @Test 56 | public void customExceptionHandlingTest() { 57 | 58 | 59 | parser.parse(new TestAnnotatedCommand()); 60 | String[] args = new String[]{ 61 | "exceptionsub" 62 | }; 63 | var cmd = commandManager.getCommand("testa"); 64 | Assertions.assertNotNull(cmd); 65 | Assertions.assertDoesNotThrow(() -> commandManager.executeCommand(cmd, sender, args)); 66 | } 67 | 68 | @Test 69 | public void executionTest() { 70 | Assertions.assertDoesNotThrow(() -> parser.parse(new TestAnnotatedCommand())); 71 | 72 | String[] args = new String[]{ 73 | "execute" 74 | }; 75 | 76 | var cmd = commandManager.getCommand("testa"); 77 | Assertions.assertNotNull(cmd); 78 | 79 | commandManager.executeCommand(cmd, sender, args); 80 | } 81 | 82 | 83 | } 84 | -------------------------------------------------------------------------------- /common/src/test/java/io/github/mqzn/commands/test/TestCommandManager.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.test; 2 | 3 | import io.github.mqzn.commands.base.manager.AbstractCommandManager; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public final class TestCommandManager extends AbstractCommandManager { 7 | 8 | 9 | public TestCommandManager(@NotNull TestBootstrap plugin) { 10 | super(plugin, new ClientSenderWrapper()); 11 | } 12 | 13 | @Override 14 | public char commandPrefix() { 15 | return '/'; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /common/src/test/java/io/github/mqzn/commands/test/annotations/TestAnnotatedCommand.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.test.annotations; 2 | 3 | import io.github.mqzn.commands.annotations.base.*; 4 | import io.github.mqzn.commands.base.context.CommandArgs; 5 | import io.github.mqzn.commands.base.manager.CommandExecutionCoordinator; 6 | import io.github.mqzn.commands.test.ClientSender; 7 | 8 | import java.util.concurrent.TimeUnit; 9 | 10 | @Command(name = "punish", executionType = CommandExecutionCoordinator.Type.SYNC) 11 | @Cooldown(value = 1, unit = TimeUnit.MINUTES) 12 | public class TestAnnotatedCommand { 13 | 14 | 15 | @ExecutionMeta(syntax = "", senderType = ClientSender.class) 16 | public void execute(ClientSender sender, CommandArgs args, 17 | @Arg(id="name") String name, 18 | @Flag(name = "silent") boolean silent) { 19 | if(silent) { 20 | System.out.println("Silent running"); 21 | }else { 22 | System.out.println("Silent not running"); 23 | } 24 | System.out.println("Punishing " + name + " !"); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mqzn/mCommands/ef3a8cf21a3f189e472d667d87078d3b11329bab/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-7.5.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | 7 | 8 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if %ERRORLEVEL% equ 0 goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if %ERRORLEVEL% equ 0 goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | set EXIT_CODE=%ERRORLEVEL% 84 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 85 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 86 | exit /b %EXIT_CODE% 87 | 88 | :mainEnd 89 | if "%OS%"=="Windows_NT" endlocal 90 | 91 | :omega 92 | -------------------------------------------------------------------------------- /img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mqzn/mCommands/ef3a8cf21a3f189e472d667d87078d3b11329bab/img.png -------------------------------------------------------------------------------- /jcord/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'com.github.johnrengelman.shadow' version '7.1.0' 4 | id 'maven-publish' 5 | id 'signing' 6 | } 7 | 8 | group 'io.github.mqzn' 9 | version '1.1.7' 10 | 11 | repositories { 12 | mavenCentral() 13 | maven { 14 | url 'https://oss.sonatype.org/content/repositories/snapshots' 15 | } 16 | } 17 | 18 | dependencies { 19 | 20 | implementation project(":common") 21 | compileOnly 'org.jetbrains:annotations:24.0.1' 22 | compileOnly 'org.javacord:javacord:3.8.0' 23 | compileOnly "net.kyori:adventure-api:4.13.1" 24 | compileOnly "net.kyori:adventure-platform-bungeecord:4.3.0" 25 | } 26 | 27 | def targetJavaVersion = 17 28 | 29 | java { 30 | def javaVersion = JavaVersion.toVersion(targetJavaVersion) 31 | sourceCompatibility = javaVersion 32 | targetCompatibility = javaVersion 33 | if (JavaVersion.current() < javaVersion) { 34 | toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) 35 | } 36 | withSourcesJar() 37 | withJavadocJar() 38 | 39 | } 40 | 41 | compileJava { 42 | options.encoding = "UTF-8" 43 | } 44 | 45 | 46 | shadowJar { 47 | setArchiveName("mCommands-JCord-${project.version}.jar") 48 | } 49 | 50 | publishing { 51 | publications { 52 | mavenJava(MavenPublication) { 53 | groupId project.group 54 | artifactId 'mCommands-jcord' 55 | version project.version 56 | from components.java 57 | 58 | 59 | pom { 60 | name = 'mCommands' 61 | description = 'Advanced command dispatching java library' 62 | url = 'https://github.com/Mqzn/mCommands' 63 | inceptionYear = '2023' 64 | 65 | licenses { 66 | license { 67 | name = 'MIT License' 68 | url = 'http://www.opensource.org/licenses/mit-license.php' 69 | } 70 | } 71 | developers { 72 | developer { 73 | id = 'mqzn' 74 | name = 'Mqzen' 75 | email = 'mezoahmed2507@gmail.com' 76 | } 77 | } 78 | scm { 79 | connection = 'scm:git:git:github.com/Mqzn/mCommands.git' 80 | developerConnection = 'scm:git:ssh://github.com/Mqzn/mCommands.git' 81 | url = 'https://github.com/Mqzn/mCommands' 82 | } 83 | } 84 | 85 | } 86 | 87 | } 88 | repositories { 89 | maven { 90 | name = "OSSRH" 91 | url = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" 92 | 93 | credentials { 94 | username = project.properties["ossrhUsername"] 95 | password = project.properties["ossrhPassword"] 96 | } 97 | 98 | } 99 | 100 | } 101 | } 102 | 103 | signing { 104 | sign publishing.publications.mavenJava 105 | } 106 | 107 | apply plugin: 'java' 108 | apply plugin: 'com.github.johnrengelman.shadow' 109 | apply plugin: 'maven-publish' 110 | apply plugin: 'signing' 111 | 112 | -------------------------------------------------------------------------------- /jcord/src/main/java/io/github/mqzn/commands/JCordCommandManager.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands; 2 | 3 | import io.github.mqzn.commands.base.Command; 4 | import io.github.mqzn.commands.base.SenderWrapper; 5 | import io.github.mqzn.commands.base.manager.AbstractCommandManager; 6 | import net.kyori.adventure.text.TextComponent; 7 | import org.javacord.api.DiscordApi; 8 | import org.javacord.api.entity.channel.ServerTextChannel; 9 | import org.javacord.api.entity.permission.PermissionType; 10 | import org.javacord.api.entity.server.Server; 11 | import org.javacord.api.entity.user.User; 12 | import org.jetbrains.annotations.NotNull; 13 | import org.jetbrains.annotations.Nullable; 14 | 15 | import java.util.regex.Pattern; 16 | 17 | public final class JCordCommandManager extends AbstractCommandManager { 18 | 19 | 20 | private final char commandStarter; 21 | 22 | public JCordCommandManager(@NotNull DiscordApi bootstrap, 23 | char commandPrefix, 24 | @NotNull Server server, 25 | @NotNull ServerTextChannel commandsChannel) { 26 | super(bootstrap, new MemberWrapper(server, commandsChannel)); 27 | this.commandStarter = commandPrefix; 28 | } 29 | 30 | public JCordCommandManager(char commandPrefix, 31 | @NotNull DiscordApi bootstrap, 32 | @NotNull Server server, 33 | @NotNull ServerTextChannel commandsChannel) { 34 | super(bootstrap, new MemberWrapper(server, commandsChannel)); 35 | this.commandStarter = commandPrefix; 36 | 37 | bootstrap.addMessageCreateListener((e) -> { 38 | String rawString = e.getMessageContent(); 39 | String[] split = rawString.split(Pattern.quote(" ")); 40 | 41 | String cmdUsed = split[0]; 42 | char prefix = cmdUsed.charAt(0); 43 | if (prefix != commandPrefix() || e.getChannel().getId() != commandsChannel.getId()) return; 44 | 45 | String cmd = cmdUsed.substring(1); 46 | 47 | String[] actualArgs = new String[split.length - 1]; 48 | System.arraycopy(split, 1, actualArgs, 0, split.length - 1); 49 | 50 | var officialCmd = getCommand(cmd); 51 | if (officialCmd != null) { 52 | e.getMessageAuthor().asUser() 53 | .ifPresent((user) -> executeCommand(officialCmd, user, actualArgs)); 54 | 55 | } 56 | 57 | }); 58 | } 59 | 60 | @Override 61 | public char commandPrefix() { 62 | return commandStarter; 63 | } 64 | 65 | @Override 66 | public > void registerCommand(C command) { 67 | super.registerCommand(command); 68 | } 69 | 70 | private static class MemberWrapper implements SenderWrapper { 71 | 72 | private final Server server; 73 | private final ServerTextChannel channel; 74 | 75 | public MemberWrapper(Server server, ServerTextChannel textChannel) { 76 | this.server = server; 77 | this.channel = textChannel; 78 | } 79 | 80 | 81 | @Override 82 | public Class senderType() { 83 | return User.class; 84 | } 85 | 86 | @Override 87 | public boolean isConsole(User sender) { 88 | return false; 89 | } 90 | 91 | @Override 92 | public void sendMessage(User sender, String msg) { 93 | channel.sendMessage(msg).join(); 94 | } 95 | 96 | @Override 97 | public void sendMessage(User sender, TextComponent component) { 98 | sendMessage(sender, component.content()); 99 | } 100 | 101 | @Override 102 | public boolean canBeSender(Class type) { 103 | return User.class.isAssignableFrom(type); 104 | } 105 | 106 | @Override 107 | public boolean hasPermission(User user, @Nullable String name) { 108 | 109 | try { 110 | PermissionType permissionType = PermissionType.valueOf(name); 111 | return server.hasPermission(user, permissionType); 112 | } catch (EnumConstantNotPresentException ex) { 113 | return false; 114 | } 115 | 116 | } 117 | 118 | @Override 119 | public String senderName(User sender) { 120 | return sender.getName(); 121 | } 122 | 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /jcord/src/main/java/io/github/mqzn/commands/JCordCommandSyntaxBuilder.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands; 2 | 3 | import io.github.mqzn.commands.base.syntax.CommandSyntaxBuilder; 4 | import org.javacord.api.entity.user.User; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | public final class JCordCommandSyntaxBuilder extends CommandSyntaxBuilder { 8 | 9 | private JCordCommandSyntaxBuilder(@NotNull JCordCommandManager manager, 10 | @NotNull Class senderClass, @NotNull String label) { 11 | super(manager, senderClass, label); 12 | } 13 | 14 | public static JCordCommandSyntaxBuilder builder(@NotNull JCordCommandManager manager, 15 | @NotNull Class senderClass, @NotNull String label) { 16 | return new JCordCommandSyntaxBuilder<>(manager, senderClass, label); 17 | } 18 | 19 | public static JCordCommandSyntaxBuilder builder(@NotNull JCordCommandManager manager, @NotNull String label) { 20 | return builder(manager, User.class, label); 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /jcord/src/main/java/io/github/mqzn/commands/JCordSubCommandBuilder.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands; 2 | 3 | import io.github.mqzn.commands.base.syntax.SubCommandBuilder; 4 | import org.javacord.api.entity.user.User; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | public class JCordSubCommandBuilder extends SubCommandBuilder { 8 | protected JCordSubCommandBuilder(@NotNull JCordCommandManager manager, @NotNull Class senderClass, @NotNull String label, @NotNull String name) { 9 | super(manager, senderClass, label, name); 10 | } 11 | 12 | public static JCordSubCommandBuilder builder(@NotNull JCordCommandManager manager, @NotNull Class senderClass, 13 | @NotNull String label, 14 | @NotNull String name) { 15 | return new JCordSubCommandBuilder<>(manager, senderClass, label, name); 16 | } 17 | 18 | public static JCordSubCommandBuilder builder(@NotNull JCordCommandManager manager, @NotNull String label, @NotNull String name) { 19 | return builder(manager, User.class, label, name); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'mCommands' 2 | include 'common' 3 | include 'spigot' 4 | include 'bungee' 5 | include 'velocity' 6 | include 'jcord' 7 | 8 | -------------------------------------------------------------------------------- /spigot/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'com.github.johnrengelman.shadow' version '7.1.0' 4 | id 'maven-publish' 5 | id 'signing' 6 | } 7 | 8 | group 'io.github.mqzn' 9 | version '1.1.7' 10 | 11 | repositories { 12 | mavenCentral() 13 | mavenLocal() 14 | maven { 15 | url 'https://jitpack.io' 16 | } 17 | maven { 18 | url "https://libraries.minecraft.net" 19 | } 20 | 21 | } 22 | 23 | dependencies { 24 | //compileOnly 'org.projectlombok:lombok:1.18.26' 25 | //annotationProcessor 'org.projectlombok:lombok:1.18.26' 26 | 27 | compileOnly 'org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT' 28 | implementation project(":common") 29 | 30 | compileOnly "net.kyori:adventure-api:4.13.1" 31 | compileOnly "net.kyori:adventure-platform-bukkit:4.3.0" 32 | } 33 | 34 | def targetJavaVersion = 17 35 | java { 36 | def javaVersion = JavaVersion.toVersion(targetJavaVersion) 37 | sourceCompatibility = javaVersion 38 | targetCompatibility = javaVersion 39 | if (JavaVersion.current() < javaVersion) { 40 | toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) 41 | } 42 | 43 | withSourcesJar() 44 | withJavadocJar() 45 | } 46 | 47 | 48 | compileJava { 49 | options.encoding = "UTF-8" 50 | } 51 | 52 | 53 | shadowJar { 54 | setArchiveName("mCommands-Spigot-${project.version}.jar") 55 | destinationDirectory.set(file("F:\\Test-Local-Server-MC\\1.8-server\\plugins")) 56 | exclude 'META-INF/**' 57 | exclude 'LICENSE' 58 | } 59 | 60 | 61 | publishing { 62 | publications { 63 | 64 | 65 | mavenJava(MavenPublication) { 66 | 67 | 68 | groupId project.group 69 | artifactId 'mCommands-spigot' 70 | version project.version 71 | from components.java 72 | 73 | 74 | pom { 75 | name = 'mCommands' 76 | description = 'Advanced command dispatching java library' 77 | url = 'https://github.com/Mqzn/mCommands' 78 | inceptionYear = '2023' 79 | 80 | licenses { 81 | license { 82 | name = 'MIT License' 83 | url = 'http://www.opensource.org/licenses/mit-license.php' 84 | } 85 | } 86 | developers { 87 | developer { 88 | id = 'mqzn' 89 | name = 'Mqzen' 90 | email = 'mezoahmed2507@gmail.com' 91 | } 92 | } 93 | scm { 94 | connection = 'scm:git:git:github.com/Mqzn/mCommands.git' 95 | developerConnection = 'scm:git:ssh://github.com/Mqzn/mCommands.git' 96 | url = 'https://github.com/Mqzn/mCommands' 97 | } 98 | } 99 | 100 | } 101 | 102 | } 103 | repositories { 104 | 105 | 106 | maven { 107 | name = "OSSRH" 108 | url = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" 109 | 110 | credentials { 111 | username = project.properties["ossrhUsername"] 112 | password = project.properties["ossrhPassword"] 113 | } 114 | 115 | } 116 | 117 | } 118 | 119 | } 120 | 121 | 122 | signing { 123 | sign publishing.publications.mavenJava 124 | } 125 | 126 | apply plugin: 'java' 127 | apply plugin: 'com.github.johnrengelman.shadow' 128 | apply plugin: 'maven-publish' 129 | apply plugin: 'signing' 130 | -------------------------------------------------------------------------------- /spigot/src/main/java/io/github/mqzn/commands/InternalSpigotCommand.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands; 2 | 3 | import io.github.mqzn.commands.base.Command; 4 | import org.bukkit.command.CommandSender; 5 | import org.bukkit.command.PluginIdentifiableCommand; 6 | import org.bukkit.plugin.Plugin; 7 | import org.jetbrains.annotations.ApiStatus; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | import java.util.Arrays; 11 | import java.util.List; 12 | 13 | @ApiStatus.Internal 14 | final class InternalSpigotCommand extends org.bukkit.command.Command implements PluginIdentifiableCommand { 15 | 16 | @NotNull 17 | private final SpigotCommandManager manager; 18 | 19 | @NotNull 20 | private final Command command; 21 | 22 | 23 | InternalSpigotCommand(@NotNull SpigotCommandManager manager, 24 | @NotNull Command command) { 25 | super(command.name(), command.info().description(), "", 26 | Arrays.asList(command.info().aliases())); 27 | this.manager = manager; 28 | this.command = command; 29 | } 30 | 31 | @Override 32 | public boolean execute(@NotNull CommandSender sender, 33 | @NotNull String label, 34 | String[] raw) { 35 | 36 | try { 37 | manager.executeCommand(command, sender, raw); 38 | return true; 39 | } catch (Exception ex) { 40 | ex.printStackTrace(); 41 | return false; 42 | } 43 | 44 | } 45 | 46 | @Override 47 | public @NotNull Plugin getPlugin() { 48 | return manager.getBootstrap(); 49 | } 50 | 51 | @Override 52 | public @NotNull List tabComplete(@NotNull CommandSender sender, 53 | @NotNull String alias, 54 | String[] args) throws IllegalArgumentException { 55 | return manager.suggest(command, sender, args); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /spigot/src/main/java/io/github/mqzn/commands/SpigotCaption.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands; 2 | 3 | import io.github.mqzn.commands.base.caption.Caption; 4 | import io.github.mqzn.commands.base.caption.CaptionKey; 5 | import io.github.mqzn.commands.base.caption.Message; 6 | import net.kyori.adventure.text.Component; 7 | import net.kyori.adventure.text.TextComponent; 8 | import net.kyori.adventure.text.format.NamedTextColor; 9 | import org.bukkit.command.CommandSender; 10 | 11 | /** 12 | * Interface whose only purpose 13 | * is to contain most common captions for spigot platform 14 | * as constants 15 | */ 16 | interface SpigotCaption { 17 | 18 | /** 19 | * Sent when an unknown syntax is used 20 | */ 21 | Caption UNKNOWN_COMMAND = Caption.builder(CaptionKey.UNKNOWN_COMMAND) 22 | .withMessage((sender, context, ex) -> (TextComponent) Message.prefixed(Message.EXECUTION_ERROR) 23 | .append(Component.text(String.format("Unknown Command In Syntax '%s'", context.rawFormat()), NamedTextColor.YELLOW)) 24 | .appendNewline() 25 | .append(Message.prefixed(Component.text("Try `/" + context.commandUsed().name() + " help`", NamedTextColor.GRAY)))) 26 | .build(); 27 | 28 | /** 29 | * Sent when the user has no permission 30 | */ 31 | Caption NO_PERMISSION = Caption.builder(CaptionKey.NO_PERMISSION) 32 | .withMessage((sender, context, ex) -> Message.prefixed(Message.EXECUTION_ERROR) 33 | .append(Component.text("You don't have the permission to do this !", NamedTextColor.GRAY))) 34 | .build(); 35 | 36 | /** 37 | * Sent when the console is trying to execute a command 38 | * which is only executable by a player 39 | */ 40 | Caption ONLY_PLAYER_EXECUTABLE = Caption.builder(CaptionKey.ONLY_PLAYER_EXECUTABLE) 41 | .withMessage((sender, context, ex) -> Message.prefixed(Message.EXECUTION_ERROR).append(Component.text("Only a player can execute this !", NamedTextColor.RED))) 42 | .build(); 43 | 44 | /** 45 | * Sent when Invalid argument type used in the syntax 46 | */ 47 | Caption INVALID_ARGUMENT = Caption.builder(CaptionKey.INVALID_ARGUMENT) 48 | .withMessage((sender, context, ex) -> { 49 | String msg = ex == null ? "Invalid argument used" : ex.getMessage(); 50 | return Message.prefixed(Message.INVALID_ARGUMENT_ERROR).append(Component.text(msg, NamedTextColor.YELLOW)); 51 | }) 52 | .build(); 53 | 54 | /** 55 | * Sent when trying to access a help topic of a command which doesn't exist 56 | */ 57 | Caption NO_HELP_TOPIC_AVAILABLE = Caption.builder(CaptionKey.NO_HELP_TOPIC_AVAILABLE) 58 | .withMessage((sender, context, ex) -> Message.prefixed(Message.EXECUTION_ERROR).append(Component.text("There's no help topic for this command '/" + context.commandUsed().name() + "'", NamedTextColor.RED))) 59 | .build(); 60 | 61 | 62 | } 63 | -------------------------------------------------------------------------------- /spigot/src/main/java/io/github/mqzn/commands/SpigotCommandManager.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands; 2 | 3 | import io.github.mqzn.commands.arguments.ArgumentOfflinePlayer; 4 | import io.github.mqzn.commands.arguments.ArgumentOnlinePlayer; 5 | import io.github.mqzn.commands.arguments.ArgumentUUID; 6 | import io.github.mqzn.commands.arguments.ArgumentWorld; 7 | import io.github.mqzn.commands.base.Command; 8 | import io.github.mqzn.commands.base.manager.AbstractCommandManager; 9 | import org.bukkit.Bukkit; 10 | import org.bukkit.OfflinePlayer; 11 | import org.bukkit.World; 12 | import org.bukkit.command.CommandSender; 13 | import org.bukkit.command.SimpleCommandMap; 14 | import org.bukkit.entity.Player; 15 | import org.bukkit.plugin.Plugin; 16 | import org.jetbrains.annotations.NotNull; 17 | 18 | import java.lang.reflect.Field; 19 | import java.util.UUID; 20 | 21 | public final class SpigotCommandManager extends AbstractCommandManager { 22 | 23 | 24 | @NotNull 25 | private final Plugin plugin; 26 | 27 | @NotNull 28 | private final SimpleCommandMap cmdMap; 29 | 30 | public SpigotCommandManager(@NotNull Plugin plugin) { 31 | super(plugin, new SpigotSenderWrapper(plugin)); 32 | this.plugin = plugin; 33 | try { 34 | Field commandMap = Bukkit.getServer().getClass().getDeclaredField("commandMap"); 35 | commandMap.setAccessible(true); 36 | cmdMap = (SimpleCommandMap) commandMap.get(Bukkit.getServer()); 37 | } catch (NoSuchFieldException | IllegalAccessException e) { 38 | throw new RuntimeException(e); 39 | } 40 | 41 | this.registerCaptions(); 42 | this.registerTypes(); 43 | } 44 | 45 | @Override 46 | public @NotNull Plugin getBootstrap() { 47 | return plugin; 48 | } 49 | 50 | @Override 51 | public char commandPrefix() { 52 | return '/'; 53 | } 54 | 55 | @Override 56 | public > void registerCommand(C command) { 57 | super.registerCommand(command); 58 | cmdMap.register(command.name(), new InternalSpigotCommand(this, command)); 59 | } 60 | 61 | private void registerCaptions() { 62 | captionRegistry.registerCaption(SpigotCaption.INVALID_ARGUMENT); 63 | captionRegistry.registerCaption(SpigotCaption.UNKNOWN_COMMAND); 64 | captionRegistry.registerCaption(SpigotCaption.NO_PERMISSION); 65 | captionRegistry.registerCaption(SpigotCaption.ONLY_PLAYER_EXECUTABLE); 66 | captionRegistry.registerCaption(SpigotCaption.NO_HELP_TOPIC_AVAILABLE); 67 | } 68 | 69 | 70 | private void registerTypes() { 71 | typeRegistry().registerArgumentConverter(OfflinePlayer.class, ArgumentOfflinePlayer::new); 72 | typeRegistry().registerArgumentConverter(Player.class, ArgumentOnlinePlayer::new); 73 | typeRegistry().registerArgumentConverter(World.class, ArgumentWorld::new); 74 | typeRegistry().registerArgumentConverter(UUID.class, ArgumentUUID::new); 75 | 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /spigot/src/main/java/io/github/mqzn/commands/SpigotCommandRequirement.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands; 2 | 3 | import io.github.mqzn.commands.base.CommandRequirement; 4 | import io.github.mqzn.commands.base.caption.CaptionKey; 5 | import io.github.mqzn.commands.base.context.Context; 6 | import org.bukkit.command.CommandSender; 7 | import org.bukkit.entity.Player; 8 | 9 | /** 10 | * A pre-requirement container made for the users to use the constants inside it 11 | * such as "ONLY_PLAYER_EXECUTABLE" 12 | */ 13 | public interface SpigotCommandRequirement extends CommandRequirement { 14 | 15 | /** 16 | * The requirement of making the command executable only by players 17 | */ 18 | SpigotCommandRequirement ONLY_PLAYER_EXECUTABLE = new SpigotCommandRequirement() { 19 | @Override 20 | public boolean accepts(CommandSender sender, Context commandContext) { 21 | return sender instanceof Player; 22 | } 23 | 24 | @Override 25 | public CaptionKey caption() { 26 | return CaptionKey.ONLY_PLAYER_EXECUTABLE; 27 | } 28 | }; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /spigot/src/main/java/io/github/mqzn/commands/SpigotCommandSyntaxBuilder.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands; 2 | 3 | import io.github.mqzn.commands.base.syntax.CommandSyntaxBuilder; 4 | import org.bukkit.command.CommandSender; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | /** 8 | * The builder for syntax in spigot's platform 9 | * 10 | * @param the sender type 11 | */ 12 | public final class SpigotCommandSyntaxBuilder extends CommandSyntaxBuilder { 13 | 14 | private SpigotCommandSyntaxBuilder(SpigotCommandManager commandManager, 15 | @NotNull Class senderClass, @NotNull String label) { 16 | super(commandManager, senderClass, label); 17 | } 18 | 19 | /** 20 | * Creating the builder's instance 21 | * 22 | * @param senderClass custom sender class (optional) 23 | * @param commandLabel the command name 24 | * @param the sender type 25 | * @return the syntax builder 26 | * @see CommandSyntaxBuilder 27 | */ 28 | public static SpigotCommandSyntaxBuilder builder(@NotNull SpigotCommandManager commandManager, 29 | @NotNull Class senderClass, 30 | @NotNull String commandLabel) { 31 | return new SpigotCommandSyntaxBuilder<>(commandManager, senderClass, commandLabel); 32 | } 33 | 34 | /** 35 | * Creating the builder's instance 36 | * 37 | * @param commandLabel the command name 38 | * @return the syntax builder 39 | * @see CommandSyntaxBuilder 40 | */ 41 | public static SpigotCommandSyntaxBuilder builder(@NotNull SpigotCommandManager commandManager, @NotNull String commandLabel) { 42 | return builder(commandManager, CommandSender.class, commandLabel); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /spigot/src/main/java/io/github/mqzn/commands/SpigotSenderWrapper.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands; 2 | 3 | import io.github.mqzn.commands.base.SenderWrapper; 4 | import net.kyori.adventure.platform.bukkit.BukkitAudiences; 5 | import net.kyori.adventure.text.TextComponent; 6 | import org.bukkit.ChatColor; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | import org.bukkit.plugin.Plugin; 10 | import org.jetbrains.annotations.ApiStatus; 11 | import org.jetbrains.annotations.NotNull; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | @ApiStatus.Internal 15 | final class SpigotSenderWrapper implements SenderWrapper { 16 | 17 | @NotNull 18 | private final BukkitAudiences audience; 19 | 20 | 21 | public SpigotSenderWrapper(Plugin plugin) { 22 | audience = BukkitAudiences.create(plugin); 23 | } 24 | 25 | @Override 26 | public Class senderType() { 27 | return CommandSender.class; 28 | } 29 | 30 | @Override 31 | public boolean isConsole(CommandSender sender) { 32 | return !(sender instanceof Player); 33 | } 34 | 35 | @Override 36 | public void sendMessage(CommandSender sender, String msg) { 37 | sender.sendMessage(ChatColor.translateAlternateColorCodes('&', msg)); 38 | } 39 | 40 | @Override 41 | public void sendMessage(CommandSender sender, TextComponent component) { 42 | audience.sender(sender) 43 | .sendMessage(component); 44 | } 45 | 46 | @Override 47 | public boolean canBeSender(Class type) { 48 | return CommandSender.class.isAssignableFrom(type); 49 | } 50 | 51 | @Override 52 | public boolean hasPermission(CommandSender sender, @Nullable String name) { 53 | if (name == null || name.isEmpty()) return true; 54 | return sender.hasPermission(name); 55 | } 56 | 57 | @Override 58 | public String senderName(CommandSender sender) { 59 | return sender.getName(); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /spigot/src/main/java/io/github/mqzn/commands/SpigotSubCommandBuilder.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands; 2 | 3 | import io.github.mqzn.commands.base.syntax.CommandSyntaxBuilder; 4 | import io.github.mqzn.commands.base.syntax.SubCommandBuilder; 5 | import io.github.mqzn.commands.base.syntax.SubCommandSyntax; 6 | import org.bukkit.command.CommandSender; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * Class that reperesents a builder for subcommands in 11 | * the platform "spigot" 12 | * 13 | * @param the sender type 14 | * @see CommandSyntaxBuilder 15 | * @see SubCommandSyntax 16 | * @see SubCommandBuilder 17 | */ 18 | public final class SpigotSubCommandBuilder extends SubCommandBuilder { 19 | 20 | private SpigotSubCommandBuilder(@NotNull SpigotCommandManager commandManager, @NotNull Class senderClass, @NotNull String label, @NotNull String name) { 21 | super(commandManager, senderClass, label, name); 22 | } 23 | 24 | /** 25 | * Creating the builder's instance, that will be used to build the syntax 26 | * of the subcommand 27 | * 28 | * @param senderClass custom sender class (optional) 29 | * @param commandLabel the command name 30 | * @param name the subcommand name 31 | * @param the sender type 32 | * @return the syntax builder 33 | * @see CommandSyntaxBuilder 34 | */ 35 | public static SpigotSubCommandBuilder builder(@NotNull SpigotCommandManager commandManager, 36 | @NotNull Class senderClass, 37 | @NotNull String commandLabel, 38 | @NotNull String name) { 39 | return new SpigotSubCommandBuilder<>(commandManager, senderClass, commandLabel, name); 40 | } 41 | 42 | /** 43 | * Creating the builder's instance, that will be used to build the syntax 44 | * of the subcommand 45 | * 46 | * @param commandLabel the command name 47 | * @param name the subcommand name 48 | * @return the subcommand-syntax builder 49 | * @see CommandSyntaxBuilder 50 | */ 51 | public static SpigotSubCommandBuilder builder(@NotNull SpigotCommandManager commandManager, 52 | @NotNull String commandLabel, @NotNull String name) { 53 | return builder(commandManager, CommandSender.class, commandLabel, name); 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /spigot/src/main/java/io/github/mqzn/commands/arguments/ArgumentLocation.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.arguments; 2 | 3 | import io.github.mqzn.commands.exceptions.types.ArgumentParseException; 4 | import org.bukkit.Location; 5 | import org.bukkit.entity.Player; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.UnknownNullability; 8 | 9 | public final class ArgumentLocation extends AbstractArgument { 10 | 11 | private final static String EXCEPTION_MESSAGE = "Invalid location `%s`, provide the location in the format: `x,y,z`"; 12 | 13 | public ArgumentLocation(@NotNull String id) { 14 | super(id, Location.class); 15 | } 16 | 17 | public ArgumentLocation(@NotNull ArgumentData data) { 18 | super(data, Location.class); 19 | } 20 | 21 | @Override 22 | public Location parse(@UnknownNullability S sender, 23 | @NotNull String command, 24 | @NotNull String input) throws ArgumentParseException { 25 | if (!(sender instanceof Player player)) { 26 | throw new ArgumentParseException("Location cannot be parsed," + 27 | " cannot find the world since the sender is not a player !", input, command); 28 | } 29 | 30 | String[] split = input.split(","); 31 | if (split.length != 3) { 32 | throw new ArgumentParseException(EXCEPTION_MESSAGE, input, command); 33 | } 34 | 35 | try { 36 | double x = Double.parseDouble(split[0]); 37 | double y = Double.parseDouble(split[1]); 38 | double z = Double.parseDouble(split[2]); 39 | 40 | return new Location(player.getWorld(), x, y, z); 41 | } catch (NumberFormatException ex) { 42 | throw new ArgumentParseException(EXCEPTION_MESSAGE, input, command); 43 | } 44 | 45 | } 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /spigot/src/main/java/io/github/mqzn/commands/arguments/ArgumentOfflinePlayer.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.arguments; 2 | 3 | import org.bukkit.Bukkit; 4 | import org.bukkit.OfflinePlayer; 5 | import org.jetbrains.annotations.NotNull; 6 | import org.jetbrains.annotations.UnknownNullability; 7 | 8 | /** 9 | * Offline-player argument class 10 | */ 11 | public final class ArgumentOfflinePlayer extends AbstractArgument { 12 | 13 | /** 14 | * Creating argument using the id 15 | * 16 | * @param id the name/id of the argument 17 | */ 18 | public ArgumentOfflinePlayer(@NotNull String id) { 19 | super(id, OfflinePlayer.class); 20 | } 21 | 22 | /** 23 | * Creating argument using the data 24 | * 25 | * @param data the data of the arguments 26 | */ 27 | public ArgumentOfflinePlayer(@NotNull ArgumentData data) { 28 | super(data, OfflinePlayer.class); 29 | } 30 | 31 | @Override 32 | @SuppressWarnings("deprecation") 33 | public OfflinePlayer parse(@UnknownNullability S sender, 34 | @NotNull String command, @NotNull String input) { 35 | return Bukkit.getOfflinePlayer(input); 36 | } 37 | 38 | @Override 39 | public String toString(OfflinePlayer obj) { 40 | return obj.getName(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /spigot/src/main/java/io/github/mqzn/commands/arguments/ArgumentOnlinePlayer.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.arguments; 2 | 3 | 4 | import io.github.mqzn.commands.exceptions.types.ArgumentParseException; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.entity.Player; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.UnknownNullability; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | /** 14 | * Online-player argument class 15 | */ 16 | public final class ArgumentOnlinePlayer extends AbstractArgument { 17 | 18 | 19 | /** 20 | * Creating argument using the id 21 | * 22 | * @param id the name/id of the argument 23 | */ 24 | public ArgumentOnlinePlayer( 25 | @NotNull String id) { 26 | super(id, Player.class); 27 | } 28 | 29 | /** 30 | * Creating argument using the data 31 | * 32 | * @param data the data of the arguments 33 | */ 34 | public ArgumentOnlinePlayer(@NotNull ArgumentData data) { 35 | super(data, Player.class); 36 | } 37 | 38 | @Override 39 | public Player parse(@UnknownNullability S sender, @NotNull String command, @NotNull String input) throws ArgumentParseException { 40 | 41 | Player player = Bukkit.getPlayer(input); 42 | if (player == null || !player.isOnline()) { 43 | throw new ArgumentParseException( 44 | String.format("Player %s is offline or doesn't exist", input), input, command); 45 | } 46 | return player; 47 | } 48 | 49 | @Override 50 | public @NotNull List suggestions() { 51 | return new ArrayList<>(Bukkit.getOnlinePlayers()); 52 | } 53 | 54 | @Override 55 | public boolean isSuggestionDynamic() { 56 | return true; 57 | } 58 | 59 | @Override 60 | public String toString(Player obj) { 61 | return obj.getName(); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /spigot/src/main/java/io/github/mqzn/commands/arguments/ArgumentUUID.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.arguments; 2 | 3 | import io.github.mqzn.commands.exceptions.types.ArgumentParseException; 4 | import org.jetbrains.annotations.NotNull; 5 | import org.jetbrains.annotations.UnknownNullability; 6 | 7 | import java.util.UUID; 8 | 9 | public final class ArgumentUUID extends AbstractArgument { 10 | 11 | public ArgumentUUID(@NotNull String id) { 12 | super(id, UUID.class); 13 | } 14 | 15 | public ArgumentUUID(@NotNull ArgumentData data) { 16 | super(data, UUID.class); 17 | } 18 | 19 | @Override 20 | public UUID parse(@UnknownNullability S sender, @NotNull String command, @NotNull String input) throws ArgumentParseException { 21 | try { 22 | return UUID.fromString(input); 23 | } catch (Exception ex) { 24 | throw new ArgumentParseException( 25 | String.format("The uuid `%s` is not valid", input), input, command 26 | ); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /spigot/src/main/java/io/github/mqzn/commands/arguments/ArgumentWorld.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.arguments; 2 | 3 | import io.github.mqzn.commands.exceptions.types.ArgumentParseException; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.World; 6 | import org.jetbrains.annotations.NotNull; 7 | import org.jetbrains.annotations.UnknownNullability; 8 | 9 | import java.util.List; 10 | 11 | public final class ArgumentWorld extends AbstractArgument { 12 | 13 | public ArgumentWorld(@NotNull String id) { 14 | super(id, World.class); 15 | } 16 | 17 | public ArgumentWorld(@NotNull ArgumentData data) { 18 | super(data, World.class); 19 | } 20 | 21 | @Override 22 | public World parse(@UnknownNullability S sender, @NotNull String command, @NotNull String input) throws ArgumentParseException { 23 | World world = Bukkit.getWorld(input); 24 | if (world == null) { 25 | throw new ArgumentParseException( 26 | String.format("Unknown world `%s` provided", input), input, command 27 | ); 28 | } 29 | return world; 30 | } 31 | 32 | @Override 33 | public @NotNull List suggestions() { 34 | return Bukkit.getWorlds(); 35 | } 36 | 37 | @Override 38 | public boolean isSuggestionDynamic() { 39 | return true; 40 | } 41 | 42 | @Override 43 | public String toString(World obj) { 44 | return obj == null ? "null" : obj.getName(); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /spigot/src/main/java/io/github/mqzn/commands/arguments/SpigotArgument.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.arguments; 2 | 3 | import org.bukkit.OfflinePlayer; 4 | import org.bukkit.World; 5 | import org.bukkit.entity.Player; 6 | 7 | import java.util.UUID; 8 | 9 | /** 10 | * Class for static access to the well known-arguments of spigot 11 | */ 12 | public interface SpigotArgument { 13 | 14 | /** 15 | * creates a new offline player 16 | * argument in the syntax 17 | * 18 | * @param id the argument's name/id 19 | * @return the new offline player argument instance 20 | * @see OfflinePlayer 21 | */ 22 | static ArgumentOfflinePlayer offlinePlayer(String id) { 23 | return new ArgumentOfflinePlayer(id); 24 | } 25 | 26 | /** 27 | * creates a new online player 28 | * argument in the syntax 29 | * 30 | * @param id the argument's name/id 31 | * @return the new online player argument instance 32 | * @see Player 33 | */ 34 | static ArgumentOnlinePlayer onlinePlayer(String id) { 35 | return new ArgumentOnlinePlayer(id); 36 | } 37 | 38 | /** 39 | * creates a new UUID 40 | * argument in the syntax 41 | * 42 | * @param id the argument's name/id 43 | * @return the new UUID argument instance 44 | * @see UUID 45 | */ 46 | static ArgumentUUID uuid(String id) { 47 | return new ArgumentUUID(id); 48 | } 49 | 50 | /** 51 | * creates a new `World` 52 | * argument in the syntax 53 | * 54 | * @param id the argument's name/id 55 | * @return the new world argument instance 56 | * @see World 57 | */ 58 | static ArgumentWorld world(String id) { 59 | return new ArgumentWorld(id); 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /spigot/src/main/java/io/github/mqzn/commands/help/SpigotCommandHelpProvider.java: -------------------------------------------------------------------------------- 1 | package io.github.mqzn.commands.help; 2 | 3 | import org.bukkit.command.CommandSender; 4 | 5 | public interface SpigotCommandHelpProvider extends CommandHelpProvider { 6 | } 7 | -------------------------------------------------------------------------------- /spigot/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: mCommands-Spigot-Test 2 | version: 1.0.7 3 | main: io.github.mqzn.commands.test.SpigotPluginTest 4 | author: Mqzen -------------------------------------------------------------------------------- /velocity/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | id 'com.github.johnrengelman.shadow' version '7.1.0' 4 | id 'maven-publish' 5 | id 'signing' 6 | } 7 | 8 | group 'io.github.mqzn' 9 | version '1.1.7' 10 | 11 | repositories { 12 | mavenCentral() 13 | 14 | maven { 15 | url 'https://oss.sonatype.org/content/repositories/snapshots' 16 | } 17 | 18 | 19 | maven { 20 | url 'https://repo.papermc.io/repository/maven-public/' 21 | } 22 | 23 | } 24 | 25 | dependencies { 26 | implementation project(":common") 27 | compileOnly 'org.jetbrains:annotations:24.0.1' 28 | 29 | compileOnly "com.velocitypowered:velocity-api:3.1.1" 30 | annotationProcessor("com.velocitypowered:velocity-api:3.1.1") 31 | 32 | compileOnly "net.kyori:adventure-api:4.13.1" 33 | // compileOnly "net.kyori:adventure-platform-velocity:1.1.0" 34 | } 35 | 36 | def targetJavaVersion = 17 37 | 38 | java { 39 | def javaVersion = JavaVersion.toVersion(targetJavaVersion) 40 | sourceCompatibility = javaVersion 41 | targetCompatibility = javaVersion 42 | if (JavaVersion.current() < javaVersion) { 43 | toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) 44 | } 45 | withSourcesJar() 46 | withJavadocJar() 47 | 48 | } 49 | 50 | compileJava { 51 | options.encoding = "UTF-8" 52 | } 53 | 54 | 55 | shadowJar { 56 | setArchiveName("mCommands-Velocity-${project.version}.jar") 57 | } 58 | 59 | publishing { 60 | publications { 61 | mavenJava(MavenPublication) { 62 | groupId project.group 63 | artifactId 'mCommands-velocity' 64 | version project.version 65 | from components.java 66 | 67 | 68 | pom { 69 | name = 'mCommands' 70 | description = 'Advanced command dispatching java library' 71 | url = 'https://github.com/Mqzn/mCommands' 72 | inceptionYear = '2023' 73 | 74 | licenses { 75 | license { 76 | name = 'MIT License' 77 | url = 'http://www.opensource.org/licenses/mit-license.php' 78 | } 79 | } 80 | developers { 81 | developer { 82 | id = 'mqzn' 83 | name = 'Mqzen' 84 | email = 'mezoahmed2507@gmail.com' 85 | } 86 | } 87 | scm { 88 | connection = 'scm:git:git:github.com/Mqzn/mCommands.git' 89 | developerConnection = 'scm:git:ssh://github.com/Mqzn/mCommands.git' 90 | url = 'https://github.com/Mqzn/mCommands' 91 | } 92 | } 93 | 94 | } 95 | 96 | } 97 | repositories { 98 | maven { 99 | name = "OSSRH" 100 | url = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" 101 | 102 | credentials { 103 | username = project.properties["ossrhUsername"] 104 | password = project.properties["ossrhPassword"] 105 | } 106 | 107 | } 108 | 109 | } 110 | } 111 | 112 | signing { 113 | sign publishing.publications.mavenJava 114 | } 115 | 116 | apply plugin: 'java' 117 | apply plugin: 'com.github.johnrengelman.shadow' 118 | apply plugin: 'maven-publish' 119 | apply plugin: 'signing' 120 | 121 | -------------------------------------------------------------------------------- /velocity/src/main/java/io/github/commands/InternalVelocityCommand.java: -------------------------------------------------------------------------------- 1 | package io.github.commands; 2 | 3 | import com.velocitypowered.api.command.CommandSource; 4 | import com.velocitypowered.api.command.SimpleCommand; 5 | import io.github.mqzn.commands.base.Command; 6 | 7 | import java.util.List; 8 | 9 | class InternalVelocityCommand implements SimpleCommand { 10 | private final VelocityCommandManager commandManager; 11 | private final Command command; 12 | 13 | InternalVelocityCommand(VelocityCommandManager commandManager, Command command) { 14 | 15 | this.commandManager = commandManager; 16 | this.command = command; 17 | } 18 | 19 | /** 20 | * Executes the command for the specified invocation. 21 | * 22 | * @param invocation the invocation context 23 | */ 24 | @Override 25 | public void execute(Invocation invocation) { 26 | String[] args = invocation.arguments(); 27 | CommandSource sender = invocation.source(); 28 | 29 | commandManager.executeCommand(command, sender, args); 30 | } 31 | 32 | /** 33 | * Provides tab complete suggestions for the specified invocation. 34 | * 35 | * @param invocation the invocation context 36 | * @return the tab complete suggestions 37 | */ 38 | @Override 39 | public List suggest(Invocation invocation) { 40 | return commandManager.suggest(command, invocation.source(), invocation.arguments()); 41 | } 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /velocity/src/main/java/io/github/commands/VelocityCaption.java: -------------------------------------------------------------------------------- 1 | package io.github.commands; 2 | 3 | import com.velocitypowered.api.command.CommandSource; 4 | import io.github.mqzn.commands.base.caption.Caption; 5 | import io.github.mqzn.commands.base.caption.CaptionKey; 6 | import io.github.mqzn.commands.base.caption.Message; 7 | import net.kyori.adventure.text.Component; 8 | import net.kyori.adventure.text.format.NamedTextColor; 9 | 10 | public interface VelocityCaption { 11 | 12 | 13 | Caption UNKNOWN_COMMAND = Caption.builder(CaptionKey.UNKNOWN_COMMAND) 14 | .withMessage((sender, context, ex) -> Message.prefixed(Message.EXECUTION_ERROR) 15 | .append(Component.text(String.format("Unknown Command In ExecutionMeta '%s'", context.rawFormat())))) 16 | .build(); 17 | 18 | Caption NO_PERMISSION = Caption.builder(CaptionKey.NO_PERMISSION) 19 | .withMessage((sender, context, ex) -> Message.prefixed(Message.EXECUTION_ERROR) 20 | .append(Component.text("You don't have the permission to do this !", NamedTextColor.GRAY))) 21 | .build(); 22 | 23 | 24 | Caption ONLY_PLAYER_EXECUTABLE = Caption.builder(CaptionKey.ONLY_PLAYER_EXECUTABLE) 25 | .withMessage((sender, context, ex) -> Message.prefixed(Message.EXECUTION_ERROR).append(Component.text("Only a player can execute this !", NamedTextColor.RED))) 26 | .build(); 27 | 28 | 29 | Caption INVALID_ARGUMENT = Caption.builder(CaptionKey.INVALID_ARGUMENT) 30 | .withMessage((sender, context, ex) -> { 31 | String msg = ex == null ? "Invalid argument used" : ex.getMessage(); 32 | return Message.prefixed(Message.INVALID_ARGUMENT_ERROR).append(Component.text(msg, NamedTextColor.YELLOW)); 33 | }) 34 | .build(); 35 | 36 | 37 | Caption NO_HELP_TOPIC_AVAILABLE = Caption.builder(CaptionKey.NO_HELP_TOPIC_AVAILABLE) 38 | .withMessage((sender, context, ex) -> Message.prefixed(Message.EXECUTION_ERROR).append(Component.text("There's no help topic for this command '/" + context.commandUsed().name() + "'", NamedTextColor.RED))) 39 | .build(); 40 | 41 | } 42 | -------------------------------------------------------------------------------- /velocity/src/main/java/io/github/commands/VelocityCommandManager.java: -------------------------------------------------------------------------------- 1 | package io.github.commands; 2 | 3 | import com.velocitypowered.api.command.CommandMeta; 4 | import com.velocitypowered.api.command.CommandSource; 5 | import com.velocitypowered.api.proxy.Player; 6 | import com.velocitypowered.api.proxy.ProxyServer; 7 | import io.github.commands.arguments.ArgumentOnlinePlayer; 8 | import io.github.mqzn.commands.base.Command; 9 | import io.github.mqzn.commands.base.manager.AbstractCommandManager; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | public final class VelocityCommandManager extends AbstractCommandManager { 13 | 14 | @NotNull 15 | private final Object bootstrapObj; 16 | 17 | public VelocityCommandManager(@NotNull Object bootstrapObj, @NotNull ProxyServer plugin) { 18 | super(plugin, new VelocitySenderWrapper()); 19 | this.bootstrapObj = bootstrapObj; 20 | this.registerCaptions(); 21 | this.registerTypes(); 22 | } 23 | 24 | @Override 25 | public char commandPrefix() { 26 | return '/'; 27 | } 28 | 29 | private void registerCaptions() { 30 | captionRegistry.registerCaption(VelocityCaption.INVALID_ARGUMENT); 31 | captionRegistry.registerCaption(VelocityCaption.UNKNOWN_COMMAND); 32 | captionRegistry.registerCaption(VelocityCaption.NO_PERMISSION); 33 | captionRegistry.registerCaption(VelocityCaption.ONLY_PLAYER_EXECUTABLE); 34 | captionRegistry.registerCaption(VelocityCaption.NO_HELP_TOPIC_AVAILABLE); 35 | } 36 | 37 | private void registerTypes() { 38 | typeRegistry().registerArgumentConverter(Player.class, (data) -> new ArgumentOnlinePlayer(this.bootstrap, data)); 39 | } 40 | 41 | @Override 42 | public > void registerCommand(C command) { 43 | super.registerCommand(command); 44 | 45 | CommandMeta commandMeta = bootstrap.getCommandManager().metaBuilder(command.name()) 46 | .aliases(command.info().aliases()) 47 | .plugin(bootstrapObj) 48 | .build(); 49 | 50 | this.bootstrap.getCommandManager().register(commandMeta, new InternalVelocityCommand(this, command)); 51 | } 52 | 53 | @Override 54 | public void unregisterCommand(String name) { 55 | super.unregisterCommand(name); 56 | this.bootstrap.getCommandManager().unregister(name); 57 | } 58 | 59 | 60 | } 61 | -------------------------------------------------------------------------------- /velocity/src/main/java/io/github/commands/VelocityCommandRequirement.java: -------------------------------------------------------------------------------- 1 | package io.github.commands; 2 | 3 | import com.velocitypowered.api.command.CommandSource; 4 | import com.velocitypowered.api.proxy.Player; 5 | import io.github.mqzn.commands.base.CommandRequirement; 6 | import io.github.mqzn.commands.base.caption.CaptionKey; 7 | import io.github.mqzn.commands.base.context.Context; 8 | 9 | public interface VelocityCommandRequirement extends CommandRequirement { 10 | 11 | VelocityCommandRequirement ONLY_PLAYER_EXECUTABLE = new VelocityCommandRequirement() { 12 | @Override 13 | public boolean accepts(CommandSource sender, Context commandContext) { 14 | return sender instanceof Player; 15 | } 16 | 17 | @Override 18 | public CaptionKey caption() { 19 | return CaptionKey.ONLY_PLAYER_EXECUTABLE; 20 | } 21 | }; 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /velocity/src/main/java/io/github/commands/VelocitySenderWrapper.java: -------------------------------------------------------------------------------- 1 | package io.github.commands; 2 | 3 | import com.velocitypowered.api.command.CommandSource; 4 | import com.velocitypowered.api.proxy.Player; 5 | import io.github.mqzn.commands.base.SenderWrapper; 6 | import net.kyori.adventure.text.Component; 7 | import net.kyori.adventure.text.TextComponent; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | final class VelocitySenderWrapper implements SenderWrapper { 11 | 12 | public static final String VELOCITY_CONSOLE_SENDER_NAME = "CONSOLE"; 13 | 14 | @Override 15 | public Class senderType() { 16 | return CommandSource.class; 17 | } 18 | 19 | @Override 20 | public boolean isConsole(CommandSource sender) { 21 | return !(sender instanceof Player); 22 | } 23 | 24 | @Override 25 | public void sendMessage(CommandSource sender, String msg) { 26 | sender.sendMessage(Component.text(msg)); 27 | } 28 | 29 | @Override 30 | public void sendMessage(CommandSource sender, TextComponent component) { 31 | sender.sendMessage(component); 32 | } 33 | 34 | @Override 35 | public boolean canBeSender(Class type) { 36 | return CommandSource.class.isAssignableFrom(type); 37 | } 38 | 39 | @Override 40 | public boolean hasPermission(CommandSource sender, @Nullable String name) { 41 | return sender.hasPermission(name); 42 | } 43 | 44 | @Override 45 | public String senderName(CommandSource sender) { 46 | if (sender instanceof Player player) { 47 | return player.getUsername(); 48 | } 49 | return VELOCITY_CONSOLE_SENDER_NAME; 50 | } 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /velocity/src/main/java/io/github/commands/VelocitySubCommandBuilder.java: -------------------------------------------------------------------------------- 1 | package io.github.commands; 2 | 3 | import com.velocitypowered.api.command.CommandSource; 4 | import io.github.mqzn.commands.base.syntax.CommandSyntaxBuilder; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | public final class VelocitySubCommandBuilder extends CommandSyntaxBuilder { 8 | 9 | private VelocitySubCommandBuilder(@NotNull VelocityCommandManager manager, @NotNull Class senderClass, @NotNull String label) { 10 | super(manager, senderClass, label); 11 | } 12 | 13 | public static VelocitySubCommandBuilder builder(@NotNull VelocityCommandManager manager, @NotNull Class senderClass, 14 | @NotNull String commandLabel) { 15 | return new VelocitySubCommandBuilder<>(manager, senderClass, commandLabel); 16 | } 17 | 18 | public static VelocitySubCommandBuilder builder(@NotNull VelocityCommandManager manager, @NotNull String commandLabel) { 19 | return builder(manager, CommandSource.class, commandLabel); 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /velocity/src/main/java/io/github/commands/arguments/ArgumentOnlinePlayer.java: -------------------------------------------------------------------------------- 1 | package io.github.commands.arguments; 2 | 3 | import com.velocitypowered.api.proxy.Player; 4 | import com.velocitypowered.api.proxy.ProxyServer; 5 | import io.github.mqzn.commands.arguments.AbstractArgument; 6 | import io.github.mqzn.commands.arguments.ArgumentData; 7 | import io.github.mqzn.commands.exceptions.types.ArgumentParseException; 8 | import org.jetbrains.annotations.NotNull; 9 | import org.jetbrains.annotations.UnknownNullability; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.Optional; 14 | 15 | public final class ArgumentOnlinePlayer extends AbstractArgument { 16 | 17 | private final ProxyServer proxyServer; 18 | 19 | public ArgumentOnlinePlayer(@NotNull ProxyServer server, 20 | @NotNull String id) { 21 | super(id, Player.class); 22 | this.proxyServer = server; 23 | } 24 | 25 | public ArgumentOnlinePlayer(@NotNull ProxyServer server, @NotNull ArgumentData data) { 26 | super(data, Player.class); 27 | this.proxyServer = server; 28 | } 29 | 30 | @Override 31 | public Player parse(@UnknownNullability S sender, 32 | @NotNull String command, 33 | @NotNull String input) throws ArgumentParseException { 34 | 35 | Optional player = proxyServer.getPlayer(input); 36 | if (player.isEmpty()) { 37 | throw new ArgumentParseException(String.format("Player %s is offline or doesn't exist", input), input, command); 38 | } 39 | return player.get(); 40 | } 41 | 42 | @Override 43 | public @NotNull List suggestions() { 44 | return new ArrayList<>(proxyServer.getAllPlayers()); 45 | } 46 | 47 | @Override 48 | public boolean isSuggestionDynamic() { 49 | return true; 50 | } 51 | 52 | @Override 53 | public String toString(Player obj) { 54 | return obj.getUsername(); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /velocity/src/main/java/io/github/commands/arguments/VelocityArgument.java: -------------------------------------------------------------------------------- 1 | package io.github.commands.arguments; 2 | 3 | import com.velocitypowered.api.proxy.ProxyServer; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public interface VelocityArgument { 7 | 8 | static ArgumentOnlinePlayer onlinePlayer(@NotNull ProxyServer server, String id) { 9 | return new ArgumentOnlinePlayer(server, id); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /velocity/src/main/java/io/github/commands/help/VelocityCommandHelpProvider.java: -------------------------------------------------------------------------------- 1 | package io.github.commands.help; 2 | 3 | import com.velocitypowered.api.command.CommandSource; 4 | import io.github.mqzn.commands.help.CommandHelpProvider; 5 | 6 | public interface VelocityCommandHelpProvider extends CommandHelpProvider { 7 | } 8 | --------------------------------------------------------------------------------