Command annotations added instead of methods, improved channels #31
5 changed files with 104 additions and 65 deletions
|
@ -13,42 +13,36 @@ import buttondevteam.lib.chat.TBMCCommandBase;
|
|||
|
||||
public class CommandCaller implements CommandExecutor {
|
||||
|
||||
private static final String REGISTER_ERROR_MSG = "An error occured while registering commands";
|
||||
|
||||
private CommandCaller() {
|
||||
}
|
||||
|
||||
private static CommandCaller instance;
|
||||
|
||||
public static void RegisterCommand(TBMCCommandBase cmd) {
|
||||
public static void RegisterCommand(TBMCCommandBase cmd) throws Exception {
|
||||
if (instance == null)
|
||||
instance = new CommandCaller();
|
||||
if (cmd.GetCommandPath() == null) {
|
||||
TBMCCoreAPI.SendException(REGISTER_ERROR_MSG,
|
||||
new Exception("Command " + cmd.getClass().getSimpleName() + " has no command path!"));
|
||||
return;
|
||||
}
|
||||
if (cmd.getPlugin() == null) {
|
||||
TBMCCoreAPI.SendException(REGISTER_ERROR_MSG,
|
||||
new Exception("Command " + cmd.GetCommandPath() + " has no plugin!"));
|
||||
return;
|
||||
}
|
||||
String topcmd = cmd.GetCommandPath();
|
||||
if (topcmd == null)
|
||||
throw new Exception("Command " + cmd.getClass().getSimpleName() + " has no command path!");
|
||||
if (cmd.getPlugin() == null)
|
||||
throw new Exception("Command " + cmd.GetCommandPath() + " has no plugin!");
|
||||
int i;
|
||||
String topcmd;
|
||||
if ((i = (topcmd = cmd.GetCommandPath()).indexOf(' ')) != -1) // Get top-level command
|
||||
topcmd = cmd.GetCommandPath().substring(0, i);
|
||||
if ((i = topcmd.indexOf(' ')) != -1) // Get top-level command
|
||||
topcmd = topcmd.substring(0, i);
|
||||
{
|
||||
PluginCommand pc = ((JavaPlugin) cmd.getPlugin()).getCommand(topcmd);
|
||||
if (pc == null)
|
||||
TBMCCoreAPI.SendException(REGISTER_ERROR_MSG, new Exception("Top level command " + topcmd
|
||||
+ " not registered in plugin.yml for plugin: " + cmd.getPlugin().getName()));
|
||||
throw new Exception("Top level command " + topcmd + " not registered in plugin.yml for plugin: "
|
||||
+ cmd.getPlugin().getName());
|
||||
else
|
||||
pc.setExecutor(instance);
|
||||
System.out.println("Executor set");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String alias, String[] args) {
|
||||
System.out.println("onCommand called");
|
||||
String path = command.getName().toLowerCase();
|
||||
for (String arg : args)
|
||||
path += " " + arg;
|
||||
|
|
|
@ -201,8 +201,10 @@ public class TBMCCoreAPI {
|
|||
SendUnsentExceptions();
|
||||
TBMCExceptionEvent event = new TBMCExceptionEvent(sourcemsg, e);
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
if (!event.isHandled())
|
||||
exceptionsToSend.put(sourcemsg, e);
|
||||
synchronized (exceptionsToSend) {
|
||||
if (!event.isHandled())
|
||||
exceptionsToSend.put(sourcemsg, e);
|
||||
}
|
||||
Bukkit.getLogger().warning(sourcemsg);
|
||||
e.printStackTrace();
|
||||
if (debugPotato) {
|
||||
|
@ -212,7 +214,6 @@ public class TBMCCoreAPI {
|
|||
devsOnline.add(player);
|
||||
}
|
||||
}
|
||||
;
|
||||
if (!devsOnline.isEmpty()) {
|
||||
DebugPotato potato = new DebugPotato()
|
||||
.setMessage(new String[] { //
|
||||
|
@ -234,8 +235,10 @@ public class TBMCCoreAPI {
|
|||
SendUnsentDebugMessages();
|
||||
TBMCDebugMessageEvent event = new TBMCDebugMessageEvent(debugMessage);
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
if (!event.isSent())
|
||||
debugMessagesToSend.add(debugMessage);
|
||||
synchronized (debugMessagesToSend) {
|
||||
if (!event.isSent())
|
||||
debugMessagesToSend.add(debugMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -258,28 +261,32 @@ public class TBMCCoreAPI {
|
|||
* Send exceptions that haven't been sent (their events didn't get handled). This method is used by the DiscordPlugin's ready event
|
||||
*/
|
||||
public static void SendUnsentExceptions() {
|
||||
if (exceptionsToSend.size() > 20) {
|
||||
exceptionsToSend.clear(); // Don't call more and more events if all the handler plugins are unloaded
|
||||
Bukkit.getLogger().warning("Unhandled exception list is over 20! Clearing!");
|
||||
}
|
||||
for (Entry<String, Throwable> entry : exceptionsToSend.entrySet()) {
|
||||
TBMCExceptionEvent event = new TBMCExceptionEvent(entry.getKey(), entry.getValue());
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
if (event.isHandled())
|
||||
exceptionsToSend.remove(entry.getKey());
|
||||
synchronized (exceptionsToSend) {
|
||||
if (exceptionsToSend.size() > 20) {
|
||||
exceptionsToSend.clear(); // Don't call more and more events if all the handler plugins are unloaded
|
||||
Bukkit.getLogger().warning("Unhandled exception list is over 20! Clearing!");
|
||||
}
|
||||
for (Entry<String, Throwable> entry : exceptionsToSend.entrySet()) {
|
||||
TBMCExceptionEvent event = new TBMCExceptionEvent(entry.getKey(), entry.getValue());
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
if (event.isHandled())
|
||||
exceptionsToSend.remove(entry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void SendUnsentDebugMessages() {
|
||||
if (debugMessagesToSend.size() > 20) {
|
||||
debugMessagesToSend.clear(); // Don't call more and more DebugMessages if all the handler plugins are unloaded
|
||||
Bukkit.getLogger().warning("Unhandled Debug Message list is over 20! Clearing!");
|
||||
}
|
||||
for (String message : debugMessagesToSend) {
|
||||
TBMCDebugMessageEvent event = new TBMCDebugMessageEvent(message);
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
if (event.isSent())
|
||||
debugMessagesToSend.remove(message);
|
||||
synchronized (debugMessagesToSend) {
|
||||
if (debugMessagesToSend.size() > 20) {
|
||||
debugMessagesToSend.clear(); // Don't call more and more DebugMessages if all the handler plugins are unloaded
|
||||
Bukkit.getLogger().warning("Unhandled Debug Message list is over 20! Clearing!");
|
||||
}
|
||||
for (String message : debugMessagesToSend) {
|
||||
TBMCDebugMessageEvent event = new TBMCDebugMessageEvent(message);
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
if (event.isSent())
|
||||
debugMessagesToSend.remove(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,13 @@ import java.lang.annotation.Retention;
|
|||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* <b>Abstract classes with no {@link CommandClass} annotations will be ignored.</b> Classes that are not abstract or have the annotation will be included in the command path unless
|
||||
* {@link #excludeFromPath()} is true.
|
||||
*
|
||||
* @author NorbiPeti
|
||||
*
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Inherited
|
||||
|
@ -26,4 +33,9 @@ public @interface CommandClass {
|
|||
* @return The command path, <i>which is the command class name by default</i> (removing any "command" from it)
|
||||
*/
|
||||
public String path() default "";
|
||||
|
||||
/**
|
||||
* Exclude this class from the path. Useful if more commands share some property but aren't subcommands of a common command. See {@link CommandClass} for more details.
|
||||
*/
|
||||
public boolean excludeFromPath() default false;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import java.lang.reflect.Modifier;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
|
@ -61,20 +62,20 @@ public class TBMCChatAPI {
|
|||
cmds.add("§6---- Subcommands ----");
|
||||
cmds.add(cmd);
|
||||
};
|
||||
for (TBMCCommandBase cmd : TBMCChatAPI.GetCommands().values()) {
|
||||
if (cmd.GetCommandPath().startsWith(command + " ")) {
|
||||
if (cmd.isPlayerOnly() && !(sender instanceof Player))
|
||||
for (Entry<String, TBMCCommandBase> cmd : TBMCChatAPI.GetCommands().entrySet()) {
|
||||
if (cmd.getKey().startsWith(command + " ")) {
|
||||
if (cmd.getValue().isPlayerOnly() && !(sender instanceof Player))
|
||||
continue;
|
||||
if (cmd.getClass().getAnnotation(CommandClass.class).modOnly()
|
||||
&& !MainPlugin.permission.has(sender, "tbmc.admin"))
|
||||
continue;
|
||||
int ind = cmd.GetCommandPath().indexOf(' ', command.length() + 2);
|
||||
int ind = cmd.getKey().indexOf(' ', command.length() + 2);
|
||||
if (ind >= 0) {
|
||||
String newcmd = cmd.GetCommandPath().substring(0, ind);
|
||||
String newcmd = cmd.getKey().substring(0, ind);
|
||||
if (!cmds.contains("/" + newcmd))
|
||||
addToCmds.accept("/" + newcmd);
|
||||
} else
|
||||
addToCmds.accept("/" + cmd.GetCommandPath());
|
||||
addToCmds.accept("/" + cmd.getKey());
|
||||
}
|
||||
}
|
||||
return cmds.toArray(new String[cmds.size()]);
|
||||
|
@ -99,15 +100,22 @@ public class TBMCChatAPI {
|
|||
* @param acmdclass
|
||||
* A command's class to get the package name for commands. The provided class's package and subpackages are scanned for commands.
|
||||
*/
|
||||
public static void AddCommands(JavaPlugin plugin, Class<? extends TBMCCommandBase> acmdclass) {
|
||||
plugin.getLogger().info("Registering commands for " + plugin.getName());
|
||||
public static synchronized void AddCommands(JavaPlugin plugin, Class<? extends TBMCCommandBase> acmdclass) {
|
||||
plugin.getLogger().info("Registering commands from " + acmdclass.getPackage().getName());
|
||||
Reflections rf = new Reflections(new ConfigurationBuilder()
|
||||
.setUrls(ClasspathHelper.forPackage(acmdclass.getPackage().getName(),
|
||||
plugin.getClass().getClassLoader()))
|
||||
.addUrls(
|
||||
ClasspathHelper.forClass(OptionallyPlayerCommandBase.class,
|
||||
OptionallyPlayerCommandBase.class.getClassLoader()),
|
||||
ClasspathHelper.forClass(PlayerCommandBase.class, PlayerCommandBase.class.getClassLoader())) // http://stackoverflow.com/questions/12917417/using-reflections-for-finding-the-transitive-subtypes-of-a-class-when-not-all
|
||||
.addClassLoader(plugin.getClass().getClassLoader()).addScanners(new SubTypesScanner()));
|
||||
Set<Class<? extends TBMCCommandBase>> cmds = rf.getSubTypesOf(TBMCCommandBase.class);
|
||||
for (Class<? extends TBMCCommandBase> cmd : cmds) {
|
||||
try {
|
||||
if (!cmd.getPackage().getName().startsWith(acmdclass.getPackage().getName()))
|
||||
continue; // It keeps including the commands from here
|
||||
//System.out.println("Class found: " + cmd.getName());
|
||||
if (Modifier.isAbstract(cmd.getModifiers()))
|
||||
continue;
|
||||
TBMCCommandBase c = cmd.newInstance();
|
||||
|
@ -116,9 +124,7 @@ public class TBMCChatAPI {
|
|||
continue;
|
||||
commands.put(c.GetCommandPath(), c);
|
||||
CommandCaller.RegisterCommand(c);
|
||||
} catch (InstantiationException e) {
|
||||
TBMCCoreAPI.SendException("An error occured while registering command " + cmd.getName(), e);
|
||||
} catch (IllegalAccessException e) {
|
||||
} catch (Exception e) {
|
||||
TBMCCoreAPI.SendException("An error occured while registering command " + cmd.getName(), e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,9 +5,11 @@ import java.util.function.Function;
|
|||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import javassist.Modifier;
|
||||
|
||||
/**
|
||||
* Extend this class to create new TBMCCommand and use {@link TBMCChatAPI#AddCommand(org.bukkit.plugin.java.JavaPlugin, TBMCCommandBase)} to add it. <u><b>Note:</b></u> The command path (command name
|
||||
* and subcommand arguments) will be the class name by default, removing any "command" from it. To change it (especially for subcommands), override {@link #GetCommandPath()}.
|
||||
* and subcommand arguments) will be the class name by default, removing any "command" from it. To change it (especially for subcommands), use the path field in the {@link CommandClass} annotation.
|
||||
*
|
||||
* @author Norbi
|
||||
*
|
||||
|
@ -15,36 +17,54 @@ import org.bukkit.plugin.Plugin;
|
|||
public abstract class TBMCCommandBase {
|
||||
|
||||
public TBMCCommandBase() {
|
||||
path = getcmdpath();
|
||||
}
|
||||
|
||||
public abstract boolean OnCommand(CommandSender sender, String alias, String[] args);
|
||||
|
||||
public abstract String[] GetHelpText(String alias);
|
||||
|
||||
private String path = null;
|
||||
|
||||
/**
|
||||
* The command's path, or name if top-level command.<br>
|
||||
* For example:<br>
|
||||
* "u admin updateplugin" or "u" for the top level one<br>
|
||||
* <u>The path must be lowercase!</u><br>
|
||||
* <b>Abstract classes with no {@link CommandClass} annotations will be ignored.</b>
|
||||
*
|
||||
* @return The command path, <i>which is the command class name by default</i> (removing any "command" from it)
|
||||
* @return The command path, <i>which is the command class name by default</i> (removing any "command" from it) - Change via the {@link CommandClass} annotation
|
||||
*/
|
||||
public final String GetCommandPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
private final String getcmdpath() {
|
||||
if (!getClass().isAnnotationPresent(CommandClass.class))
|
||||
throw new RuntimeException("No @Command annotation on command class " + getClass().getSimpleName() + "!");
|
||||
Function<Class<?>, String> getFromClass = cl -> getClass().getSimpleName().toLowerCase()
|
||||
.replace("commandbase", "").replace("command", "");
|
||||
String path = getClass().getAnnotation(CommandClass.class).path(), prevpath = path; // TODO: Check if annotation exists (No @Inherited?)
|
||||
throw new RuntimeException(
|
||||
"No @CommandClass annotation on command class " + getClass().getSimpleName() + "!");
|
||||
Function<Class<?>, String> getFromClass = cl -> cl.getSimpleName().toLowerCase().replace("commandbase", "") // <-- ...
|
||||
.replace("command", "");
|
||||
String path = getClass().getAnnotation(CommandClass.class).path(),
|
||||
prevpath = path = path.length() == 0 ? getFromClass.apply(getClass()) : path;
|
||||
// System.out.println("Path: " + (path.length() == 0 ? getFromClass.apply(getClass()) : path))
|
||||
for (Class<?> cl = getClass().getSuperclass(); cl != null
|
||||
&& !cl.getName().equals(TBMCCommandBase.class.getName()); cl = cl.getSuperclass()) {
|
||||
//com.sun.xml.internal.bind.v2.TODO.prototype();
|
||||
String newpath = cl.getAnnotation(CommandClass.class).path();
|
||||
if (newpath.length() == 0)
|
||||
&& !cl.getPackage().getName().equals(TBMCCommandBase.class.getPackage().getName()); cl = cl
|
||||
.getSuperclass()) { //
|
||||
String newpath;
|
||||
if (!cl.isAnnotationPresent(CommandClass.class)
|
||||
|| (newpath = cl.getAnnotation(CommandClass.class).path()).length() == 0
|
||||
|| newpath.equals(prevpath)) {
|
||||
if (Modifier.isAbstract(cl.getModifiers()) && (!cl.isAnnotationPresent(CommandClass.class))
|
||||
|| cl.getAnnotation(CommandClass.class).excludeFromPath()) // <--
|
||||
continue;
|
||||
newpath = getFromClass.apply(cl);
|
||||
if (!newpath.equals(prevpath))
|
||||
path = (prevpath = newpath) + " " + path;
|
||||
}
|
||||
path = (prevpath = newpath) + " " + path;
|
||||
// System.out.println("Path: " + (path.length() == 0 ? getFromClass.apply(cl) : path));
|
||||
}
|
||||
return path.length() == 0 ? getFromClass.apply(getClass()) : path;
|
||||
//System.out.println("Path: " + path);
|
||||
return path;
|
||||
}
|
||||
|
||||
Plugin plugin; // Used By TBMCChatAPI
|
||||
|
|
Loading…
Reference in a new issue