2019-06-02 05:03:08 +03:00
|
|
|
package pro.gravit.launchserver;
|
2018-09-17 10:07:32 +03:00
|
|
|
|
2021-04-13 12:47:42 +03:00
|
|
|
import org.apache.logging.log4j.LogManager;
|
|
|
|
import org.apache.logging.log4j.Logger;
|
2019-06-03 10:58:10 +03:00
|
|
|
import pro.gravit.launcher.Launcher;
|
|
|
|
import pro.gravit.launcher.NeedGarbageCollection;
|
|
|
|
import pro.gravit.launcher.hasher.HashedDir;
|
|
|
|
import pro.gravit.launcher.managers.ConfigManager;
|
|
|
|
import pro.gravit.launcher.managers.GarbageManager;
|
2019-08-26 13:24:19 +03:00
|
|
|
import pro.gravit.launcher.modules.events.ClosePhase;
|
2019-06-03 10:58:10 +03:00
|
|
|
import pro.gravit.launcher.profiles.ClientProfile;
|
2019-06-02 05:03:08 +03:00
|
|
|
import pro.gravit.launchserver.auth.AuthProviderPair;
|
2021-06-21 10:14:25 +03:00
|
|
|
import pro.gravit.launchserver.auth.core.RejectAuthCoreProvider;
|
2020-12-15 15:27:38 +03:00
|
|
|
import pro.gravit.launchserver.auth.session.MemorySessionStorage;
|
2021-05-25 12:17:29 +03:00
|
|
|
import pro.gravit.launchserver.binary.EXEL4JLauncherBinary;
|
|
|
|
import pro.gravit.launchserver.binary.EXELauncherBinary;
|
|
|
|
import pro.gravit.launchserver.binary.JARLauncherBinary;
|
|
|
|
import pro.gravit.launchserver.binary.LauncherBinary;
|
2019-08-25 10:49:44 +03:00
|
|
|
import pro.gravit.launchserver.config.LaunchServerConfig;
|
2019-06-02 05:03:08 +03:00
|
|
|
import pro.gravit.launchserver.config.LaunchServerRuntimeConfig;
|
2020-04-13 20:16:17 +03:00
|
|
|
import pro.gravit.launchserver.launchermodules.LauncherModuleLoader;
|
2020-06-24 07:06:08 +03:00
|
|
|
import pro.gravit.launchserver.manangers.*;
|
2019-06-02 05:03:08 +03:00
|
|
|
import pro.gravit.launchserver.manangers.hook.AuthHookManager;
|
2021-03-26 18:06:44 +03:00
|
|
|
import pro.gravit.launchserver.modules.events.*;
|
2019-08-26 13:24:19 +03:00
|
|
|
import pro.gravit.launchserver.modules.impl.LaunchServerModulesManager;
|
2019-07-01 10:37:01 +03:00
|
|
|
import pro.gravit.launchserver.socket.handlers.NettyServerSocketHandler;
|
2021-05-23 17:11:27 +03:00
|
|
|
import pro.gravit.launchserver.socket.response.auth.RestoreResponse;
|
2019-08-31 15:44:43 +03:00
|
|
|
import pro.gravit.utils.command.Command;
|
|
|
|
import pro.gravit.utils.command.CommandHandler;
|
|
|
|
import pro.gravit.utils.command.SubCommand;
|
2019-06-03 10:58:10 +03:00
|
|
|
import pro.gravit.utils.helper.CommonHelper;
|
|
|
|
import pro.gravit.utils.helper.IOHelper;
|
|
|
|
import pro.gravit.utils.helper.JVMHelper;
|
2021-04-13 14:23:39 +03:00
|
|
|
import pro.gravit.utils.helper.SecurityHelper;
|
2018-09-17 10:07:32 +03:00
|
|
|
|
2019-10-19 19:46:04 +03:00
|
|
|
import java.io.BufferedReader;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.lang.ProcessBuilder.Redirect;
|
2019-11-15 16:37:43 +03:00
|
|
|
import java.lang.invoke.MethodHandles;
|
|
|
|
import java.lang.invoke.MethodType;
|
2019-10-19 19:46:04 +03:00
|
|
|
import java.nio.file.*;
|
|
|
|
import java.nio.file.attribute.BasicFileAttributes;
|
|
|
|
import java.security.interfaces.ECPrivateKey;
|
|
|
|
import java.security.interfaces.ECPublicKey;
|
|
|
|
import java.util.*;
|
|
|
|
import java.util.Map.Entry;
|
2021-05-10 09:52:12 +03:00
|
|
|
import java.util.concurrent.Executors;
|
|
|
|
import java.util.concurrent.ScheduledExecutorService;
|
2019-10-19 19:46:04 +03:00
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
|
2021-04-06 13:39:14 +03:00
|
|
|
/**
|
|
|
|
* The main LaunchServer class. Contains links to all necessary objects
|
|
|
|
* Not a singletron
|
|
|
|
*/
|
2019-08-25 09:07:09 +03:00
|
|
|
public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurable {
|
2020-04-05 10:27:04 +03:00
|
|
|
public static final Class<? extends LauncherBinary> defaultLauncherEXEBinaryClass = null;
|
2021-04-06 13:39:14 +03:00
|
|
|
/**
|
|
|
|
* Working folder path
|
|
|
|
*/
|
2018-09-17 10:07:32 +03:00
|
|
|
public final Path dir;
|
2021-04-06 13:39:14 +03:00
|
|
|
/**
|
|
|
|
* Environment type (test / production)
|
|
|
|
*/
|
2019-08-25 12:39:08 +03:00
|
|
|
public final LaunchServerEnv env;
|
2021-04-06 13:39:14 +03:00
|
|
|
/**
|
|
|
|
* The path to the folder with libraries for the launcher
|
|
|
|
*/
|
2019-01-15 06:35:39 +03:00
|
|
|
public final Path launcherLibraries;
|
2021-04-06 13:39:14 +03:00
|
|
|
/**
|
|
|
|
* The path to the folder with compile-only libraries for the launcher
|
|
|
|
*/
|
2019-04-03 16:27:40 +03:00
|
|
|
public final Path launcherLibrariesCompile;
|
2021-04-06 13:39:14 +03:00
|
|
|
/**
|
|
|
|
* The path to the folder with updates/webroot
|
|
|
|
*/
|
2018-09-17 10:07:32 +03:00
|
|
|
public final Path updatesDir;
|
2021-05-25 12:17:29 +03:00
|
|
|
|
|
|
|
// Constant paths
|
2021-04-06 13:39:14 +03:00
|
|
|
/**
|
|
|
|
* Save/Reload LaunchServer config
|
|
|
|
*/
|
2019-08-25 12:39:08 +03:00
|
|
|
public final LaunchServerConfigManager launchServerConfigManager;
|
2021-04-06 13:39:14 +03:00
|
|
|
/**
|
|
|
|
* The path to the folder with profiles
|
|
|
|
*/
|
2018-09-17 10:07:32 +03:00
|
|
|
public final Path profilesDir;
|
2021-04-13 14:23:39 +03:00
|
|
|
public final Path tmpDir;
|
2021-04-06 13:39:14 +03:00
|
|
|
/**
|
|
|
|
* This object contains runtime configuration
|
|
|
|
*/
|
2019-10-19 19:43:25 +03:00
|
|
|
public final LaunchServerRuntimeConfig runtime;
|
2021-04-07 10:00:30 +03:00
|
|
|
@Deprecated
|
2019-10-16 12:38:44 +03:00
|
|
|
public final ECPublicKey publicKey;
|
2021-04-07 10:00:30 +03:00
|
|
|
@Deprecated
|
2019-10-16 12:38:44 +03:00
|
|
|
public final ECPrivateKey privateKey;
|
2021-04-06 13:39:14 +03:00
|
|
|
/**
|
|
|
|
* Pipeline for building JAR
|
|
|
|
*/
|
2019-01-07 08:01:15 +03:00
|
|
|
public final JARLauncherBinary launcherBinary;
|
2021-04-06 13:39:14 +03:00
|
|
|
/**
|
|
|
|
* Pipeline for building EXE
|
|
|
|
*/
|
|
|
|
public final LauncherBinary launcherEXEBinary;
|
2020-04-05 10:27:04 +03:00
|
|
|
//public static LaunchServer server = null;
|
2019-10-19 19:43:25 +03:00
|
|
|
public final Class<? extends LauncherBinary> launcherEXEBinaryClass;
|
2020-04-05 10:27:04 +03:00
|
|
|
// Server config
|
2018-09-17 10:07:32 +03:00
|
|
|
public final SessionManager sessionManager;
|
2018-12-31 10:51:49 +03:00
|
|
|
public final AuthHookManager authHookManager;
|
2019-08-26 13:24:19 +03:00
|
|
|
public final LaunchServerModulesManager modulesManager;
|
2020-04-05 10:27:04 +03:00
|
|
|
// Launcher binary
|
2018-10-18 12:39:36 +03:00
|
|
|
public final MirrorManager mirrorManager;
|
2021-05-16 16:07:44 +03:00
|
|
|
public final AuthManager authManager;
|
2018-12-26 15:40:53 +03:00
|
|
|
public final ReconfigurableManager reconfigurableManager;
|
2019-04-03 13:09:53 +03:00
|
|
|
public final ConfigManager configManager;
|
2020-06-24 07:06:08 +03:00
|
|
|
public final PingServerManager pingServerManager;
|
2020-12-20 11:27:29 +03:00
|
|
|
public final FeaturesManager featuresManager;
|
2021-04-07 10:00:30 +03:00
|
|
|
public final KeyAgreementManager keyAgreementManager;
|
2021-06-01 01:48:33 +03:00
|
|
|
public final UpdatesManager updatesManager;
|
2020-04-05 10:27:04 +03:00
|
|
|
// HWID ban + anti-brutforce
|
2019-07-01 17:45:11 +03:00
|
|
|
public final CertificateManager certificateManager;
|
2020-04-05 10:27:04 +03:00
|
|
|
// Server
|
2018-09-17 10:07:32 +03:00
|
|
|
public final CommandHandler commandHandler;
|
2019-01-29 18:42:31 +03:00
|
|
|
public final NettyServerSocketHandler nettyServerSocketHandler;
|
2021-05-10 09:52:12 +03:00
|
|
|
@Deprecated
|
2020-04-05 10:27:04 +03:00
|
|
|
public final Timer taskPool;
|
2021-05-10 09:52:12 +03:00
|
|
|
public final ScheduledExecutorService service;
|
2020-04-13 20:16:17 +03:00
|
|
|
public final AtomicBoolean started = new AtomicBoolean(false);
|
|
|
|
public final LauncherModuleLoader launcherModuleLoader;
|
2021-05-25 12:17:29 +03:00
|
|
|
private final Logger logger = LogManager.getLogger();
|
2020-04-05 10:27:04 +03:00
|
|
|
public LaunchServerConfig config;
|
2021-06-01 01:48:33 +03:00
|
|
|
@Deprecated
|
2020-04-05 10:27:04 +03:00
|
|
|
public volatile Map<String, HashedDir> updatesDirMap;
|
2018-09-17 10:07:32 +03:00
|
|
|
// Updates and profiles
|
2021-05-04 14:08:13 +03:00
|
|
|
private volatile Set<ClientProfile> profilesList;
|
2019-08-25 12:39:08 +03:00
|
|
|
|
2021-04-07 10:00:30 +03:00
|
|
|
public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, LaunchServerConfig config, LaunchServerRuntimeConfig runtimeConfig, LaunchServerConfigManager launchServerConfigManager, LaunchServerModulesManager modulesManager, KeyAgreementManager keyAgreementManager, CommandHandler commandHandler, CertificateManager certificateManager) throws IOException {
|
2019-08-25 12:39:08 +03:00
|
|
|
this.dir = directories.dir;
|
2021-04-13 14:23:39 +03:00
|
|
|
this.tmpDir = directories.tmpDir;
|
2019-08-25 12:39:08 +03:00
|
|
|
this.env = env;
|
|
|
|
this.config = config;
|
|
|
|
this.launchServerConfigManager = launchServerConfigManager;
|
|
|
|
this.modulesManager = modulesManager;
|
|
|
|
this.profilesDir = directories.profilesDir;
|
|
|
|
this.updatesDir = directories.updatesDir;
|
2021-04-07 10:00:30 +03:00
|
|
|
this.keyAgreementManager = keyAgreementManager;
|
|
|
|
this.publicKey = keyAgreementManager.ecdsaPublicKey;
|
|
|
|
this.privateKey = keyAgreementManager.ecdsaPrivateKey;
|
2019-08-25 12:39:08 +03:00
|
|
|
this.commandHandler = commandHandler;
|
|
|
|
this.runtime = runtimeConfig;
|
2019-10-17 16:22:24 +03:00
|
|
|
this.certificateManager = certificateManager;
|
2021-05-10 09:52:12 +03:00
|
|
|
this.service = Executors.newScheduledThreadPool(config.netty.performance.schedulerThread);
|
2019-02-03 14:15:30 +03:00
|
|
|
taskPool = new Timer("Timered task worker thread", true);
|
2019-11-28 19:14:06 +03:00
|
|
|
launcherLibraries = directories.launcherLibrariesDir;
|
|
|
|
launcherLibrariesCompile = directories.launcherLibrariesCompileDir;
|
2019-08-25 12:39:08 +03:00
|
|
|
|
|
|
|
config.setLaunchServer(this);
|
2018-09-17 10:07:32 +03:00
|
|
|
|
2019-08-26 13:24:19 +03:00
|
|
|
modulesManager.invokeEvent(new NewLaunchServerInstanceEvent(this));
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
|
|
// Print keypair fingerprints
|
2018-09-22 17:33:00 +03:00
|
|
|
|
2019-04-07 10:53:39 +03:00
|
|
|
// Load class bindings.
|
|
|
|
launcherEXEBinaryClass = defaultLauncherEXEBinaryClass;
|
|
|
|
|
2019-04-12 00:58:45 +03:00
|
|
|
runtime.verify();
|
2018-09-17 10:07:32 +03:00
|
|
|
config.verify();
|
2021-03-20 11:53:22 +03:00
|
|
|
if (config.sessions == null) config.sessions = new MemorySessionStorage();
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
|
|
// build hooks, anti-brutforce and other
|
2020-10-26 19:17:05 +03:00
|
|
|
sessionManager = new SessionManager(this);
|
2018-10-18 12:39:36 +03:00
|
|
|
mirrorManager = new MirrorManager();
|
2018-12-26 15:40:53 +03:00
|
|
|
reconfigurableManager = new ReconfigurableManager();
|
2018-12-31 10:51:49 +03:00
|
|
|
authHookManager = new AuthHookManager();
|
2019-04-03 13:09:53 +03:00
|
|
|
configManager = new ConfigManager();
|
2020-06-24 07:06:08 +03:00
|
|
|
pingServerManager = new PingServerManager(this);
|
2020-12-20 11:27:29 +03:00
|
|
|
featuresManager = new FeaturesManager(this);
|
2021-05-16 16:07:44 +03:00
|
|
|
authManager = new AuthManager(this);
|
2021-06-01 01:48:33 +03:00
|
|
|
updatesManager = new UpdatesManager(this);
|
2021-05-23 17:11:27 +03:00
|
|
|
RestoreResponse.registerProviders(this);
|
2021-07-09 13:18:29 +03:00
|
|
|
|
2019-08-25 09:48:26 +03:00
|
|
|
config.init(ReloadType.FULL);
|
2019-08-25 09:07:09 +03:00
|
|
|
registerObject("launchServer", this);
|
2018-09-17 10:07:32 +03:00
|
|
|
GarbageManager.registerNeedGC(sessionManager);
|
2018-09-22 17:33:00 +03:00
|
|
|
|
2019-08-25 12:39:08 +03:00
|
|
|
pro.gravit.launchserver.command.handler.CommandHandler.registerCommands(commandHandler, this);
|
2019-07-06 15:54:10 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
// init modules
|
2019-08-26 13:24:19 +03:00
|
|
|
modulesManager.invokeEvent(new LaunchServerInitPhase(this));
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
|
|
// Set launcher EXE binary
|
|
|
|
launcherBinary = new JARLauncherBinary(this);
|
|
|
|
launcherEXEBinary = binary();
|
2019-01-15 06:35:39 +03:00
|
|
|
|
2019-01-08 16:57:01 +03:00
|
|
|
launcherBinary.init();
|
|
|
|
launcherEXEBinary.init();
|
2018-09-17 10:07:32 +03:00
|
|
|
syncLauncherBinaries();
|
2020-04-13 20:16:17 +03:00
|
|
|
launcherModuleLoader = new LauncherModuleLoader(this);
|
2021-03-26 17:56:17 +03:00
|
|
|
if (config.components != null) {
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.debug("Init components");
|
2021-03-26 17:56:17 +03:00
|
|
|
config.components.forEach((k, v) -> {
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.debug("Init component {}", k);
|
2021-03-26 17:56:17 +03:00
|
|
|
v.setComponentName(k);
|
|
|
|
v.init(this);
|
|
|
|
});
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.debug("Init components successful");
|
2021-03-26 17:56:17 +03:00
|
|
|
}
|
2020-04-13 20:16:17 +03:00
|
|
|
launcherModuleLoader.init();
|
2019-10-19 19:43:25 +03:00
|
|
|
nettyServerSocketHandler = new NettyServerSocketHandler(this);
|
2018-09-17 10:07:32 +03:00
|
|
|
// post init modules
|
2019-08-26 13:24:19 +03:00
|
|
|
modulesManager.invokeEvent(new LaunchServerPostInitPhase(this));
|
2018-09-17 10:07:32 +03:00
|
|
|
}
|
|
|
|
|
2020-04-05 10:27:04 +03:00
|
|
|
public void reload(ReloadType type) throws Exception {
|
|
|
|
config.close(type);
|
|
|
|
Map<String, AuthProviderPair> pairs = null;
|
|
|
|
if (type.equals(ReloadType.NO_AUTH)) {
|
|
|
|
pairs = config.auth;
|
|
|
|
}
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.info("Reading LaunchServer config file");
|
2020-04-05 10:27:04 +03:00
|
|
|
config = launchServerConfigManager.readConfig();
|
|
|
|
config.setLaunchServer(this);
|
|
|
|
if (type.equals(ReloadType.NO_AUTH)) {
|
|
|
|
config.auth = pairs;
|
|
|
|
}
|
|
|
|
config.verify();
|
|
|
|
config.init(type);
|
|
|
|
if (type.equals(ReloadType.FULL) && config.components != null) {
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.debug("Init components");
|
2020-04-05 10:27:04 +03:00
|
|
|
config.components.forEach((k, v) -> {
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.debug("Init component {}", k);
|
2021-03-26 17:56:17 +03:00
|
|
|
v.setComponentName(k);
|
2020-04-05 10:27:04 +03:00
|
|
|
v.init(this);
|
|
|
|
});
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.debug("Init components successful");
|
2020-04-05 10:27:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Map<String, Command> getCommands() {
|
|
|
|
Map<String, Command> commands = new HashMap<>();
|
2021-06-21 10:14:25 +03:00
|
|
|
SubCommand reload = new SubCommand("[type]", "reload launchserver config") {
|
2020-04-05 10:27:04 +03:00
|
|
|
@Override
|
|
|
|
public void invoke(String... args) throws Exception {
|
|
|
|
if (args.length == 0) {
|
|
|
|
reload(ReloadType.FULL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
switch (args[0]) {
|
2021-09-22 08:19:18 +03:00
|
|
|
case "full" -> reload(ReloadType.FULL);
|
|
|
|
case "no_auth" -> reload(ReloadType.NO_AUTH);
|
|
|
|
case "no_components" -> reload(ReloadType.NO_COMPONENTS);
|
|
|
|
default -> reload(ReloadType.FULL);
|
2020-04-05 10:27:04 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
commands.put("reload", reload);
|
2021-06-21 10:14:25 +03:00
|
|
|
SubCommand save = new SubCommand("[]", "save launchserver config") {
|
2021-02-12 17:33:05 +03:00
|
|
|
@Override
|
|
|
|
public void invoke(String... args) throws Exception {
|
|
|
|
launchServerConfigManager.writeConfig(config);
|
|
|
|
launchServerConfigManager.writeRuntimeConfig(runtime);
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.info("LaunchServerConfig saved");
|
2021-02-12 17:33:05 +03:00
|
|
|
}
|
|
|
|
};
|
|
|
|
commands.put("save", save);
|
2021-06-21 10:14:25 +03:00
|
|
|
LaunchServer instance = this;
|
|
|
|
SubCommand resetauth = new SubCommand("authId", "reset auth by id") {
|
|
|
|
@Override
|
|
|
|
public void invoke(String... args) throws Exception {
|
|
|
|
verifyArgs(args, 1);
|
|
|
|
AuthProviderPair pair = config.getAuthProviderPair(args[0]);
|
2021-07-21 19:43:47 +03:00
|
|
|
if (pair == null) {
|
2021-06-21 10:14:25 +03:00
|
|
|
logger.error("Pair not found");
|
|
|
|
return;
|
|
|
|
}
|
2021-07-21 19:43:47 +03:00
|
|
|
if (pair.isUseCore()) {
|
2021-06-21 10:14:25 +03:00
|
|
|
pair.core.close();
|
|
|
|
} else {
|
|
|
|
pair.provider.close();
|
|
|
|
pair.handler.close();
|
|
|
|
pair.handler = null;
|
|
|
|
pair.provider = null;
|
|
|
|
}
|
|
|
|
pair.core = new RejectAuthCoreProvider();
|
|
|
|
pair.core.init(instance);
|
|
|
|
}
|
2021-07-21 19:43:47 +03:00
|
|
|
};
|
|
|
|
commands.put("resetauth", resetauth);
|
2020-04-05 10:27:04 +03:00
|
|
|
return commands;
|
|
|
|
}
|
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
private LauncherBinary binary() {
|
2019-05-15 14:11:22 +03:00
|
|
|
if (launcherEXEBinaryClass != null) {
|
|
|
|
try {
|
2019-11-15 16:37:43 +03:00
|
|
|
return (LauncherBinary) MethodHandles.publicLookup().findConstructor(launcherEXEBinaryClass, MethodType.methodType(void.class, LaunchServer.class)).invoke(this);
|
|
|
|
} catch (Throwable e) {
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.error(e);
|
2019-05-15 14:11:22 +03:00
|
|
|
}
|
|
|
|
}
|
2019-01-15 06:35:39 +03:00
|
|
|
try {
|
|
|
|
Class.forName("net.sf.launch4j.Builder");
|
2018-12-26 16:17:47 +03:00
|
|
|
if (config.launch4j.enabled) return new EXEL4JLauncherBinary(this);
|
2019-01-15 06:35:39 +03:00
|
|
|
} catch (ClassNotFoundException ignored) {
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.warn("Launch4J isn't in classpath.");
|
2019-01-15 06:35:39 +03:00
|
|
|
}
|
2018-09-22 17:33:00 +03:00
|
|
|
return new EXELauncherBinary(this);
|
2018-09-17 10:07:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
public void buildLauncherBinaries() throws IOException {
|
|
|
|
launcherBinary.build();
|
|
|
|
launcherEXEBinary.build();
|
|
|
|
}
|
|
|
|
|
2019-08-25 12:39:08 +03:00
|
|
|
public void close() throws Exception {
|
2021-05-10 09:52:12 +03:00
|
|
|
service.shutdownNow();
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.info("Close server socket");
|
2021-02-12 17:19:49 +03:00
|
|
|
nettyServerSocketHandler.close();
|
2018-09-17 10:07:32 +03:00
|
|
|
// Close handlers & providers
|
2019-08-25 09:48:26 +03:00
|
|
|
config.close(ReloadType.FULL);
|
2019-08-26 13:24:19 +03:00
|
|
|
modulesManager.invokeEvent(new ClosePhase());
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.info("Save LaunchServer runtime config");
|
2019-08-25 12:39:08 +03:00
|
|
|
launchServerConfigManager.writeRuntimeConfig(runtime);
|
2018-09-17 10:07:32 +03:00
|
|
|
// Print last message before death :(
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.info("LaunchServer stopped");
|
2018-09-17 10:07:32 +03:00
|
|
|
}
|
|
|
|
|
2021-05-04 14:08:13 +03:00
|
|
|
public Set<ClientProfile> getProfiles() {
|
2018-09-17 10:07:32 +03:00
|
|
|
return profilesList;
|
|
|
|
}
|
|
|
|
|
2021-05-04 14:08:13 +03:00
|
|
|
public void setProfiles(Set<ClientProfile> profilesList) {
|
|
|
|
this.profilesList = Collections.unmodifiableSet(profilesList);
|
2019-04-07 10:53:39 +03:00
|
|
|
}
|
2018-10-13 11:01:10 +03:00
|
|
|
|
2021-06-01 01:48:33 +03:00
|
|
|
@Deprecated
|
2019-07-12 02:10:31 +03:00
|
|
|
public HashedDir getUpdateDir(String name) {
|
2018-09-17 10:07:32 +03:00
|
|
|
return updatesDirMap.get(name);
|
|
|
|
}
|
|
|
|
|
2021-06-01 01:48:33 +03:00
|
|
|
@Deprecated
|
2019-07-12 02:10:31 +03:00
|
|
|
public Set<Entry<String, HashedDir>> getUpdateDirs() {
|
2018-09-17 10:07:32 +03:00
|
|
|
return updatesDirMap.entrySet();
|
|
|
|
}
|
|
|
|
|
2019-01-29 18:42:31 +03:00
|
|
|
public void rebindNettyServerSocket() {
|
|
|
|
nettyServerSocketHandler.close();
|
|
|
|
CommonHelper.newThread("Netty Server Socket Thread", false, nettyServerSocketHandler).start();
|
|
|
|
}
|
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
if (started.getAndSet(true))
|
2018-09-22 17:33:00 +03:00
|
|
|
throw new IllegalStateException("LaunchServer has been already started");
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
|
|
// Add shutdown hook, then start LaunchServer
|
2019-08-25 12:39:08 +03:00
|
|
|
if (!this.env.equals(LaunchServerEnv.TEST)) {
|
|
|
|
JVMHelper.RUNTIME.addShutdownHook(CommonHelper.newThread(null, false, () -> {
|
|
|
|
try {
|
|
|
|
close();
|
|
|
|
} catch (Exception e) {
|
2021-06-20 06:42:13 +03:00
|
|
|
logger.error("LaunchServer close error", e);
|
2019-08-25 12:39:08 +03:00
|
|
|
}
|
|
|
|
}));
|
2019-05-15 14:11:22 +03:00
|
|
|
CommonHelper.newThread("Command Thread", true, commandHandler).start();
|
2021-03-30 12:13:41 +03:00
|
|
|
// Sync updates dir
|
|
|
|
CommonHelper.newThread("Profiles and updates sync", true, () -> {
|
|
|
|
try {
|
|
|
|
if (!IOHelper.isDir(updatesDir))
|
|
|
|
Files.createDirectory(updatesDir);
|
2021-06-01 01:48:33 +03:00
|
|
|
updatesManager.readUpdatesDir();
|
2021-03-30 12:13:41 +03:00
|
|
|
|
|
|
|
// Sync profiles dir
|
|
|
|
if (!IOHelper.isDir(profilesDir))
|
|
|
|
Files.createDirectory(profilesDir);
|
|
|
|
syncProfilesDir();
|
|
|
|
modulesManager.invokeEvent(new LaunchServerProfilesSyncEvent(this));
|
|
|
|
} catch (IOException e) {
|
2021-06-20 06:42:13 +03:00
|
|
|
logger.error("Updates/Profiles not synced", e);
|
2021-03-30 12:13:41 +03:00
|
|
|
}
|
2021-03-31 20:24:36 +03:00
|
|
|
}).start();
|
2019-04-07 10:53:39 +03:00
|
|
|
}
|
2019-04-03 16:27:40 +03:00
|
|
|
if (config.netty != null)
|
2019-01-29 18:42:31 +03:00
|
|
|
rebindNettyServerSocket();
|
2021-02-12 17:19:49 +03:00
|
|
|
try {
|
|
|
|
modulesManager.fullInitializedLaunchServer(this);
|
|
|
|
modulesManager.invokeEvent(new LaunchServerFullInitEvent(this));
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.info("LaunchServer started");
|
2021-02-12 17:19:49 +03:00
|
|
|
} catch (Throwable e) {
|
2021-07-21 19:43:47 +03:00
|
|
|
logger.error("LaunchServer startup failed", e);
|
2021-02-12 17:19:49 +03:00
|
|
|
JVMHelper.RUNTIME.exit(-1);
|
|
|
|
}
|
2018-09-17 10:07:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
public void syncLauncherBinaries() throws IOException {
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.info("Syncing launcher binaries");
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
|
|
// Syncing launcher binary
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.info("Syncing launcher binary file");
|
|
|
|
if (!launcherBinary.sync()) logger.warn("Missing launcher binary file");
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
|
|
// Syncing launcher EXE binary
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.info("Syncing launcher EXE binary file");
|
2018-09-17 10:07:32 +03:00
|
|
|
if (!launcherEXEBinary.sync() && config.launch4j.enabled)
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.warn("Missing launcher EXE binary file");
|
2018-09-17 10:07:32 +03:00
|
|
|
|
|
|
|
}
|
2018-09-22 17:33:00 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public void syncProfilesDir() throws IOException {
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.info("Syncing profiles dir");
|
2018-12-23 18:57:40 +03:00
|
|
|
List<ClientProfile> newProfies = new LinkedList<>();
|
2018-09-17 10:07:32 +03:00
|
|
|
IOHelper.walk(profilesDir, new ProfilesFileVisitor(newProfies), false);
|
|
|
|
|
|
|
|
// Sort and set new profiles
|
2018-12-23 18:57:40 +03:00
|
|
|
newProfies.sort(Comparator.comparing(a -> a));
|
2021-05-04 14:08:13 +03:00
|
|
|
profilesList = Set.copyOf(newProfies);
|
2020-09-25 18:48:33 +03:00
|
|
|
if (pingServerManager != null)
|
2020-06-24 07:06:08 +03:00
|
|
|
pingServerManager.syncServers();
|
2018-09-17 10:07:32 +03:00
|
|
|
}
|
2018-09-22 17:33:00 +03:00
|
|
|
|
2018-09-17 10:07:32 +03:00
|
|
|
public void syncUpdatesDir(Collection<String> dirs) throws IOException {
|
2021-06-01 01:48:33 +03:00
|
|
|
updatesManager.syncUpdatesDir(dirs);
|
2018-09-17 10:07:32 +03:00
|
|
|
}
|
2019-01-15 06:35:39 +03:00
|
|
|
|
2019-01-04 14:32:16 +03:00
|
|
|
public void restart() {
|
2019-01-15 06:35:39 +03:00
|
|
|
ProcessBuilder builder = new ProcessBuilder();
|
2019-01-16 11:52:47 +03:00
|
|
|
if (config.startScript != null) builder.command(Collections.singletonList(config.startScript));
|
2019-01-15 06:35:39 +03:00
|
|
|
else throw new IllegalArgumentException("Please create start script and link it as startScript in config.");
|
2019-01-04 14:32:16 +03:00
|
|
|
builder.directory(this.dir.toFile());
|
|
|
|
builder.inheritIO();
|
|
|
|
builder.redirectErrorStream(true);
|
|
|
|
builder.redirectOutput(Redirect.PIPE);
|
|
|
|
try {
|
2019-01-15 06:35:39 +03:00
|
|
|
builder.start();
|
|
|
|
} catch (IOException e) {
|
2021-06-20 06:42:13 +03:00
|
|
|
logger.error("Restart failed", e);
|
2019-01-15 06:35:39 +03:00
|
|
|
}
|
2019-01-04 14:32:16 +03:00
|
|
|
}
|
|
|
|
|
2019-04-03 16:27:40 +03:00
|
|
|
public void registerObject(String name, Object object) {
|
|
|
|
if (object instanceof Reconfigurable) {
|
2019-04-03 13:09:53 +03:00
|
|
|
reconfigurableManager.registerReconfigurable(name, (Reconfigurable) object);
|
|
|
|
}
|
2019-04-03 16:27:40 +03:00
|
|
|
if (object instanceof NeedGarbageCollection) {
|
2019-04-03 13:09:53 +03:00
|
|
|
GarbageManager.registerNeedGC((NeedGarbageCollection) object);
|
|
|
|
}
|
|
|
|
}
|
2019-05-15 14:11:22 +03:00
|
|
|
|
2019-04-27 13:17:10 +03:00
|
|
|
public void unregisterObject(String name, Object object) {
|
|
|
|
if (object instanceof Reconfigurable) {
|
|
|
|
reconfigurableManager.unregisterReconfigurable(name);
|
|
|
|
}
|
|
|
|
if (object instanceof NeedGarbageCollection) {
|
|
|
|
GarbageManager.unregisterNeedGC((NeedGarbageCollection) object);
|
|
|
|
}
|
|
|
|
}
|
2019-04-03 13:09:53 +03:00
|
|
|
|
2019-01-15 06:35:39 +03:00
|
|
|
public void fullyRestart() {
|
|
|
|
restart();
|
2019-01-04 14:32:16 +03:00
|
|
|
JVMHelper.RUNTIME.exit(0);
|
2019-01-15 06:35:39 +03:00
|
|
|
}
|
2020-04-05 10:27:04 +03:00
|
|
|
|
|
|
|
|
|
|
|
public enum ReloadType {
|
|
|
|
NO_AUTH,
|
|
|
|
NO_COMPONENTS,
|
|
|
|
FULL
|
|
|
|
}
|
|
|
|
|
|
|
|
public enum LaunchServerEnv {
|
|
|
|
TEST,
|
|
|
|
DEV,
|
|
|
|
DEBUG,
|
|
|
|
PRODUCTION
|
|
|
|
}
|
|
|
|
|
|
|
|
public interface LaunchServerConfigManager {
|
|
|
|
LaunchServerConfig readConfig() throws IOException;
|
|
|
|
|
|
|
|
LaunchServerRuntimeConfig readRuntimeConfig() throws IOException;
|
|
|
|
|
|
|
|
void writeConfig(LaunchServerConfig config) throws IOException;
|
|
|
|
|
|
|
|
void writeRuntimeConfig(LaunchServerRuntimeConfig config) throws IOException;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final class ProfilesFileVisitor extends SimpleFileVisitor<Path> {
|
|
|
|
private final Collection<ClientProfile> result;
|
2021-04-13 12:47:42 +03:00
|
|
|
private final Logger logger = LogManager.getLogger();
|
2020-04-05 10:27:04 +03:00
|
|
|
|
|
|
|
private ProfilesFileVisitor(Collection<ClientProfile> result) {
|
|
|
|
this.result = result;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.info("Syncing '{}' profile", IOHelper.getFileName(file));
|
2020-04-05 10:27:04 +03:00
|
|
|
|
|
|
|
// Read profile
|
|
|
|
ClientProfile profile;
|
|
|
|
try (BufferedReader reader = IOHelper.newReader(file)) {
|
|
|
|
profile = Launcher.gsonManager.gson.fromJson(reader, ClientProfile.class);
|
|
|
|
}
|
|
|
|
profile.verify();
|
|
|
|
|
|
|
|
// Add SIGNED profile to result list
|
|
|
|
result.add(profile);
|
|
|
|
return super.visitFile(file, attrs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static class LaunchServerDirectories {
|
|
|
|
public static final String UPDATES_NAME = "updates", PROFILES_NAME = "profiles",
|
|
|
|
TRUSTSTORE_NAME = "truststore", LAUNCHERLIBRARIES_NAME = "launcher-libraries",
|
2021-04-07 10:00:30 +03:00
|
|
|
LAUNCHERLIBRARIESCOMPILE_NAME = "launcher-libraries-compile", KEY_NAME = ".keys";
|
2020-04-05 10:27:04 +03:00
|
|
|
public Path updatesDir;
|
|
|
|
public Path profilesDir;
|
|
|
|
public Path launcherLibrariesDir;
|
|
|
|
public Path launcherLibrariesCompileDir;
|
2021-04-07 10:00:30 +03:00
|
|
|
public Path keyDirectory;
|
2020-04-05 10:27:04 +03:00
|
|
|
public Path dir;
|
|
|
|
public Path trustStore;
|
2021-04-13 14:23:39 +03:00
|
|
|
public Path tmpDir;
|
2020-04-05 10:27:04 +03:00
|
|
|
|
|
|
|
public void collect() {
|
2021-05-11 12:46:37 +03:00
|
|
|
if (updatesDir == null) updatesDir = getPath(UPDATES_NAME);
|
|
|
|
if (profilesDir == null) profilesDir = getPath(PROFILES_NAME);
|
|
|
|
if (trustStore == null) trustStore = getPath(TRUSTSTORE_NAME);
|
|
|
|
if (launcherLibrariesDir == null) launcherLibrariesDir = getPath(LAUNCHERLIBRARIES_NAME);
|
2020-04-05 10:27:04 +03:00
|
|
|
if (launcherLibrariesCompileDir == null)
|
2021-05-11 12:46:37 +03:00
|
|
|
launcherLibrariesCompileDir = getPath(LAUNCHERLIBRARIESCOMPILE_NAME);
|
2021-05-25 12:17:29 +03:00
|
|
|
if (keyDirectory == null) keyDirectory = getPath(KEY_NAME);
|
|
|
|
if (tmpDir == null)
|
|
|
|
tmpDir = Paths.get(System.getProperty("java.io.tmpdir")).resolve(String.format("launchserver-%s", SecurityHelper.randomStringToken()));
|
2020-04-05 10:27:04 +03:00
|
|
|
}
|
2021-05-11 12:46:37 +03:00
|
|
|
|
|
|
|
private Path getPath(String dirName) {
|
2021-05-25 12:17:29 +03:00
|
|
|
String property = System.getProperty("launchserver.dir." + dirName, null);
|
|
|
|
if (property == null) return dir.resolve(dirName);
|
2021-05-11 12:46:37 +03:00
|
|
|
else return Paths.get(property);
|
|
|
|
}
|
2020-04-05 10:27:04 +03:00
|
|
|
}
|
2018-09-17 10:07:32 +03:00
|
|
|
}
|