diff --git a/src/de/steamwar/bungeecore/Persistent.java b/src/de/steamwar/bungeecore/Persistent.java index 37c2f6d..5204a2c 100644 --- a/src/de/steamwar/bungeecore/Persistent.java +++ b/src/de/steamwar/bungeecore/Persistent.java @@ -1,9 +1,8 @@ package de.steamwar.bungeecore; import com.google.common.collect.Multimap; -import net.md_5.bungee.api.event.ProxyReloadEvent; +import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.plugin.*; -import net.md_5.bungee.event.EventHandler; import org.yaml.snakeyaml.Yaml; import java.io.File; @@ -18,7 +17,7 @@ import java.util.jar.JarFile; import java.util.logging.Handler; import java.util.logging.Level; -public class Persistent extends Plugin implements Listener { +public class Persistent extends Plugin { private static Persistent instance; private static String chatPrefix = ""; @@ -27,6 +26,110 @@ public class Persistent extends Plugin implements Listener { @Override public void onEnable(){ instance = this; + getProxy().getPluginManager().registerCommand(this, new Command("softreload", "bungeecore.softreload"){ + @Override + public void execute(CommandSender sender, String[] args) { + // Copied from https://www.spigotmc.org/resources/bungeepluginmanager-manage-your-bungee-plugin-at-runtime.63861/ + PluginManager pluginManager = getProxy().getPluginManager(); + Plugin bungeecore = pluginManager.getPlugin("BungeeCore"); + ClassLoader pluginClassLoader = bungeecore.getClass().getClassLoader(); + + bungeecore.onDisable(); + for(Handler h : bungeecore.getLogger().getHandlers()){ + h.close(); + } + + pluginManager.unregisterCommands(bungeecore); + pluginManager.unregisterListeners(bungeecore); + getProxy().getScheduler().cancel(bungeecore); + bungeecore.getExecutorService().shutdownNow(); + Thread.getAllStackTraces().keySet().stream() + .filter(thread -> (thread.getClass().getClassLoader() == pluginClassLoader)) + .forEach(thread -> { + try { + thread.interrupt(); + thread.join(2000); + if (thread.isAlive()) { + throw new IllegalStateException("Thread " + thread.getName() + " still running"); + } + } catch (Exception t) { + getProxy().getLogger().log(Level.SEVERE, "Failed to stop thread that belong to plugin", t); + } + }); + //remove commands that were registered by plugin not through normal means + try { + Map commandMap = ReflectionUtils.getFieldValue(pluginManager, "commandMap"); + if (commandMap != null) { + commandMap.entrySet().removeIf(entry -> entry.getValue().getClass().getClassLoader() == pluginClassLoader); + } + } catch (Exception t) { + getLogger().log(Level.SEVERE, "Failed to cleanup commandMap", t); + } + try { + Map pluginsMap = ReflectionUtils.getFieldValue(pluginManager, "plugins"); + Multimap commands = ReflectionUtils.getFieldValue(pluginManager, "commandsByPlugin"); + Multimap listeners = ReflectionUtils.getFieldValue(pluginManager, "listenersByPlugin"); + + if (pluginsMap != null && commands != null && listeners != null) { + pluginsMap.values().remove(bungeecore); + commands.removeAll(bungeecore); + listeners.removeAll(bungeecore); + } + } catch (Exception t) { + getLogger().log(Level.SEVERE, "Failed to cleanup bungee internal maps from plugin refs", t); + } + if (pluginClassLoader instanceof URLClassLoader) { + try { + ((URLClassLoader) pluginClassLoader).close(); + } catch (Exception t) { + getLogger().log(Level.SEVERE, "Failed to close classloader", t); + } + } + Set allLoaders = ReflectionUtils.getStaticFieldValue(PluginClassloader.class, "allLoaders"); + if (allLoaders != null) { + allLoaders.remove(pluginClassLoader); + } + + File pluginFile = new File(getProxy().getPluginsFolder(), "BungeeCore.jar"); + try (JarFile jar = new JarFile(pluginFile)) { + JarEntry pdf = jar.getJarEntry("plugin.yml"); + + try (InputStream in = jar.getInputStream(pdf)) { + //load description + PluginDescription desc = new Yaml().loadAs(in, PluginDescription.class); + desc.setFile(pluginFile); + //check depends + HashSet plugins = new HashSet<>(); + getProxy().getPluginManager().getPlugins().forEach(plugin -> plugins.add(plugin.getDescription().getName())); + for (String dependency : desc.getDepends()) { + if (!plugins.contains(dependency)) { + getLogger().log(Level.WARNING, "{0} (required by {1}) is unavailable", new Object[]{dependency, desc.getName()}); + return; + } + } + + // do actual loading + Class main; + try (URLClassLoader loader = new PluginClassloader(new URL[]{ + pluginFile.toURI().toURL() + })) { + main = loader.loadClass(desc.getMain()); + } + Plugin clazz = (Plugin) main.getDeclaredConstructor().newInstance(); + + // reflection + Map pluginsMap = ReflectionUtils.getFieldValue(getProxy().getPluginManager(), "plugins"); + ReflectionUtils.invokeMethod(clazz, "init", getProxy(), desc); + + pluginsMap.put(desc.getName(), clazz); + clazz.onLoad(); + clazz.onEnable(); + } + } catch (Exception t) { + getLogger().log(Level.SEVERE, "Failed to load plugin", t); + } + } + }); } @Override @@ -34,109 +137,6 @@ public class Persistent extends Plugin implements Listener { Subserver.shutdown(); } - @EventHandler - public void onGReload(ProxyReloadEvent e){ - // Copied from https://www.spigotmc.org/resources/bungeepluginmanager-manage-your-bungee-plugin-at-runtime.63861/ - PluginManager pluginManager = getProxy().getPluginManager(); - Plugin bungeecore = pluginManager.getPlugin("BungeeCore"); - ClassLoader pluginClassLoader = bungeecore.getClass().getClassLoader(); - - bungeecore.onDisable(); - for(Handler h : bungeecore.getLogger().getHandlers()){ - h.close(); - } - - pluginManager.unregisterCommands(bungeecore); - pluginManager.unregisterListeners(bungeecore); - getProxy().getScheduler().cancel(bungeecore); - bungeecore.getExecutorService().shutdownNow(); - Thread.getAllStackTraces().keySet().stream() - .filter(thread -> (thread.getClass().getClassLoader() == pluginClassLoader)) - .forEach(thread -> { - try { - thread.interrupt(); - thread.join(2000); - if (thread.isAlive()) { - throw new IllegalStateException("Thread " + thread.getName() + " still running"); - } - } catch (Exception t) { - getProxy().getLogger().log(Level.SEVERE, "Failed to stop thread that belong to plugin", t); - } - }); - //remove commands that were registered by plugin not through normal means - try { - Map commandMap = ReflectionUtils.getFieldValue(pluginManager, "commandMap"); - if (commandMap != null) { - commandMap.entrySet().removeIf(entry -> entry.getValue().getClass().getClassLoader() == pluginClassLoader); - } - } catch (Exception t) { - getLogger().log(Level.SEVERE, "Failed to cleanup commandMap", t); - } - try { - Map pluginsMap = ReflectionUtils.getFieldValue(pluginManager, "plugins"); - Multimap commands = ReflectionUtils.getFieldValue(pluginManager, "commandsByPlugin"); - Multimap listeners = ReflectionUtils.getFieldValue(pluginManager, "listenersByPlugin"); - - if (pluginsMap != null && commands != null && listeners != null) { - pluginsMap.values().remove(bungeecore); - commands.removeAll(bungeecore); - listeners.removeAll(bungeecore); - } - } catch (Exception t) { - getLogger().log(Level.SEVERE, "Failed to cleanup bungee internal maps from plugin refs", t); - } - if (pluginClassLoader instanceof URLClassLoader) { - try { - ((URLClassLoader) pluginClassLoader).close(); - } catch (Exception t) { - getLogger().log(Level.SEVERE, "Failed to close classloader", t); - } - } - Set allLoaders = ReflectionUtils.getStaticFieldValue(PluginClassloader.class, "allLoaders"); - if (allLoaders != null) { - allLoaders.remove(pluginClassLoader); - } - - File pluginFile = new File(getProxy().getPluginsFolder(), "BungeeCore.jar"); - try (JarFile jar = new JarFile(pluginFile)) { - JarEntry pdf = jar.getJarEntry("plugin.yml"); - - try (InputStream in = jar.getInputStream(pdf)) { - //load description - PluginDescription desc = new Yaml().loadAs(in, PluginDescription.class); - desc.setFile(pluginFile); - //check depends - HashSet plugins = new HashSet<>(); - getProxy().getPluginManager().getPlugins().forEach(plugin -> plugins.add(plugin.getDescription().getName())); - for (String dependency : desc.getDepends()) { - if (!plugins.contains(dependency)) { - getLogger().log(Level.WARNING, "{0} (required by {1}) is unavailable", new Object[]{dependency, desc.getName()}); - return; - } - } - - // do actual loading - Class main; - try (URLClassLoader loader = new PluginClassloader(new URL[]{ - pluginFile.toURI().toURL() - })) { - main = loader.loadClass(desc.getMain()); - } - Plugin clazz = (Plugin) main.getDeclaredConstructor().newInstance(); - - // reflection - Map pluginsMap = ReflectionUtils.getFieldValue(getProxy().getPluginManager(), "plugins"); - ReflectionUtils.invokeMethod(clazz, "init", getProxy(), desc); - - pluginsMap.put(desc.getName(), clazz); - clazz.onLoad(); - clazz.onEnable(); - } - } catch (Exception t) { - getLogger().log(Level.SEVERE, "Failed to load plugin", t); - } - } - public static void setLobbyServer(String lobbyServer) { Persistent.lobbyServer = lobbyServer; }