Command annotations added instead of methods, improved channels #31

Merged
NorbiPeti merged 9 commits from dev into master 2017-05-15 00:33:22 +00:00
5 changed files with 104 additions and 65 deletions
Showing only changes of commit 4a5479b027 - Show all commits

View file

@ -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;

View file

@ -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);
}
}
}

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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