Fixing softreload
Dieser Commit ist enthalten in:
Ursprung
9dae263d9b
Commit
b8d343f3f1
@ -1,9 +1,8 @@
|
|||||||
package de.steamwar.bungeecore;
|
package de.steamwar.bungeecore;
|
||||||
|
|
||||||
import com.google.common.collect.Multimap;
|
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.api.plugin.*;
|
||||||
import net.md_5.bungee.event.EventHandler;
|
|
||||||
import org.yaml.snakeyaml.Yaml;
|
import org.yaml.snakeyaml.Yaml;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -18,7 +17,7 @@ import java.util.jar.JarFile;
|
|||||||
import java.util.logging.Handler;
|
import java.util.logging.Handler;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
public class Persistent extends Plugin implements Listener {
|
public class Persistent extends Plugin {
|
||||||
|
|
||||||
private static Persistent instance;
|
private static Persistent instance;
|
||||||
private static String chatPrefix = "";
|
private static String chatPrefix = "";
|
||||||
@ -27,6 +26,110 @@ public class Persistent extends Plugin implements Listener {
|
|||||||
@Override
|
@Override
|
||||||
public void onEnable(){
|
public void onEnable(){
|
||||||
instance = this;
|
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<String, Command> 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<String, Plugin> pluginsMap = ReflectionUtils.getFieldValue(pluginManager, "plugins");
|
||||||
|
Multimap<Plugin, Command> commands = ReflectionUtils.getFieldValue(pluginManager, "commandsByPlugin");
|
||||||
|
Multimap<Plugin, Listener> 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<PluginClassloader> 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<String> 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<String, Plugin> 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
|
@Override
|
||||||
@ -34,109 +137,6 @@ public class Persistent extends Plugin implements Listener {
|
|||||||
Subserver.shutdown();
|
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<String, Command> 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<String, Plugin> pluginsMap = ReflectionUtils.getFieldValue(pluginManager, "plugins");
|
|
||||||
Multimap<Plugin, Command> commands = ReflectionUtils.getFieldValue(pluginManager, "commandsByPlugin");
|
|
||||||
Multimap<Plugin, Listener> 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<PluginClassloader> 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<String> 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<String, Plugin> 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) {
|
public static void setLobbyServer(String lobbyServer) {
|
||||||
Persistent.lobbyServer = lobbyServer;
|
Persistent.lobbyServer = lobbyServer;
|
||||||
}
|
}
|
||||||
|
In neuem Issue referenzieren
Einen Benutzer sperren