[FEATURE][EXPERIMENTAL] ServerWrapper launch methods

This commit is contained in:
Gravita 2022-04-07 01:34:58 +07:00
parent 7cedaef90a
commit be565e2218
9 changed files with 188 additions and 35 deletions

View file

@ -516,7 +516,7 @@ public enum SecurityManagerConfig {
}
public enum ClassLoaderConfig {
AGENT, LAUNCHER, SYSTEM_ARGS
AGENT, LAUNCHER, MODULE, SYSTEM_ARGS
}
public enum SignedClientConfig {

View file

@ -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) {

View file

@ -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<String, String> extendedTokens;
public LauncherConfig.LauncherEnvironment env;
public ModuleConf moduleConf = new ModuleConf();
}
public static final class WebSocketConf {
public static final class ModuleConf {
public List<String> modules = new ArrayList<>();
public List<String> modulePath = new ArrayList<>();
public String mainModule = "";
public Map<String, String> exports = new HashMap<>();
public Map<String, String> opens = new HashMap<>();
public Map<String, String> reads = new HashMap<>();
}
}

View file

@ -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);
}
}

View file

@ -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;
}

View file

@ -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");
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}

@ -1 +1 @@
Subproject commit a1318b09c1f5657ad60eaee362746cd493d38c8a
Subproject commit 2d9466cd82b1a6c916f045851021585e6e831bd5