From be565e22188b0f83acd8fefe643c30c4cdf4ee18 Mon Sep 17 00:00:00 2001 From: Gravita Date: Thu, 7 Apr 2022 01:34:58 +0700 Subject: [PATCH] [FEATURE][EXPERIMENTAL] ServerWrapper launch methods --- .../launcher/profiles/ClientProfile.java | 2 +- ServerWrapper/build.gradle | 23 +++++- .../gravit/launcher/server/ServerWrapper.java | 69 +++++++++--------- .../server/launch/ClasspathLaunch.java | 23 ++++++ .../gravit/launcher/server/launch/Launch.java | 7 ++ .../launcher/server/launch/ModuleLaunch.java | 10 +++ .../launcher/server/launch/SimpleLaunch.java | 17 +++++ .../launcher/server/launch/ModuleLaunch.java | 70 +++++++++++++++++++ modules | 2 +- 9 files changed, 188 insertions(+), 35 deletions(-) create mode 100644 ServerWrapper/src/main/java/pro/gravit/launcher/server/launch/ClasspathLaunch.java create mode 100644 ServerWrapper/src/main/java/pro/gravit/launcher/server/launch/Launch.java create mode 100644 ServerWrapper/src/main/java/pro/gravit/launcher/server/launch/ModuleLaunch.java create mode 100644 ServerWrapper/src/main/java/pro/gravit/launcher/server/launch/SimpleLaunch.java create mode 100644 ServerWrapper/src/main/java11/pro/gravit/launcher/server/launch/ModuleLaunch.java diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/profiles/ClientProfile.java b/LauncherAPI/src/main/java/pro/gravit/launcher/profiles/ClientProfile.java index 162b22c8..1190994a 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/profiles/ClientProfile.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/profiles/ClientProfile.java @@ -516,7 +516,7 @@ public enum SecurityManagerConfig { } public enum ClassLoaderConfig { - AGENT, LAUNCHER, SYSTEM_ARGS + AGENT, LAUNCHER, MODULE, SYSTEM_ARGS } public enum SignedClientConfig { diff --git a/ServerWrapper/build.gradle b/ServerWrapper/build.gradle index 4094ccba..ff62dd0a 100644 --- a/ServerWrapper/build.gradle +++ b/ServerWrapper/build.gradle @@ -14,16 +14,37 @@ } } +sourceSets { + java11 { + java { + srcDirs = ['src/main/java11'] + } + dependencies { + java11Implementation project(':LauncherAPI') + java11Implementation files(sourceSets.main.output.classesDirs) { builtBy compileJava } + } + } +} + sourceCompatibility = '1.8' targetCompatibility = '1.8' +compileJava11Java { + sourceCompatibility = 11 + targetCompatibility = 11 +} + jar { + into('META-INF/versions/11') { + from sourceSets.java11.output + } archiveClassifier.set('clean') manifest.attributes("Main-Class": mainClassName, "Premain-Class": mainAgentName, "Can-Redefine-Classes": "true", "Can-Retransform-Classes": "true", - "Can-Set-Native-Method-Prefix": "true") + "Can-Set-Native-Method-Prefix": "true", + "Multi-Release": "true") } task sourcesJar(type: Jar) { diff --git a/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java b/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java index a07766f2..396bd8c6 100644 --- a/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java +++ b/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java @@ -18,6 +18,10 @@ import pro.gravit.launcher.request.auth.RestoreRequest; import pro.gravit.launcher.request.update.ProfilesRequest; import pro.gravit.launcher.request.websockets.StdWebSocketService; +import pro.gravit.launcher.server.launch.ClasspathLaunch; +import pro.gravit.launcher.server.launch.Launch; +import pro.gravit.launcher.server.launch.ModuleLaunch; +import pro.gravit.launcher.server.launch.SimpleLaunch; import pro.gravit.launcher.server.setup.ServerWrapperSetup; import pro.gravit.utils.PublicURLClassLoader; import pro.gravit.utils.helper.IOHelper; @@ -139,22 +143,6 @@ public void run(String... args) throws Throwable { LogHelper.error("Auth not configured. Please use 'java -jar ServerWrapper.jar setup'"); System.exit(-1); } - Class mainClass; - if (config.classpath != null && !config.classpath.isEmpty()) { - if(config.classLoaderConfig == ClientProfile.ClassLoaderConfig.LAUNCHER) { - URL[] urls = config.classpath.stream().map(Paths::get).map(IOHelper::toURL).toArray(URL[]::new); - ucp = new PublicURLClassLoader(urls); - Thread.currentThread().setContextClassLoader(ucp); - loader = ucp; - } else if(config.classLoaderConfig == ClientProfile.ClassLoaderConfig.AGENT) { - if (!ServerAgent.isAgentStarted()) { - LogHelper.error("JavaAgent not found"); - System.exit(-1); - } - for (String c : config.classpath) - ServerAgent.addJVMClassPath(c); - } - } if (config.autoloadLibraries) { if (!ServerAgent.isAgentStarted()) { throw new UnsupportedOperationException("JavaAgent not found, autoloadLibraries not available"); @@ -165,24 +153,34 @@ public void run(String... args) throws Throwable { LogHelper.info("Load libraries"); ServerAgent.loadLibraries(librariesDir); } - if (loader != null) mainClass = Class.forName(classname, true, loader); - else mainClass = Class.forName(classname); - MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClass, "main", MethodType.methodType(void.class, String[].class)); LogHelper.info("ServerWrapper: LaunchServer address: %s. Title: %s", config.address, Launcher.profile != null ? Launcher.profile.getTitle() : "unknown"); LogHelper.info("Minecraft Version (for profile): %s", wrapper.profile == null ? "unknown" : wrapper.profile.getVersion().name); - LogHelper.info("Start Minecraft Server"); - LogHelper.debug("Invoke main method %s", mainClass.getName()); - if (config.args == null) { - String[] real_args; - if (args.length > 0) { - real_args = new String[args.length - 1]; - System.arraycopy(args, 1, real_args, 0, args.length - 1); - } else real_args = args; - - mainMethod.invoke(real_args); - } else { - mainMethod.invoke(config.args.toArray(new String[0])); + String[] real_args; + if (args.length > 0) { + real_args = new String[args.length - 1]; + System.arraycopy(args, 1, real_args, 0, args.length - 1); + } else real_args = args; + Launch launch; + switch (config.classLoaderConfig) { + case LAUNCHER: + launch = new ClasspathLaunch(); + break; + case MODULE: + launch = new ModuleLaunch(); + break; + default: + launch = new SimpleLaunch(); + break; } + LogHelper.info("Start Minecraft Server"); + LogHelper.debug("Invoke main method %s with %s", config.mainclass, launch.getClass().getName()); + try { + launch.run(config, real_args); + } catch (Throwable e) { + LogHelper.error(e); + System.exit(-1); + } + System.exit(0); } public void updateLauncherConfig() { @@ -231,8 +229,15 @@ public static final class Config { public long oauthExpireTime; public Map extendedTokens; public LauncherConfig.LauncherEnvironment env; + public ModuleConf moduleConf = new ModuleConf(); } - public static final class WebSocketConf { + public static final class ModuleConf { + public List modules = new ArrayList<>(); + public List modulePath = new ArrayList<>(); + public String mainModule = ""; + public Map exports = new HashMap<>(); + public Map opens = new HashMap<>(); + public Map reads = new HashMap<>(); } } diff --git a/ServerWrapper/src/main/java/pro/gravit/launcher/server/launch/ClasspathLaunch.java b/ServerWrapper/src/main/java/pro/gravit/launcher/server/launch/ClasspathLaunch.java new file mode 100644 index 00000000..05173094 --- /dev/null +++ b/ServerWrapper/src/main/java/pro/gravit/launcher/server/launch/ClasspathLaunch.java @@ -0,0 +1,23 @@ +package pro.gravit.launcher.server.launch; + +import pro.gravit.launcher.server.ServerWrapper; +import pro.gravit.utils.PublicURLClassLoader; +import pro.gravit.utils.helper.IOHelper; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.net.URL; +import java.nio.file.Paths; + +public class ClasspathLaunch implements Launch { + @Override + @SuppressWarnings("ConfusingArgumentToVarargsMethod") + public void run(ServerWrapper.Config config, String[] args) throws Throwable { + URL[] urls = config.classpath.stream().map(Paths::get).map(IOHelper::toURL).toArray(URL[]::new); + ClassLoader ucl = new PublicURLClassLoader(urls); + Class mainClass = Class.forName(config.mainclass, true, ucl); + MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClass, "main", MethodType.methodType(void.class, String[].class)); + mainMethod.invoke(args); + } +} diff --git a/ServerWrapper/src/main/java/pro/gravit/launcher/server/launch/Launch.java b/ServerWrapper/src/main/java/pro/gravit/launcher/server/launch/Launch.java new file mode 100644 index 00000000..becc49f8 --- /dev/null +++ b/ServerWrapper/src/main/java/pro/gravit/launcher/server/launch/Launch.java @@ -0,0 +1,7 @@ +package pro.gravit.launcher.server.launch; + +import pro.gravit.launcher.server.ServerWrapper; + +public interface Launch { + void run(ServerWrapper.Config config, String[] args) throws Throwable; +} diff --git a/ServerWrapper/src/main/java/pro/gravit/launcher/server/launch/ModuleLaunch.java b/ServerWrapper/src/main/java/pro/gravit/launcher/server/launch/ModuleLaunch.java new file mode 100644 index 00000000..2cb55ad7 --- /dev/null +++ b/ServerWrapper/src/main/java/pro/gravit/launcher/server/launch/ModuleLaunch.java @@ -0,0 +1,10 @@ +package pro.gravit.launcher.server.launch; + +import pro.gravit.launcher.server.ServerWrapper; + +public class ModuleLaunch implements Launch { + @Override + public void run(ServerWrapper.Config config, String[] args) throws Throwable { + throw new UnsupportedOperationException("Module system not supported"); + } +} diff --git a/ServerWrapper/src/main/java/pro/gravit/launcher/server/launch/SimpleLaunch.java b/ServerWrapper/src/main/java/pro/gravit/launcher/server/launch/SimpleLaunch.java new file mode 100644 index 00000000..192f95dc --- /dev/null +++ b/ServerWrapper/src/main/java/pro/gravit/launcher/server/launch/SimpleLaunch.java @@ -0,0 +1,17 @@ +package pro.gravit.launcher.server.launch; + +import pro.gravit.launcher.server.ServerWrapper; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +public class SimpleLaunch implements Launch { + @Override + @SuppressWarnings("ConfusingArgumentToVarargsMethod") + public void run(ServerWrapper.Config config, String[] args) throws Throwable { + Class mainClass = Class.forName(config.mainclass); + MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClass, "main", MethodType.methodType(void.class, String[].class)); + mainMethod.invoke(args); + } +} diff --git a/ServerWrapper/src/main/java11/pro/gravit/launcher/server/launch/ModuleLaunch.java b/ServerWrapper/src/main/java11/pro/gravit/launcher/server/launch/ModuleLaunch.java new file mode 100644 index 00000000..ffe102a6 --- /dev/null +++ b/ServerWrapper/src/main/java11/pro/gravit/launcher/server/launch/ModuleLaunch.java @@ -0,0 +1,70 @@ +package pro.gravit.launcher.server.launch; + +import pro.gravit.launcher.server.ServerWrapper; +import pro.gravit.utils.PublicURLClassLoader; +import pro.gravit.utils.helper.IOHelper; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.module.Configuration; +import java.lang.module.ModuleFinder; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.stream.Collectors; + +public class ModuleLaunch implements Launch { + @Override + @SuppressWarnings("ConfusingArgumentToVarargsMethod") + public void run(ServerWrapper.Config config, String[] args) throws Throwable { + URL[] urls = config.classpath.stream().map(Paths::get).map(IOHelper::toURL).toArray(URL[]::new); + ClassLoader ucl = new PublicURLClassLoader(urls); + // Create Module Layer + ModuleFinder finder = ModuleFinder.of(config.moduleConf.modulePath.stream().map(Paths::get).toArray(Path[]::new)); + ModuleLayer bootLayer = ModuleLayer.boot(); + Configuration configuration = bootLayer.configuration() + .resolveAndBind(ModuleFinder.of(), finder, config.moduleConf.modules); + ModuleLayer.Controller controller = ModuleLayer.defineModulesWithOneLoader(configuration, List.of(bootLayer), ucl); + ModuleLayer layer = controller.layer(); + // Configure exports / opens + for(var e : config.moduleConf.exports.entrySet()) { + String[] split = e.getKey().split("\\\\"); + Module source = layer.findModule(split[0]).orElseThrow(); + String pkg = split[1]; + Module target = layer.findModule(e.getValue()).orElseThrow(); + controller.addExports(source, pkg, target); + } + for(var e : config.moduleConf.opens.entrySet()) { + String[] split = e.getKey().split("\\\\"); + Module source = layer.findModule(split[0]).orElseThrow(); + String pkg = split[1]; + Module target = layer.findModule(e.getValue()).orElseThrow(); + controller.addOpens(source, pkg, target); + } + for(var e : config.moduleConf.reads.entrySet()) { + Module source = layer.findModule(e.getKey()).orElseThrow(); + Module target = layer.findModule(e.getValue()).orElseThrow(); + controller.addReads(source, target); + } + Module mainModule = layer.findModule(config.moduleConf.mainModule).orElseThrow(); + Module unnamed = ModuleLaunch.class.getClassLoader().getUnnamedModule(); + if(unnamed != null) { + controller.addOpens(mainModule, getPackageFromClass(config.mainclass), unnamed); + } + // Start main class + ClassLoader loader = mainModule.getClassLoader(); + Class mainClass = Class.forName(config.mainclass, true, loader); + MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClass, "main", MethodType.methodType(void.class, String[].class)); + mainMethod.invoke(args); + } + + private static String getPackageFromClass(String clazz) { + int index = clazz.indexOf("."); + if(index >= 0) { + return clazz.substring(0, index); + } + return clazz; + } +} diff --git a/modules b/modules index a1318b09..2d9466cd 160000 --- a/modules +++ b/modules @@ -1 +1 @@ -Subproject commit a1318b09c1f5657ad60eaee362746cd493d38c8a +Subproject commit 2d9466cd82b1a6c916f045851021585e6e831bd5