Update Command System

This commit is contained in:
Paldiu 2021-04-16 00:51:18 -05:00
parent cdee236c88
commit e617b0f87a
6 changed files with 100 additions and 49 deletions

View File

@ -42,7 +42,7 @@ public final class SimplexCorePlugin extends SimplexModule<SimplexCorePlugin> {
public void start() { public void start() {
try { try {
getRegistry().register(this); getRegistry().register(this);
getCommandLoader().classpath(Command_info.class).load(this); getCommandLoader().classpath(this, Command_info.class).load();
getYamlConfig().reload(); getYamlConfig().reload();
getInternals().reload(); getInternals().reload();
// //

View File

@ -1,12 +1,13 @@
package io.github.simplexdev.simplexcore.command; package io.github.simplexdev.simplexcore.command;
import io.github.simplexdev.api.annotations.CommandInfo; import io.github.simplexdev.api.annotations.CommandInfo;
import io.github.simplexdev.simplexcore.command.defaults.DefaultCommand; import io.github.simplexdev.simplexcore.SimplexCorePlugin;
import io.github.simplexdev.simplexcore.module.SimplexModule; import io.github.simplexdev.simplexcore.module.SimplexModule;
import io.github.simplexdev.simplexcore.utils.ReflectionTools; import io.github.simplexdev.simplexcore.utils.ReflectionTools;
import io.github.simplexdev.simplexcore.SimplexCorePlugin; import org.bukkit.command.CommandExecutor;
import org.bukkit.Bukkit; import org.bukkit.command.CommandMap;
import org.bukkit.command.*; import org.bukkit.command.PluginCommand;
import org.bukkit.command.TabCompleter;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.SimplePluginManager; import org.bukkit.plugin.SimplePluginManager;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -20,7 +21,10 @@ import java.util.MissingResourceException;
public final class CommandLoader { public final class CommandLoader {
private Reflections reflections; private Reflections reflections;
private ClassLoader classLoader;
private SimplexModule<?> plugin;
private static final CommandLoader instance = new CommandLoader(); private static final CommandLoader instance = new CommandLoader();
private final Registry registry = new Registry();
/** /**
* @return A Singleton Pattern instance of this class. * @return A Singleton Pattern instance of this class.
@ -38,15 +42,15 @@ public final class CommandLoader {
* If the class provided does not have the {@link CommandInfo} annotation, the loader will throw a new * If the class provided does not have the {@link CommandInfo} annotation, the loader will throw a new
* {@link MissingResourceException} and will not load your plugin's commands. * {@link MissingResourceException} and will not load your plugin's commands.
* If the class provided does not extend {@link SimplexCommand}, the loader will throw a new * If the class provided does not extend {@link SimplexCommand}, the loader will throw a new
* {@link RuntimeException} and your commands will not be loaded. * {@link CommandLoaderException} and your commands will not be loaded.
* </p> * </p>
* *
* @param clazz The command class to load from * @param clazz The command class to load from
* @return An instance of this where the classpath has been prepared for loading the commands. * @return An instance of this where the classpath has been prepared for loading the commands.
*/ */
public <T extends SimplexCommand> CommandLoader classpath(Class<T> clazz) { public <T extends SimplexCommand> CommandLoader classpath(SimplexModule<?> plugin, Class<T> clazz) {
if (clazz == null) { if (clazz == null) {
throw new IllegalStateException("The class provided cannot be found!"); throw new IllegalArgumentException("The class provided cannot be found!");
} }
if (!clazz.isAnnotationPresent(CommandInfo.class)) { if (!clazz.isAnnotationPresent(CommandInfo.class)) {
@ -54,22 +58,26 @@ public final class CommandLoader {
} }
if (!SimplexCommand.class.isAssignableFrom(clazz)) { if (!SimplexCommand.class.isAssignableFrom(clazz)) {
throw new RuntimeException("Your command must extend SimplexCommand.class for it to be used as the reference point for loading commands."); throw new CommandLoaderException("Your command must extend SimplexCommand.class for it to be used as the reference point for loading commands.");
} }
reflections = ReflectionTools.reflect(clazz); this.reflections = ReflectionTools.reflect(clazz);
this.plugin = plugin;
this.classLoader = plugin.getClass().getClassLoader();
return this; return this;
} }
/** /**
* Loads all the commands from the specified classpath. * Loads all the commands from the specified classpath.
* This should be used immediately after {@link CommandLoader#classpath(Class)} has been called. * This should be used immediately after {@link CommandLoader#classpath(SimplexModule, Class)} has been called.
* If used before, an exception will be thrown, and your commands will not be loaded. * If used before, an exception will be thrown, and your commands will not be loaded.
*
* @param plugin An instance of your plugin to assign as the parent plugin for each command.
*/ */
public void load(SimplexModule<?> plugin) { public void load() {
if (reflections == null || plugin == null || classLoader == null) {
throw new CommandLoaderException("Please run CommandLoader#classpath(SimplexModule, Class) first!");
}
reflections.getTypesAnnotatedWith(CommandInfo.class).forEach(annotated -> { reflections.getTypesAnnotatedWith(CommandInfo.class).forEach(annotated -> {
CommandInfo info = annotated.getDeclaredAnnotation(CommandInfo.class); CommandInfo info = annotated.getDeclaredAnnotation(CommandInfo.class);
@ -89,7 +97,7 @@ public final class CommandLoader {
return; return;
} }
PluginCommand command = Registry.create(plugin, info.name().toLowerCase()); PluginCommand command = registry.create(plugin, info.name().toLowerCase());
command.setAliases(Arrays.asList(info.aliases().split(","))); command.setAliases(Arrays.asList(info.aliases().split(",")));
command.setDescription(info.description()); command.setDescription(info.description());
command.setExecutor(getExecutorFromName(info.name())); command.setExecutor(getExecutorFromName(info.name()));
@ -98,7 +106,7 @@ public final class CommandLoader {
command.setPermissionMessage(info.permissionMessage()); command.setPermissionMessage(info.permissionMessage());
command.setTabCompleter(getTabFromName(info.name())); command.setTabCompleter(getTabFromName(info.name()));
command.setUsage(info.usage()); command.setUsage(info.usage());
Registry.registerCommand(command); registry.registerCommand(command);
}); });
} }
@ -110,28 +118,27 @@ public final class CommandLoader {
* @param name The name of the command. * @param name The name of the command.
* @return An instance of the command class as a CommandExecutor. * @return An instance of the command class as a CommandExecutor.
*/ */
public CommandExecutor getExecutorFromName(String name) { private CommandExecutor getExecutorFromName(String name) {
for (Class<? extends CommandExecutor> obj : reflections.getSubTypesOf(CommandExecutor.class)) { for (Class<? extends SimplexCommand> obj : reflections.getSubTypesOf(SimplexCommand.class)) {
if (!obj.isAnnotationPresent(CommandInfo.class)) { if (!obj.isAnnotationPresent(CommandInfo.class)) {
SimplexCorePlugin.getInstance() plugin.getLogger().warning(obj.getSimpleName()
.getLogger().warning(obj.getSimpleName()
+ " is missing a required annotation: " + " is missing a required annotation: "
+ CommandInfo.class.getSimpleName()); + CommandInfo.class.getSimpleName());
continue;
} }
CommandInfo info = obj.getDeclaredAnnotation(CommandInfo.class); CommandInfo info = obj.getDeclaredAnnotation(CommandInfo.class);
if (name.equalsIgnoreCase(info.name())) { if (name.equalsIgnoreCase(info.name())) {
try { Constructor<? extends CommandExecutor> constr =
Constructor<? extends CommandExecutor> constr = obj.getDeclaredConstructor(); ReflectionTools.getDeclaredConstructor(obj, SimplexModule.class);
constr.setAccessible(true); if (constr == null) {
return constr.newInstance(); throw new CommandLoaderException("Constructor does not exist! Are you extending SimplexCommand properly?");
} catch (ReflectiveOperationException ignored) {
return new DefaultCommand();
} }
return ReflectionTools.initConstructor(constr, plugin);
} }
} }
throw new RuntimeException("Unable to assign a CommandExecutor from the provided classes!"); throw new CommandLoaderException("Unable to assign a CommandExecutor from the provided classes!");
} }
/** /**
@ -143,52 +150,50 @@ public final class CommandLoader {
* @return The command as an instance of TabCompleter * @return The command as an instance of TabCompleter
*/ */
@Nullable @Nullable
public TabCompleter getTabFromName(String name) { private TabCompleter getTabFromName(String name) {
for (Class<? extends TabCompleter> obj : reflections.getSubTypesOf(TabCompleter.class)) { for (Class<? extends SimplexCommand> obj : reflections.getSubTypesOf(SimplexCommand.class)) {
if (!obj.isAnnotationPresent(CommandInfo.class)) { if (!obj.isAnnotationPresent(CommandInfo.class)) {
SimplexCorePlugin.getInstance() plugin.getLogger().warning(obj.getSimpleName()
.getLogger().warning(obj.getSimpleName()
+ " is missing required annotation: " + " is missing required annotation: "
+ CommandInfo.class.getSimpleName()); + CommandInfo.class.getSimpleName());
continue; continue;
} }
CommandInfo info = obj.getDeclaredAnnotation(CommandInfo.class); CommandInfo info = obj.getDeclaredAnnotation(CommandInfo.class);
if (name.equalsIgnoreCase(info.name())) { if (name.equalsIgnoreCase(info.name())) {
try { Constructor<? extends TabCompleter> constr = ReflectionTools.getDeclaredConstructor(obj, SimplexModule.class);
Constructor<? extends TabCompleter> constr = obj.getDeclaredConstructor(); if (constr == null) {
constr.setAccessible(true); throw new CommandLoaderException("Constructor does not exist! Are you extending SimplexCommand properly?");
return constr.newInstance();
} catch (ReflectiveOperationException ignored) {
return new DefaultCommand();
} }
return ReflectionTools.initConstructor(constr, plugin);
} }
} }
return null; throw new CommandLoaderException("Unable to assign a TabCompleter from the provided classes!");
} }
/** /**
* Registry class, which forces all necessary fields to accessible. * Registry class, which forces all necessary fields to accessible.
*/ */
private static class Registry { private final class Registry {
private static final Constructor<PluginCommand> constructor; private final Constructor<PluginCommand> constructor;
private static final Field cmdMapField; private final Field cmdMapField;
static { public Registry() {
constructor = ReflectionTools.getDeclaredConstructor(PluginCommand.class, String.class, Plugin.class); constructor = ReflectionTools.getDeclaredConstructor(PluginCommand.class, String.class, Plugin.class);
cmdMapField = ReflectionTools.getDeclaredField(SimplePluginManager.class, "commandMap"); cmdMapField = ReflectionTools.getDeclaredField(SimplePluginManager.class, "commandMap");
} }
public static PluginCommand create(@NotNull Plugin plugin, @NotNull String name) { public PluginCommand create(@NotNull SimplexModule<?> plugin, @NotNull String name) {
return ReflectionTools.initConstructor(constructor, name, plugin); return ReflectionTools.initConstructor(constructor, name, plugin);
} }
public static void registerCommand(PluginCommand command) { public void registerCommand(PluginCommand command) {
try { try {
CommandMap map = (CommandMap) cmdMapField.get(Bukkit.getPluginManager()); CommandMap map = (CommandMap) cmdMapField.get(plugin.getManager());
map.register(command.getName().toLowerCase(), command); map.register(command.getName().toLowerCase(), command);
} catch (IllegalAccessException e) { } catch (IllegalAccessException e) {
throw new RuntimeException(e); throw new CommandLoaderException(e);
} }
} }
} }

View File

@ -0,0 +1,19 @@
package io.github.simplexdev.simplexcore.command;
import org.apache.commons.lang.exception.ExceptionUtils;
public class CommandLoaderException extends RuntimeException {
public CommandLoaderException() {
super("The Command Loader has encountered an exception and has failed to execute properly. " +
"Some commands may not be loaded.");
ExceptionUtils.getMessage(super.getCause());
}
public CommandLoaderException(String msg) {
super(msg);
}
public CommandLoaderException(Throwable th) {
super(th);
}
}

View File

@ -1,6 +1,7 @@
package io.github.simplexdev.simplexcore.command; package io.github.simplexdev.simplexcore.command;
import io.github.simplexdev.simplexcore.SimplexCorePlugin; import io.github.simplexdev.simplexcore.SimplexCorePlugin;
import io.github.simplexdev.simplexcore.module.SimplexModule;
import io.github.simplexdev.simplexcore.utils.Utilities; import io.github.simplexdev.simplexcore.utils.Utilities;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
@ -15,6 +16,15 @@ import java.util.List;
import java.util.UUID; import java.util.UUID;
public abstract class SimplexCommand implements CommandExecutor, TabCompleter { public abstract class SimplexCommand implements CommandExecutor, TabCompleter {
private final SimplexModule<?> plugin;
public SimplexCommand(SimplexModule<?> plugin) {
this.plugin = plugin;
}
public final SimplexModule<?> getPlugin() {
return plugin;
}
public boolean isPlayer(CommandSender sender) { public boolean isPlayer(CommandSender sender) {
return sender instanceof Player; return sender instanceof Player;
@ -22,6 +32,7 @@ public abstract class SimplexCommand implements CommandExecutor, TabCompleter {
/** /**
* Gets an online player from their username * Gets an online player from their username
*
* @param name The player's username * @param name The player's username
* @return An instance of {@link Player} which represents the online player in question. * @return An instance of {@link Player} which represents the online player in question.
*/ */
@ -32,6 +43,7 @@ public abstract class SimplexCommand implements CommandExecutor, TabCompleter {
/** /**
* Gets an online player from their {@link UUID}. * Gets an online player from their {@link UUID}.
*
* @param uuid The player's UUID * @param uuid The player's UUID
* @return An instance of {@link Player} which represents the online player in question. * @return An instance of {@link Player} which represents the online player in question.
*/ */
@ -43,6 +55,7 @@ public abstract class SimplexCommand implements CommandExecutor, TabCompleter {
/** /**
* Gets an instance of {@link Player} based off an instance of {@link CommandSender}. * Gets an instance of {@link Player} based off an instance of {@link CommandSender}.
* This will be null if the condition {CommandSender instanceof Player} is false. * This will be null if the condition {CommandSender instanceof Player} is false.
*
* @param sender The CommandSender to cast * @param sender The CommandSender to cast
* @return An instance of Player relating to CommandSender. * @return An instance of Player relating to CommandSender.
*/ */
@ -54,7 +67,8 @@ public abstract class SimplexCommand implements CommandExecutor, TabCompleter {
/** /**
* Send a message or a group of messages to a {@link Player}. * Send a message or a group of messages to a {@link Player}.
* If you want the messages to send on new lines, put \n at the end of each message to send. * If you want the messages to send on new lines, put \n at the end of each message to send.
* @param player The Player to send a message to *
* @param player The Player to send a message to
* @param messages The messages to send. * @param messages The messages to send.
*/ */
public void playerMsg(Player player, String... messages) { public void playerMsg(Player player, String... messages) {
@ -66,7 +80,8 @@ public abstract class SimplexCommand implements CommandExecutor, TabCompleter {
/** /**
* Send a message or a group of messages to a {@link CommandSender} * Send a message or a group of messages to a {@link CommandSender}
* If you want the messages to send on new lines, put \n at the end of each message to send. * If you want the messages to send on new lines, put \n at the end of each message to send.
* @param sender The CommandSender to send a message to. *
* @param sender The CommandSender to send a message to.
* @param messages The messages to send. * @param messages The messages to send.
*/ */
public void msg(CommandSender sender, String... messages) { public void msg(CommandSender sender, String... messages) {

View File

@ -1,16 +1,23 @@
package io.github.simplexdev.simplexcore.command.defaults; package io.github.simplexdev.simplexcore.command.defaults;
import io.github.simplexdev.api.annotations.CommandInfo; import io.github.simplexdev.api.annotations.CommandInfo;
import io.github.simplexdev.simplexcore.chat.Messages;
import io.github.simplexdev.simplexcore.command.SimplexCommand; import io.github.simplexdev.simplexcore.command.SimplexCommand;
import io.github.simplexdev.simplexcore.module.SimplexModule;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@CommandInfo(name = "Info", description = "Gets info on this API / Library.", usage = "/<command>", permission = "simplex.core.info") @CommandInfo(name = "Info", description = "Gets info on this API / Library.", usage = "/<command>", permission = "simplex.core.info")
public class Command_info extends SimplexCommand { public class Command_info extends SimplexCommand {
public Command_info(SimplexModule<?> plugin) {
super(plugin);
}
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
sender.sendMessage("This is an API!"); sender.sendMessage(Messages.DISCORD.getMessage());
return true; return true;
} }
} }

View File

@ -2,12 +2,17 @@ package io.github.simplexdev.simplexcore.command.defaults;
import io.github.simplexdev.api.annotations.CommandInfo; import io.github.simplexdev.api.annotations.CommandInfo;
import io.github.simplexdev.simplexcore.command.SimplexCommand; import io.github.simplexdev.simplexcore.command.SimplexCommand;
import io.github.simplexdev.simplexcore.module.SimplexModule;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@CommandInfo(name = "default", usage = "/<command>", description = "Default plugin command.") @CommandInfo(name = "default", usage = "/<command>", description = "Default plugin command.")
public final class DefaultCommand extends SimplexCommand { public final class DefaultCommand extends SimplexCommand {
public DefaultCommand(SimplexModule<?> plugin) {
super(plugin);
}
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
sender.sendMessage("If you are seeing this when running your command, your command didn't register properly."); sender.sendMessage("If you are seeing this when running your command, your command didn't register properly.");