mirror of
https://github.com/GravitLauncher/Launcher
synced 2024-12-22 16:41:46 +03:00
Merge branch 'release/5.5.1'
This commit is contained in:
commit
2e10c78ad2
27 changed files with 915 additions and 291 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
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.api.ClientService;
|
||||
import pro.gravit.launcher.events.request.AuthRequestEvent;
|
||||
import pro.gravit.launcher.events.request.ProfilesRequestEvent;
|
||||
import pro.gravit.launcher.gui.RuntimeProvider;
|
||||
|
@ -10,10 +12,20 @@
|
|||
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.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;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ClientRuntimeProvider implements RuntimeProvider {
|
||||
|
@ -32,6 +44,12 @@ 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);
|
||||
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");
|
||||
|
@ -79,8 +97,52 @@ 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<Path> 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();
|
||||
}
|
||||
options.enableHacks = enableHacks;
|
||||
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);
|
||||
LauncherEngine.exitLauncher(-15);
|
||||
|
|
|
@ -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<ClientProfile> {
|
|||
// 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<ServerProfile> getServers() {
|
||||
return servers;
|
||||
}
|
||||
|
@ -447,7 +460,7 @@ public enum ClassLoaderConfig {
|
|||
}
|
||||
|
||||
public enum CompatibilityFlags {
|
||||
LEGACY_NATIVES_DIR
|
||||
LEGACY_NATIVES_DIR, CLASS_CONTROL_API, ENABLE_HACKS
|
||||
}
|
||||
|
||||
public static class Version implements Comparable<Version> {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String> 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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
@ -59,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
|
||||
|
@ -102,7 +113,7 @@ public static void main(String[] args) throws Throwable {
|
|||
LogHelper.debug("Verifying ClientLauncher sign and classpath");
|
||||
List<Path> 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<URL> classpathURLs = classpath.stream().map(IOHelper::toURL).collect(Collectors.toList());
|
||||
// Start client with WatchService monitoring
|
||||
RequestService service;
|
||||
|
@ -128,34 +139,40 @@ 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) {
|
||||
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 +281,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<String> 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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ public final class Version implements Comparable<Version> {
|
|||
|
||||
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 final int major;
|
||||
|
|
|
@ -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<MethodHandles.Lookup> 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
package pro.gravit.utils.launch;
|
||||
|
||||
import pro.gravit.utils.helper.HackHelper;
|
||||
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;
|
||||
private MethodHandles.Lookup hackLookup;
|
||||
|
||||
public BasicLaunch(Instrumentation instrumentation) {
|
||||
this.instrumentation = instrumentation;
|
||||
}
|
||||
|
||||
public BasicLaunch() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoaderControl init(List<Path> files, String nativePath, LaunchOptions options) {
|
||||
if(options.enableHacks) {
|
||||
hackLookup = HackHelper.createHackLookup(BasicLaunch.class);
|
||||
}
|
||||
return new BasicClassLoaderControl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launch(String mainClass, String mainModule, Collection<String> 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.asFixedArity().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<split.length;i++) {
|
||||
urls[i] = Paths.get(split[i]).toAbsolutePath().toUri().toURL();
|
||||
}
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return urls;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getClass(String name) throws ClassNotFoundException {
|
||||
return Class.forName(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoader getClassLoader() {
|
||||
return BasicLaunch.class.getClassLoader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getJava9ModuleController() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandles.Lookup getHackLookup() {
|
||||
return hackLookup;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package pro.gravit.utils.launch;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
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();
|
||||
|
||||
MethodHandles.Lookup getHackLookup();
|
||||
interface ClassTransformer {
|
||||
boolean filter(String moduleName, String name);
|
||||
byte[] transform(String moduleName, String name, ProtectionDomain protectionDomain, byte[] data);
|
||||
}
|
||||
}
|
|
@ -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<Path> files, String nativePath, LaunchOptions options);
|
||||
void launch(String mainClass, String mainModule, Collection<String> args) throws Throwable;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
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 boolean enableHacks;
|
||||
public ModuleConf moduleConf;
|
||||
|
||||
|
||||
public static final class ModuleConf {
|
||||
public List<String> modules = new ArrayList<>();
|
||||
public List<String> modulePath = new ArrayList<>();
|
||||
public Map<String, String> exports = new HashMap<>();
|
||||
public Map<String, String> opens = new HashMap<>();
|
||||
public Map<String, String> reads = new HashMap<>();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,185 @@
|
|||
package pro.gravit.utils.launch;
|
||||
|
||||
import pro.gravit.utils.helper.HackHelper;
|
||||
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;
|
||||
private MethodHandles.Lookup hackLookup;
|
||||
|
||||
@Override
|
||||
public ClassLoaderControl init(List<Path> 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;
|
||||
if(options.enableHacks) {
|
||||
hackLookup = HackHelper.createHackLookup(BasicLaunch.class);
|
||||
}
|
||||
return legacyClassLoader.makeControl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launch(String mainClass, String mainModule, Collection<String> 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.asFixedArity().invokeWithArguments((Object) args.toArray(new String[0]));
|
||||
}
|
||||
|
||||
private class LegacyClassLoader extends URLClassLoader {
|
||||
private final ClassLoader SYSTEM_CLASS_LOADER = ClassLoader.getSystemClassLoader();
|
||||
private final List<ClassLoaderControl.ClassTransformer> transformers = new ArrayList<>();
|
||||
private final Map<String, Class<?>> classMap = new ConcurrentHashMap<>();
|
||||
private String nativePath;
|
||||
|
||||
private final List<String> 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandles.Lookup getHackLookup() {
|
||||
return hackLookup;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<Path> files, String nativePath, LaunchOptions options) {
|
||||
throw new UnsupportedOperationException("Please use Multi-Release JAR");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launch(String mainClass, String mainModule, Collection<String> args) throws Throwable {
|
||||
throw new UnsupportedOperationException("Please use Multi-Release JAR");
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,306 @@
|
|||
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;
|
||||
|
||||
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.URI;
|
||||
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;
|
||||
private MethodHandles.Lookup hackLookup;
|
||||
@Override
|
||||
public ClassLoaderControl init(List<Path> 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.enableHacks) {
|
||||
hackLookup = HackHelper.createHackLookup(ModuleLaunch.class);
|
||||
}
|
||||
if(options.moduleConf != null) {
|
||||
// Create Module Layer
|
||||
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, ModuleFinder.of(), options.moduleConf.modules);
|
||||
controller = ModuleLayer.defineModulesWithOneLoader(configuration, List.of(bootLayer), moduleClassLoader);
|
||||
layer = controller.layer();
|
||||
// Configure exports / opens
|
||||
for(var e : options.moduleConf.exports.entrySet()) {
|
||||
String[] split = e.getKey().split("/");
|
||||
String moduleName = split[0];
|
||||
String pkg = split[1];
|
||||
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("/");
|
||||
String moduleName = split[0];
|
||||
String pkg = split[1];
|
||||
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()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return moduleClassLoader.makeControl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launch(String mainClass, String mainModuleName, Collection<String> 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.asFixedArity().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.asFixedArity().invokeWithArguments((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 class ModuleClassLoader extends URLClassLoader {
|
||||
private final ClassLoader SYSTEM_CLASS_LOADER = ClassLoader.getSystemClassLoader();
|
||||
private final List<ClassLoaderControl.ClassTransformer> transformers = new ArrayList<>();
|
||||
private final Map<String, Class<?>> classMap = new ConcurrentHashMap<>();
|
||||
private String nativePath;
|
||||
|
||||
private final List<String> 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 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);
|
||||
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) {
|
||||
try {
|
||||
clazz = super.findClass(name);
|
||||
} catch (ClassNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandles.Lookup getHackLookup() {
|
||||
return hackLookup;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,25 +17,27 @@
|
|||
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.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;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ServerWrapper extends JsonConfigurable<ServerWrapper.Config> {
|
||||
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 +175,34 @@ 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.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");
|
||||
LogHelper.debug("Invoke main method %s with %s", classname, launch.getClass().getName());
|
||||
try {
|
||||
launch.run(classname, config, real_args);
|
||||
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);
|
||||
System.exit(-1);
|
||||
|
@ -233,27 +250,22 @@ public static final class Config {
|
|||
public ClientProfile.ClassLoaderConfig classLoaderConfig;
|
||||
public String librariesDir;
|
||||
public String mainclass;
|
||||
public String mainmodule;
|
||||
public String nativesDir = "natives";
|
||||
public List<String> args;
|
||||
public List<String> compatClasses;
|
||||
public String authId;
|
||||
public AuthRequestEvent.OAuthRequestEvent oauth;
|
||||
public long oauthExpireTime;
|
||||
public Map<String, Request.ExtendedToken> extendedTokens;
|
||||
public LauncherConfig.LauncherEnvironment env;
|
||||
public ModuleConf moduleConf = new ModuleConf();
|
||||
public LaunchOptions.ModuleConf moduleConf = new LaunchOptions.ModuleConf();
|
||||
|
||||
public byte[] encodedServerRsaPublicKey;
|
||||
|
||||
public byte[] encodedServerEcPublicKey;
|
||||
public boolean enableHacks;
|
||||
|
||||
public Map<String, String> properties;
|
||||
}
|
||||
|
||||
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<>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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'
|
||||
|
||||
apply from: 'props.gradle'
|
||||
|
||||
|
|
2
modules
2
modules
|
@ -1 +1 @@
|
|||
Subproject commit 5847a9b937039eb6402082a458ad0afac7163840
|
||||
Subproject commit 67462dafa9b8de8e3d7366f495c959b9c2c037d5
|
Loading…
Reference in a new issue