581f3be791
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <git-5w3l@lixfel.de>
204 Zeilen
8.2 KiB
Java
204 Zeilen
8.2 KiB
Java
/*
|
|
* This file is a part of the SteamWar software.
|
|
*
|
|
* Copyright (C) 2024 SteamWar.de-Serverteam
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package de.steamwar.persistent;
|
|
|
|
import com.google.inject.AbstractModule;
|
|
import com.google.inject.Inject;
|
|
import com.google.inject.Module;
|
|
import com.google.inject.name.Names;
|
|
import com.mojang.brigadier.Command;
|
|
import com.velocitypowered.api.command.BrigadierCommand;
|
|
import com.velocitypowered.api.command.CommandManager;
|
|
import com.velocitypowered.api.command.CommandMeta;
|
|
import com.velocitypowered.api.event.EventManager;
|
|
import com.velocitypowered.api.event.Subscribe;
|
|
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
|
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
|
|
import com.velocitypowered.api.plugin.Plugin;
|
|
import com.velocitypowered.api.plugin.PluginContainer;
|
|
import com.velocitypowered.api.plugin.PluginDescription;
|
|
import com.velocitypowered.api.plugin.PluginManager;
|
|
import com.velocitypowered.api.plugin.annotation.DataDirectory;
|
|
import com.velocitypowered.api.proxy.ProxyServer;
|
|
import com.velocitypowered.api.scheduler.ScheduledTask;
|
|
import com.velocitypowered.proxy.plugin.PluginClassLoader;
|
|
import com.velocitypowered.proxy.plugin.VelocityPluginManager;
|
|
import com.velocitypowered.proxy.plugin.loader.VelocityPluginContainer;
|
|
import com.velocitypowered.proxy.plugin.loader.java.JavaPluginLoader;
|
|
import lombok.Getter;
|
|
import net.kyori.adventure.text.Component;
|
|
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
|
|
|
import java.io.IOException;
|
|
import java.nio.file.Path;
|
|
import java.util.NoSuchElementException;
|
|
import java.util.ResourceBundle;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
|
|
@Plugin(
|
|
id = "persistentvelocitycore",
|
|
name = "PersistentVelocityCore"
|
|
)
|
|
public class Persistent {
|
|
|
|
private static final Reflection.Method<VelocityPluginManager> registerPlugin = new Reflection.Method<>(VelocityPluginManager.class, "registerPlugin", PluginContainer.class);
|
|
|
|
@Getter
|
|
private static Persistent instance;
|
|
|
|
@Getter
|
|
private final ProxyServer proxy;
|
|
@Getter
|
|
private final Logger logger;
|
|
private final Path directory;
|
|
|
|
@Inject
|
|
public Persistent(ProxyServer proxy, Logger logger, @DataDirectory Path dataDirectory) {
|
|
instance = this;
|
|
this.proxy = proxy;
|
|
this.logger = logger;
|
|
this.directory = dataDirectory;
|
|
}
|
|
|
|
@Subscribe
|
|
public void onEnable(ProxyInitializeEvent event) {
|
|
proxy.getCommandManager().register(
|
|
new BrigadierCommand(
|
|
BrigadierCommand.literalArgumentBuilder("softreload")
|
|
.requires(commandSource -> commandSource.hasPermission("bungeecore.softreload"))
|
|
.executes(commandContext -> softreload())
|
|
.build()
|
|
)
|
|
);
|
|
}
|
|
|
|
@Subscribe
|
|
public void onDisable(ProxyShutdownEvent event) {
|
|
Subserver.shutdown();
|
|
}
|
|
|
|
public int softreload() {
|
|
PluginContainer container = null;
|
|
ReloadablePlugin plugin = null;
|
|
try {
|
|
container = proxy.getPluginManager().getPlugin("velocitycore").orElseThrow();
|
|
plugin = (ReloadablePlugin) container.getInstance().orElseThrow();
|
|
} catch (NoSuchElementException e) {
|
|
logger.log(Level.WARNING, "Could not find loaded VelocityCore, continuing without unloading.");
|
|
}
|
|
|
|
PluginContainer newContainer;
|
|
try {
|
|
newContainer = prepareLoad();
|
|
} catch (Exception e) {
|
|
logger.log(Level.SEVERE, "Could not instantiate new VelocityCore, aborting softreload.", e);
|
|
return Command.SINGLE_SUCCESS;
|
|
}
|
|
|
|
broadcast("§eNetwork update is starting§8.");
|
|
try {
|
|
if(container != null && plugin != null) {
|
|
plugin.onProxyShutdown(new ProxyShutdownEvent());
|
|
unload(container, plugin);
|
|
}
|
|
|
|
registerPlugin.invoke((VelocityPluginManager) proxy.getPluginManager(), newContainer);
|
|
((ReloadablePlugin) newContainer.getInstance().orElseThrow()).onProxyInitialization(new ProxyInitializeEvent());
|
|
} catch (Throwable t) {
|
|
logger.log(Level.SEVERE, "Error during softreload", t);
|
|
broadcast("§cNetwork update failed§8, §cexpect network restart soon§8.");
|
|
return Command.SINGLE_SUCCESS;
|
|
}
|
|
|
|
broadcast("§eNetwork update complete§8.");
|
|
return Command.SINGLE_SUCCESS;
|
|
}
|
|
|
|
private void broadcast(String message) {
|
|
Component component = LegacyComponentSerializer.legacySection().deserialize("§eSteam§8War» " + message);
|
|
proxy.getAllPlayers().forEach(player -> player.sendMessage(component));
|
|
proxy.getConsoleCommandSource().sendMessage(component);
|
|
}
|
|
|
|
private PluginContainer prepareLoad() throws Exception {
|
|
Path plugins = directory.getParent();
|
|
JavaPluginLoader loader = new JavaPluginLoader(proxy, plugins);
|
|
PluginDescription description = loader.createPluginFromCandidate(loader.loadCandidate(plugins.resolve("VelocityCore.jar")));
|
|
PluginContainer container = new VelocityPluginContainer(description);
|
|
|
|
AbstractModule commonModule = new AbstractModule() {
|
|
@Override
|
|
protected void configure() {
|
|
this.bind(ProxyServer.class).toInstance(proxy);
|
|
this.bind(PluginManager.class).toInstance(proxy.getPluginManager());
|
|
this.bind(EventManager.class).toInstance(proxy.getEventManager());
|
|
this.bind(CommandManager.class).toInstance(proxy.getCommandManager());
|
|
this.bind(PluginContainer.class).annotatedWith(Names.named(container.getDescription().getId())).toInstance(container);
|
|
}
|
|
};
|
|
|
|
Module module = loader.createModule(container);
|
|
loader.createPlugin(container, module, commonModule);
|
|
|
|
return container;
|
|
}
|
|
|
|
private void unload(PluginContainer container, Object plugin) throws InterruptedException, IOException {
|
|
PluginClassLoader classLoader = ((PluginClassLoader) plugin.getClass().getClassLoader());
|
|
|
|
CommandManager commandManager = proxy.getCommandManager();
|
|
for(String alias : commandManager.getAliases()) {
|
|
CommandMeta meta = commandManager.getCommandMeta(alias);
|
|
if(meta != null && meta.getPlugin() == plugin)
|
|
commandManager.unregister(meta);
|
|
}
|
|
|
|
proxy.getEventManager().unregisterListeners(plugin);
|
|
proxy.getScheduler().tasksByPlugin(plugin).forEach(ScheduledTask::cancel);
|
|
|
|
container.getExecutorService().shutdown();
|
|
if(!container.getExecutorService().awaitTermination(100, TimeUnit.MILLISECONDS))
|
|
logger.log(Level.WARNING, "ExecutorService termination took longer than 100ms, continuing.");
|
|
|
|
for(Thread thread : Thread.getAllStackTraces().keySet()) {
|
|
if(thread.getClass().getClassLoader() != classLoader)
|
|
continue;
|
|
|
|
thread.interrupt();
|
|
thread.join(100);
|
|
|
|
if (thread.isAlive())
|
|
logger.log(Level.WARNING, "Could not stop thread %s of plugin %s. Still running".formatted(thread.getName(), container.getDescription().getId()));
|
|
}
|
|
|
|
//TODO close all log handlers
|
|
/*for (Handler handler : plugin.getLogger().getHandlers()) {
|
|
handler.close();
|
|
}*/
|
|
|
|
|
|
//Clear resource bundle cache
|
|
ResourceBundle.clearCache(classLoader);
|
|
classLoader.close();
|
|
}
|
|
}
|