From 90e116720c6ffccb333e93a4cee6965ed5fc9dc2 Mon Sep 17 00:00:00 2001 From: Gravita <12893402+gravit0@users.noreply.github.com> Date: Tue, 31 Oct 2023 16:22:44 +0700 Subject: [PATCH 1/9] [FIX] onHardwareReport NPE --- .../auth/protect/AdvancedProtectHandler.java | 10 +++++++--- .../java/pro/gravit/launchserver/socket/Client.java | 3 ++- .../src/main/java/pro/gravit/utils/Version.java | 4 ++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/protect/AdvancedProtectHandler.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/protect/AdvancedProtectHandler.java index 1899292a..83ce07e2 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/protect/AdvancedProtectHandler.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/protect/AdvancedProtectHandler.java @@ -49,6 +49,10 @@ public void onHardwareReport(HardwareReportResponse response, Client client) { response.sendError("Access denied"); return; } + if(client.trustLevel.hardwareInfo != null) { + response.sendResult(new HardwareReportRequestEvent(createHardwareToken(client.username, client.trustLevel.hardwareInfo), SECONDS.toMillis(server.config.netty.security.hardwareTokenExpire))); + return; + } logger.debug("HardwareInfo received"); { var authSupportHardware = client.auth.isSupport(AuthSupportHardware.class); @@ -63,7 +67,7 @@ public void onHardwareReport(HardwareReportResponse response, Client client) { if (hardware.isBanned()) { throw new SecurityException("Your hardware banned"); } - client.trustLevel.hardwareInfo = hardware.getHardwareInfo(); + client.trustLevel.hardwareInfo = hardware; response.sendResult(new HardwareReportRequestEvent(createHardwareToken(client.username, hardware), SECONDS.toMillis(server.config.netty.security.hardwareTokenExpire))); } else { logger.error("AuthCoreProvider not supported hardware"); @@ -83,7 +87,7 @@ public VerifySecureLevelKeyRequestEvent onSuccessVerify(Client client) { if (hardware.isBanned()) { throw new SecurityException("Your hardware banned"); } - client.trustLevel.hardwareInfo = hardware.getHardwareInfo(); + client.trustLevel.hardwareInfo = hardware; authSupportHardware.connectUserAndHardware(client.sessionObject, hardware); return new VerifySecureLevelKeyRequestEvent(false, false, createPublicKeyToken(client.username, client.trustLevel.publicKey), SECONDS.toMillis(server.config.netty.security.publicKeyTokenExpire)); } else { @@ -145,7 +149,7 @@ public boolean accept(Client client, AuthProviderPair pair, String extendedToken if (hardwareSupport == null) return false; UserHardware hardware = hardwareSupport.getHardwareInfoById(hardwareInfoId); if (client.trustLevel == null) client.trustLevel = new Client.TrustLevel(); - client.trustLevel.hardwareInfo = hardware.getHardwareInfo(); + client.trustLevel.hardwareInfo = hardware; return true; } catch (Throwable e) { logger.error("Hardware JWT error", e); diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/Client.java b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/Client.java index 3ac02c04..3853bf93 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/Client.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/Client.java @@ -5,6 +5,7 @@ import pro.gravit.launcher.request.secure.HardwareReportRequest; import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.auth.AuthProviderPair; +import pro.gravit.launchserver.auth.core.interfaces.UserHardware; import pro.gravit.launchserver.socket.response.auth.AuthResponse; import java.util.HashMap; @@ -87,7 +88,7 @@ public static class TrustLevel { public byte[] verifySecureKey; public boolean keyChecked; public byte[] publicKey; - public HardwareReportRequest.HardwareInfo hardwareInfo; + public UserHardware hardwareInfo; // May be used later public double rating; public long latestMillis; diff --git a/LauncherCore/src/main/java/pro/gravit/utils/Version.java b/LauncherCore/src/main/java/pro/gravit/utils/Version.java index 1f1c5356..13ff9f72 100644 --- a/LauncherCore/src/main/java/pro/gravit/utils/Version.java +++ b/LauncherCore/src/main/java/pro/gravit/utils/Version.java @@ -6,9 +6,9 @@ public final class Version implements Comparable { public static final int MAJOR = 5; public static final int MINOR = 5; - public static final int PATCH = 0; + public static final int PATCH = 1; public static final int BUILD = 1; - public static final Version.Type RELEASE = Type.STABLE; + public static final Version.Type RELEASE = Type.DEV; public final int major; public final int minor; public final int patch; From 429c7a45c409b07afbf9426e0dbf1f080e450eda Mon Sep 17 00:00:00 2001 From: Gravita <12893402+gravit0@users.noreply.github.com> Date: Tue, 31 Oct 2023 16:59:23 +0700 Subject: [PATCH 2/9] [ANY] Update modules --- modules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules b/modules index 5847a9b9..d844e7cc 160000 --- a/modules +++ b/modules @@ -1 +1 @@ -Subproject commit 5847a9b937039eb6402082a458ad0afac7163840 +Subproject commit d844e7cc9b6b05d6cdbfc514efa9ad8a8df96b25 From c1df5482587268b22c9e79dfb6c0ef972d1c5e4d Mon Sep 17 00:00:00 2001 From: Gravita <12893402+gravit0@users.noreply.github.com> Date: Tue, 31 Oct 2023 22:18:10 +0700 Subject: [PATCH 3/9] [FEATURE] Unified launch --- .../launcher/debug/ClientRuntimeProvider.java | 51 +++- .../launcher/profiles/ClientProfile.java | 15 +- .../gravit/launcher/api/ClientService.java | 11 +- .../launcher/client/ClientClassLoader.java | 92 ------- .../client/ClientLauncherEntryPoint.java | 55 ++-- .../client/ClientProcessClassLoaderEvent.java | 10 +- .../launcher/utils/ApiBridgeService.java | 9 - .../pro/gravit/utils/launch/BasicLaunch.java | 124 +++++++++ .../utils/launch/ClassLoaderControl.java | 23 ++ .../java/pro/gravit/utils/launch/Launch.java | 10 + .../gravit/utils/launch/LaunchOptions.java | 19 ++ .../pro/gravit/utils/launch/LegacyLaunch.java | 175 +++++++++++++ .../pro/gravit/utils/launch/ModuleLaunch.java | 17 ++ .../pro/gravit/utils/launch/ModuleLaunch.java | 237 ++++++++++++++++++ .../gravit/launcher/server/ServerWrapper.java | 33 ++- .../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 | 69 ----- build.gradle | 2 +- 21 files changed, 725 insertions(+), 284 deletions(-) delete mode 100644 LauncherClient/src/main/java/pro/gravit/launcher/client/ClientClassLoader.java create mode 100644 LauncherCore/src/main/java/pro/gravit/utils/launch/BasicLaunch.java create mode 100644 LauncherCore/src/main/java/pro/gravit/utils/launch/ClassLoaderControl.java create mode 100644 LauncherCore/src/main/java/pro/gravit/utils/launch/Launch.java create mode 100644 LauncherCore/src/main/java/pro/gravit/utils/launch/LaunchOptions.java create mode 100644 LauncherCore/src/main/java/pro/gravit/utils/launch/LegacyLaunch.java create mode 100644 LauncherCore/src/main/java/pro/gravit/utils/launch/ModuleLaunch.java create mode 100644 LauncherCore/src/main/java11/pro/gravit/utils/launch/ModuleLaunch.java delete mode 100644 ServerWrapper/src/main/java/pro/gravit/launcher/server/launch/ClasspathLaunch.java delete mode 100644 ServerWrapper/src/main/java/pro/gravit/launcher/server/launch/Launch.java delete mode 100644 ServerWrapper/src/main/java/pro/gravit/launcher/server/launch/ModuleLaunch.java delete mode 100644 ServerWrapper/src/main/java/pro/gravit/launcher/server/launch/SimpleLaunch.java delete mode 100644 ServerWrapper/src/main/java11/pro/gravit/launcher/server/launch/ModuleLaunch.java diff --git a/Launcher/src/main/java/pro/gravit/launcher/debug/ClientRuntimeProvider.java b/Launcher/src/main/java/pro/gravit/launcher/debug/ClientRuntimeProvider.java index 3fad11ca..369258e6 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/debug/ClientRuntimeProvider.java +++ b/Launcher/src/main/java/pro/gravit/launcher/debug/ClientRuntimeProvider.java @@ -1,6 +1,7 @@ package pro.gravit.launcher.debug; import pro.gravit.launcher.ClientPermissions; +import pro.gravit.launcher.Launcher; import pro.gravit.launcher.LauncherEngine; import pro.gravit.launcher.api.AuthService; import pro.gravit.launcher.events.request.AuthRequestEvent; @@ -10,10 +11,17 @@ import pro.gravit.launcher.request.Request; import pro.gravit.launcher.request.auth.AuthRequest; import pro.gravit.launcher.request.update.ProfilesRequest; +import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.LogHelper; +import pro.gravit.utils.launch.*; +import java.io.File; +import java.io.Reader; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.UUID; public class ClientRuntimeProvider implements RuntimeProvider { @@ -32,6 +40,11 @@ public void run(String[] args) { long expire = Long.parseLong(System.getProperty("launcher.runtime.auth.expire", "0")); String profileUUID = System.getProperty("launcher.runtime.profileuuid", null); String mainClass = System.getProperty("launcher.runtime.mainclass", null); + String mainModule = System.getProperty("launcher.runtime.mainmodule", null); + String launchMode = System.getProperty("launcher.runtime.launch", "basic"); + String compatClasses = System.getProperty("launcher.runtime.launch.compat", null); + String nativesDir = System.getProperty("launcher.runtime.launch.natives", "natives"); + String launcherOptionsPath = System.getProperty("launcher.runtime.launch.options", null); ClientPermissions permissions = new ClientPermissions(); if(mainClass == null) { throw new NullPointerException("Add `-Dlauncher.runtime.mainclass=YOUR_MAIN_CLASS` to jvmArgs"); @@ -79,8 +92,42 @@ public void run(String[] args) { AuthService.uuid = UUID.fromString(uuid); AuthService.username = username; AuthService.permissions = permissions; - Class mainClazz = Class.forName(mainClass); - mainClazz.getMethod("main", String[].class).invoke(null, (Object) newArgs.toArray(new String[0])); + Launch launch; + switch (launchMode) { + case "basic": { + launch = new BasicLaunch(); + break; + } + case "legacy": { + launch = new LegacyLaunch(); + break; + } + case "module": { + launch = new ModuleLaunch(); + break; + } + default: { + throw new UnsupportedOperationException(String.format("Unknown launch mode: '%s'", launchMode)); + } + } + List classpath = new ArrayList<>(); + try { + for(var c : System.getProperty("java.class.path").split(File.pathSeparator)) { + classpath.add(Paths.get(c)); + } + } catch (Throwable e) { + LogHelper.error(e); + } + LaunchOptions options; + if(launcherOptionsPath != null) { + try(Reader reader = IOHelper.newReader(Paths.get(launcherOptionsPath))) { + options = Launcher.gsonManager.gson.fromJson(reader, LaunchOptions.class); + } + } else { + options = new LaunchOptions(); + } + launch.init(classpath, nativesDir, options); + launch.launch(mainClass, mainModule, Arrays.asList(args)); } catch (Throwable e) { LogHelper.error(e); LauncherEngine.exitLauncher(-15); 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 36c533ee..e484f551 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/profiles/ClientProfile.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/profiles/ClientProfile.java @@ -8,6 +8,7 @@ import pro.gravit.launcher.profiles.optional.triggers.OptionalTrigger; import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.VerifyHelper; +import pro.gravit.utils.launch.LaunchOptions; import java.lang.reflect.Type; import java.net.InetSocketAddress; @@ -80,6 +81,10 @@ public final class ClientProfile implements Comparable { // Client launcher @LauncherNetworkAPI private String mainClass; + @LauncherNetworkAPI + private String mainModule; + @LauncherNetworkAPI + private LaunchOptions.ModuleConf moduleConf; public ClientProfile() { update = new ArrayList<>(); @@ -211,6 +216,14 @@ public String getMainClass() { return mainClass; } + public String getMainModule() { + return mainModule; + } + + public LaunchOptions.ModuleConf getModuleConf() { + return moduleConf; + } + public List getServers() { return servers; } @@ -447,7 +460,7 @@ public enum ClassLoaderConfig { } public enum CompatibilityFlags { - LEGACY_NATIVES_DIR + LEGACY_NATIVES_DIR, CLASS_CONTROL_API } public static class Version implements Comparable { diff --git a/LauncherClient/src/main/java/pro/gravit/launcher/api/ClientService.java b/LauncherClient/src/main/java/pro/gravit/launcher/api/ClientService.java index 20e29eb6..a065319d 100644 --- a/LauncherClient/src/main/java/pro/gravit/launcher/api/ClientService.java +++ b/LauncherClient/src/main/java/pro/gravit/launcher/api/ClientService.java @@ -1,21 +1,20 @@ package pro.gravit.launcher.api; import pro.gravit.launcher.utils.ApiBridgeService; +import pro.gravit.utils.helper.IOHelper; +import pro.gravit.utils.helper.JVMHelper; +import pro.gravit.utils.launch.ClassLoaderControl; import java.lang.instrument.Instrumentation; import java.net.URL; public class ClientService { public static Instrumentation instrumentation; - public static ClassLoader classLoader; + public static ClassLoaderControl classLoaderControl; public static String nativePath; public static URL[] baseURLs; - public static ClassLoader getClassLoader() { - return classLoader; - } - public static String findLibrary(String name) { - return ApiBridgeService.findLibrary(classLoader, name); + return nativePath.concat(IOHelper.PLATFORM_SEPARATOR).concat(JVMHelper.NATIVE_PREFIX).concat(name).concat(JVMHelper.NATIVE_EXTENSION); } } diff --git a/LauncherClient/src/main/java/pro/gravit/launcher/client/ClientClassLoader.java b/LauncherClient/src/main/java/pro/gravit/launcher/client/ClientClassLoader.java deleted file mode 100644 index 624ed0d1..00000000 --- a/LauncherClient/src/main/java/pro/gravit/launcher/client/ClientClassLoader.java +++ /dev/null @@ -1,92 +0,0 @@ -package pro.gravit.launcher.client; - -import pro.gravit.utils.helper.IOHelper; -import pro.gravit.utils.helper.JVMHelper; - -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.List; - -public class ClientClassLoader extends URLClassLoader { - private static final ClassLoader SYSTEM_CLASS_LOADER = ClassLoader.getSystemClassLoader(); - public String nativePath; - - private final List packages = new ArrayList<>(); - - /** - * Constructs a new URLClassLoader for the specified URLs using the - * default delegation parent {@code ClassLoader}. The URLs will - * be searched in the order specified for classes and resources after - * first searching in the parent class loader. Any URL that ends with - * a '/' is assumed to refer to a directory. Otherwise, the URL is - * assumed to refer to a JAR file which will be downloaded and opened - * as needed. - * - *

If there is a security manager, this method first - * calls the security manager's {@code checkCreateClassLoader} method - * to ensure creation of a class loader is allowed. - * - * @param urls the URLs from which to load classes and resources - * @throws SecurityException if a security manager exists and its - * {@code checkCreateClassLoader} method doesn't allow - * creation of a class loader. - * @throws NullPointerException if {@code urls} is {@code null}. - */ - public ClientClassLoader(URL[] urls) { - super(urls); - packages.add("pro.gravit.launcher."); - packages.add("pro.gravit.utils."); - } - - /** - * Constructs a new URLClassLoader for the given URLs. The URLs will be - * searched in the order specified for classes and resources after first - * searching in the specified parent class loader. Any {@code jar:} - * scheme URL is assumed to refer to a JAR file. Any {@code file:} scheme - * URL that ends with a '/' is assumed to refer to a directory. Otherwise, - * the URL is assumed to refer to a JAR file which will be downloaded and - * opened as needed. - * - *

If there is a security manager, this method first - * calls the security manager's {@code checkCreateClassLoader} method - * to ensure creation of a class loader is allowed. - * - * @param urls the URLs from which to load classes and resources - * @param parent the parent class loader for delegation - * @throws SecurityException if a security manager exists and its - * {@code checkCreateClassLoader} method doesn't allow - * creation of a class loader. - * @throws NullPointerException if {@code urls} is {@code null}. - */ - public ClientClassLoader(URL[] urls, ClassLoader parent) { - super(urls, parent); - } - - @Override - protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { - if(name != null) { - for(String pkg : packages) { - if(name.startsWith(pkg)) { - return SYSTEM_CLASS_LOADER.loadClass(name); - } - } - } - return super.loadClass(name, resolve); - } - - @Override - public String findLibrary(String name) { - return nativePath.concat(IOHelper.PLATFORM_SEPARATOR).concat(JVMHelper.NATIVE_PREFIX).concat(name).concat(JVMHelper.NATIVE_EXTENSION); - } - - public void addAllowedPackage(String pkg) { - packages.add(pkg); - } - - @Override - public void addURL(URL url) { - super.addURL(url); - } -} - diff --git a/LauncherClient/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java b/LauncherClient/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java index 41bc3ab7..9fd2a165 100644 --- a/LauncherClient/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java +++ b/LauncherClient/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java @@ -21,6 +21,7 @@ import pro.gravit.launcher.serialize.HInput; import pro.gravit.launcher.utils.DirWatcher; import pro.gravit.utils.helper.*; +import pro.gravit.utils.launch.*; import java.io.File; import java.io.IOException; @@ -39,10 +40,12 @@ import java.util.stream.Stream; public class ClientLauncherEntryPoint { - private static ClassLoader classLoader; public static ClientModuleManager modulesManager; public static ClientParams clientParams; + private static Launch launch; + private static ClassLoaderControl classLoaderControl; + private static ClientParams readParams(SocketAddress address) throws IOException { try (Socket socket = IOHelper.newSocket()) { socket.connect(address); @@ -102,7 +105,7 @@ public static void main(String[] args) throws Throwable { LogHelper.debug("Verifying ClientLauncher sign and classpath"); List classpath = resolveClassPath(clientDir, params.actions, params.profile) .filter(x -> !profile.getModulePath().contains(clientDir.relativize(x).toString())) - .collect(Collectors.toList()); + .collect(Collectors.toCollection(ArrayList::new)); List classpathURLs = classpath.stream().map(IOHelper::toURL).collect(Collectors.toList()); // Start client with WatchService monitoring RequestService service; @@ -128,34 +131,39 @@ public static void main(String[] args) throws Throwable { } LogHelper.debug("Natives dir %s", params.nativesDir); ClientProfile.ClassLoaderConfig classLoaderConfig = profile.getClassLoaderConfig(); + LaunchOptions options = new LaunchOptions(); + options.moduleConf = profile.getModuleConf(); if (classLoaderConfig == ClientProfile.ClassLoaderConfig.LAUNCHER) { - ClientClassLoader classLoader = new ClientClassLoader(classpathURLs.toArray(new URL[0]), ClientLauncherEntryPoint.class.getClassLoader()); + if(JVMHelper.JVM_VERSION <= 11) { + launch = new LegacyLaunch(); + } else { + launch = new ModuleLaunch(); + } + classLoaderControl = launch.init(classpath, params.nativesDir, options); System.setProperty("java.class.path", classpath.stream().map(Path::toString).collect(Collectors.joining(File.pathSeparator))); - ClientLauncherEntryPoint.classLoader = classLoader; - Thread.currentThread().setContextClassLoader(classLoader); - classLoader.nativePath = params.nativesDir; - modulesManager.invokeEvent(new ClientProcessClassLoaderEvent(classLoader, profile)); - ClientService.classLoader = classLoader; - ClientService.nativePath = classLoader.nativePath; - classLoader.addURL(IOHelper.getCodeSource(ClientLauncherEntryPoint.class).toUri().toURL()); - ClientService.baseURLs = classLoader.getURLs(); + modulesManager.invokeEvent(new ClientProcessClassLoaderEvent(launch, classLoaderControl, profile)); + ClientService.nativePath = params.nativesDir; + ClientService.baseURLs = classLoaderControl.getURLs(); } else if (classLoaderConfig == ClientProfile.ClassLoaderConfig.AGENT) { - ClientLauncherEntryPoint.classLoader = ClassLoader.getSystemClassLoader(); + launch = new BasicLaunch(LauncherAgent.inst); classpathURLs.add(IOHelper.getCodeSource(ClientLauncherEntryPoint.class).toUri().toURL()); + classLoaderControl = launch.init(classpath, params.nativesDir, options); for (URL url : classpathURLs) { LauncherAgent.addJVMClassPath(Paths.get(url.toURI())); } ClientService.instrumentation = LauncherAgent.inst; ClientService.nativePath = params.nativesDir; - modulesManager.invokeEvent(new ClientProcessClassLoaderEvent(classLoader, profile)); - ClientService.classLoader = classLoader; + modulesManager.invokeEvent(new ClientProcessClassLoaderEvent(launch, null, profile)); ClientService.baseURLs = classpathURLs.toArray(new URL[0]); } else if (classLoaderConfig == ClientProfile.ClassLoaderConfig.SYSTEM_ARGS) { - ClientLauncherEntryPoint.classLoader = ClassLoader.getSystemClassLoader(); - ClientService.classLoader = ClassLoader.getSystemClassLoader(); + launch = new BasicLaunch(); + classLoaderControl = launch.init(classpath, params.nativesDir, options); ClientService.baseURLs = classpathURLs.toArray(new URL[0]); ClientService.nativePath = params.nativesDir; } + if(profile.hasFlag(ClientProfile.CompatibilityFlags.CLASS_CONTROL_API)) { + ClientService.classLoaderControl = classLoaderControl; + } AuthService.username = params.playerProfile.username; AuthService.uuid = params.playerProfile.uuid; KeyService.serverRsaPublicKey = Launcher.getConfig().rsaPublicKey; @@ -264,27 +272,20 @@ private static void launch(ClientProfile profile, ClientParams params) throws Th } LogHelper.debug("Args: " + copy); // Resolve main class and method - Class mainClass = classLoader.loadClass(profile.getMainClass()); - if (LogHelper.isDevEnabled() && classLoader instanceof URLClassLoader) { - for (URL u : ((URLClassLoader) classLoader).getURLs()) { - LogHelper.dev("ClassLoader URL: %s", u.toString()); - } - } modulesManager.invokeEvent(new ClientProcessPreInvokeMainClassEvent(params, profile, args)); // Invoke main method try { { List compatClasses = profile.getCompatClasses(); for (String e : compatClasses) { - Class clazz = classLoader.loadClass(e); - MethodHandle runMethod = MethodHandles.lookup().findStatic(clazz, "run", MethodType.methodType(void.class)); - runMethod.invoke(); + Class clazz = classLoaderControl.getClass(e); + MethodHandle runMethod = MethodHandles.lookup().findStatic(clazz, "run", MethodType.methodType(void.class, ClassLoaderControl.class)); + runMethod.invoke(classLoaderControl); } } - MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClass, "main", MethodType.methodType(void.class, String[].class)).asFixedArity(); Launcher.LAUNCHED.set(true); JVMHelper.fullGC(); - mainMethod.invokeWithArguments((Object) args.toArray(new String[0])); + launch.launch(params.profile.getMainClass(), params.profile.getMainModule(), args); LogHelper.debug("Main exit successful"); } catch (Throwable e) { LogHelper.error(e); diff --git a/LauncherClient/src/main/java/pro/gravit/launcher/client/events/client/ClientProcessClassLoaderEvent.java b/LauncherClient/src/main/java/pro/gravit/launcher/client/events/client/ClientProcessClassLoaderEvent.java index 9a97c1e7..e42c17a9 100644 --- a/LauncherClient/src/main/java/pro/gravit/launcher/client/events/client/ClientProcessClassLoaderEvent.java +++ b/LauncherClient/src/main/java/pro/gravit/launcher/client/events/client/ClientProcessClassLoaderEvent.java @@ -2,13 +2,17 @@ import pro.gravit.launcher.modules.LauncherModule; import pro.gravit.launcher.profiles.ClientProfile; +import pro.gravit.utils.launch.ClassLoaderControl; +import pro.gravit.utils.launch.Launch; public class ClientProcessClassLoaderEvent extends LauncherModule.Event { - public final ClassLoader clientClassLoader; + public final Launch launch; + public final ClassLoaderControl classLoaderControl; public final ClientProfile profile; - public ClientProcessClassLoaderEvent(ClassLoader clientClassLoader, ClientProfile profile) { - this.clientClassLoader = clientClassLoader; + public ClientProcessClassLoaderEvent(Launch launch, ClassLoaderControl classLoaderControl, ClientProfile profile) { + this.launch = launch; + this.classLoaderControl = classLoaderControl; this.profile = profile; } } diff --git a/LauncherClient/src/main/java/pro/gravit/launcher/utils/ApiBridgeService.java b/LauncherClient/src/main/java/pro/gravit/launcher/utils/ApiBridgeService.java index 76cb77d9..8e0dcab9 100644 --- a/LauncherClient/src/main/java/pro/gravit/launcher/utils/ApiBridgeService.java +++ b/LauncherClient/src/main/java/pro/gravit/launcher/utils/ApiBridgeService.java @@ -2,7 +2,6 @@ import pro.gravit.launcher.Launcher; import pro.gravit.launcher.LauncherTrustManager; -import pro.gravit.launcher.client.ClientClassLoader; import java.security.cert.X509Certificate; @@ -16,12 +15,4 @@ public static void checkCertificatesSuccess(X509Certificate[] certs) throws Exce LauncherTrustManager trustManager = Launcher.getConfig().trustManager; trustManager.checkCertificatesSuccess(certs, trustManager::stdCertificateChecker); } - - public static String findLibrary(ClassLoader classLoader, String library) { - if (classLoader instanceof ClientClassLoader) { - ClientClassLoader clientClassLoader = (ClientClassLoader) classLoader; - return clientClassLoader.findLibrary(library); - } - return null; - } } diff --git a/LauncherCore/src/main/java/pro/gravit/utils/launch/BasicLaunch.java b/LauncherCore/src/main/java/pro/gravit/utils/launch/BasicLaunch.java new file mode 100644 index 00000000..740553e3 --- /dev/null +++ b/LauncherCore/src/main/java/pro/gravit/utils/launch/BasicLaunch.java @@ -0,0 +1,124 @@ +package pro.gravit.utils.launch; + +import pro.gravit.utils.helper.JVMHelper; + +import java.io.File; +import java.io.IOException; +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.IllegalClassFormatException; +import java.lang.instrument.Instrumentation; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.ProtectionDomain; +import java.util.Collection; +import java.util.List; +import java.util.jar.JarFile; + +public class BasicLaunch implements Launch { + + private Instrumentation instrumentation; + + public BasicLaunch(Instrumentation instrumentation) { + this.instrumentation = instrumentation; + } + + public BasicLaunch() { + } + + @Override + public ClassLoaderControl init(List files, String nativePath, LaunchOptions options) { + return new BasicClassLoaderControl(); + } + + @Override + public void launch(String mainClass, String mainModule, Collection args) throws Throwable { + Class mainClazz = Class.forName(mainClass); + MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClazz, "main", MethodType.methodType(void.class, String[].class)).asFixedArity(); + JVMHelper.fullGC(); + mainMethod.invokeWithArguments((Object) args.toArray(new String[0])); + } + + private class BasicClassLoaderControl implements ClassLoaderControl { + + @Override + public void addLauncherPackage(String prefix) { + throw new UnsupportedOperationException(); + } + + @Override + public void addTransformer(ClassTransformer transformer) { + if (instrumentation == null) { + throw new UnsupportedOperationException(); + } + instrumentation.addTransformer(new ClassFileTransformer() { + @Override + public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { + if(transformer.filter(null, className)) { + return transformer.transform(null, className, protectionDomain, classfileBuffer); + } + return classfileBuffer; + } + }); + } + + @Override + public void addURL(URL url) { + if (instrumentation == null) { + throw new UnsupportedOperationException(); + } + try { + instrumentation.appendToSystemClassLoaderSearch(new JarFile(new File(url.toURI()))); + } catch (URISyntaxException | IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void addJar(Path path) { + if (instrumentation == null) { + throw new UnsupportedOperationException(); + } + try { + instrumentation.appendToSystemClassLoaderSearch(new JarFile(path.toFile())); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public URL[] getURLs() { + String classpath = System.getProperty("java.class.path"); + String[] split = classpath.split(File.pathSeparator); + URL[] urls = new URL[split.length]; + try { + for(int i=0;i getClass(String name) throws ClassNotFoundException { + return Class.forName(name); + } + + @Override + public ClassLoader getClassLoader() { + return BasicLaunch.class.getClassLoader(); + } + + @Override + public Object getJava9ModuleController() { + return null; + } + } +} diff --git a/LauncherCore/src/main/java/pro/gravit/utils/launch/ClassLoaderControl.java b/LauncherCore/src/main/java/pro/gravit/utils/launch/ClassLoaderControl.java new file mode 100644 index 00000000..c8b857b4 --- /dev/null +++ b/LauncherCore/src/main/java/pro/gravit/utils/launch/ClassLoaderControl.java @@ -0,0 +1,23 @@ +package pro.gravit.utils.launch; + +import java.net.URL; +import java.nio.file.Path; +import java.security.ProtectionDomain; + +public interface ClassLoaderControl { + void addLauncherPackage(String prefix); + void addTransformer(ClassTransformer transformer); + void addURL(URL url); + void addJar(Path path); + URL[] getURLs(); + + Class getClass(String name) throws ClassNotFoundException; + + ClassLoader getClassLoader(); + + Object getJava9ModuleController(); + interface ClassTransformer { + boolean filter(String moduleName, String name); + byte[] transform(String moduleName, String name, ProtectionDomain protectionDomain, byte[] data); + } +} diff --git a/LauncherCore/src/main/java/pro/gravit/utils/launch/Launch.java b/LauncherCore/src/main/java/pro/gravit/utils/launch/Launch.java new file mode 100644 index 00000000..67e89af6 --- /dev/null +++ b/LauncherCore/src/main/java/pro/gravit/utils/launch/Launch.java @@ -0,0 +1,10 @@ +package pro.gravit.utils.launch; + +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; + +public interface Launch { + ClassLoaderControl init(List files, String nativePath, LaunchOptions options); + void launch(String mainClass, String mainModule, Collection args) throws Throwable; +} diff --git a/LauncherCore/src/main/java/pro/gravit/utils/launch/LaunchOptions.java b/LauncherCore/src/main/java/pro/gravit/utils/launch/LaunchOptions.java new file mode 100644 index 00000000..59312f30 --- /dev/null +++ b/LauncherCore/src/main/java/pro/gravit/utils/launch/LaunchOptions.java @@ -0,0 +1,19 @@ +package pro.gravit.utils.launch; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class LaunchOptions { + public ModuleConf moduleConf; + + + public static final class ModuleConf { + public List modules = new ArrayList<>(); + public List modulePath = new ArrayList<>(); + public Map exports = new HashMap<>(); + public Map opens = new HashMap<>(); + public Map reads = new HashMap<>(); + } +} diff --git a/LauncherCore/src/main/java/pro/gravit/utils/launch/LegacyLaunch.java b/LauncherCore/src/main/java/pro/gravit/utils/launch/LegacyLaunch.java new file mode 100644 index 00000000..aca88038 --- /dev/null +++ b/LauncherCore/src/main/java/pro/gravit/utils/launch/LegacyLaunch.java @@ -0,0 +1,175 @@ +package pro.gravit.utils.launch; + +import pro.gravit.utils.helper.IOHelper; +import pro.gravit.utils.helper.JVMHelper; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class LegacyLaunch implements Launch { + private LegacyClassLoader legacyClassLoader; + + @Override + public ClassLoaderControl init(List files, String nativePath, LaunchOptions options) { + legacyClassLoader = new LegacyClassLoader(files.stream().map((e) -> { + try { + return e.toUri().toURL(); + } catch (MalformedURLException ex) { + throw new RuntimeException(ex); + } + }).toArray(URL[]::new), BasicLaunch.class.getClassLoader()); + legacyClassLoader.nativePath = nativePath; + return legacyClassLoader.makeControl(); + } + + @Override + public void launch(String mainClass, String mainModule, Collection args) throws Throwable { + Thread.currentThread().setContextClassLoader(legacyClassLoader); + Class mainClazz = Class.forName(mainClass, true, legacyClassLoader); + MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClazz, "main", MethodType.methodType(void.class, String[].class)).asFixedArity(); + JVMHelper.fullGC(); + mainMethod.invokeWithArguments((Object) args.toArray(new String[0])); + } + + private static class LegacyClassLoader extends URLClassLoader { + private final ClassLoader SYSTEM_CLASS_LOADER = ClassLoader.getSystemClassLoader(); + private final List transformers = new ArrayList<>(); + private final Map> classMap = new ConcurrentHashMap<>(); + private String nativePath; + + private final List packages = new ArrayList<>(); + public LegacyClassLoader(URL[] urls) { + super(urls); + packages.add("pro.gravit.launcher."); + packages.add("pro.gravit.utils."); + } + public LegacyClassLoader(URL[] urls, ClassLoader parent) { + super(urls, parent); + } + + @Override + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + if(name != null) { + for(String pkg : packages) { + if(name.startsWith(pkg)) { + return SYSTEM_CLASS_LOADER.loadClass(name); + } + } + } + return super.loadClass(name, resolve); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + Class clazz; + { + clazz = classMap.get(name); + if(clazz != null) { + return clazz; + } + } + if(name != null && !transformers.isEmpty()) { + boolean needTransform = false; + for(ClassLoaderControl.ClassTransformer t : transformers) { + if(t.filter(null, name)) { + needTransform = true; + break; + } + } + if(needTransform) { + String rawClassName = name.replace(".", "/").concat(".class"); + try(InputStream input = getResourceAsStream(rawClassName)) { + byte[] bytes = IOHelper.read(input); + for(ClassLoaderControl.ClassTransformer t : transformers) { + bytes = t.transform(null, name, null, bytes); + } + clazz = defineClass(name, bytes, 0, bytes.length); + } catch (IOException e) { + throw new ClassNotFoundException(name, e); + } + } + } + if(clazz == null) { + clazz = super.findClass(name); + } + if(clazz != null) { + classMap.put(name, clazz); + return clazz; + } else { + throw new ClassNotFoundException(name); + } + } + + @Override + public String findLibrary(String name) { + return nativePath.concat(IOHelper.PLATFORM_SEPARATOR).concat(JVMHelper.NATIVE_PREFIX).concat(name).concat(JVMHelper.NATIVE_EXTENSION); + } + + public void addAllowedPackage(String pkg) { + packages.add(pkg); + } + + private LegacyClassLoaderControl makeControl() { + return new LegacyClassLoaderControl(); + } + + public class LegacyClassLoaderControl implements ClassLoaderControl { + + @Override + public void addLauncherPackage(String prefix) { + addAllowedPackage(prefix); + } + + @Override + public void addTransformer(ClassTransformer transformer) { + transformers.add(transformer); + } + + @Override + public void addURL(URL url) { + LegacyClassLoader.this.addURL(url); + } + + @Override + public void addJar(Path path) { + try { + LegacyClassLoader.this.addURL(path.toUri().toURL()); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + + @Override + public URL[] getURLs() { + return LegacyClassLoader.this.getURLs(); + } + + @Override + public Class getClass(String name) throws ClassNotFoundException { + return Class.forName(name, false, LegacyClassLoader.this); + } + + @Override + public ClassLoader getClassLoader() { + return LegacyClassLoader.this; + } + + @Override + public Object getJava9ModuleController() { + return null; + } + } + } +} diff --git a/LauncherCore/src/main/java/pro/gravit/utils/launch/ModuleLaunch.java b/LauncherCore/src/main/java/pro/gravit/utils/launch/ModuleLaunch.java new file mode 100644 index 00000000..0301ad02 --- /dev/null +++ b/LauncherCore/src/main/java/pro/gravit/utils/launch/ModuleLaunch.java @@ -0,0 +1,17 @@ +package pro.gravit.utils.launch; + +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; + +public class ModuleLaunch implements Launch { + @Override + public ClassLoaderControl init(List files, String nativePath, LaunchOptions options) { + throw new UnsupportedOperationException("Please use Multi-Release JAR"); + } + + @Override + public void launch(String mainClass, String mainModule, Collection args) throws Throwable { + throw new UnsupportedOperationException("Please use Multi-Release JAR"); + } +} diff --git a/LauncherCore/src/main/java11/pro/gravit/utils/launch/ModuleLaunch.java b/LauncherCore/src/main/java11/pro/gravit/utils/launch/ModuleLaunch.java new file mode 100644 index 00000000..45286e79 --- /dev/null +++ b/LauncherCore/src/main/java11/pro/gravit/utils/launch/ModuleLaunch.java @@ -0,0 +1,237 @@ +package pro.gravit.utils.launch; + +import pro.gravit.utils.helper.IOHelper; +import pro.gravit.utils.helper.JVMHelper; + +import java.io.IOException; +import java.io.InputStream; +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.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class ModuleLaunch implements Launch { + private ModuleClassLoader moduleClassLoader; + private Configuration configuration; + private ModuleLayer.Controller controller; + private ModuleFinder moduleFinder; + private ModuleLayer layer; + @Override + public ClassLoaderControl init(List files, String nativePath, LaunchOptions options) { + moduleClassLoader = new ModuleClassLoader(files.stream().map((e) -> { + try { + return e.toUri().toURL(); + } catch (MalformedURLException ex) { + throw new RuntimeException(ex); + } + }).toArray(URL[]::new), BasicLaunch.class.getClassLoader()); + moduleClassLoader.nativePath = nativePath; + { + if(options.moduleConf != null) { + // Create Module Layer + moduleFinder = ModuleFinder.of(options.moduleConf.modulePath.stream().map(Paths::get).toArray(Path[]::new)); + ModuleLayer bootLayer = ModuleLayer.boot(); + configuration = bootLayer.configuration() + .resolveAndBind(ModuleFinder.of(), moduleFinder, options.moduleConf.modules); + controller = ModuleLayer.defineModulesWithOneLoader(configuration, List.of(bootLayer), moduleClassLoader); + moduleClassLoader.controller = controller; + layer = controller.layer(); + // Configure exports / opens + for(var e : options.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 : options.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 : options.moduleConf.reads.entrySet()) { + Module source = layer.findModule(e.getKey()).orElseThrow(); + Module target = layer.findModule(e.getValue()).orElseThrow(); + controller.addReads(source, target); + } + } + } + return moduleClassLoader.makeControl(); + } + + @Override + public void launch(String mainClass, String mainModuleName, Collection args) throws Throwable { + Thread.currentThread().setContextClassLoader(moduleClassLoader); + if(mainModuleName == null) { + Class mainClazz = Class.forName(mainClass, true, moduleClassLoader); + MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClazz, "main", MethodType.methodType(void.class, String[].class)).asFixedArity(); + JVMHelper.fullGC(); + mainMethod.invokeWithArguments((Object) args.toArray(new String[0])); + return; + } + Module mainModule = layer.findModule(mainModuleName).orElseThrow(); + Module unnamed = ModuleLaunch.class.getClassLoader().getUnnamedModule(); + if(unnamed != null) { + controller.addOpens(mainModule, getPackageFromClass(mainClass), unnamed); + } + // Start main class + ClassLoader loader = mainModule.getClassLoader(); + Class mainClazz = Class.forName(mainClass, true, loader); + MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClazz, "main", MethodType.methodType(void.class, String[].class)); + mainMethod.invoke((Object) args.toArray(new String[0])); + } + + private static String getPackageFromClass(String clazz) { + int index = clazz.lastIndexOf("."); + if(index >= 0) { + return clazz.substring(0, index); + } + return clazz; + } + + private static class ModuleClassLoader extends URLClassLoader { + private final ClassLoader SYSTEM_CLASS_LOADER = ClassLoader.getSystemClassLoader(); + private final List transformers = new ArrayList<>(); + private final Map> classMap = new ConcurrentHashMap<>(); + private String nativePath; + + private ModuleLayer.Controller controller; + + private final List packages = new ArrayList<>(); + public ModuleClassLoader(URL[] urls) { + super(urls); + packages.add("pro.gravit.launcher."); + packages.add("pro.gravit.utils."); + } + public ModuleClassLoader(URL[] urls, ClassLoader parent) { + super(urls, parent); + } + + @Override + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + if(name != null) { + for(String pkg : packages) { + if(name.startsWith(pkg)) { + return SYSTEM_CLASS_LOADER.loadClass(name); + } + } + } + return super.loadClass(name, resolve); + } + + @Override + protected Class findClass(String moduleName, String name) { + + Class clazz; + { + clazz = classMap.get(name); + if(clazz != null) { + return clazz; + } + } + if(name != null && !transformers.isEmpty()) { + boolean needTransform = false; + for(ClassLoaderControl.ClassTransformer t : transformers) { + if(t.filter(moduleName, name)) { + needTransform = true; + break; + } + } + if(needTransform) { + String rawClassName = name.replace(".", "/").concat(".class"); + try(InputStream input = getResourceAsStream(rawClassName)) { + byte[] bytes = IOHelper.read(input); + for(ClassLoaderControl.ClassTransformer t : transformers) { + bytes = t.transform(moduleName, name, null, bytes); + } + clazz = defineClass(name, bytes, 0, bytes.length); + } catch (IOException e) { + return null; + } + } + } + if(clazz == null) { + clazz = super.findClass(moduleName, name); + } + if(clazz != null) { + classMap.put(name, clazz); + return clazz; + } else { + return null; + } + } + + @Override + public String findLibrary(String name) { + return nativePath.concat(IOHelper.PLATFORM_SEPARATOR).concat(JVMHelper.NATIVE_PREFIX).concat(name).concat(JVMHelper.NATIVE_EXTENSION); + } + + public void addAllowedPackage(String pkg) { + packages.add(pkg); + } + + private ModuleClassLoaderControl makeControl() { + return new ModuleClassLoaderControl(); + } + + private class ModuleClassLoaderControl implements ClassLoaderControl { + + @Override + public void addLauncherPackage(String prefix) { + addAllowedPackage(prefix); + } + + @Override + public void addTransformer(ClassTransformer transformer) { + transformers.add(transformer); + } + + @Override + public void addURL(URL url) { + ModuleClassLoader.this.addURL(url); + } + + @Override + public void addJar(Path path) { + try { + ModuleClassLoader.this.addURL(path.toUri().toURL()); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + + @Override + public URL[] getURLs() { + return ModuleClassLoader.this.getURLs(); + } + + @Override + public Class getClass(String name) throws ClassNotFoundException { + return Class.forName(name, false, ModuleClassLoader.this); + } + + @Override + public ClassLoader getClassLoader() { + return ModuleClassLoader.this; + } + + @Override + public Object getJava9ModuleController() { + return controller; + } + } + } +} 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 0e07b07d..266edc6a 100644 --- a/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java +++ b/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java @@ -17,25 +17,24 @@ import pro.gravit.launcher.request.update.ProfilesRequest; import pro.gravit.launcher.request.websockets.StdWebSocketService; import pro.gravit.launcher.server.authlib.InstallAuthlib; -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; import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.SecurityHelper; +import pro.gravit.utils.launch.*; import java.lang.reflect.Type; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; +import java.util.stream.Collectors; public class ServerWrapper extends JsonConfigurable { public static final Path configFile = Paths.get(System.getProperty("serverwrapper.configFile", "ServerWrapperConfig.json")); public static final boolean disableSetup = Boolean.parseBoolean(System.getProperty("serverwrapper.disableSetup", "false")); public static ServerWrapper wrapper; + public static ClassLoaderControl classLoaderControl; public Config config; public PublicURLClassLoader ucp; public ClassLoader loader; @@ -173,19 +172,26 @@ public void run(String... args) throws Throwable { Launch launch; switch (config.classLoaderConfig) { case LAUNCHER: - launch = new ClasspathLaunch(); + launch = new LegacyLaunch(); break; case MODULE: launch = new ModuleLaunch(); break; default: - launch = new SimpleLaunch(); + if(ServerAgent.isAgentStarted()) { + launch = new BasicLaunch(ServerAgent.inst); + } else { + launch = new BasicLaunch(); + } break; } + LaunchOptions options = new LaunchOptions(); + options.moduleConf = config.moduleConf; + classLoaderControl = launch.init(config.classpath.stream().map(Paths::get).collect(Collectors.toCollection(ArrayList::new)), config.nativesDir, options); LogHelper.info("Start Minecraft Server"); LogHelper.debug("Invoke main method %s with %s", classname, launch.getClass().getName()); try { - launch.run(classname, config, real_args); + launch.launch(config.mainclass, config.mainmodule, Arrays.asList(real_args)); } catch (Throwable e) { LogHelper.error(e); System.exit(-1); @@ -233,13 +239,15 @@ public static final class Config { public ClientProfile.ClassLoaderConfig classLoaderConfig; public String librariesDir; public String mainclass; + public String mainmodule; + public String nativesDir = "natives"; public List args; public String authId; public AuthRequestEvent.OAuthRequestEvent oauth; public long oauthExpireTime; public Map extendedTokens; public LauncherConfig.LauncherEnvironment env; - public ModuleConf moduleConf = new ModuleConf(); + public LaunchOptions.ModuleConf moduleConf = new LaunchOptions.ModuleConf(); public byte[] encodedServerRsaPublicKey; @@ -247,13 +255,4 @@ public static final class Config { public Map properties; } - - 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 deleted file mode 100644 index 554ca32f..00000000 --- a/ServerWrapper/src/main/java/pro/gravit/launcher/server/launch/ClasspathLaunch.java +++ /dev/null @@ -1,23 +0,0 @@ -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(String mainclass, 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(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 deleted file mode 100644 index f4e915e8..00000000 --- a/ServerWrapper/src/main/java/pro/gravit/launcher/server/launch/Launch.java +++ /dev/null @@ -1,7 +0,0 @@ -package pro.gravit.launcher.server.launch; - -import pro.gravit.launcher.server.ServerWrapper; - -public interface Launch { - void run(String mainclass, 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 deleted file mode 100644 index b0e8102e..00000000 --- a/ServerWrapper/src/main/java/pro/gravit/launcher/server/launch/ModuleLaunch.java +++ /dev/null @@ -1,10 +0,0 @@ -package pro.gravit.launcher.server.launch; - -import pro.gravit.launcher.server.ServerWrapper; - -public class ModuleLaunch implements Launch { - @Override - public void run(String mainclass, ServerWrapper.Config config, String[] args) { - 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 deleted file mode 100644 index 8b2b6df7..00000000 --- a/ServerWrapper/src/main/java/pro/gravit/launcher/server/launch/SimpleLaunch.java +++ /dev/null @@ -1,17 +0,0 @@ -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(String mainclass, ServerWrapper.Config config, String[] args) throws Throwable { - Class mainClass = Class.forName(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 deleted file mode 100644 index 53cbfa7a..00000000 --- a/ServerWrapper/src/main/java11/pro/gravit/launcher/server/launch/ModuleLaunch.java +++ /dev/null @@ -1,69 +0,0 @@ -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; - -public class ModuleLaunch implements Launch { - @Override - @SuppressWarnings("ConfusingArgumentToVarargsMethod") - public void run(String mainclass, 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(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.lastIndexOf("."); - if(index >= 0) { - return clazz.substring(0, index); - } - return clazz; - } -} diff --git a/build.gradle b/build.gradle index 8ec0579d..fd75f711 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ id 'org.openjfx.javafxplugin' version '0.0.10' apply false } group = 'pro.gravit.launcher' -version = '5.5.0' +version = '5.5.1-SNAPSHOT' apply from: 'props.gradle' From 0f36dfec16abf20a16c45ce6b130360fdd66a493 Mon Sep 17 00:00:00 2001 From: Gravita <12893402+gravit0@users.noreply.github.com> Date: Tue, 31 Oct 2023 22:27:55 +0700 Subject: [PATCH 4/9] [FEATURE] Support compat classes in client debug --- .../launcher/debug/ClientRuntimeProvider.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Launcher/src/main/java/pro/gravit/launcher/debug/ClientRuntimeProvider.java b/Launcher/src/main/java/pro/gravit/launcher/debug/ClientRuntimeProvider.java index 369258e6..6c270e76 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/debug/ClientRuntimeProvider.java +++ b/Launcher/src/main/java/pro/gravit/launcher/debug/ClientRuntimeProvider.java @@ -4,6 +4,7 @@ import pro.gravit.launcher.Launcher; import pro.gravit.launcher.LauncherEngine; import pro.gravit.launcher.api.AuthService; +import pro.gravit.launcher.api.ClientService; import pro.gravit.launcher.events.request.AuthRequestEvent; import pro.gravit.launcher.events.request.ProfilesRequestEvent; import pro.gravit.launcher.gui.RuntimeProvider; @@ -17,6 +18,9 @@ import java.io.File; import java.io.Reader; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; @@ -126,7 +130,16 @@ public void run(String[] args) { } else { options = new LaunchOptions(); } - launch.init(classpath, nativesDir, options); + ClassLoaderControl classLoaderControl = launch.init(classpath, nativesDir, options); + ClientService.classLoaderControl = classLoaderControl; + if(compatClasses != null) { + String[] compatClassesList = compatClasses.split(","); + for (String e : compatClassesList) { + Class clazz = classLoaderControl.getClass(e); + MethodHandle runMethod = MethodHandles.lookup().findStatic(clazz, "run", MethodType.methodType(void.class, ClassLoaderControl.class)); + runMethod.invoke(classLoaderControl); + } + } launch.launch(mainClass, mainModule, Arrays.asList(args)); } catch (Throwable e) { LogHelper.error(e); From f2cbf0ed3863e100c34e4c3e63384b6559710fdf Mon Sep 17 00:00:00 2001 From: Gravita <12893402+gravit0@users.noreply.github.com> Date: Tue, 31 Oct 2023 22:52:21 +0700 Subject: [PATCH 5/9] [ANY] Update modules --- modules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules b/modules index d844e7cc..67462daf 160000 --- a/modules +++ b/modules @@ -1 +1 @@ -Subproject commit d844e7cc9b6b05d6cdbfc514efa9ad8a8df96b25 +Subproject commit 67462dafa9b8de8e3d7366f495c959b9c2c037d5 From b638efc0d186d819489a706076041c26253f59ef Mon Sep 17 00:00:00 2001 From: Gravita <12893402+gravit0@users.noreply.github.com> Date: Tue, 31 Oct 2023 22:57:37 +0700 Subject: [PATCH 6/9] [FEATURE] Support compat classes in ServerWrapper --- .../pro/gravit/launcher/server/ServerWrapper.java | 11 +++++++++++ 1 file changed, 11 insertions(+) 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 266edc6a..42f2aa47 100644 --- a/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java +++ b/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java @@ -24,6 +24,9 @@ import pro.gravit.utils.helper.SecurityHelper; import pro.gravit.utils.launch.*; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.lang.reflect.Type; import java.nio.file.Path; import java.nio.file.Paths; @@ -191,6 +194,13 @@ public void run(String... args) throws Throwable { LogHelper.info("Start Minecraft Server"); LogHelper.debug("Invoke main method %s with %s", classname, launch.getClass().getName()); try { + if(config.compatClasses != null) { + for (String e : config.compatClasses) { + Class clazz = classLoaderControl.getClass(e); + MethodHandle runMethod = MethodHandles.lookup().findStatic(clazz, "run", MethodType.methodType(void.class, ClassLoaderControl.class)); + runMethod.invoke(classLoaderControl); + } + } launch.launch(config.mainclass, config.mainmodule, Arrays.asList(real_args)); } catch (Throwable e) { LogHelper.error(e); @@ -242,6 +252,7 @@ public static final class Config { public String mainmodule; public String nativesDir = "natives"; public List args; + public List compatClasses; public String authId; public AuthRequestEvent.OAuthRequestEvent oauth; public long oauthExpireTime; From 6cd5a69149caa80b1300707cabf054b413b2f157 Mon Sep 17 00:00:00 2001 From: Gravita <12893402+gravit0@users.noreply.github.com> Date: Wed, 1 Nov 2023 00:10:19 +0700 Subject: [PATCH 7/9] [FEATURE] ModuleLaunch bug fix --- .../pro/gravit/utils/launch/ModuleLaunch.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/LauncherCore/src/main/java11/pro/gravit/utils/launch/ModuleLaunch.java b/LauncherCore/src/main/java11/pro/gravit/utils/launch/ModuleLaunch.java index 45286e79..bef68378 100644 --- a/LauncherCore/src/main/java11/pro/gravit/utils/launch/ModuleLaunch.java +++ b/LauncherCore/src/main/java11/pro/gravit/utils/launch/ModuleLaunch.java @@ -2,6 +2,7 @@ import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.JVMHelper; +import pro.gravit.utils.helper.LogHelper; import java.io.IOException; import java.io.InputStream; @@ -133,8 +134,16 @@ protected Class loadClass(String name, boolean resolve) throws ClassNotFoundE } @Override - protected Class findClass(String moduleName, String name) { + protected Class findClass(String name) throws ClassNotFoundException { + var clazz = findClass(null, name); + if(clazz == null) { + throw new ClassNotFoundException(name); + } + return clazz; + } + @Override + protected Class findClass(String moduleName, String name) { Class clazz; { clazz = classMap.get(name); @@ -164,7 +173,11 @@ protected Class findClass(String moduleName, String name) { } } if(clazz == null) { - clazz = super.findClass(moduleName, name); + try { + clazz = super.findClass(name); + } catch (ClassNotFoundException e) { + return null; + } } if(clazz != null) { classMap.put(name, clazz); From 44bc8b0bbc734e8410c9bcec4fa929de524fde6d Mon Sep 17 00:00:00 2001 From: Gravita <12893402+gravit0@users.noreply.github.com> Date: Wed, 1 Nov 2023 04:05:14 +0700 Subject: [PATCH 8/9] [FEATURE] Support modular start --- .../launcher/debug/ClientRuntimeProvider.java | 2 + .../launcher/profiles/ClientProfile.java | 2 +- .../client/ClientLauncherEntryPoint.java | 11 ++- .../pro/gravit/utils/helper/HackHelper.java | 33 +++++++ .../pro/gravit/utils/launch/BasicLaunch.java | 12 ++- .../utils/launch/ClassLoaderControl.java | 3 + .../gravit/utils/launch/LaunchOptions.java | 1 + .../pro/gravit/utils/launch/LegacyLaunch.java | 14 ++- .../pro/gravit/utils/launch/ModuleHacks.java | 15 +++ .../pro/gravit/utils/launch/ModuleLaunch.java | 94 +++++++++++++++---- .../gravit/launcher/server/ServerWrapper.java | 2 + 11 files changed, 165 insertions(+), 24 deletions(-) create mode 100644 LauncherCore/src/main/java/pro/gravit/utils/helper/HackHelper.java create mode 100644 LauncherCore/src/main/java11/pro/gravit/utils/launch/ModuleHacks.java diff --git a/Launcher/src/main/java/pro/gravit/launcher/debug/ClientRuntimeProvider.java b/Launcher/src/main/java/pro/gravit/launcher/debug/ClientRuntimeProvider.java index 6c270e76..82bd51e2 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/debug/ClientRuntimeProvider.java +++ b/Launcher/src/main/java/pro/gravit/launcher/debug/ClientRuntimeProvider.java @@ -49,6 +49,7 @@ public void run(String[] args) { String compatClasses = System.getProperty("launcher.runtime.launch.compat", null); String nativesDir = System.getProperty("launcher.runtime.launch.natives", "natives"); String launcherOptionsPath = System.getProperty("launcher.runtime.launch.options", null); + boolean enableHacks = Boolean.getBoolean("launcher.runtime.launch.enablehacks"); ClientPermissions permissions = new ClientPermissions(); if(mainClass == null) { throw new NullPointerException("Add `-Dlauncher.runtime.mainclass=YOUR_MAIN_CLASS` to jvmArgs"); @@ -130,6 +131,7 @@ public void run(String[] args) { } else { options = new LaunchOptions(); } + options.enableHacks = enableHacks; ClassLoaderControl classLoaderControl = launch.init(classpath, nativesDir, options); ClientService.classLoaderControl = classLoaderControl; if(compatClasses != null) { 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 e484f551..70d9dd2a 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/profiles/ClientProfile.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/profiles/ClientProfile.java @@ -460,7 +460,7 @@ public enum ClassLoaderConfig { } public enum CompatibilityFlags { - LEGACY_NATIVES_DIR, CLASS_CONTROL_API + LEGACY_NATIVES_DIR, CLASS_CONTROL_API, ENABLE_HACKS } public static class Version implements Comparable { diff --git a/LauncherClient/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java b/LauncherClient/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java index 9fd2a165..70c7c739 100644 --- a/LauncherClient/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java +++ b/LauncherClient/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java @@ -62,12 +62,20 @@ private static ClientParams readParams(SocketAddress address) throws IOException } } - public static void main(String[] args) throws Throwable { + public static void main(String[] args) { JVMHelper.verifySystemProperties(ClientLauncherEntryPoint.class, true); EnvHelper.checkDangerousParams(); JVMHelper.checkStackTrace(ClientLauncherEntryPoint.class); LogHelper.printVersion("Client Launcher"); ClientLauncherMethods.checkClass(ClientLauncherEntryPoint.class); + try { + realMain(args); + } catch (Throwable e) { + LogHelper.error(e); + } + } + + private static void realMain(String[] args) throws Throwable { modulesManager = new ClientModuleManager(); modulesManager.loadModule(new ClientLauncherCoreModule()); LauncherConfig.initModules(modulesManager); //INIT @@ -132,6 +140,7 @@ public static void main(String[] args) throws Throwable { LogHelper.debug("Natives dir %s", params.nativesDir); ClientProfile.ClassLoaderConfig classLoaderConfig = profile.getClassLoaderConfig(); LaunchOptions options = new LaunchOptions(); + options.enableHacks = profile.hasFlag(ClientProfile.CompatibilityFlags.ENABLE_HACKS); options.moduleConf = profile.getModuleConf(); if (classLoaderConfig == ClientProfile.ClassLoaderConfig.LAUNCHER) { if(JVMHelper.JVM_VERSION <= 11) { diff --git a/LauncherCore/src/main/java/pro/gravit/utils/helper/HackHelper.java b/LauncherCore/src/main/java/pro/gravit/utils/helper/HackHelper.java new file mode 100644 index 00000000..87d95ddc --- /dev/null +++ b/LauncherCore/src/main/java/pro/gravit/utils/helper/HackHelper.java @@ -0,0 +1,33 @@ +package pro.gravit.utils.helper; + +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.function.Consumer; + +public class HackHelper { + + private static MethodHandles.Lookup createHackLookupImpl(Class lookupClass) { + try { + Field trusted = MethodHandles.Lookup.class.getDeclaredField("TRUSTED"); + trusted.setAccessible(true); + int value = (int) trusted.get(null); + Constructor constructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, Class.class,int.class); + constructor.setAccessible(true); + return constructor.newInstance(lookupClass, null, value); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + public static MethodHandles.Lookup createHackLookup(Class lookupClass) { + Exception e = new Exception(); + StackTraceElement[] elements = e.getStackTrace(); + String className = elements[elements.length-1].getClassName(); + if(!className.startsWith("pro.gravit.launcher.") && !className.startsWith("pro.gravit.utils.")) { + throw new SecurityException(String.format("Untrusted class %s", className)); + } + return createHackLookupImpl(lookupClass); + } +} diff --git a/LauncherCore/src/main/java/pro/gravit/utils/launch/BasicLaunch.java b/LauncherCore/src/main/java/pro/gravit/utils/launch/BasicLaunch.java index 740553e3..1cb326a4 100644 --- a/LauncherCore/src/main/java/pro/gravit/utils/launch/BasicLaunch.java +++ b/LauncherCore/src/main/java/pro/gravit/utils/launch/BasicLaunch.java @@ -1,5 +1,6 @@ package pro.gravit.utils.launch; +import pro.gravit.utils.helper.HackHelper; import pro.gravit.utils.helper.JVMHelper; import java.io.File; @@ -23,6 +24,7 @@ public class BasicLaunch implements Launch { private Instrumentation instrumentation; + private MethodHandles.Lookup hackLookup; public BasicLaunch(Instrumentation instrumentation) { this.instrumentation = instrumentation; @@ -33,6 +35,9 @@ public BasicLaunch() { @Override public ClassLoaderControl init(List files, String nativePath, LaunchOptions options) { + if(options.enableHacks) { + hackLookup = HackHelper.createHackLookup(BasicLaunch.class); + } return new BasicClassLoaderControl(); } @@ -41,7 +46,7 @@ public void launch(String mainClass, String mainModule, Collection args) Class mainClazz = Class.forName(mainClass); MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClazz, "main", MethodType.methodType(void.class, String[].class)).asFixedArity(); JVMHelper.fullGC(); - mainMethod.invokeWithArguments((Object) args.toArray(new String[0])); + mainMethod.asFixedArity().invokeWithArguments((Object) args.toArray(new String[0])); } private class BasicClassLoaderControl implements ClassLoaderControl { @@ -120,5 +125,10 @@ public ClassLoader getClassLoader() { public Object getJava9ModuleController() { return null; } + + @Override + public MethodHandles.Lookup getHackLookup() { + return hackLookup; + } } } diff --git a/LauncherCore/src/main/java/pro/gravit/utils/launch/ClassLoaderControl.java b/LauncherCore/src/main/java/pro/gravit/utils/launch/ClassLoaderControl.java index c8b857b4..08e44065 100644 --- a/LauncherCore/src/main/java/pro/gravit/utils/launch/ClassLoaderControl.java +++ b/LauncherCore/src/main/java/pro/gravit/utils/launch/ClassLoaderControl.java @@ -1,5 +1,6 @@ package pro.gravit.utils.launch; +import java.lang.invoke.MethodHandles; import java.net.URL; import java.nio.file.Path; import java.security.ProtectionDomain; @@ -16,6 +17,8 @@ public interface ClassLoaderControl { ClassLoader getClassLoader(); Object getJava9ModuleController(); + + MethodHandles.Lookup getHackLookup(); interface ClassTransformer { boolean filter(String moduleName, String name); byte[] transform(String moduleName, String name, ProtectionDomain protectionDomain, byte[] data); diff --git a/LauncherCore/src/main/java/pro/gravit/utils/launch/LaunchOptions.java b/LauncherCore/src/main/java/pro/gravit/utils/launch/LaunchOptions.java index 59312f30..f5232739 100644 --- a/LauncherCore/src/main/java/pro/gravit/utils/launch/LaunchOptions.java +++ b/LauncherCore/src/main/java/pro/gravit/utils/launch/LaunchOptions.java @@ -6,6 +6,7 @@ import java.util.Map; public class LaunchOptions { + public boolean enableHacks; public ModuleConf moduleConf; diff --git a/LauncherCore/src/main/java/pro/gravit/utils/launch/LegacyLaunch.java b/LauncherCore/src/main/java/pro/gravit/utils/launch/LegacyLaunch.java index aca88038..08f65795 100644 --- a/LauncherCore/src/main/java/pro/gravit/utils/launch/LegacyLaunch.java +++ b/LauncherCore/src/main/java/pro/gravit/utils/launch/LegacyLaunch.java @@ -1,5 +1,6 @@ package pro.gravit.utils.launch; +import pro.gravit.utils.helper.HackHelper; import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.JVMHelper; @@ -20,6 +21,7 @@ public class LegacyLaunch implements Launch { private LegacyClassLoader legacyClassLoader; + private MethodHandles.Lookup hackLookup; @Override public ClassLoaderControl init(List files, String nativePath, LaunchOptions options) { @@ -31,6 +33,9 @@ public ClassLoaderControl init(List files, String nativePath, LaunchOption } }).toArray(URL[]::new), BasicLaunch.class.getClassLoader()); legacyClassLoader.nativePath = nativePath; + if(options.enableHacks) { + hackLookup = HackHelper.createHackLookup(BasicLaunch.class); + } return legacyClassLoader.makeControl(); } @@ -40,10 +45,10 @@ public void launch(String mainClass, String mainModule, Collection args) Class mainClazz = Class.forName(mainClass, true, legacyClassLoader); MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClazz, "main", MethodType.methodType(void.class, String[].class)).asFixedArity(); JVMHelper.fullGC(); - mainMethod.invokeWithArguments((Object) args.toArray(new String[0])); + mainMethod.asFixedArity().invokeWithArguments((Object) args.toArray(new String[0])); } - private static class LegacyClassLoader extends URLClassLoader { + private class LegacyClassLoader extends URLClassLoader { private final ClassLoader SYSTEM_CLASS_LOADER = ClassLoader.getSystemClassLoader(); private final List transformers = new ArrayList<>(); private final Map> classMap = new ConcurrentHashMap<>(); @@ -170,6 +175,11 @@ public ClassLoader getClassLoader() { public Object getJava9ModuleController() { return null; } + + @Override + public MethodHandles.Lookup getHackLookup() { + return hackLookup; + } } } } diff --git a/LauncherCore/src/main/java11/pro/gravit/utils/launch/ModuleHacks.java b/LauncherCore/src/main/java11/pro/gravit/utils/launch/ModuleHacks.java new file mode 100644 index 00000000..de357291 --- /dev/null +++ b/LauncherCore/src/main/java11/pro/gravit/utils/launch/ModuleHacks.java @@ -0,0 +1,15 @@ +package pro.gravit.utils.launch; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +public class ModuleHacks { + + public static ModuleLayer.Controller createController(MethodHandles.Lookup lookup, ModuleLayer layer) { + try { + return (ModuleLayer.Controller) lookup.findConstructor(ModuleLayer.Controller.class, MethodType.methodType(void.class, ModuleLayer.class)).invoke(layer); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } +} diff --git a/LauncherCore/src/main/java11/pro/gravit/utils/launch/ModuleLaunch.java b/LauncherCore/src/main/java11/pro/gravit/utils/launch/ModuleLaunch.java index bef68378..c1582557 100644 --- a/LauncherCore/src/main/java11/pro/gravit/utils/launch/ModuleLaunch.java +++ b/LauncherCore/src/main/java11/pro/gravit/utils/launch/ModuleLaunch.java @@ -1,5 +1,6 @@ package pro.gravit.utils.launch; +import pro.gravit.utils.helper.HackHelper; import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.JVMHelper; import pro.gravit.utils.helper.LogHelper; @@ -12,6 +13,7 @@ import java.lang.module.Configuration; import java.lang.module.ModuleFinder; import java.net.MalformedURLException; +import java.net.URI; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Path; @@ -28,6 +30,7 @@ public class ModuleLaunch implements Launch { private ModuleLayer.Controller controller; private ModuleFinder moduleFinder; private ModuleLayer layer; + private MethodHandles.Lookup hackLookup; @Override public ClassLoaderControl init(List files, String nativePath, LaunchOptions options) { moduleClassLoader = new ModuleClassLoader(files.stream().map((e) -> { @@ -39,34 +42,84 @@ public ClassLoaderControl init(List files, String nativePath, LaunchOption }).toArray(URL[]::new), BasicLaunch.class.getClassLoader()); moduleClassLoader.nativePath = nativePath; { + if(options.enableHacks) { + hackLookup = HackHelper.createHackLookup(ModuleLaunch.class); + } if(options.moduleConf != null) { // Create Module Layer - moduleFinder = ModuleFinder.of(options.moduleConf.modulePath.stream().map(Paths::get).toArray(Path[]::new)); + moduleFinder = ModuleFinder.of(options.moduleConf.modulePath.stream().map(Paths::get).map(Path::toAbsolutePath).toArray(Path[]::new)); ModuleLayer bootLayer = ModuleLayer.boot(); + if(options.moduleConf.modules.contains("ALL-MODULE-PATH")) { + var set = moduleFinder.findAll(); + if(LogHelper.isDevEnabled()) { + for(var m : set) { + LogHelper.dev("Found module %s in %s", m.descriptor().name(), m.location().map(URI::toString).orElse("unknown")); + } + LogHelper.dev("Found %d modules", set.size()); + } + for(var m : set) { + options.moduleConf.modules.add(m.descriptor().name()); + } + options.moduleConf.modules.remove("ALL-MODULE-PATH"); + } configuration = bootLayer.configuration() - .resolveAndBind(ModuleFinder.of(), moduleFinder, options.moduleConf.modules); + .resolveAndBind(moduleFinder, ModuleFinder.of(), options.moduleConf.modules); controller = ModuleLayer.defineModulesWithOneLoader(configuration, List.of(bootLayer), moduleClassLoader); - moduleClassLoader.controller = controller; layer = controller.layer(); // Configure exports / opens for(var e : options.moduleConf.exports.entrySet()) { - String[] split = e.getKey().split("\\\\"); - Module source = layer.findModule(split[0]).orElseThrow(); + String[] split = e.getKey().split("/"); + String moduleName = split[0]; String pkg = split[1]; - Module target = layer.findModule(e.getValue()).orElseThrow(); - controller.addExports(source, pkg, target); + LogHelper.dev("Export module: %s package: %s to %s", moduleName, pkg, e.getValue()); + Module source = layer.findModule(split[0]).orElse(null); + if(source == null) { + throw new RuntimeException(String.format("Module %s not found", moduleName)); + } + Module target = layer.findModule(e.getValue()).orElse(null); + if(target == null) { + throw new RuntimeException(String.format("Module %s not found", e.getValue())); + } + if(options.enableHacks && source.getLayer() != layer) { + ModuleHacks.createController(hackLookup, source.getLayer()).addExports(source, pkg, target); + } else { + controller.addExports(source, pkg, target); + } } for(var e : options.moduleConf.opens.entrySet()) { - String[] split = e.getKey().split("\\\\"); - Module source = layer.findModule(split[0]).orElseThrow(); + String[] split = e.getKey().split("/"); + String moduleName = split[0]; String pkg = split[1]; - Module target = layer.findModule(e.getValue()).orElseThrow(); - controller.addOpens(source, pkg, target); + LogHelper.dev("Open module: %s package: %s to %s", moduleName, pkg, e.getValue()); + Module source = layer.findModule(split[0]).orElse(null); + if(source == null) { + throw new RuntimeException(String.format("Module %s not found", moduleName)); + } + Module target = layer.findModule(e.getValue()).orElse(null); + if(target == null) { + throw new RuntimeException(String.format("Module %s not found", e.getValue())); + } + if(options.enableHacks && source.getLayer() != layer) { + ModuleHacks.createController(hackLookup, source.getLayer()).addOpens(source, pkg, target); + } else { + controller.addOpens(source, pkg, target); + } } for(var e : options.moduleConf.reads.entrySet()) { - Module source = layer.findModule(e.getKey()).orElseThrow(); - Module target = layer.findModule(e.getValue()).orElseThrow(); - controller.addReads(source, target); + LogHelper.dev("Read module %s to %s", e.getKey(), e.getValue()); + Module source = layer.findModule(e.getKey()).orElse(null); + if(source == null) { + throw new RuntimeException(String.format("Module %s not found", e.getKey())); + } + Module target = layer.findModule(e.getValue()).orElse(null); + if(target == null) { + throw new RuntimeException(String.format("Module %s not found", e.getValue())); + } + if(options.enableHacks && source.getLayer() != layer) { + ModuleHacks.createController(hackLookup, source.getLayer()).addReads(source, target); + } else { + controller.addReads(source, target); + } } } } @@ -80,7 +133,7 @@ public void launch(String mainClass, String mainModuleName, Collection a Class mainClazz = Class.forName(mainClass, true, moduleClassLoader); MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClazz, "main", MethodType.methodType(void.class, String[].class)).asFixedArity(); JVMHelper.fullGC(); - mainMethod.invokeWithArguments((Object) args.toArray(new String[0])); + mainMethod.asFixedArity().invokeWithArguments((Object) args.toArray(new String[0])); return; } Module mainModule = layer.findModule(mainModuleName).orElseThrow(); @@ -92,7 +145,7 @@ public void launch(String mainClass, String mainModuleName, Collection a ClassLoader loader = mainModule.getClassLoader(); Class mainClazz = Class.forName(mainClass, true, loader); MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClazz, "main", MethodType.methodType(void.class, String[].class)); - mainMethod.invoke((Object) args.toArray(new String[0])); + mainMethod.asFixedArity().invokeWithArguments((Object) args.toArray(new String[0])); } private static String getPackageFromClass(String clazz) { @@ -103,14 +156,12 @@ private static String getPackageFromClass(String clazz) { return clazz; } - private static class ModuleClassLoader extends URLClassLoader { + private class ModuleClassLoader extends URLClassLoader { private final ClassLoader SYSTEM_CLASS_LOADER = ClassLoader.getSystemClassLoader(); private final List transformers = new ArrayList<>(); private final Map> classMap = new ConcurrentHashMap<>(); private String nativePath; - private ModuleLayer.Controller controller; - private final List packages = new ArrayList<>(); public ModuleClassLoader(URL[] urls) { super(urls); @@ -245,6 +296,11 @@ public ClassLoader getClassLoader() { public Object getJava9ModuleController() { return controller; } + + @Override + public MethodHandles.Lookup getHackLookup() { + return hackLookup; + } } } } 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 42f2aa47..6884ef9b 100644 --- a/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java +++ b/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java @@ -189,6 +189,7 @@ public void run(String... args) throws Throwable { break; } LaunchOptions options = new LaunchOptions(); + options.enableHacks = config.enableHacks; options.moduleConf = config.moduleConf; classLoaderControl = launch.init(config.classpath.stream().map(Paths::get).collect(Collectors.toCollection(ArrayList::new)), config.nativesDir, options); LogHelper.info("Start Minecraft Server"); @@ -263,6 +264,7 @@ public static final class Config { public byte[] encodedServerRsaPublicKey; public byte[] encodedServerEcPublicKey; + public boolean enableHacks; public Map properties; } From b6be2e243db68aa3e48bd99f38446bf16bc703bd Mon Sep 17 00:00:00 2001 From: Gravita <12893402+gravit0@users.noreply.github.com> Date: Wed, 1 Nov 2023 17:40:52 +0700 Subject: [PATCH 9/9] [ANY] 5.5.1 stable --- LauncherCore/src/main/java/pro/gravit/utils/Version.java | 2 +- build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LauncherCore/src/main/java/pro/gravit/utils/Version.java b/LauncherCore/src/main/java/pro/gravit/utils/Version.java index 13ff9f72..536aa8f1 100644 --- a/LauncherCore/src/main/java/pro/gravit/utils/Version.java +++ b/LauncherCore/src/main/java/pro/gravit/utils/Version.java @@ -8,7 +8,7 @@ public final class Version implements Comparable { public static final int MINOR = 5; public static final int PATCH = 1; public static final int BUILD = 1; - public static final Version.Type RELEASE = Type.DEV; + public static final Version.Type RELEASE = Type.STABLE; public final int major; public final int minor; public final int patch; diff --git a/build.gradle b/build.gradle index fd75f711..e6ff1f44 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ id 'org.openjfx.javafxplugin' version '0.0.10' apply false } group = 'pro.gravit.launcher' -version = '5.5.1-SNAPSHOT' +version = '5.5.1' apply from: 'props.gradle'