summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPepich <benedikt.abel@yahoo.de>2017-03-29 09:30:15 +0200
committerPepich <benedikt.abel@yahoo.de>2017-03-29 09:30:15 +0200
commitdbca8263a05c3e6320abaa74aadb110417feab8d (patch)
tree2200fc779c8aa8b7bc569a9e1e820b67aa7a8211
parent0db0c9b5f6012d788b4e7562ccc672e1d3074921 (diff)
Added dynamic loading functionality, Updated API to v3.1.0
-rw-r--r--src/com/redstoner/coremods/moduleLoader/ModuleLoader.java234
-rw-r--r--src/com/redstoner/misc/VersionHelper.java11
2 files changed, 214 insertions, 31 deletions
diff --git a/src/com/redstoner/coremods/moduleLoader/ModuleLoader.java b/src/com/redstoner/coremods/moduleLoader/ModuleLoader.java
index ab0e3ec..cf1dd98 100644
--- a/src/com/redstoner/coremods/moduleLoader/ModuleLoader.java
+++ b/src/com/redstoner/coremods/moduleLoader/ModuleLoader.java
@@ -1,9 +1,14 @@
package com.redstoner.coremods.moduleLoader;
import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
import java.util.HashMap;
import org.bukkit.Bukkit;
+import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
@@ -22,6 +27,8 @@ import com.redstoner.misc.VersionHelper;
import com.redstoner.modules.CoreModule;
import com.redstoner.modules.Module;
+import net.minecraft.server.v1_11_R1.MinecraftServer;
+
/** The module loader, mother of all modules. Responsible for loading and taking care of all modules.
*
* @author Pepich */
@@ -30,9 +37,21 @@ public final class ModuleLoader implements CoreModule
{
private static ModuleLoader instance;
private static final HashMap<Module, Boolean> modules = new HashMap<Module, Boolean>();
+ private static URL[] urls;
+ private static URLClassLoader mainLoader;
private ModuleLoader()
- {}
+ {
+ try
+ {
+ urls = new URL[] {(new File(Main.plugin.getDataFolder(), "classes")).toURI().toURL()};
+ mainLoader = new URLClassLoader(urls, this.getClass().getClassLoader());
+ }
+ catch (MalformedURLException e)
+ {
+ System.out.println("Sumtin is wong with ya filesüstem m8. Fix eeeet or I won't werk!");
+ }
+ }
public static void init()
{
@@ -42,10 +61,12 @@ public final class ModuleLoader implements CoreModule
Main.plugin);
}
- /** This method will add a module to the module list, without enabling it
+ /** 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);
@@ -60,6 +81,20 @@ public final class ModuleLoader implements CoreModule
}
}
+ @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()
@@ -69,23 +104,7 @@ public final class ModuleLoader implements CoreModule
{
if (modules.get(module))
continue;
- try
- {
- 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);
- Utils.info("Loaded module " + module.getClass().getName());
- }
- else
- Utils.error("Failed to load module " + module.getClass().getName());
- }
- catch (Exception e)
- {
- Utils.error("Failed to load module " + module.getClass().getName());
- e.printStackTrace();
- }
+ enableLoadedModule(module);
}
Utils.info("Modules enabled, running post initialization.");
for (Module module : modules.keySet())
@@ -111,11 +130,13 @@ public final class ModuleLoader implements CoreModule
}
}
- /** 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.
+ /** 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>
+ * This method is deprecated, use enableDynamicModule instead. When using this method, dynamic reloading of the module will not be supported.
*
* @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);
@@ -123,6 +144,11 @@ public final class ModuleLoader implements CoreModule
{
if (module.getClass().equals(clazz))
{
+ if (modules.get(module))
+ {
+ Utils.info("Module was already enabled! Ignoring module.!");
+ return true;
+ }
if (module.onEnable())
{
if (module.getClass().isAnnotationPresent(AutoRegisterListener.class)
@@ -130,13 +156,13 @@ public final class ModuleLoader implements CoreModule
{
Bukkit.getPluginManager().registerEvents((Listener) module, Main.plugin);
}
- Utils.info("Loaded module " + module.getClass().getName());
+ Utils.info("Enabled module " + module.getClass().getName());
modules.put(module, true);
return true;
}
else
{
- Utils.error("Failed to load module " + module.getClass().getName());
+ Utils.error("Failed to enable module " + module.getClass().getName());
return false;
}
}
@@ -151,12 +177,12 @@ public final class ModuleLoader implements CoreModule
{
Bukkit.getPluginManager().registerEvents((Listener) m, Main.plugin);
}
- Utils.info("Loaded module " + m.getClass().getName());
+ Utils.info("Loaded and enabled module " + m.getClass().getName());
return true;
}
else
{
- Utils.error("Failed to load module " + m.getClass().getName());
+ Utils.error("Failed to enable module " + m.getClass().getName());
return false;
}
}
@@ -167,6 +193,32 @@ public final class ModuleLoader implements CoreModule
}
}
+ @SuppressWarnings("deprecation")
+ private static final void enableLoadedModule(Module module)
+ {
+ try
+ {
+ 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()))
+ {
+ module.postEnable();
+ }
+ Utils.info("Loaded module " + module.getClass().getName());
+ }
+ else
+ Utils.error("Failed to load module " + module.getClass().getName());
+ }
+ catch (Exception e)
+ {
+ Utils.error("Failed to load module " + module.getClass().getName());
+ e.printStackTrace();
+ }
+ }
+
/** 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.
@@ -194,19 +246,141 @@ public final class ModuleLoader implements CoreModule
{
for (Module module : modules.keySet())
{
- if (modules.get(module))
+ disableModule(module);
+ }
+ }
+
+ public static void disableModule(Module module)
+ {
+ if (modules.get(module))
+ {
+ module.onDisable();
+ if (module.getClass().isAnnotationPresent(AutoRegisterListener.class) && (module instanceof Listener))
{
- module.onDisable();
- if (module.getClass().isAnnotationPresent(AutoRegisterListener.class) && (module instanceof Listener))
+ HandlerList.unregisterAll((Listener) module);
+ }
+ }
+ }
+
+ @Command(hook = "load")
+ public boolean loadModule(CommandSender sender, String name)
+ {
+ addDynamicModule(name);
+ return true;
+ }
+
+ public static final void addDynamicModule(String name)
+ {
+ Object[] status = getServerStatus();
+ for (Module m : modules.keySet())
+ {
+ if (m.getClass().getName().equals(name))
+ {
+ Utils.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:");
+ boolean differs = false;
+ Utils.info("Old class definition: Class@" + m.getClass().hashCode());
+ ClassLoader delegateParent = Module.class.getClassLoader();
+ Class<?> newClass = null;
+ try (URLClassLoader cl = new URLClassLoader(urls, delegateParent))
+ {
+ newClass = cl.loadClass(m.getClass().getName());
+ Utils.info("Found new class definition: Class@" + newClass.hashCode());
+ differs = m.getClass() != newClass;
+ }
+ catch (IOException | ClassNotFoundException e)
+ {
+ Utils.error("Could not find a class definition, aborting now!");
+ e.printStackTrace();
+ return;
+ }
+ if (!differs)
+ {
+ Utils.warn("New class definition equals old definition, are you sure you did everything right?");
+ Utils.info("Aborting now...");
+ return;
+ }
+ Utils.info("Found new class definition, attempting to instantiate:");
+ Module module = null;
+ try
{
- HandlerList.unregisterAll((Listener) module);
+ module = (Module) newClass.newInstance();
}
+ catch (InstantiationException | IllegalAccessException e)
+ {
+ Utils.error("Could not instantiate the module, aborting!");
+ e.printStackTrace();
+ return;
+ }
+ Utils.info("Instantiated new class definition, checking versions:");
+ Version oldVersion = m.getClass().getAnnotation(Version.class);
+ Utils.info("Current version: " + VersionHelper.getString(oldVersion));
+ Version newVersion = module.getClass().getAnnotation(Version.class);
+ Utils.info("Version of remote class: " + VersionHelper.getString(newVersion));
+ if (oldVersion.equals(newVersion))
+ {
+ Utils.error("Detected equal module versions, aborting now...");
+ return;
+ }
+ Utils.info("Versions differ, disabling old module:");
+ disableModule(m);
+ Utils.info("Disabled module, overriding the implementation:");
+ modules.remove(m);
+ modules.put(module, false);
+ 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;
}
}
+ try
+ {
+ Class<?> clazz = mainLoader.loadClass(name);
+ Module module = (Module) clazz.newInstance();
+ addLoadedModule(module);
+ enableLoadedModule(module);
+ }
+ catch (ClassNotFoundException | InstantiationException | IllegalAccessException e)
+ {
+ if (!name.startsWith("com.redstoner.modules."))
+ {
+ Utils.warn(
+ "Couldn't find class definition, suspecting missing path. Autocompleting path, trying again.");
+ addDynamicModule("com.redstoner.modules." + name);
+ }
+ else
+ e.printStackTrace();
+ }
}
- public static boolean loadModule(File f)
+ @SuppressWarnings("deprecation")
+ private static final Object[] getServerStatus()
{
- return false;
+ 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;
}
}
diff --git a/src/com/redstoner/misc/VersionHelper.java b/src/com/redstoner/misc/VersionHelper.java
index 9e7bf35..2c4a266 100644
--- a/src/com/redstoner/misc/VersionHelper.java
+++ b/src/com/redstoner/misc/VersionHelper.java
@@ -8,7 +8,7 @@ import com.redstoner.exceptions.MissingVersionException;
/** This class can be used to compare modules against the loader version or against each other to prevent dependency issues.
*
* @author Pepich */
-@Version(major = 2, minor = 1, revision = 1, compatible = 0)
+@Version(major = 2, minor = 1, revision = 2, compatible = 0)
public final class VersionHelper
{
private VersionHelper()
@@ -86,6 +86,15 @@ public final class VersionHelper
if (!clazz.isAnnotationPresent(Version.class))
throw new MissingVersionException("The given class is not associated with a version.");
Version ver = clazz.getAnnotation(Version.class);
+ return getString(ver);
+ }
+
+ /** Returns the String representation of a version.
+ *
+ * @param ver The version to be represented.
+ * @return The String representation. */
+ public static String getString(Version ver)
+ {
return ver.major() + "." + ver.minor() + "." + ver.revision() + "." + ver.compatible();
}