APIv4, support for spigot 1.12 (and up) #6
@ -1,3 +1,3 @@
|
||||
Manifest-Version: 1.0
|
||||
Class-Path: ../lib/CommandManager.jar
|
||||
Class-Path: ../lib/CommandManager.jar ../lib/ChatAPI.jar ../lib/ChestAPI.jar
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
name: JavaUtils
|
||||
version: 1.0.0
|
||||
name: ModuleLoader
|
||||
version: 4.0.0
|
||||
authors: [pepich1851]
|
||||
main: com.redstoner.misc.Main
|
||||
softdepend: [Vault]
|
@ -5,10 +5,11 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/** Debugable annotation, to be added to methods that invoke the Debugger.notifyMethod method for debugging purposes.
|
||||
*
|
||||
* @author Pepich */
|
||||
@Target(ElementType.METHOD)
|
||||
import com.redstoner.misc.CommandHolderType;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Debugable
|
||||
{}
|
||||
public @interface Commands
|
||||
{
|
||||
CommandHolderType value();
|
||||
}
|
@ -1,201 +0,0 @@
|
||||
package com.redstoner.coremods.debugger;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
import com.nemez.cmdmgr.Command;
|
||||
import com.nemez.cmdmgr.CommandManager;
|
||||
import com.redstoner.annotations.Debugable;
|
||||
import com.redstoner.annotations.Version;
|
||||
import com.redstoner.misc.Main;
|
||||
import com.redstoner.misc.Utils;
|
||||
import com.redstoner.modules.CoreModule;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
|
||||
/** The Debugger class, first Module to be loaded, responsible for debug interactions such as subscribing to method calls and getting field values on runtime.
|
||||
*
|
||||
* @author Pepich */
|
||||
@Version(major = 3, minor = 0, revision = 0, compatible = -1)
|
||||
public final class Debugger implements CoreModule, Listener
|
||||
{
|
||||
private static Debugger instance;
|
||||
private static HashMap<CommandSender, ArrayList<String>> subs;
|
||||
private static final boolean enabled = true;
|
||||
|
||||
private Debugger()
|
||||
{
|
||||
subs = new HashMap<>();
|
||||
}
|
||||
|
||||
public static void init()
|
||||
{
|
||||
if (instance == null)
|
||||
instance = new Debugger();
|
||||
CommandManager.registerCommand(instance.getCommandString(), instance, Main.plugin);
|
||||
}
|
||||
|
||||
public static void notifyMethod(CommandSender recipient, Object... params)
|
||||
{
|
||||
Exception e = new Exception();
|
||||
String method = e.getStackTrace()[1].getMethodName();
|
||||
if (!method.equals("notifyMethod"))
|
||||
notifyMethod((Object) recipient, params);
|
||||
String classname = e.getStackTrace()[1].getClassName();
|
||||
if (!classname.equals("com.redstoner.coremods.debugger.Debugger"))
|
||||
notifyMethod((Object) recipient, params);
|
||||
for (StackTraceElement element : e.getStackTrace())
|
||||
{
|
||||
if (element.getMethodName().equals("notifyMethod"))
|
||||
continue;
|
||||
classname = element.getClassName();
|
||||
method = element.getMethodName();
|
||||
break;
|
||||
}
|
||||
boolean subscribed = false;
|
||||
for (String s : subs.get(recipient))
|
||||
{
|
||||
if (s.equals(classname + "." + method))
|
||||
{
|
||||
subscribed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (subscribed)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder("&7");
|
||||
sb.append(method);
|
||||
sb.append("(");
|
||||
if (params != null)
|
||||
{
|
||||
for (Object obj : params)
|
||||
{
|
||||
if (obj == null)
|
||||
sb.append("&cNULL");
|
||||
else
|
||||
sb.append(obj.toString());
|
||||
sb.append("&7, &e");
|
||||
}
|
||||
sb.delete(sb.length() - 6, sb.length());
|
||||
}
|
||||
sb.append("&7)\n&eTypes:\n&7");
|
||||
int i = 0;
|
||||
for (Object obj : params)
|
||||
sb.append(i++ + ": &e" + (obj == null ? "&cNULL" : obj.getClass().getName()) + "&7\n");
|
||||
String message = "&2---=[ DEBUGGER ]=---\n" + sb.toString();
|
||||
message = ChatColor.translateAlternateColorCodes('&', message);
|
||||
recipient.sendMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void notifyMethod(Object... params)
|
||||
{
|
||||
if (!enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (Player p : Bukkit.getOnlinePlayers())
|
||||
if (subs.containsKey(p))
|
||||
notifyMethod(p, params);
|
||||
CommandSender p = Bukkit.getConsoleSender();
|
||||
if (subs.containsKey(p))
|
||||
notifyMethod(p, params);
|
||||
}
|
||||
|
||||
// @noformat
|
||||
@Override
|
||||
public String getCommandString()
|
||||
{
|
||||
return "command debugger {\n" +
|
||||
" subscribe [string:classname] [string:methodname] {\n" +
|
||||
" help Subscribes to all calls of the corresponding debugable method.;\n" +
|
||||
" perm jutils.debugger.subscribe;\n" +
|
||||
" run subscribe classname methodname;\n" +
|
||||
" }\n" +
|
||||
" unsubscribe [string:classname] [string:methodname] {\n" +
|
||||
" help Unsubscribes from all calls of the corresponding debugable method.;\n" +
|
||||
" perm jutils.debugger.subscribe;\n" +
|
||||
" run unsubscribe classname methodname;\n" +
|
||||
" }\n" +
|
||||
"}";
|
||||
}
|
||||
// @format
|
||||
|
||||
@Command(hook = "subscribe")
|
||||
@Debugable
|
||||
public boolean subscribeCommand(CommandSender sender, String classname, String methodname)
|
||||
{
|
||||
if (!enabled)
|
||||
{
|
||||
Utils.sendMessage(sender, null, "Debugger is currently disabled!");
|
||||
return true;
|
||||
}
|
||||
Class<?> clazz = null;
|
||||
try
|
||||
{
|
||||
clazz = Class.forName(classname);
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
Utils.sendErrorMessage(sender, null, "Could not find the class: " + classname);
|
||||
return true;
|
||||
}
|
||||
boolean found = false;
|
||||
for (Method m : clazz.getMethods())
|
||||
{
|
||||
if (m.getName().matches(methodname))
|
||||
{
|
||||
if (m.isAnnotationPresent(Debugable.class))
|
||||
{
|
||||
found = true;
|
||||
if (!subs.containsKey(sender))
|
||||
subs.put(sender, new ArrayList<String>());
|
||||
subs.get(sender).add(classname + "." + methodname);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
Utils.sendErrorMessage(sender, null, "The method you chose either doesn't exist or is not debugable!");
|
||||
return true;
|
||||
}
|
||||
Utils.sendMessage(sender, null, "Successfully subsribed to the method &e" + classname + ":" + methodname, '&');
|
||||
return true;
|
||||
}
|
||||
|
||||
@Command(hook = "unsubscribe")
|
||||
@Debugable
|
||||
public boolean unsubscribeCommand(CommandSender sender, String classname, String methodname)
|
||||
{
|
||||
if (!enabled)
|
||||
{
|
||||
Utils.sendMessage(sender, null, "Debugger is currently disabled!");
|
||||
return true;
|
||||
}
|
||||
if (subs.containsKey(sender))
|
||||
{
|
||||
if (subs.get(sender).remove(classname + "." + methodname))
|
||||
{
|
||||
Utils.sendMessage(sender, null,
|
||||
"Successfully unsubscribed from the method &e" + classname + ":" + methodname, '&');
|
||||
}
|
||||
else
|
||||
{
|
||||
Utils.sendErrorMessage(sender, null, "You were not listening to &e" + classname + ":" + methodname,
|
||||
'&');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Utils.sendErrorMessage(sender, null, "You are not listening to any methods!");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,18 +1,33 @@
|
||||
command modules {
|
||||
[empty] {
|
||||
help Lists all modules. Color indicates status: §aENABLED §cDISABLED;
|
||||
perm moduleloader.modules.list;
|
||||
run list;
|
||||
}
|
||||
list {
|
||||
help Lists all modules. Color indicates status: §aENABLED §cDISABLED;
|
||||
perm jutils.modules.list;
|
||||
perm moduleloader.modules.list;
|
||||
run list;
|
||||
}
|
||||
-v {
|
||||
help Lists all modules. Color indicates status: §aENABLED §cDISABLED;
|
||||
perm moduleloader.modules.list;
|
||||
run listv;
|
||||
}
|
||||
list -v {
|
||||
help Lists all modules. Color indicates status: §aENABLED §cDISABLED;
|
||||
perm moduleloader.modules.list;
|
||||
run listv;
|
||||
}
|
||||
load [string:name...] {
|
||||
help (Re)-Loads a module. WARNING: Handle with care! This has direct affect on code being executed. This command will temporarily halt the main thread until the class loading operation was completed.;
|
||||
perm jtuils.modules.admin;
|
||||
perm moduleloader.modules.admin;
|
||||
run load name;
|
||||
type console;
|
||||
}
|
||||
unload [string:name...] {
|
||||
help Unloads a module. WARNING: Handle with care! This has direct affect on code being executed. This command will temporarily halt the main thread until the class loading operation was completed.;
|
||||
perm jutils.modules.admin;
|
||||
perm moduleloader.modules.admin;
|
||||
run unload name;
|
||||
type console;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package com.redstoner.coremods.moduleLoader;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
@ -12,7 +13,6 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.InvalidConfigurationException;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
@ -23,30 +23,32 @@ import com.nemez.cmdmgr.Command;
|
||||
import com.nemez.cmdmgr.Command.AsyncType;
|
||||
import com.nemez.cmdmgr.CommandManager;
|
||||
import com.redstoner.annotations.AutoRegisterListener;
|
||||
import com.redstoner.annotations.Debugable;
|
||||
import com.redstoner.annotations.Commands;
|
||||
import com.redstoner.annotations.Version;
|
||||
import com.redstoner.coremods.debugger.Debugger;
|
||||
import com.redstoner.exceptions.MissingVersionException;
|
||||
import com.redstoner.misc.Main;
|
||||
import com.redstoner.misc.Utils;
|
||||
import com.redstoner.misc.VersionHelper;
|
||||
import com.redstoner.modules.CoreModule;
|
||||
import com.redstoner.modules.Module;
|
||||
import com.redstoner.modules.ModuleLogger;
|
||||
|
||||
import net.minecraft.server.v1_11_R1.MinecraftServer;
|
||||
import net.nemez.chatapi.click.Message;
|
||||
|
||||
/** The module loader, mother of all modules. Responsible for loading and taking care of all modules.
|
||||
*
|
||||
* @author Pepich */
|
||||
@Version(major = 3, minor = 2, revision = 4, compatible = 2)
|
||||
@Version(major = 4, minor = 0, revision = 1, compatible = 4)
|
||||
public final class ModuleLoader implements CoreModule
|
||||
{
|
||||
private static ModuleLoader instance;
|
||||
private static final HashMap<Module, Boolean> modules = new HashMap<Module, Boolean>();
|
||||
private static final HashMap<Module, Boolean> modules = new HashMap<>();
|
||||
private static URL[] urls;
|
||||
private static URLClassLoader mainLoader;
|
||||
private static HashMap<Module, URLClassLoader> loaders = new HashMap<Module, URLClassLoader>();
|
||||
private static HashMap<Module, URLClassLoader> loaders = new HashMap<>();
|
||||
private static File configFile;
|
||||
private static FileConfiguration config;
|
||||
private static boolean debugMode = false;
|
||||
private static HashMap<Module, ModuleLogger> loggers = new HashMap<>();
|
||||
|
||||
private ModuleLoader()
|
||||
{
|
||||
@ -60,6 +62,7 @@ public final class ModuleLoader implements CoreModule
|
||||
catch (MalformedURLException e)
|
||||
{
|
||||
System.out.println("Sumtin is wong with ya filesüstem m8. Fix eeeet or I won't werk!");
|
||||
Bukkit.getPluginManager().disablePlugin(Main.plugin);
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,6 +70,7 @@ public final class ModuleLoader implements CoreModule
|
||||
{
|
||||
if (instance == null)
|
||||
instance = new ModuleLoader();
|
||||
loggers.put(instance, new ModuleLogger("ModuleLoader"));
|
||||
CommandManager.registerCommand(ModuleLoader.class.getResourceAsStream("ModuleLoader.cmd"), instance,
|
||||
Main.plugin);
|
||||
}
|
||||
@ -99,7 +103,7 @@ public final class ModuleLoader implements CoreModule
|
||||
{
|
||||
e1.printStackTrace();
|
||||
}
|
||||
Utils.error("Invalid config file! Creating new, blank file!");
|
||||
instance.getLogger().error("Invalid config file! Creating new, blank file!");
|
||||
}
|
||||
List<String> coremods = config.getStringList("coremods");
|
||||
if (coremods == null || coremods.isEmpty())
|
||||
@ -129,60 +133,36 @@ public final class ModuleLoader implements CoreModule
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (!config.contains("debugMode"))
|
||||
{
|
||||
config.set("debugMode", false);
|
||||
try
|
||||
{
|
||||
config.save(configFile);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
debugMode = config.getBoolean("debugMode");
|
||||
for (String s : coremods)
|
||||
if (!s.startsWith("#"))
|
||||
ModuleLoader.addDynamicModule(s);
|
||||
enableModules();
|
||||
if (!ModuleLoader.addDynamicModule(s))
|
||||
{
|
||||
instance.getLogger().error("Couldn't autocomplete path for module name: " + s
|
||||
+ "! If you're on a case sensitive filesystem, please take note that case correction does not work. Make sure that the classname has proper capitalisation.");
|
||||
|
||||
}
|
||||
for (String s : autoload)
|
||||
if (!s.startsWith("#"))
|
||||
ModuleLoader.addDynamicModule(s);
|
||||
}
|
||||
if (!ModuleLoader.addDynamicModule(s))
|
||||
{
|
||||
instance.getLogger().error("Couldn't autocomplete path for module name: " + s
|
||||
+ "! If you're on a case sensitive filesystem, please take note that case correction does not work. Make sure that the classname has proper capitalisation.");
|
||||
|
||||
/** This method will add a module to the module list, without enabling it.</br>
|
||||
* This method is deprecated, use addDynamicModule(String name) instead. When using this method, dynamic reloading of the module will not be supported.
|
||||
*
|
||||
* @param clazz The class of the module to be added. */
|
||||
@Debugable
|
||||
@Deprecated
|
||||
public static final void addModule(Class<? extends Module> clazz)
|
||||
{
|
||||
Debugger.notifyMethod(clazz);
|
||||
try
|
||||
{
|
||||
Module module = clazz.newInstance();
|
||||
modules.put(module, false);
|
||||
}
|
||||
catch (InstantiationException | IllegalAccessException e)
|
||||
{
|
||||
Utils.error("Could not add " + clazz.getName() + " to the list, constructor not accessible.");
|
||||
}
|
||||
}
|
||||
|
||||
@Debugable
|
||||
private static final void addLoadedModule(Module m)
|
||||
{
|
||||
Debugger.notifyMethod(m);
|
||||
if (modules.containsKey(m))
|
||||
if (modules.get(m))
|
||||
{
|
||||
Utils.error(
|
||||
"Module m was already loaded and enabled. Disable the module before attempting to reload it.");
|
||||
return;
|
||||
}
|
||||
modules.put(m, false);
|
||||
}
|
||||
|
||||
/** Call this to enable all not-yet enabled modules that are known to the loader. */
|
||||
@Debugable
|
||||
public static final void enableModules()
|
||||
{
|
||||
Debugger.notifyMethod();
|
||||
for (Module module : modules.keySet())
|
||||
{
|
||||
if (modules.get(module))
|
||||
continue;
|
||||
enableLoadedModule(module);
|
||||
}
|
||||
}
|
||||
updateConfig();
|
||||
}
|
||||
|
||||
/** This method enables a specific module. If no module with that name is known to the loader yet it will be added to the list.</br>
|
||||
@ -190,18 +170,16 @@ public final class ModuleLoader implements CoreModule
|
||||
*
|
||||
* @param clazz The class of the module to be enabled.
|
||||
* @return true, when the module was successfully enabled. */
|
||||
@Debugable
|
||||
@Deprecated
|
||||
public static final boolean enableModule(Class<? extends Module> clazz)
|
||||
{
|
||||
Debugger.notifyMethod(clazz);
|
||||
for (Module module : modules.keySet())
|
||||
{
|
||||
if (module.getClass().equals(clazz))
|
||||
{
|
||||
if (modules.get(module))
|
||||
{
|
||||
Utils.info("Module was already enabled! Ignoring module.!");
|
||||
instance.getLogger().info("Module was already enabled! Ignoring module.!");
|
||||
return true;
|
||||
}
|
||||
if (module.onEnable())
|
||||
@ -211,14 +189,14 @@ public final class ModuleLoader implements CoreModule
|
||||
{
|
||||
Bukkit.getPluginManager().registerEvents((Listener) module, Main.plugin);
|
||||
}
|
||||
Utils.info("Enabled module " + module.getClass().getName());
|
||||
Utils.info("Loaded module " + module.getClass().getName());
|
||||
instance.getLogger().info("Enabled module " + module.getClass().getName());
|
||||
instance.getLogger().info("Loaded module " + module.getClass().getName());
|
||||
modules.put(module, true);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Utils.error("Failed to enable module " + module.getClass().getName());
|
||||
instance.getLogger().error("Failed to enable module " + module.getClass().getName());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -233,49 +211,72 @@ public final class ModuleLoader implements CoreModule
|
||||
{
|
||||
Bukkit.getPluginManager().registerEvents((Listener) m, Main.plugin);
|
||||
}
|
||||
Utils.info("Loaded and enabled module " + m.getClass().getName());
|
||||
Utils.info("Loaded module " + m.getClass().getName());
|
||||
instance.getLogger().info("Loaded and enabled module " + m.getClass().getName());
|
||||
instance.getLogger().info("Loaded module " + m.getClass().getName());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Utils.error("Failed to enable module " + m.getClass().getName());
|
||||
instance.getLogger().error("Failed to enable module " + m.getClass().getName());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (InstantiationException | IllegalAccessException e)
|
||||
{
|
||||
Utils.error("Could not add " + clazz.getName() + " to the list, constructor not accessible.");
|
||||
instance.getLogger()
|
||||
.error("Could not add " + clazz.getName() + " to the list, constructor not accessible.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static final void enableLoadedModule(Module module)
|
||||
private static final void enableLoadedModule(Module module, Version oldVersion)
|
||||
{
|
||||
try
|
||||
{
|
||||
loggers.put(module, new ModuleLogger(module.getClass().getSimpleName()));
|
||||
if (module.onEnable())
|
||||
{
|
||||
if (VersionHelper.isCompatible(VersionHelper.create(2, 0, 0, -1), module.getClass()))
|
||||
CommandManager.registerCommand(module.getCommandString(), module, Main.plugin);
|
||||
modules.put(module, true);
|
||||
if (VersionHelper.isCompatible(VersionHelper.create(3, 0, 0, 3), module.getClass()))
|
||||
{
|
||||
if (VersionHelper.getString(oldVersion).equals("0.0.0.0"))
|
||||
module.firstLoad();
|
||||
else if (!VersionHelper.getVersion(module.getClass()).equals(VersionHelper.getString(oldVersion)))
|
||||
module.migrate(oldVersion);
|
||||
if (VersionHelper.isCompatible(VersionHelper.create(4, 0, 0, 3), module.getClass()))
|
||||
module.postEnable();
|
||||
}
|
||||
Utils.info("Loaded module " + module.getClass().getName());
|
||||
if (module.getClass().isAnnotationPresent(AutoRegisterListener.class) && (module instanceof Listener))
|
||||
if (VersionHelper.isCompatible(VersionHelper.create(4, 0, 0, 4), module.getClass()))
|
||||
{
|
||||
Bukkit.getPluginManager().registerEvents((Listener) module, Main.plugin);
|
||||
Commands ann = module.getClass().getAnnotation(Commands.class);
|
||||
if (ann != null)
|
||||
{
|
||||
switch (ann.value())
|
||||
{
|
||||
case File:
|
||||
File f = new File("plugins/ModuleLoader/classes/"
|
||||
+ module.getClass().getName().replace(".", "/") + ".cmd");
|
||||
CommandManager.registerCommand(f, module, Main.plugin);
|
||||
break;
|
||||
case Stream:
|
||||
InputStream stream = module.getClass()
|
||||
.getResourceAsStream(module.getClass().getSimpleName() + ".cmd");
|
||||
CommandManager.registerCommand(stream, module, Main.plugin);
|
||||
case String:
|
||||
CommandManager.registerCommand(module.getCommandString(), module, Main.plugin);
|
||||
break;
|
||||
case None:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
instance.getLogger().info("Loaded module " + module.getClass().getName());
|
||||
if (module.getClass().isAnnotationPresent(AutoRegisterListener.class) && (module instanceof Listener))
|
||||
Bukkit.getPluginManager().registerEvents((Listener) module, Main.plugin);
|
||||
}
|
||||
else
|
||||
Utils.error("Failed to load module " + module.getClass().getName());
|
||||
instance.getLogger().error("Failed to load module " + module.getClass().getName());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utils.error("Failed to load module " + module.getClass().getName());
|
||||
instance.getLogger().error("Failed to load module " + module.getClass().getName());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@ -287,19 +288,52 @@ public final class ModuleLoader implements CoreModule
|
||||
@Command(hook = "list", async = AsyncType.ALWAYS)
|
||||
public boolean listModulesCommand(CommandSender sender)
|
||||
{
|
||||
Utils.sendModuleHeader(sender);
|
||||
StringBuilder sb = new StringBuilder("Modules:\n");
|
||||
for (Module module : modules.keySet())
|
||||
Message m = new Message(sender, null);
|
||||
m.appendText(getLogger().getHeader());
|
||||
m.appendText("§2Modules:\n&e");
|
||||
Module[] modules = ModuleLoader.modules.keySet().toArray(new Module[] {});
|
||||
for (int i = 0; i < modules.length; i++)
|
||||
{
|
||||
Module module = modules[i];
|
||||
String[] classPath = module.getClass().getName().split("\\.");
|
||||
String classname = classPath[classPath.length - 1];
|
||||
sb.append(modules.get(module) ? "&a" : "&c");
|
||||
sb.append(classname);
|
||||
sb.append(", ");
|
||||
m.appendText((ModuleLoader.modules.get(module) ? "§a" : "§c") + classname);
|
||||
if (i + 1 < modules.length)
|
||||
m.appendText("§7, ");
|
||||
}
|
||||
sb.delete(sb.length() - 2, sb.length());
|
||||
Utils.sendMessage(sender, " §e", sb.toString(), '&');
|
||||
Utils.sendMessage(sender, " §7", "For more detailed information, consult the debugger.");
|
||||
m.send();
|
||||
return true;
|
||||
}
|
||||
|
||||
/** This method lists all modules to the specified CommandSender. The modules will be color coded correspondingly to their enabled status.
|
||||
*
|
||||
* @param sender The person to send the info to, usually the issuer of the command or the console sender.
|
||||
* @return true. */
|
||||
@Command(hook = "listv", async = AsyncType.ALWAYS)
|
||||
public boolean listModulesCommandVersion(CommandSender sender)
|
||||
{
|
||||
Message m = new Message(sender, null);
|
||||
m.appendText(getLogger().getHeader());
|
||||
m.appendText("§2Modules:\n&e");
|
||||
Module[] modules = ModuleLoader.modules.keySet().toArray(new Module[] {});
|
||||
for (int i = 0; i < modules.length; i++)
|
||||
{
|
||||
Module module = modules[i];
|
||||
String[] classPath = module.getClass().getName().split("\\.");
|
||||
String classname = classPath[classPath.length - 1];
|
||||
try
|
||||
{
|
||||
m.appendText((ModuleLoader.modules.get(module) ? "§a" : "§c") + classname + "§e("
|
||||
+ VersionHelper.getVersion(module.getClass()) + ")");
|
||||
}
|
||||
catch (MissingVersionException e)
|
||||
{
|
||||
m.appendText((ModuleLoader.modules.get(module) ? "§a" : "§c") + classname + "§c" + "(Unknown Version)");
|
||||
}
|
||||
if (i + 1 < modules.length)
|
||||
m.appendText("§7, ");
|
||||
}
|
||||
m.send();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -339,7 +373,7 @@ public final class ModuleLoader implements CoreModule
|
||||
|
||||
private static ArrayList<String> getAllHooks(Module module)
|
||||
{
|
||||
ArrayList<String> commands = new ArrayList<String>();
|
||||
ArrayList<String> commands = new ArrayList<>();
|
||||
for (Method m : module.getClass().getMethods())
|
||||
{
|
||||
Command cmd = m.getDeclaredAnnotation(Command.class);
|
||||
@ -353,47 +387,55 @@ public final class ModuleLoader implements CoreModule
|
||||
@Command(hook = "load")
|
||||
public boolean loadModule(CommandSender sender, String name)
|
||||
{
|
||||
addDynamicModule(name);
|
||||
if (!addDynamicModule(name))
|
||||
{
|
||||
instance.getLogger().message(sender, true, "Couldn't autocomplete path for module name: " + name
|
||||
+ "! If you're on a case sensitive filesystem, please take note that case correction does not work. Make sure that the classname has proper capitalisation.");
|
||||
|
||||
}
|
||||
updateConfig();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Command(hook = "unload")
|
||||
public boolean unloadModule(CommandSender sender, String name)
|
||||
{
|
||||
removeDynamicModule(name);
|
||||
if (!removeDynamicModule(name))
|
||||
instance.getLogger().error("Couldn't find module! Couldn't disable nonexisting module!");
|
||||
return true;
|
||||
}
|
||||
|
||||
public static final void addDynamicModule(String name)
|
||||
public static final boolean addDynamicModule(String raw_name)
|
||||
{
|
||||
Object[] status = getServerStatus();
|
||||
String[] raw = raw_name.split(" ");
|
||||
String name = raw[0];
|
||||
Version oldVersion;
|
||||
if (raw.length > 1)
|
||||
oldVersion = VersionHelper.getVersion(raw[1]);
|
||||
else
|
||||
oldVersion = VersionHelper.create(0, 0, 0, 0);
|
||||
for (Module m : modules.keySet())
|
||||
{
|
||||
if (m.getClass().getName().equals(name))
|
||||
{
|
||||
Utils.info(
|
||||
instance.getLogger().info(
|
||||
"Found existing module, attempting override. WARNING! This operation will halt the main thread until it is completed.");
|
||||
Utils.info("Current server status:");
|
||||
Utils.info("Current system time: " + status[0]);
|
||||
Utils.info("Current tick: " + status[1]);
|
||||
Utils.info("Last TPS: " + status[2]);
|
||||
Utils.info("Entity count: " + status[3]);
|
||||
Utils.info("Player count: " + status[4]);
|
||||
Utils.info("Attempting to load new class definition before disabling and removing the old module:");
|
||||
instance.getLogger()
|
||||
.info("Attempting to load new class definition before disabling and removing the old module");
|
||||
boolean differs = false;
|
||||
Utils.info("Old class definition: Class@" + m.getClass().hashCode());
|
||||
instance.getLogger().info("Old class definition: Class@" + m.getClass().hashCode());
|
||||
ClassLoader delegateParent = mainLoader.getParent();
|
||||
Class<?> newClass = null;
|
||||
URLClassLoader cl = new URLClassLoader(urls, delegateParent);
|
||||
try
|
||||
{
|
||||
newClass = cl.loadClass(m.getClass().getName());
|
||||
Utils.info("Found new class definition: Class@" + newClass.hashCode());
|
||||
instance.getLogger().info("Found new class definition: Class@" + newClass.hashCode());
|
||||
differs = m.getClass() != newClass;
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
Utils.error("Could not find a class definition, aborting now!");
|
||||
instance.getLogger().error("Could not find a class definition, aborting now!");
|
||||
e.printStackTrace();
|
||||
try
|
||||
{
|
||||
@ -403,23 +445,31 @@ public final class ModuleLoader implements CoreModule
|
||||
{
|
||||
e1.printStackTrace();
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (!differs)
|
||||
{
|
||||
Utils.warn("New class definition equals old definition, are you sure you did everything right?");
|
||||
Utils.info("Aborting now...");
|
||||
try
|
||||
if (!debugMode)
|
||||
{
|
||||
cl.close();
|
||||
instance.getLogger().warn(
|
||||
"New class definition equals old definition, are you sure you did everything right?");
|
||||
instance.getLogger().info("Aborting now...");
|
||||
try
|
||||
{
|
||||
cl.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return;
|
||||
else
|
||||
instance.getLogger().warn(
|
||||
"New class definition equals old definition, but debugMode is enabled. Loading anyways.");
|
||||
}
|
||||
Utils.info("Found new class definition, attempting to instantiate:");
|
||||
else
|
||||
instance.getLogger().info("Found new class definition, attempting to instantiate:");
|
||||
Module module = null;
|
||||
try
|
||||
{
|
||||
@ -427,7 +477,7 @@ public final class ModuleLoader implements CoreModule
|
||||
}
|
||||
catch (InstantiationException | IllegalAccessException e)
|
||||
{
|
||||
Utils.error("Could not instantiate the module, aborting!");
|
||||
instance.getLogger().error("Could not instantiate the module, aborting!");
|
||||
e.printStackTrace();
|
||||
try
|
||||
{
|
||||
@ -437,29 +487,41 @@ public final class ModuleLoader implements CoreModule
|
||||
{
|
||||
e1.printStackTrace();
|
||||
}
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
Utils.info("Instantiated new class definition, checking versions:");
|
||||
Version oldVersion = m.getClass().getAnnotation(Version.class);
|
||||
Utils.info("Current version: " + VersionHelper.getString(oldVersion));
|
||||
instance.getLogger().info("Instantiated new class definition, checking versions");
|
||||
oldVersion = m.getClass().getAnnotation(Version.class);
|
||||
instance.getLogger().info("Current version: " + VersionHelper.getString(oldVersion));
|
||||
Version newVersion = module.getClass().getAnnotation(Version.class);
|
||||
Utils.info("Version of remote class: " + VersionHelper.getString(newVersion));
|
||||
instance.getLogger().info("Version of remote class: " + VersionHelper.getString(newVersion));
|
||||
if (oldVersion.equals(newVersion))
|
||||
{
|
||||
Utils.error("Detected equal module versions, aborting now...");
|
||||
try
|
||||
if (!debugMode)
|
||||
{
|
||||
cl.close();
|
||||
instance.getLogger().error("Detected equal module versions, " + (debugMode
|
||||
? " aborting now... Set debugMode to true in your config if you want to continue!"
|
||||
: " continueing anyways."));
|
||||
if (!debugMode)
|
||||
{
|
||||
try
|
||||
{
|
||||
cl.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return;
|
||||
else
|
||||
instance.getLogger()
|
||||
.warn("New version equals old version, but debugMode is enabled. Loading anyways.");
|
||||
}
|
||||
Utils.info("Versions differ, disabling old module:");
|
||||
else
|
||||
instance.getLogger().info("Versions differ, disabling old module");
|
||||
disableModule(m);
|
||||
Utils.info("Disabled module, overriding the implementation:");
|
||||
instance.getLogger().info("Disabled module, overriding the implementation");
|
||||
modules.remove(m);
|
||||
try
|
||||
{
|
||||
@ -472,93 +534,115 @@ public final class ModuleLoader implements CoreModule
|
||||
}
|
||||
modules.put(module, false);
|
||||
loaders.put(module, cl);
|
||||
Utils.info("Successfully updated class definition. Enabling new implementation:");
|
||||
enableLoadedModule(module);
|
||||
Object[] newStatus = getServerStatus();
|
||||
Utils.info("Task complete! Took " + ((long) newStatus[0] - (long) status[0]) + "ms to finish!");
|
||||
Utils.info("Current server status:");
|
||||
Utils.info("Current system time: " + newStatus[0]);
|
||||
Utils.info("Current tick: " + newStatus[1]);
|
||||
Utils.info("Last TPS: " + newStatus[2]);
|
||||
Utils.info("Entity count: " + newStatus[3]);
|
||||
Utils.info("Player count: " + newStatus[4]);
|
||||
return;
|
||||
instance.getLogger().info("Successfully updated class definition. Enabling new implementation:");
|
||||
enableLoadedModule(module, oldVersion);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
ClassLoader delegateParent = mainLoader.getParent();
|
||||
URLClassLoader cl = new URLClassLoader(urls, delegateParent);
|
||||
try
|
||||
{
|
||||
Class<?> clazz = mainLoader.loadClass(name);
|
||||
Class<?> clazz = cl.loadClass(name);
|
||||
Module module = (Module) clazz.newInstance();
|
||||
addLoadedModule(module);
|
||||
enableLoadedModule(module);
|
||||
modules.put(module, false);
|
||||
loaders.put(module, cl);
|
||||
enableLoadedModule(module, oldVersion);
|
||||
return true;
|
||||
}
|
||||
catch (ClassNotFoundException | InstantiationException | IllegalAccessException e)
|
||||
catch (NoClassDefFoundError | ClassNotFoundException | InstantiationException | IllegalAccessException e)
|
||||
{
|
||||
if (!name.startsWith("com.redstoner.modules."))
|
||||
try
|
||||
{
|
||||
Utils.warn(
|
||||
"Couldn't find class definition, suspecting missing path. Autocompleting path, trying again.");
|
||||
addDynamicModule("com.redstoner.modules." + name);
|
||||
cl.close();
|
||||
}
|
||||
catch (IOException e1)
|
||||
{}
|
||||
if (e instanceof NoClassDefFoundError)
|
||||
{
|
||||
NoClassDefFoundError exception = (NoClassDefFoundError) e;
|
||||
String[] exMessage = exception.getMessage().split(" ");
|
||||
String moduleName = exMessage[exMessage.length - 1]
|
||||
.substring(0, exMessage[exMessage.length - 1].length()
|
||||
- (exMessage[exMessage.length - 1].endsWith(")") ? 1 : 0))
|
||||
.replace("/", ".");
|
||||
if (!moduleName.equalsIgnoreCase(name))
|
||||
{
|
||||
instance.getLogger()
|
||||
.error("Class &e" + moduleName + "&r couldn't be found! Suspecting a missing dependency!");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
instance.getLogger().warn(
|
||||
"Couldn't find class definition, attempting to get proper classname from thrown Exception.");
|
||||
if (addDynamicModule(moduleName))
|
||||
return true;
|
||||
}
|
||||
if (name.endsWith(".class"))
|
||||
{
|
||||
instance.getLogger().warn(
|
||||
"Couldn't find class definition, but path ends with .class -> Attempting again with removed file suffix.");
|
||||
if (addDynamicModule(name.replaceAll(".class$", "")))
|
||||
return true;
|
||||
}
|
||||
if (!name.contains("."))
|
||||
{
|
||||
instance.getLogger().warn(
|
||||
"Couldn't find class definition, suspecting incomplete path. Attempting autocompletion of path by adding a package name and trying again.");
|
||||
if (addDynamicModule(name.toLowerCase() + "." + name))
|
||||
return true;
|
||||
}
|
||||
if (!name.startsWith("com.redstoner.modules.") && name.contains("."))
|
||||
{
|
||||
instance.getLogger().warn(
|
||||
"Couldn't find class definition, suspecting incomplete path. Attempting autocompletion of package name and trying again.");
|
||||
if (addDynamicModule("com.redstoner.modules." + name))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static final void removeDynamicModule(String name)
|
||||
public static final boolean removeDynamicModule(String name)
|
||||
{
|
||||
Object[] status = getServerStatus();
|
||||
for (Module m : modules.keySet())
|
||||
{
|
||||
if (m.getClass().getName().equals(name))
|
||||
{
|
||||
Utils.info(
|
||||
instance.getLogger().info(
|
||||
"Found existing module, attempting unload. WARNING! This operation will halt the main thread until it is completed.");
|
||||
Utils.info("Current server status:");
|
||||
Utils.info("Current system time: " + status[0]);
|
||||
Utils.info("Current tick: " + status[1]);
|
||||
Utils.info("Last TPS: " + status[2]);
|
||||
Utils.info("Entity count: " + status[3]);
|
||||
Utils.info("Player count: " + status[4]);
|
||||
Utils.info("Attempting to disable module properly:");
|
||||
instance.getLogger().info("Attempting to disable module properly:");
|
||||
disableModule(m);
|
||||
modules.remove(m);
|
||||
Utils.info("Disabled module, overriding the implementation:");
|
||||
Object[] newStatus = getServerStatus();
|
||||
Utils.info("Task complete! Took " + ((long) newStatus[0] - (long) status[0]) + "ms to finish!");
|
||||
Utils.info("Current server status:");
|
||||
Utils.info("Current system time: " + newStatus[0]);
|
||||
Utils.info("Current tick: " + newStatus[1]);
|
||||
Utils.info("Last TPS: " + newStatus[2]);
|
||||
Utils.info("Entity count: " + newStatus[3]);
|
||||
Utils.info("Player count: " + newStatus[4]);
|
||||
return;
|
||||
instance.getLogger().info("Disabled module.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!name.startsWith("com.redstoner.modules."))
|
||||
{
|
||||
Utils.warn("Couldn't find class definition, suspecting missing path. Autocompleting path, trying again.");
|
||||
removeDynamicModule("com.redstoner.modules." + name);
|
||||
if (name.endsWith(".class"))
|
||||
{
|
||||
instance.getLogger().warn(
|
||||
"Couldn't find class definition, but path ends with .class -> Attempting again with removed file suffix.");
|
||||
if (removeDynamicModule(name.replaceAll(".class$", "")))
|
||||
return true;
|
||||
}
|
||||
if (!name.contains("."))
|
||||
{
|
||||
instance.getLogger().warn(
|
||||
"Couldn't find class definition, suspecting incomplete path. Attempting autocompletion of path by adding a package name and trying again.");
|
||||
if (removeDynamicModule(name.toLowerCase() + "." + name))
|
||||
return true;
|
||||
}
|
||||
if (!name.startsWith("com.redstoner.modules."))
|
||||
{
|
||||
instance.getLogger().warn(
|
||||
"Couldn't find class definition, suspecting incomplete path. Attempting autocompletion of package name and trying again.");
|
||||
if (removeDynamicModule("com.redstoner.modules." + name))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
Utils.error("Couldn't find module! Couldn't ");
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static final Object[] getServerStatus()
|
||||
{
|
||||
final Object[] status = new Object[5];
|
||||
status[0] = System.currentTimeMillis();
|
||||
status[1] = MinecraftServer.currentTick;
|
||||
status[2] = MinecraftServer.getServer().recentTps[0];
|
||||
int i = 0;
|
||||
for (World w : Bukkit.getWorlds())
|
||||
{
|
||||
i += w.getEntities().size();
|
||||
}
|
||||
status[3] = i;
|
||||
status[4] = Bukkit.getOnlinePlayers().size();
|
||||
return status;
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Finds a module by name for other modules to reference it.
|
||||
@ -568,8 +652,85 @@ public final class ModuleLoader implements CoreModule
|
||||
public static Module getModule(String name)
|
||||
{
|
||||
for (Module m : modules.keySet())
|
||||
if (m.getClass().getSimpleName().equals(name) || m.getClass().getName().equals(name))
|
||||
if (m.getClass().getSimpleName().equalsIgnoreCase(name) || m.getClass().getName().equalsIgnoreCase(name))
|
||||
return m;
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Finds a module by name for other modules to reference it.
|
||||
*
|
||||
* @param name the name of the module. Use the full path if you are not sure about the module's SimpleClassName being unique.
|
||||
* @return the instance of the module or @null it none could be found */
|
||||
public static boolean exists(String name)
|
||||
{
|
||||
for (Module m : modules.keySet())
|
||||
if (m.getClass().getSimpleName().equals(name) || m.getClass().getName().equals(name))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static ModuleLogger getModuleLogger(Module module)
|
||||
{
|
||||
return loggers.get(module);
|
||||
}
|
||||
|
||||
public static void updateConfig()
|
||||
{
|
||||
List<String> coremods = config.getStringList("coremods");
|
||||
ArrayList<String> new_coremods = new ArrayList<>();
|
||||
List<String> autoload = config.getStringList("autoload");
|
||||
ArrayList<String> new_autoload = new ArrayList<>();
|
||||
|
||||
for (String s : coremods)
|
||||
{
|
||||
if (s.startsWith("#"))
|
||||
{
|
||||
new_coremods.add(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
s = s.split(" ")[0];
|
||||
try
|
||||
{
|
||||
new_coremods.add(getModule(s).getClass().getName() + " "
|
||||
+ VersionHelper.getVersion(getModule(s).getClass()));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
new_coremods.add(s + " " + VersionHelper.getString(VersionHelper.create(0, 0, 0, 0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (String s : autoload)
|
||||
{
|
||||
if (s.startsWith("#"))
|
||||
{
|
||||
new_autoload.add(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
s = s.split(" ")[0];
|
||||
try
|
||||
{
|
||||
new_autoload.add(getModule(s).getClass().getName() + " "
|
||||
+ VersionHelper.getVersion(getModule(s).getClass()));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
new_autoload.add(s + " " + VersionHelper.getString(VersionHelper.create(0, 0, 0, 0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config.set("coremods", new_coremods);
|
||||
config.set("autoload", new_autoload);
|
||||
try
|
||||
{
|
||||
config.save(configFile);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
13
src/com/redstoner/misc/CommandHolderType.java
Normal file
13
src/com/redstoner/misc/CommandHolderType.java
Normal file
@ -0,0 +1,13 @@
|
||||
package com.redstoner.misc;
|
||||
|
||||
import com.redstoner.annotations.Version;
|
||||
|
||||
/** @author Pepich */
|
||||
@Version(major = 4, minor = 0, revision = 0, compatible = -1)
|
||||
public enum CommandHolderType
|
||||
{
|
||||
Stream,
|
||||
File,
|
||||
String,
|
||||
None
|
||||
}
|
@ -55,25 +55,35 @@ public class JsonManager
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (destination.exists())
|
||||
destination.delete();
|
||||
else if (!destination.getParentFile().exists())
|
||||
destination.getParentFile().mkdirs();
|
||||
try
|
||||
{
|
||||
destination.createNewFile();
|
||||
FileWriter writer = new FileWriter(destination);
|
||||
object.writeJSONString(writer);
|
||||
writer.flush();
|
||||
writer.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{}
|
||||
saveSync(object, destination);
|
||||
}
|
||||
});
|
||||
t.start();
|
||||
}
|
||||
|
||||
/** Saves a JSONObject to a file. Will create the necessary FileStructure like folders and the file itself.</br>
|
||||
* Note that this operation will be run on the same thread that you are calling it from!
|
||||
*
|
||||
* @param object the JSONObject to save.
|
||||
* @param destination the file to write to. */
|
||||
public static void saveSync(JSONObject object, File destination)
|
||||
{
|
||||
if (destination.exists())
|
||||
destination.delete();
|
||||
else if (!destination.getParentFile().exists())
|
||||
destination.getParentFile().mkdirs();
|
||||
try
|
||||
{
|
||||
destination.createNewFile();
|
||||
FileWriter writer = new FileWriter(destination);
|
||||
object.writeJSONString(writer);
|
||||
writer.flush();
|
||||
writer.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{}
|
||||
}
|
||||
|
||||
/** Loads a JSONArray from a file.
|
||||
*
|
||||
* @param source the file to load from.
|
||||
@ -106,22 +116,32 @@ public class JsonManager
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (destination.exists())
|
||||
destination.delete();
|
||||
else if (!destination.getParentFile().exists())
|
||||
destination.getParentFile().mkdirs();
|
||||
try
|
||||
{
|
||||
destination.createNewFile();
|
||||
FileWriter writer = new FileWriter(destination);
|
||||
array.writeJSONString(writer);
|
||||
writer.flush();
|
||||
writer.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{}
|
||||
saveSync(array, destination);
|
||||
}
|
||||
});
|
||||
t.start();
|
||||
}
|
||||
|
||||
/** Saves a JSONArray to a file. Will create the necessary FileStructure like folders and the file itself.</br>
|
||||
* Note that this operation will be run on the same thread that you are calling it from!
|
||||
*
|
||||
* @param object the JSONArray to save.
|
||||
* @param destination the file to write to. */
|
||||
public static void saveSync(JSONArray array, File destination)
|
||||
{
|
||||
if (destination.exists())
|
||||
destination.delete();
|
||||
else if (!destination.getParentFile().exists())
|
||||
destination.getParentFile().mkdirs();
|
||||
try
|
||||
{
|
||||
destination.createNewFile();
|
||||
FileWriter writer = new FileWriter(destination);
|
||||
array.writeJSONString(writer);
|
||||
writer.flush();
|
||||
writer.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{}
|
||||
}
|
||||
}
|
||||
|
@ -3,14 +3,15 @@ package com.redstoner.misc;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import com.redstoner.annotations.Version;
|
||||
import com.redstoner.coremods.debugger.Debugger;
|
||||
import com.redstoner.coremods.moduleLoader.ModuleLoader;
|
||||
import com.redstoner.misc.mysql.MysqlHandler;
|
||||
|
||||
import net.nemez.chatapi.ChatAPI;
|
||||
|
||||
/** Main class. Duh.
|
||||
*
|
||||
* @author Pepich */
|
||||
@Version(major = 3, minor = 2, revision = 0, compatible = -1)
|
||||
@Version(major = 4, minor = 0, revision = 0, compatible = -1)
|
||||
public class Main extends JavaPlugin
|
||||
{
|
||||
public static JavaPlugin plugin;
|
||||
@ -19,12 +20,12 @@ public class Main extends JavaPlugin
|
||||
public void onEnable()
|
||||
{
|
||||
plugin = this;
|
||||
Debugger.init();
|
||||
ChatAPI.initialize(this);
|
||||
// Configger.init();
|
||||
MysqlHandler.init();
|
||||
ModuleLoader.init();
|
||||
// Load modules from config
|
||||
ModuleLoader.loadFromConfig();
|
||||
// And enable them
|
||||
ModuleLoader.enableModules();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,116 +1,29 @@
|
||||
package com.redstoner.misc;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import com.redstoner.annotations.Debugable;
|
||||
import com.redstoner.annotations.Version;
|
||||
import com.redstoner.coremods.debugger.Debugger;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
|
||||
/** The utils class containing utility functions. Those include but are not limited to sending formatted messages, broadcasts and more.
|
||||
*
|
||||
* @author Pepich */
|
||||
@Version(major = 1, minor = 3, revision = 7, compatible = 1)
|
||||
@Version(major = 4, minor = 0, revision = 1, compatible = 1)
|
||||
public final class Utils
|
||||
{
|
||||
/** The SimpleDateFormat used for getting the current date. */
|
||||
/** The @SimpleDateFormat used for getting the current date. */
|
||||
public static SimpleDateFormat dateFormat = new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]");
|
||||
|
||||
/** Hidden constructor. Do not instantiate UTILS classes! :) */
|
||||
private Utils()
|
||||
{}
|
||||
|
||||
/** This will send a message to the specified recipient. It will generate the module prefix if you want it to.
|
||||
*
|
||||
* @param recipient Whom to sent the message to.
|
||||
* @param prefix The prefix for the message. If null, the default prefix will be used: &8[&2MODULE&8]
|
||||
* @param message The message to sent. Will default to &7 (light_grey) if not specified otherwise. */
|
||||
@Debugable
|
||||
public static void sendMessage(CommandSender recipient, String prefix, String message)
|
||||
{
|
||||
Debugger.notifyMethod((Object) recipient, prefix, message);
|
||||
if (prefix == null)
|
||||
prefix = "§8[§2" + getCaller() + "§8]: ";
|
||||
recipient.sendMessage(prefix + "§7" + message);
|
||||
}
|
||||
|
||||
/** This will send a message to the specified recipient. It will generate the module prefix if you want it to. Also, this will be logged to console as a warning.
|
||||
*
|
||||
* @param recipient Whom to sent the message to.
|
||||
* @param prefix The prefix for the message. If null, the default prefix will be used: &8[&cMODULE&8]
|
||||
* @param message The message to sent. Will default to &7 (light_grey) if not specified otherwise. */
|
||||
@Debugable
|
||||
public static void sendErrorMessage(CommandSender recipient, String prefix, String message)
|
||||
{
|
||||
Debugger.notifyMethod((Object) recipient, prefix, message);
|
||||
if (prefix == null)
|
||||
prefix = "§8[§c" + getCaller() + "§8]: ";
|
||||
recipient.sendMessage(prefix + "§7" + message);
|
||||
}
|
||||
|
||||
/** Invokes sendMessage. This method will additionally translate alternate color codes for you.
|
||||
*
|
||||
* @param recipient Whom to sent the message to.
|
||||
* @param prefix The prefix for the message. If null, the default prefix will be used: &8[&cMODULE&8]
|
||||
* @param message The message to sent. Will default to &7 (light_grey) if not specified otherwise.
|
||||
* @param alternateColorCode The alternate color code indicator to use. If set to '&' then "&7" would be translated to "§7". Works with any char. */
|
||||
public static void sendMessage(CommandSender recipient, String prefix, String message, char alternateColorCode)
|
||||
{
|
||||
if (prefix == null)
|
||||
prefix = "§8[§2" + getCaller() + "§8]: ";
|
||||
sendMessage(recipient, ChatColor.translateAlternateColorCodes(alternateColorCode, prefix).replace("&§", "&"),
|
||||
ChatColor.translateAlternateColorCodes(alternateColorCode, message).replace("&§", "&"));
|
||||
}
|
||||
|
||||
/** Invokes sendErrorMessage. This method will additionally translate alternate color codes for you.
|
||||
*
|
||||
* @param recipient Whom to sent the message to.
|
||||
* @param prefix The prefix for the message. If null, the default prefix will be used: &8[&cMODULE&8]
|
||||
* @param message The message to sent. Will default to &7 (light_grey) if not specified otherwise.
|
||||
* @param alternateColorCode The alternate color code indicator to use. If set to '&' then "&7" would be translated to "§7". Works with any char. */
|
||||
public static void sendErrorMessage(CommandSender recipient, String prefix, String message, char alternateColorCode)
|
||||
{
|
||||
if (prefix == null)
|
||||
prefix = "§8[§c" + getCaller() + "§8]: ";
|
||||
sendErrorMessage(recipient,
|
||||
ChatColor.translateAlternateColorCodes(alternateColorCode, prefix).replace("&§", "&"),
|
||||
ChatColor.translateAlternateColorCodes(alternateColorCode, message).replace("&§", "&"));
|
||||
}
|
||||
|
||||
/** This method broadcasts a message to all players (and console) that are allowed by the filter. Set the filter to NULL to broadcast to everyone.</br>
|
||||
* This will not be logged to console except when you return true in the filter.
|
||||
*
|
||||
* @param message the message to be sent around
|
||||
* @param filter the BroadcastFilter to be applied.</br>
|
||||
* Write a class implementing the interface and pass it to this method, the "sendTo()" method will be called for each recipient.
|
||||
* @return the amount of people that received the message. */
|
||||
public static int broadcast(String prefix, String message, BroadcastFilter filter)
|
||||
{
|
||||
return broadcast(prefix, message, filter, null);
|
||||
}
|
||||
|
||||
/** This method broadcasts a message to all players (and console) that are allowed by the filter. Set the filter to NULL to broadcast to everyone.</br>
|
||||
* This will not be logged to console except when you return true in the filter.
|
||||
*
|
||||
* @param message the message to be sent around
|
||||
* @param filter the BroadcastFilter to be applied.</br>
|
||||
* Write a class implementing the interface and pass it to this method, the "sendTo()" method will be called for each recipient.
|
||||
* @param alternateColorCode The alternate color code indicator to use. If set to '&' then "&7" would be translated to "§7". Works with any char.
|
||||
* @return the amount of people that received the message. */
|
||||
public static int broadcast(String prefix, String message, BroadcastFilter filter, char alternateColorCode)
|
||||
{
|
||||
if (prefix == null)
|
||||
prefix = "§8[§2" + getCaller() + "§8]: ";
|
||||
return broadcast(ChatColor.translateAlternateColorCodes(alternateColorCode, prefix).replace("&§", "&"),
|
||||
ChatColor.translateAlternateColorCodes(alternateColorCode, message).replace("&§", "&"), filter, null);
|
||||
}
|
||||
|
||||
/** This method broadcasts a message to all players and console that are allowed by the filter. Set the filter to NULL to broadcast to everyone.</br>
|
||||
* If you want to, you can set a message that will be logged to console. Set to null to not log anything.</br>
|
||||
* You can still allow console in the filter to log the original message.
|
||||
@ -121,21 +34,16 @@ public final class Utils
|
||||
* Write a class implementing the interface and pass it to this method, the "sendTo()" method will be called for each recipient.
|
||||
* @param logmessage the log message to appear in console. Set to null to not log this (you can still log the original message by returning true in the filter).
|
||||
* @return the amount of people that received the message. */
|
||||
@Debugable
|
||||
public static int broadcast(String prefix, String message, BroadcastFilter filter, String logmessage)
|
||||
public static int broadcast(String prefix, String message, BroadcastFilter filter)
|
||||
{
|
||||
if (prefix == null)
|
||||
prefix = "§8[§2" + getCaller() + "§8]: ";
|
||||
Debugger.notifyMethod(message, filter, logmessage);
|
||||
if (logmessage != null)
|
||||
sendMessage(Bukkit.getConsoleSender(), prefix, logmessage);
|
||||
if (filter == null)
|
||||
{
|
||||
for (Player p : Bukkit.getOnlinePlayers())
|
||||
p.sendMessage(prefix + message);
|
||||
if (logmessage == null)
|
||||
Bukkit.getConsoleSender().sendMessage(prefix + message);
|
||||
return Bukkit.getOnlinePlayers().size();
|
||||
Bukkit.getConsoleSender().sendMessage(prefix + message);
|
||||
return Bukkit.getOnlinePlayers().size() + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -146,64 +54,15 @@ public final class Utils
|
||||
p.sendMessage(prefix + message);
|
||||
count++;
|
||||
}
|
||||
if (logmessage == null)
|
||||
if (filter.sendTo(Bukkit.getConsoleSender()))
|
||||
{
|
||||
Bukkit.getConsoleSender().sendMessage(prefix + message);
|
||||
count++;
|
||||
}
|
||||
if (filter.sendTo(Bukkit.getConsoleSender()))
|
||||
{
|
||||
Bukkit.getConsoleSender().sendMessage(prefix + message);
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
/** Deprecated. Use Utils.info(message) instead.
|
||||
*
|
||||
* @param message The message to be put into console. Prefixes are automatically generated. */
|
||||
@Deprecated
|
||||
public static void log(String message)
|
||||
{
|
||||
info(message);
|
||||
}
|
||||
|
||||
/** Prints an info message into console. Supports "&" color codes.
|
||||
*
|
||||
* @param message The message to be put into console. Prefixes are automatically generated. Color defaults to grey. */
|
||||
@Debugable
|
||||
public static void info(String message)
|
||||
{
|
||||
Debugger.notifyMethod(message);
|
||||
String classname = getCaller();
|
||||
String prefix = "§8[§2" + classname + "§8]: ";
|
||||
Bukkit.getConsoleSender()
|
||||
.sendMessage(ChatColor.translateAlternateColorCodes('&', prefix + "§7" + message).replace("&§", "&"));
|
||||
}
|
||||
|
||||
/** Prints a warning message into console. Supports "&" color codes.
|
||||
*
|
||||
* @param message The message to be put into console. Prefixes are automatically generated. Color defaults to grey. */
|
||||
@Debugable
|
||||
public static void warn(String message)
|
||||
{
|
||||
Debugger.notifyMethod(message);
|
||||
String classname = getCaller();
|
||||
String prefix = "§e[WARN]: §8[§e" + classname + "§8]: ";
|
||||
Bukkit.getConsoleSender()
|
||||
.sendMessage(ChatColor.translateAlternateColorCodes('&', prefix + "§7" + message).replace("&§", "&"));
|
||||
}
|
||||
|
||||
/** Used to make an error output to console. Supports "&" color codes.
|
||||
*
|
||||
* @param message The message to be put into console. Prefixes are automatically generated. Color defaults to red. */
|
||||
@Debugable
|
||||
public static void error(String message)
|
||||
{
|
||||
Debugger.notifyMethod(message);
|
||||
String classname = getCaller();
|
||||
String prefix = "§c[ERROR]: §8[§c" + classname + "§8]: ";
|
||||
Bukkit.getConsoleSender()
|
||||
.sendMessage(ChatColor.translateAlternateColorCodes('&', prefix + "§7" + message).replace("&§", "&"));
|
||||
}
|
||||
|
||||
/** This method will find the next parent caller and return their class name, omitting package names.
|
||||
*
|
||||
* @return the Name of the calling class. */
|
||||
@ -222,36 +81,20 @@ public final class Utils
|
||||
*
|
||||
* @param directCaller used to prevent this method from returning the caller itself. Null if supposed to be ignored.
|
||||
* @return the name of the calling class. */
|
||||
public static final String getCaller(String directCaller)
|
||||
public static final String getCaller(String... directCaller)
|
||||
{
|
||||
if (directCaller == null || directCaller.length == 0)
|
||||
return getCaller();
|
||||
StackTraceElement[] stackTrace = (new Exception()).getStackTrace();
|
||||
String classname = (directCaller == null ? "Utils" : directCaller);
|
||||
for (int i = 0; classname.equals(directCaller) || classname.equals("Utils"); i++)
|
||||
String classname = "Utils";
|
||||
List<String> callers = Arrays.asList(directCaller);
|
||||
for (int i = 0; callers.contains(classname) || classname.equals("Utils"); i++)
|
||||
{
|
||||
classname = stackTrace[i].getClassName().replaceAll(".*\\.", "");
|
||||
}
|
||||
return classname;
|
||||
}
|
||||
|
||||
/** Displays the module header to the recipient.</br>
|
||||
* Format: &2--=[ %MODULE% ]=--
|
||||
*
|
||||
* @param recipient Whom to display the header to. */
|
||||
public static void sendModuleHeader(CommandSender recipient)
|
||||
{
|
||||
sendModuleHeader(recipient, getCaller());
|
||||
}
|
||||
|
||||
/** Displays the module header to the recipient.</br>
|
||||
* Format: &2--=[ %HEADER% ]=--
|
||||
*
|
||||
* @param recipient Whom to display the header to.
|
||||
* @param header The module name. */
|
||||
public static void sendModuleHeader(CommandSender recipient, String header)
|
||||
{
|
||||
recipient.sendMessage("§2--=[ " + header + " ]=--");
|
||||
}
|
||||
|
||||
/** Provides a uniform way of getting the date for all modules.
|
||||
*
|
||||
* @return The current date in the format "[dd-mm-yyyy hh:mm:ss]" */
|
||||
@ -261,31 +104,29 @@ public final class Utils
|
||||
return dateFormat.format(date);
|
||||
}
|
||||
|
||||
/** Provides a uniform way of getting the (display)name of a CommandSender.
|
||||
/** Provides a uniform way of getting the (display)name of a @CommandSender.
|
||||
*
|
||||
* @param sender The CommandSender to get the name of.
|
||||
* @return The DisplayName of the CommandSender or if not a player, the name in blue. */
|
||||
* @param sender The @CommandSender to get the name of.
|
||||
* @return The DisplayName of the @CommandSender or if not a @Player, the name in blue. */
|
||||
public static String getName(CommandSender sender)
|
||||
{
|
||||
if (sender instanceof Player)
|
||||
return ((Player) sender).getDisplayName();
|
||||
else
|
||||
return "&9" + sender.getName();
|
||||
return "§9" + sender.getName();
|
||||
}
|
||||
|
||||
/** This method "colorifies" a message using proper permissions.
|
||||
/** Provides a uniform way of getting the UUID of a @CommandSender.
|
||||
*
|
||||
* @param message the message to be colored.
|
||||
* @param sender the command sender whose permissions shall be applied.
|
||||
* @return the colorified message. */
|
||||
public static String colorify(String message, CommandSender sender)
|
||||
* @param sender The @CommandSender to get the UUID of.
|
||||
* @return The UUID of the @CommandSender or if not a player, "CONSOLE" in blue. */
|
||||
public static String getID(CommandSender sender)
|
||||
{
|
||||
if (sender.hasPermission("essentials.chat.color"))
|
||||
message = message.replaceAll("&([0-9a-fA-FrR])", "§$1");
|
||||
if (sender.hasPermission("essentials.chat.format"))
|
||||
message = message.replaceAll("&(l-oL-OrR)", "§$1");
|
||||
if (sender.hasPermission("essentials.chat.magic"))
|
||||
message = message.replaceAll("&([kKrR])", "§$1");
|
||||
return message.replace("&§", "&");
|
||||
String id;
|
||||
if (sender instanceof Player)
|
||||
id = ((Player) sender).getUniqueId().toString();
|
||||
else
|
||||
id = "CONSOLE";
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
@ -97,6 +97,15 @@ public final class VersionHelper
|
||||
return ver.major() + "." + ver.minor() + "." + ver.revision() + "." + ver.compatible();
|
||||
}
|
||||
|
||||
public static Version getVersion(String ver)
|
||||
{
|
||||
String[] raw = ver.split("\\.");
|
||||
if (raw.length != 4)
|
||||
return null;
|
||||
return VersionHelper.create(Integer.parseInt(raw[0]), Integer.parseInt(raw[1]), Integer.parseInt(raw[2]),
|
||||
Integer.parseInt(raw[3]));
|
||||
}
|
||||
|
||||
/** This method creates a new Version to use for compatibility checks.
|
||||
*
|
||||
* @param major The major version
|
||||
|
@ -1,11 +1,12 @@
|
||||
package com.redstoner.modules;
|
||||
|
||||
import com.redstoner.annotations.Version;
|
||||
import com.redstoner.coremods.moduleLoader.ModuleLoader;
|
||||
|
||||
/** Interface for the Module class. Modules must always have an empty constructor to be invoked by the ModuleLoader.
|
||||
*
|
||||
* @author Pepich */
|
||||
@Version(major = 3, minor = 0, revision = 0, compatible = 2)
|
||||
@Version(major = 4, minor = 0, revision = 0, compatible = 0)
|
||||
public interface Module
|
||||
{
|
||||
/** Will be called when the module gets enabled. */
|
||||
@ -23,13 +24,31 @@ public interface Module
|
||||
public default void onDisable()
|
||||
{}
|
||||
|
||||
/** Gets called on registration of the module.
|
||||
* THIS WAS ONLY KEPT FOR COMPATIBILITY REASONS. Please register commands yourself instead using the "postEnable" method.
|
||||
/** Gets called on registration of the module, when this option is selected for command registration
|
||||
*
|
||||
* @return The String used for the CommandManager to register the commands. */
|
||||
@Deprecated
|
||||
public default String getCommandString()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public default ModuleLogger getLogger()
|
||||
{
|
||||
return ModuleLoader.getModuleLogger(this);
|
||||
}
|
||||
|
||||
/** This method gets run the very first time a module gets loaded. You can use this to set up file structures or background data. */
|
||||
public default void firstLoad()
|
||||
{}
|
||||
|
||||
/** This method gets run every time a module gets loaded and its version has changed.
|
||||
*
|
||||
* @param old The version of the previous module. */
|
||||
public default void migrate(Version old)
|
||||
{}
|
||||
|
||||
default void setPrefix(final String name)
|
||||
{
|
||||
getLogger().setName(name);
|
||||
}
|
||||
}
|
||||
|
76
src/com/redstoner/modules/ModuleLogger.java
Normal file
76
src/com/redstoner/modules/ModuleLogger.java
Normal file
@ -0,0 +1,76 @@
|
||||
package com.redstoner.modules;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import com.redstoner.annotations.Version;
|
||||
|
||||
import net.nemez.chatapi.ChatAPI;
|
||||
import net.nemez.chatapi.click.Message;
|
||||
|
||||
@Version(major = 4, minor = 0, revision = 0, compatible = -1)
|
||||
public class ModuleLogger
|
||||
{
|
||||
public static final String PREFIX_WARN = "§8[§eWARN§8]:§7 ";
|
||||
public static final String PREFIX_ERROR = "§8[§cERROR§8]:§7 ";
|
||||
|
||||
private String name;
|
||||
|
||||
public ModuleLogger(final String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void info(final String message)
|
||||
{
|
||||
Bukkit.getConsoleSender().sendMessage(getPrefix() + ChatAPI.colorify(null, message));
|
||||
}
|
||||
|
||||
public void warn(final String message)
|
||||
{
|
||||
Bukkit.getConsoleSender().sendMessage(PREFIX_WARN + getPrefix() + ChatAPI.colorify(null, message));
|
||||
}
|
||||
|
||||
public void error(final String message)
|
||||
{
|
||||
Bukkit.getConsoleSender().sendMessage(PREFIX_ERROR + getPrefix() + ChatAPI.colorify(null, message));
|
||||
}
|
||||
|
||||
public void message(final CommandSender recipient, final String... message)
|
||||
{
|
||||
message(recipient, false, message);
|
||||
}
|
||||
|
||||
public void message(final CommandSender recipient, final boolean error, final String... message)
|
||||
{
|
||||
Message m = new Message(recipient, null);
|
||||
if (message.length == 1)
|
||||
m.appendText(getPrefix(error) + message[0]);
|
||||
else
|
||||
{
|
||||
m.appendText(getHeader());
|
||||
m.appendText("&7" + String.join("\n&7", message));
|
||||
}
|
||||
m.send();
|
||||
}
|
||||
|
||||
public String getPrefix()
|
||||
{
|
||||
return getPrefix(false);
|
||||
}
|
||||
|
||||
public String getPrefix(final boolean error)
|
||||
{
|
||||
return "§8[§" + (error ? 'c' : '2') + name + "§8]§7 ";
|
||||
}
|
||||
|
||||
public String getHeader()
|
||||
{
|
||||
return "§2--=[ " + name + " ]=--\n";
|
||||
}
|
||||
|
||||
protected final void setName(final String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user