From 92990227b96d020b4436e98484cf3766e367ac32 Mon Sep 17 00:00:00 2001 From: Paldiu Date: Tue, 8 Feb 2022 23:22:57 -0600 Subject: [PATCH] Final Update? Possibly ready for release? --- .../io/github/simplex/cl/CommandBase.java | 115 +++++++++++++++--- .../io/github/simplex/cl/CommandLoader.java | 84 +++++++++---- .../io/github/simplex/cl/DummyCommand.java | 21 ++++ .../io/github/simplex/cl/Permissible.java | 24 ++++ .../io/github/simplex/cl/api/ICommand.java | 9 +- .../io/github/simplex/cl/api/SubCommand.java | 5 +- .../simplex/cl/api/annotations/Info.java | 15 +++ .../simplex/cl/impl/ExampleCommand.java | 15 +-- .../github/simplex/msgutils/BasicColors.java | 6 +- .../simplex/msgutils/TextFormatting.java | 22 ++++ 10 files changed, 266 insertions(+), 50 deletions(-) create mode 100644 src/main/java/io/github/simplex/msgutils/TextFormatting.java diff --git a/src/main/java/io/github/simplex/cl/CommandBase.java b/src/main/java/io/github/simplex/cl/CommandBase.java index c09abf7..57448d9 100644 --- a/src/main/java/io/github/simplex/cl/CommandBase.java +++ b/src/main/java/io/github/simplex/cl/CommandBase.java @@ -1,7 +1,7 @@ package io.github.simplex.cl; -import io.github.simplex.api.ICommand; -import io.github.simplex.api.SubCommand; +import io.github.simplex.cl.api.ICommand; +import io.github.simplex.cl.api.SubCommand; import io.github.simplex.msgutils.AdvancedColors; import io.github.simplex.msgutils.BasicColors; import net.kyori.adventure.text.Component; @@ -19,19 +19,35 @@ import java.util.List; import java.util.UUID; public abstract class CommandBase extends Permissible implements ICommand { - public CommandBase(String permission, String permissionMessage, boolean allowConsole) { + /** + * @param permission The permission the user should have to run the command + * @param permissionMessage The message to send when the user does not have the permission to run the command. + * @param allowConsole Whether to allow the command to be run anywhere, or only in game. + */ + public CommandBase(@NotNull String permission, String permissionMessage, boolean allowConsole) { super(permission, permissionMessage, allowConsole); } - public CommandBase(String permission, String permissionMessage) { + /** + * @param permission The permission the user should have to run the command + * @param permissionMessage The message to send when the user does not have the permission to run the command. + */ + public CommandBase(@NotNull String permission, String permissionMessage) { this(permission, permissionMessage, true); } - public CommandBase(String permission, boolean allowConsole) { + /** + * @param permission The permission the user should have to run the command + * @param allowConsole Whether to allow the command to be run anywhere, or only in game. + */ + public CommandBase(@NotNull String permission, boolean allowConsole) { this(permission, "You do not have permission to use this command!", allowConsole); } - public CommandBase(String permission) { + /** + * @param permission The permission the user should have to run the command + */ + public CommandBase(@NotNull String permission) { this(permission, "You do not have permission to use this command!", true); } @@ -55,29 +71,100 @@ public abstract class CommandBase extends Permissible implements ICommand { return new ArrayList<>(); } - public TextComponent msg(String text) { + /** + * Returns a text component for Kyori friendly messaging. + * + * @param text The text to convert to a component + * @return A {@link TextComponent} containing the message provided in {@param text} + */ + @NotNull + public TextComponent msg(@NotNull String text) { return Component.empty().content(text); } - public TextComponent msg(String text, BasicColors color) { + /** + * Returns a text component for Kyori friendly messaging. + * + * @param text The text to convert to a Component + * @param color The color you'd like the text. These colors are basic and the majority of which are provided by Minecraft's native color system. + * @return A {@link TextComponent} containing the message provided in {@param text} with the provided {@param color} + */ + @NotNull + public TextComponent msg(@NotNull String text, @NotNull BasicColors color) { return Component.empty().content(text).color(color.getColor()); } - public TextComponent msg(String text, AdvancedColors color) { + /** + * Returns a text component for Kyori friendly messaging. + * + * @param text The text to convert to a Component + * @param color The color you'd like the text. These colors are much more diverse for viewing pleasure. + * @return A {@link TextComponent} containing the message provided in {@param text} with the provided {@param color} + */ + @NotNull + public TextComponent msg(@NotNull String text, @NotNull AdvancedColors color) { return Component.empty().content(text).color(color.getColor()); } - public void subCommand(String name, String[] args, SubCommand command) { - if (args[0].equalsIgnoreCase(name)) { - command.execute(); + /** + * Runs a subcommand provided that the user has the required permission. + * + * @param name The name of the subcommand. + * @param sender The user who executed the command. (Provided by Paper) + * @param permission The permission required to run the subcommand. + * @param args The arguments the user input to run the subcommand. (Provided by Paper) + * @param command The SubCommand to run. + * This is a functional interface to provide easy implementation of the command's details. + */ + public void subCommand(@NotNull String name, @NotNull CommandSender sender, @NotNull String permission, String @NotNull [] args, @NotNull SubCommand command) { + if (!sender.hasPermission(permission)) { + sender.sendMessage(msg(getPermissionMessage())); + return; + } + + if (args.length == 0) { + return; + } + + String[] tieredCmd = name.split(" "); + + if (args.length == 1 && tieredCmd.length == 1) { + if (args[0].equalsIgnoreCase(name)) { + command.execute(); + return; + } + } + if (args.length == 2 && tieredCmd.length == 2) { + if (args[0].equalsIgnoreCase(tieredCmd[0]) && args[1].equalsIgnoreCase(tieredCmd[1])) { + command.execute(); + return; + } + } + if (args.length == 3 && tieredCmd.length == 3) { + if (args[0].equalsIgnoreCase(tieredCmd[0]) + && args[1].equalsIgnoreCase(tieredCmd[1]) + && args[2].equalsIgnoreCase(tieredCmd[2])) { + command.execute(); + return; + } + } + if (args.length == 4 && tieredCmd.length == 4) { + if (args[0].equalsIgnoreCase(tieredCmd[0]) + && args[1].equalsIgnoreCase(tieredCmd[1]) + && args[2].equalsIgnoreCase(tieredCmd[2]) + && args[3].equalsIgnoreCase(tieredCmd[3])) { + command.execute(); + } } } - public Player getPlayer(String name) { + @Nullable + public Player getPlayer(@NotNull String name) { return Bukkit.getServer().getPlayer(name); } - public Player getPlayer(UUID uuid) { + @Nullable + public Player getPlayer(@NotNull UUID uuid) { return Bukkit.getServer().getPlayer(uuid); } diff --git a/src/main/java/io/github/simplex/cl/CommandLoader.java b/src/main/java/io/github/simplex/cl/CommandLoader.java index 0078efc..50062b3 100644 --- a/src/main/java/io/github/simplex/cl/CommandLoader.java +++ b/src/main/java/io/github/simplex/cl/CommandLoader.java @@ -3,43 +3,75 @@ package io.github.simplex.cl; import io.github.simplex.cl.api.annotations.Info; import org.bukkit.Bukkit; import org.bukkit.plugin.Plugin; -import org.reflections.Reflections; -import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; import java.util.Arrays; -import java.util.Set; +import java.util.List; public class CommandLoader { private final Plugin plugin; + private final List commandList = new ArrayList<>(); + private final String FALLBACK_PREFIX; - public CommandLoader(Plugin plugin) { + /** + * @param plugin Your plugin instance + * @param fallbackPrefix The fallback prefix to use in case your plugin fails to provide a namespace. + */ + public CommandLoader(Plugin plugin, String fallbackPrefix) { + this.FALLBACK_PREFIX = fallbackPrefix; this.plugin = plugin; } - public void registerCommands(Class commandRoot) { - Reflections reflections = new Reflections(commandRoot); - if (commandRoot.getDeclaredAnnotation(Info.class) != null) { - Set> classSet = reflections.getTypesAnnotatedWith(Info.class); - classSet.forEach(cmd -> { - Info info = cmd.getDeclaredAnnotation(Info.class); - try { - CommandBase base = (CommandBase) cmd.getConstructor().newInstance(); - DummyCommand dummy = new DummyCommand(plugin, - base, - info.name(), - info.description(), - info.usage(), - Arrays.asList(info.aliases().split(","))); - Bukkit.getCommandMap().register(info.name(), "SimplexCL", dummy); - } catch (NoSuchMethodException - | InvocationTargetException - | IllegalAccessException - | InstantiationException e) { - plugin.getLogger().severe(e.getMessage() + "\n\n\n" + e.getCause()); - } - }); + /** + * This method will register your command internally within the CommandLoader. + * Instances of commands will be cached and readied for loading into Paper. + * You should always run this before using {@link CommandLoader#load()}, otherwise your commands will not be loaded. + * + * @param command A new instance of your command which should extend CommandBase. + */ + public void register(CommandBase command) { + Class cmd = command.getClass(); + if (cmd.getDeclaredAnnotation(Info.class) != null) { + Info info = cmd.getDeclaredAnnotation(Info.class); + DummyCommand dummy = new DummyCommand(plugin, + command, + info.name(), + info.description(), + info.usage(), + Arrays.asList(info.aliases().split(","))); + commandList.add(dummy); } else { throw new RuntimeException("Missing a required annotation! Unable to load the command."); } } + + /** + * This will run a loop for each command instance you input and register them all within one method. + * This simply runs the array through a stream and uses a lambda reference to {@link CommandLoader#register(CommandBase)} in order to register the commands. + * Each command will be loaded in the order it is provided. + * + * @param commands An indefinite amount of command instances to be registered. + */ + public void register(CommandBase... commands) { + Arrays.stream(commands).forEachOrdered(this::register); + } + + /** + * This will load your commands and initialize them within Paper's CommandMap. + */ + public void load() { + commandList.forEach(cmd -> { + Bukkit.getCommandMap().register(cmd.getName(), FALLBACK_PREFIX, cmd); + }); + } + + /** + * This will effectively register all your commands and then load them into the Paper CommandMap. + * + * @param commands An indefinite amount of command instances to be registered and loaded. + */ + public void registerAndLoad(CommandBase... commands) { + register(commands); + load(); + } } diff --git a/src/main/java/io/github/simplex/cl/DummyCommand.java b/src/main/java/io/github/simplex/cl/DummyCommand.java index f61b422..c18b76c 100644 --- a/src/main/java/io/github/simplex/cl/DummyCommand.java +++ b/src/main/java/io/github/simplex/cl/DummyCommand.java @@ -1,5 +1,6 @@ package io.github.simplex.cl; +import net.kyori.adventure.text.Component; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.command.PluginIdentifiableCommand; @@ -12,6 +13,14 @@ public final class DummyCommand extends Command implements PluginIdentifiableCom private final CommandBase base; private final Plugin plugin; + /** + * @param plugin Your plugin instance. + * @param base Your command instance. + * @param name The name of your command + * @param description The description of your command + * @param usageMessage The usage for your command + * @param aliases The aliases for your command. + */ DummyCommand(@NotNull Plugin plugin, @NotNull CommandBase base, @NotNull String name, @NotNull String description, @NotNull String usageMessage, @NotNull List aliases) { super(name, description, usageMessage, aliases); this.setName(name); @@ -19,16 +28,28 @@ public final class DummyCommand extends Command implements PluginIdentifiableCom this.setUsage(usageMessage); this.setAliases(aliases); this.setPermission(base.getPermission()); + this.permissionMessage(Component.empty().content(base.getPermissionMessage())); this.base = base; this.plugin = plugin; } + /** + * The actual executor method. + * + * @param sender The user who sent the command + * @param commandLabel The name of the command + * @param args Any additional arguments the user may input + * @return Successfully executed the command or not + */ @Override public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { base.onCommand(sender, this, commandLabel, args); return true; } + /** + * @return Gets your plugin (Generic) + */ @Override public @NotNull Plugin getPlugin() { return plugin; diff --git a/src/main/java/io/github/simplex/cl/Permissible.java b/src/main/java/io/github/simplex/cl/Permissible.java index ac4a1c5..946a472 100644 --- a/src/main/java/io/github/simplex/cl/Permissible.java +++ b/src/main/java/io/github/simplex/cl/Permissible.java @@ -7,24 +7,48 @@ public abstract class Permissible { private final String permissionMessage; private final boolean allowConsole; + /** + * @param permission The permission the user should have to run the command + * @param permissionMessage The message to send when the user does not have the permission to run the command. + * @param allowConsole Whether to allow the command to be run anywhere, or only in game. + */ public Permissible(String permission, String permissionMessage, boolean allowConsole) { this.permission = permission; this.permissionMessage = permissionMessage; this.allowConsole = allowConsole; } + /** + * Gets the permission for the command it represents. + * + * @return The permission required to run the command. + */ public String getPermission() { return permission; } + /** + * Gets the message to display when a user doesn't have permission to run the command. + * + * @return The message to send the user when they do not have the required permission. + */ public String getPermissionMessage() { return permissionMessage; } + /** + * Checks if the source of the command has the permission required to run it. + * + * @param sender The command source + * @return Whether the sender has the permission or not. + */ public boolean hasPermission(CommandSender sender) { return sender.hasPermission(getPermission()); } + /** + * @return Whether to allow the command to be run from anywhere, or only players. + */ public boolean allowConsole() { return allowConsole; } diff --git a/src/main/java/io/github/simplex/cl/api/ICommand.java b/src/main/java/io/github/simplex/cl/api/ICommand.java index 9fe1557..a51db3c 100644 --- a/src/main/java/io/github/simplex/cl/api/ICommand.java +++ b/src/main/java/io/github/simplex/cl/api/ICommand.java @@ -1,9 +1,16 @@ -package io.github.simplex.api; +package io.github.simplex.cl.api; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.command.TabCompleter; public interface ICommand extends CommandExecutor, TabCompleter { + /** + * This is the actual onCommand method. This should be used when you want to execute the command itself. + * + * @param sender The user who sent the command. (Provided by Paper) + * @param args The additional arguments to the command, if applicable (Provided by Paper) + * @param allowConsole Whether the command should be allowed anywhere, or only in game. + */ void execute(CommandSender sender, String[] args, boolean allowConsole); } diff --git a/src/main/java/io/github/simplex/cl/api/SubCommand.java b/src/main/java/io/github/simplex/cl/api/SubCommand.java index 3da34be..1bbe3d2 100644 --- a/src/main/java/io/github/simplex/cl/api/SubCommand.java +++ b/src/main/java/io/github/simplex/cl/api/SubCommand.java @@ -1,6 +1,9 @@ -package io.github.simplex.api; +package io.github.simplex.cl.api; @FunctionalInterface public interface SubCommand { + /** + * This provides a way to use a functional interface to clearly define subcommands and their actions. + */ void execute(); } diff --git a/src/main/java/io/github/simplex/cl/api/annotations/Info.java b/src/main/java/io/github/simplex/cl/api/annotations/Info.java index 5c8c9e4..a45285b 100644 --- a/src/main/java/io/github/simplex/cl/api/annotations/Info.java +++ b/src/main/java/io/github/simplex/cl/api/annotations/Info.java @@ -8,8 +8,23 @@ import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Info { + /** + * @return The name of the command. + */ String name(); + + /** + * @return The description of the command. + */ String description(); + + /** + * @return The proper usage of the command. + */ String usage(); + + /** + * @return The aliases for the command, separated by commas (alias1,alias2,alias3) + */ String aliases() default ""; } diff --git a/src/main/java/io/github/simplex/cl/impl/ExampleCommand.java b/src/main/java/io/github/simplex/cl/impl/ExampleCommand.java index df29811..6297aaf 100644 --- a/src/main/java/io/github/simplex/cl/impl/ExampleCommand.java +++ b/src/main/java/io/github/simplex/cl/impl/ExampleCommand.java @@ -2,8 +2,8 @@ package io.github.simplex.cl.impl; import io.github.simplex.cl.CommandBase; import io.github.simplex.cl.api.annotations.Info; +import io.github.simplex.msgutils.AdvancedColors; import io.github.simplex.msgutils.BasicColors; -import net.kyori.adventure.text.format.TextColor; import org.bukkit.command.CommandSender; @Info(name = "example", description = "An example command implementation to see how this works.", usage = "/example [info]", aliases = "ex, impl") @@ -16,12 +16,13 @@ public final class ExampleCommand extends CommandBase { @Override public void execute(CommandSender sender, String[] args, boolean allowConsole) { - if (args.length == 1) { - subCommand("info", - args, - () -> sender.sendMessage(msg("SimplexCL was created by Simplex Development Group.", BasicColors.GOLD))); - return; - } + subCommand("info", sender, getPermission() + ".info", args, () -> { + sender.sendMessage(msg("SimplexCL was created by SimplexDevelopment!", BasicColors.GOLD)); + }); + + subCommand("info more", sender, getPermission() + ".info.more", args, () -> { + sender.sendMessage(msg("https://github.com/SimplexDevelopment", AdvancedColors.FUCHSIA)); + }); sender.sendMessage(msg("This is an example command.", BasicColors.BLUE)); } diff --git a/src/main/java/io/github/simplex/msgutils/BasicColors.java b/src/main/java/io/github/simplex/msgutils/BasicColors.java index bb0665a..d21de3a 100644 --- a/src/main/java/io/github/simplex/msgutils/BasicColors.java +++ b/src/main/java/io/github/simplex/msgutils/BasicColors.java @@ -18,7 +18,11 @@ public enum BasicColors { PURPLE(TextColor.color(255, 0, 255)), DARK_PURPLE(TextColor.color(127, 0, 127)), PINK(TextColor.color(255, 105, 180)), - DARK_PINK(TextColor.color(231, 84, 128)); + DARK_PINK(TextColor.color(231, 84, 128)), + WHITE(TextColor.color(255, 255, 255)), + BLACK(TextColor.color(0, 0, 0)), + LIGHT_GRAY(TextColor.color(127, 127, 127)), + DARK_GRAY(TextColor.color(65, 65, 65)); final TextColor color; diff --git a/src/main/java/io/github/simplex/msgutils/TextFormatting.java b/src/main/java/io/github/simplex/msgutils/TextFormatting.java new file mode 100644 index 0000000..009613a --- /dev/null +++ b/src/main/java/io/github/simplex/msgutils/TextFormatting.java @@ -0,0 +1,22 @@ +package io.github.simplex.msgutils; + +import net.kyori.adventure.text.format.TextDecoration; +import net.kyori.adventure.text.format.TextFormat; + +public enum TextFormatting { + BOLD(TextDecoration.BOLD), + ITALICS(TextDecoration.ITALIC), + UNDERLINED(TextDecoration.UNDERLINED), + STRIKETHROUGH(TextDecoration.STRIKETHROUGH), + MAGIC(TextDecoration.OBFUSCATED); + + final TextDecoration format; + + TextFormatting(TextDecoration format) { + this.format = format; + } + + public TextFormat getFormat() { + return format; + } +}