2019-08-25 12:39:08 +03:00
|
|
|
package pro.gravit.launchserver;
|
|
|
|
|
2021-04-13 12:47:42 +03:00
|
|
|
import org.apache.logging.log4j.LogManager;
|
|
|
|
import org.apache.logging.log4j.Logger;
|
2019-10-16 12:38:44 +03:00
|
|
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
2019-08-25 12:39:08 +03:00
|
|
|
import pro.gravit.launcher.Launcher;
|
2020-04-05 10:27:04 +03:00
|
|
|
import pro.gravit.launcher.LauncherTrustManager;
|
2019-08-26 13:24:19 +03:00
|
|
|
import pro.gravit.launcher.modules.events.PreConfigPhase;
|
2020-09-12 09:41:58 +03:00
|
|
|
import pro.gravit.launcher.profiles.optional.actions.OptionalAction;
|
2021-06-18 07:34:32 +03:00
|
|
|
import pro.gravit.launcher.profiles.optional.triggers.OptionalTrigger;
|
2019-09-27 03:31:19 +03:00
|
|
|
import pro.gravit.launcher.request.auth.AuthRequest;
|
2021-04-13 15:30:06 +03:00
|
|
|
import pro.gravit.launcher.request.auth.GetAvailabilityAuthRequest;
|
2021-05-16 16:07:44 +03:00
|
|
|
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
|
2021-05-19 22:18:01 +03:00
|
|
|
import pro.gravit.launchserver.auth.password.PasswordVerifier;
|
2019-08-25 12:39:08 +03:00
|
|
|
import pro.gravit.launchserver.auth.protect.ProtectHandler;
|
|
|
|
import pro.gravit.launchserver.auth.texture.TextureProvider;
|
|
|
|
import pro.gravit.launchserver.components.Component;
|
|
|
|
import pro.gravit.launchserver.config.LaunchServerConfig;
|
|
|
|
import pro.gravit.launchserver.config.LaunchServerRuntimeConfig;
|
2019-10-17 16:22:24 +03:00
|
|
|
import pro.gravit.launchserver.manangers.CertificateManager;
|
2019-08-25 12:39:08 +03:00
|
|
|
import pro.gravit.launchserver.manangers.LaunchServerGsonManager;
|
2019-08-26 13:24:19 +03:00
|
|
|
import pro.gravit.launchserver.modules.impl.LaunchServerModulesManager;
|
2019-08-25 12:39:08 +03:00
|
|
|
import pro.gravit.launchserver.socket.WebSocketService;
|
2022-12-22 15:07:46 +03:00
|
|
|
import pro.gravit.utils.Version;
|
2019-08-25 12:39:08 +03:00
|
|
|
import pro.gravit.utils.command.CommandHandler;
|
|
|
|
import pro.gravit.utils.command.JLineCommandHandler;
|
|
|
|
import pro.gravit.utils.command.StdCommandHandler;
|
|
|
|
import pro.gravit.utils.helper.IOHelper;
|
|
|
|
import pro.gravit.utils.helper.JVMHelper;
|
|
|
|
import pro.gravit.utils.helper.LogHelper;
|
|
|
|
|
2022-12-09 17:17:07 +03:00
|
|
|
import java.io.*;
|
2019-10-19 19:46:04 +03:00
|
|
|
import java.nio.file.Files;
|
|
|
|
import java.nio.file.Path;
|
|
|
|
import java.security.Security;
|
|
|
|
import java.security.cert.CertificateException;
|
2022-12-22 15:07:46 +03:00
|
|
|
import java.util.List;
|
2019-10-19 19:46:04 +03:00
|
|
|
|
2019-08-25 12:39:08 +03:00
|
|
|
public class LaunchServerStarter {
|
2019-10-19 19:43:25 +03:00
|
|
|
public static final boolean allowUnsigned = Boolean.getBoolean("launchserver.allowUnsigned");
|
2021-03-30 12:13:41 +03:00
|
|
|
public static final boolean prepareMode = Boolean.getBoolean("launchserver.prepareMode");
|
2021-04-13 12:47:42 +03:00
|
|
|
private static final Logger logger = LogManager.getLogger();
|
2019-10-19 19:46:04 +03:00
|
|
|
|
2019-08-25 12:39:08 +03:00
|
|
|
public static void main(String[] args) throws Exception {
|
|
|
|
JVMHelper.checkStackTrace(LaunchServerStarter.class);
|
|
|
|
JVMHelper.verifySystemProperties(LaunchServer.class, true);
|
2021-04-13 12:47:42 +03:00
|
|
|
//LogHelper.addOutput(IOHelper.WORKING_DIR.resolve("LaunchServer.log"));
|
2019-08-25 12:39:08 +03:00
|
|
|
LogHelper.printVersion("LaunchServer");
|
|
|
|
LogHelper.printLicense("LaunchServer");
|
|
|
|
if (!StarterAgent.isAgentStarted()) {
|
|
|
|
LogHelper.error("StarterAgent is not started!");
|
|
|
|
LogHelper.error("You should add to JVM options this option: `-javaagent:LaunchServer.jar`");
|
|
|
|
}
|
|
|
|
Path dir = IOHelper.WORKING_DIR;
|
|
|
|
Path configFile, runtimeConfigFile;
|
2021-02-12 16:56:55 +03:00
|
|
|
try {
|
|
|
|
Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider");
|
|
|
|
Security.addProvider(new BouncyCastleProvider());
|
|
|
|
} catch (ClassNotFoundException ex) {
|
|
|
|
LogHelper.error("Library BouncyCastle not found! Is directory 'libraries' empty?");
|
|
|
|
return;
|
|
|
|
}
|
2019-10-17 16:22:24 +03:00
|
|
|
CertificateManager certificateManager = new CertificateManager();
|
|
|
|
try {
|
|
|
|
certificateManager.readTrustStore(dir.resolve("truststore"));
|
|
|
|
} catch (CertificateException e) {
|
|
|
|
throw new IOException(e);
|
|
|
|
}
|
2019-10-17 16:58:52 +03:00
|
|
|
{
|
2021-01-09 17:06:35 +03:00
|
|
|
LauncherTrustManager.CheckClassResult result = certificateManager.checkClass(LaunchServer.class);
|
2021-03-20 11:53:22 +03:00
|
|
|
if (result.type == LauncherTrustManager.CheckClassResultType.SUCCESS) {
|
2021-10-15 17:29:26 +03:00
|
|
|
logger.info("LaunchServer signed by {}", result.endCertificate.getSubjectX500Principal().getName());
|
2021-03-20 11:53:22 +03:00
|
|
|
} else if (result.type == LauncherTrustManager.CheckClassResultType.NOT_SIGNED) {
|
2021-01-09 17:06:35 +03:00
|
|
|
// None
|
2021-03-20 11:53:22 +03:00
|
|
|
} else {
|
|
|
|
if (result.exception != null) {
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.error(result.exception);
|
2021-01-09 17:06:35 +03:00
|
|
|
}
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.warn("LaunchServer signed incorrectly. Status: {}", result.type.name());
|
2021-01-09 17:06:35 +03:00
|
|
|
}
|
2019-10-17 16:58:52 +03:00
|
|
|
}
|
2019-08-25 12:39:08 +03:00
|
|
|
|
|
|
|
LaunchServerRuntimeConfig runtimeConfig;
|
|
|
|
LaunchServerConfig config;
|
|
|
|
LaunchServer.LaunchServerEnv env = LaunchServer.LaunchServerEnv.PRODUCTION;
|
2019-10-17 16:22:24 +03:00
|
|
|
LaunchServerModulesManager modulesManager = new LaunchServerModulesManager(dir.resolve("modules"), dir.resolve("config"), certificateManager.trustManager);
|
2019-08-26 13:24:19 +03:00
|
|
|
modulesManager.autoload();
|
|
|
|
modulesManager.initModules(null);
|
2019-08-25 12:39:08 +03:00
|
|
|
registerAll();
|
2019-08-26 13:24:19 +03:00
|
|
|
initGson(modulesManager);
|
2022-12-22 15:07:46 +03:00
|
|
|
printExperimentalBranch();
|
2019-08-25 12:39:08 +03:00
|
|
|
if (IOHelper.exists(dir.resolve("LaunchServer.conf"))) {
|
|
|
|
configFile = dir.resolve("LaunchServer.conf");
|
|
|
|
} else {
|
|
|
|
configFile = dir.resolve("LaunchServer.json");
|
|
|
|
}
|
|
|
|
if (IOHelper.exists(dir.resolve("RuntimeLaunchServer.conf"))) {
|
|
|
|
runtimeConfigFile = dir.resolve("RuntimeLaunchServer.conf");
|
|
|
|
} else {
|
|
|
|
runtimeConfigFile = dir.resolve("RuntimeLaunchServer.json");
|
|
|
|
}
|
|
|
|
CommandHandler localCommandHandler;
|
|
|
|
try {
|
|
|
|
Class.forName("org.jline.terminal.Terminal");
|
|
|
|
|
|
|
|
// JLine2 available
|
|
|
|
localCommandHandler = new JLineCommandHandler();
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.info("JLine2 terminal enabled");
|
2019-08-25 12:39:08 +03:00
|
|
|
} catch (ClassNotFoundException ignored) {
|
|
|
|
localCommandHandler = new StdCommandHandler(true);
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.warn("JLine2 isn't in classpath, using std");
|
2019-08-25 12:39:08 +03:00
|
|
|
}
|
2019-08-26 14:22:24 +03:00
|
|
|
modulesManager.invokeEvent(new PreConfigPhase());
|
|
|
|
generateConfigIfNotExists(configFile, localCommandHandler, env);
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.info("Reading LaunchServer config file");
|
2019-08-26 14:22:24 +03:00
|
|
|
try (BufferedReader reader = IOHelper.newReader(configFile)) {
|
|
|
|
config = Launcher.gsonManager.gson.fromJson(reader, LaunchServerConfig.class);
|
|
|
|
}
|
2019-08-25 12:39:08 +03:00
|
|
|
if (!Files.exists(runtimeConfigFile)) {
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.info("Reset LaunchServer runtime config file");
|
2019-08-25 12:39:08 +03:00
|
|
|
runtimeConfig = new LaunchServerRuntimeConfig();
|
|
|
|
runtimeConfig.reset();
|
|
|
|
} else {
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.info("Reading LaunchServer runtime config file");
|
2019-08-25 12:39:08 +03:00
|
|
|
try (BufferedReader reader = IOHelper.newReader(runtimeConfigFile)) {
|
|
|
|
runtimeConfig = Launcher.gsonManager.gson.fromJson(reader, LaunchServerRuntimeConfig.class);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LaunchServer.LaunchServerConfigManager launchServerConfigManager = new LaunchServer.LaunchServerConfigManager() {
|
|
|
|
@Override
|
|
|
|
public LaunchServerConfig readConfig() throws IOException {
|
|
|
|
LaunchServerConfig config1;
|
|
|
|
try (BufferedReader reader = IOHelper.newReader(configFile)) {
|
|
|
|
config1 = Launcher.gsonManager.gson.fromJson(reader, LaunchServerConfig.class);
|
|
|
|
}
|
|
|
|
return config1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public LaunchServerRuntimeConfig readRuntimeConfig() throws IOException {
|
|
|
|
LaunchServerRuntimeConfig config1;
|
|
|
|
try (BufferedReader reader = IOHelper.newReader(runtimeConfigFile)) {
|
|
|
|
config1 = Launcher.gsonManager.gson.fromJson(reader, LaunchServerRuntimeConfig.class);
|
|
|
|
}
|
|
|
|
return config1;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void writeConfig(LaunchServerConfig config) throws IOException {
|
2022-12-09 17:17:07 +03:00
|
|
|
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
|
|
|
try (Writer writer = IOHelper.newWriter(output)) {
|
2019-08-25 12:39:08 +03:00
|
|
|
if (Launcher.gsonManager.configGson != null) {
|
|
|
|
Launcher.gsonManager.configGson.toJson(config, writer);
|
|
|
|
} else {
|
2022-12-09 17:23:18 +03:00
|
|
|
logger.error("Error writing LaunchServer config file. Gson is null");
|
2019-08-25 12:39:08 +03:00
|
|
|
}
|
|
|
|
}
|
2022-12-09 17:23:18 +03:00
|
|
|
byte[] bytes = output.toByteArray();
|
|
|
|
if(bytes.length > 0) {
|
|
|
|
IOHelper.write(configFile, bytes);
|
|
|
|
}
|
2019-08-25 12:39:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void writeRuntimeConfig(LaunchServerRuntimeConfig config) throws IOException {
|
2022-12-09 17:17:07 +03:00
|
|
|
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
|
|
|
try (Writer writer = IOHelper.newWriter(output)) {
|
2019-08-25 12:39:08 +03:00
|
|
|
if (Launcher.gsonManager.configGson != null) {
|
|
|
|
Launcher.gsonManager.configGson.toJson(config, writer);
|
|
|
|
} else {
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.error("Error writing LaunchServer runtime config file. Gson is null");
|
2019-08-25 12:39:08 +03:00
|
|
|
}
|
|
|
|
}
|
2022-12-09 17:23:18 +03:00
|
|
|
byte[] bytes = output.toByteArray();
|
|
|
|
if(bytes.length > 0) {
|
|
|
|
IOHelper.write(runtimeConfigFile, bytes);
|
|
|
|
}
|
2019-08-25 12:39:08 +03:00
|
|
|
}
|
|
|
|
};
|
2019-11-28 19:14:06 +03:00
|
|
|
LaunchServer.LaunchServerDirectories directories = new LaunchServer.LaunchServerDirectories();
|
|
|
|
directories.dir = dir;
|
2019-08-25 12:39:08 +03:00
|
|
|
LaunchServer server = new LaunchServerBuilder()
|
2019-11-28 19:14:06 +03:00
|
|
|
.setDirectories(directories)
|
2019-08-25 12:39:08 +03:00
|
|
|
.setEnv(env)
|
|
|
|
.setCommandHandler(localCommandHandler)
|
|
|
|
.setRuntimeConfig(runtimeConfig)
|
|
|
|
.setConfig(config)
|
|
|
|
.setModulesManager(modulesManager)
|
|
|
|
.setLaunchServerConfigManager(launchServerConfigManager)
|
2019-10-17 16:22:24 +03:00
|
|
|
.setCertificateManager(certificateManager)
|
2019-08-25 12:39:08 +03:00
|
|
|
.build();
|
2021-05-25 12:17:29 +03:00
|
|
|
if (!prepareMode) {
|
2021-03-30 12:13:41 +03:00
|
|
|
server.run();
|
|
|
|
} else {
|
|
|
|
server.close();
|
|
|
|
}
|
2019-08-25 12:39:08 +03:00
|
|
|
}
|
|
|
|
|
2019-08-26 13:24:19 +03:00
|
|
|
public static void initGson(LaunchServerModulesManager modulesManager) {
|
|
|
|
Launcher.gsonManager = new LaunchServerGsonManager(modulesManager);
|
2019-08-25 12:39:08 +03:00
|
|
|
Launcher.gsonManager.initGson();
|
|
|
|
}
|
|
|
|
|
2021-05-16 16:07:44 +03:00
|
|
|
@SuppressWarnings("deprecation")
|
2019-10-19 19:46:04 +03:00
|
|
|
public static void registerAll() {
|
2021-05-16 16:07:44 +03:00
|
|
|
AuthCoreProvider.registerProviders();
|
2021-05-19 22:18:01 +03:00
|
|
|
PasswordVerifier.registerProviders();
|
2019-08-25 12:39:08 +03:00
|
|
|
TextureProvider.registerProviders();
|
|
|
|
Component.registerComponents();
|
|
|
|
ProtectHandler.registerHandlers();
|
|
|
|
WebSocketService.registerResponses();
|
2019-09-27 03:31:19 +03:00
|
|
|
AuthRequest.registerProviders();
|
2021-04-13 15:30:06 +03:00
|
|
|
GetAvailabilityAuthRequest.registerProviders();
|
2020-09-12 09:41:58 +03:00
|
|
|
OptionalAction.registerProviders();
|
2021-06-18 07:34:32 +03:00
|
|
|
OptionalTrigger.registerProviders();
|
2019-08-25 12:39:08 +03:00
|
|
|
}
|
|
|
|
|
2022-12-22 15:07:46 +03:00
|
|
|
private static void printExperimentalBranch() {
|
|
|
|
try(Reader reader = IOHelper.newReader(IOHelper.getResourceURL("experimental-build.json"))) {
|
|
|
|
ExperimentalBuild info = Launcher.gsonManager.configGson.fromJson(reader, ExperimentalBuild.class);
|
|
|
|
if(info.features == null || info.features.isEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
logger.warn("This is experimental build. Please do not use this in production");
|
|
|
|
logger.warn("Experimental features: [{}]", String.join(",", info.features));
|
|
|
|
} catch (Throwable e) {
|
|
|
|
logger.warn("Build information not found");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
record ExperimentalBuild(List<String> features) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-08-25 12:39:08 +03:00
|
|
|
public static void generateConfigIfNotExists(Path configFile, CommandHandler commandHandler, LaunchServer.LaunchServerEnv env) throws IOException {
|
|
|
|
if (IOHelper.isFile(configFile))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Create new config
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.info("Creating LaunchServer config");
|
2019-08-25 12:39:08 +03:00
|
|
|
|
|
|
|
|
|
|
|
LaunchServerConfig newConfig = LaunchServerConfig.getDefault(env);
|
|
|
|
// Set server address
|
|
|
|
String address;
|
|
|
|
if (env.equals(LaunchServer.LaunchServerEnv.TEST)) {
|
|
|
|
address = "localhost";
|
|
|
|
newConfig.setProjectName("test");
|
|
|
|
} else {
|
2021-05-11 12:46:37 +03:00
|
|
|
address = System.getenv("ADDRESS");
|
2021-05-25 12:17:29 +03:00
|
|
|
if (address == null) {
|
2021-05-11 12:46:37 +03:00
|
|
|
address = System.getProperty("launchserver.address", null);
|
|
|
|
}
|
2021-05-25 12:17:29 +03:00
|
|
|
if (address == null) {
|
2021-05-11 12:46:37 +03:00
|
|
|
System.out.println("LaunchServer address(default: localhost): ");
|
|
|
|
address = commandHandler.readLine();
|
|
|
|
}
|
|
|
|
String projectName = System.getenv("PROJECTNAME");
|
2021-05-25 12:17:29 +03:00
|
|
|
if (projectName == null) {
|
2021-05-11 12:46:37 +03:00
|
|
|
projectName = System.getProperty("launchserver.projectname", null);
|
|
|
|
}
|
2021-05-25 12:17:29 +03:00
|
|
|
if (projectName == null) {
|
2021-05-11 12:46:37 +03:00
|
|
|
System.out.println("LaunchServer projectName: ");
|
|
|
|
projectName = commandHandler.readLine();
|
|
|
|
}
|
|
|
|
newConfig.setProjectName(projectName);
|
2019-08-25 12:39:08 +03:00
|
|
|
}
|
|
|
|
if (address == null || address.isEmpty()) {
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.error("Address null. Using localhost");
|
2019-08-25 12:39:08 +03:00
|
|
|
address = "localhost";
|
|
|
|
}
|
|
|
|
if (newConfig.projectName == null || newConfig.projectName.isEmpty()) {
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.error("ProjectName null. Using MineCraft");
|
2019-08-25 12:39:08 +03:00
|
|
|
newConfig.projectName = "MineCraft";
|
|
|
|
}
|
|
|
|
|
|
|
|
newConfig.netty.address = "ws://" + address + ":9274/api";
|
|
|
|
newConfig.netty.downloadURL = "http://" + address + ":9274/%dirname%/";
|
|
|
|
newConfig.netty.launcherURL = "http://" + address + ":9274/Launcher.jar";
|
|
|
|
newConfig.netty.launcherEXEURL = "http://" + address + ":9274/Launcher.exe";
|
|
|
|
|
|
|
|
// Write LaunchServer config
|
2021-04-13 12:47:42 +03:00
|
|
|
logger.info("Writing LaunchServer config file");
|
2019-08-25 12:39:08 +03:00
|
|
|
try (BufferedWriter writer = IOHelper.newWriter(configFile)) {
|
|
|
|
Launcher.gsonManager.configGson.toJson(newConfig, writer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|