mirror of
https://github.com/GravitLauncher/Launcher
synced 2024-12-23 00:51:01 +03:00
[FEATURE][EXPERIMENTAL] Новое API запуска процесса клиента
This commit is contained in:
parent
9ccaf3b1d7
commit
c7468eba14
5 changed files with 526 additions and 1 deletions
|
@ -9,7 +9,6 @@
|
||||||
-target 8
|
-target 8
|
||||||
-forceprocessing
|
-forceprocessing
|
||||||
|
|
||||||
-overloadaggressively
|
|
||||||
-repackageclasses 'pro.gravit.launcher'
|
-repackageclasses 'pro.gravit.launcher'
|
||||||
-keepattributes SourceFile,LineNumberTable,*Annotation*
|
-keepattributes SourceFile,LineNumberTable,*Annotation*
|
||||||
-renamesourcefileattribute SourceFile
|
-renamesourcefileattribute SourceFile
|
||||||
|
|
|
@ -39,6 +39,7 @@ public static X509Certificate[] getCertificates(Class<?> clazz) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final AtomicBoolean IS_CLIENT = new AtomicBoolean(false);
|
public static final AtomicBoolean IS_CLIENT = new AtomicBoolean(false);
|
||||||
|
public static ClientLauncherProcess.ClientParams clientParams;
|
||||||
|
|
||||||
public static void checkClass(Class<?> clazz) throws SecurityException {
|
public static void checkClass(Class<?> clazz) throws SecurityException {
|
||||||
LauncherTrustManager trustManager = Launcher.getConfig().trustManager;
|
LauncherTrustManager trustManager = Launcher.getConfig().trustManager;
|
||||||
|
|
|
@ -0,0 +1,261 @@
|
||||||
|
package pro.gravit.launcher.client;
|
||||||
|
|
||||||
|
import pro.gravit.launcher.Launcher;
|
||||||
|
import pro.gravit.launcher.LauncherAgent;
|
||||||
|
import pro.gravit.launcher.LauncherConfig;
|
||||||
|
import pro.gravit.launcher.LauncherEngine;
|
||||||
|
import pro.gravit.launcher.api.AuthService;
|
||||||
|
import pro.gravit.launcher.api.ClientService;
|
||||||
|
import pro.gravit.launcher.client.events.ClientLaunchPhase;
|
||||||
|
import pro.gravit.launcher.client.events.ClientLauncherInitPhase;
|
||||||
|
import pro.gravit.launcher.client.events.ClientLauncherPostInitPhase;
|
||||||
|
import pro.gravit.launcher.guard.LauncherGuardManager;
|
||||||
|
import pro.gravit.launcher.hasher.FileNameMatcher;
|
||||||
|
import pro.gravit.launcher.hasher.HashedDir;
|
||||||
|
import pro.gravit.launcher.managers.ClientGsonManager;
|
||||||
|
import pro.gravit.launcher.modules.events.PreConfigPhase;
|
||||||
|
import pro.gravit.launcher.patches.FMLPatcher;
|
||||||
|
import pro.gravit.launcher.profiles.ClientProfile;
|
||||||
|
import pro.gravit.launcher.request.Request;
|
||||||
|
import pro.gravit.launcher.request.RequestException;
|
||||||
|
import pro.gravit.launcher.request.auth.RestoreSessionRequest;
|
||||||
|
import pro.gravit.launcher.serialize.HInput;
|
||||||
|
import pro.gravit.launcher.utils.DirWatcher;
|
||||||
|
import pro.gravit.utils.helper.*;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
|
import java.net.*;
|
||||||
|
import java.nio.file.FileVisitResult;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.SimpleFileVisitor;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class ClientLauncherEntryPoint {
|
||||||
|
private ClientLauncherProcess.ClientParams readParams(SocketAddress address) throws IOException {
|
||||||
|
try (Socket socket = IOHelper.newSocket())
|
||||||
|
{
|
||||||
|
socket.connect(address);
|
||||||
|
try(HInput input = new HInput(socket.getInputStream()))
|
||||||
|
{
|
||||||
|
byte[] serialized = input.readByteArray(0);
|
||||||
|
ClientLauncherProcess.ClientParams params = Launcher.gsonManager.gson.fromJson(new String(serialized, IOHelper.UNICODE_CHARSET), ClientLauncherProcess.ClientParams.class);
|
||||||
|
params.clientHDir = new HashedDir(input);
|
||||||
|
params.assetHDir = new HashedDir(input);
|
||||||
|
params.javaHDir = new HashedDir(input);
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static ClientClassLoader classLoader;
|
||||||
|
public void main(String[] args) throws Throwable {
|
||||||
|
LauncherEngine.IS_CLIENT.set(true);
|
||||||
|
LauncherEngine engine = LauncherEngine.clientInstance();
|
||||||
|
LauncherEngine.checkClass(LauncherEngine.class);
|
||||||
|
LauncherEngine.checkClass(LauncherAgent.class);
|
||||||
|
LauncherEngine.checkClass(ClientLauncher.class);
|
||||||
|
LauncherEngine.modulesManager = new ClientModuleManager();
|
||||||
|
LauncherConfig.initModules(LauncherEngine.modulesManager); //INIT
|
||||||
|
LauncherEngine.modulesManager.initModules(null);
|
||||||
|
initGson(LauncherEngine.modulesManager);
|
||||||
|
LauncherEngine.verifyNoAgent();
|
||||||
|
LauncherEngine.modulesManager.invokeEvent(new PreConfigPhase());
|
||||||
|
JVMHelper.verifySystemProperties(ClientLauncher.class, true);
|
||||||
|
EnvHelper.checkDangerousParams();
|
||||||
|
JVMHelper.checkStackTrace(ClientLauncher.class);
|
||||||
|
LogHelper.printVersion("Client Launcher");
|
||||||
|
engine.readKeys();
|
||||||
|
LauncherGuardManager.initGuard(true);
|
||||||
|
LogHelper.debug("Reading ClientLauncher params");
|
||||||
|
ClientLauncherProcess.ClientParams params = readParams(new InetSocketAddress("127.0.0.1", Launcher.getConfig().clientPort));
|
||||||
|
ClientProfile profile = params.profile;
|
||||||
|
Launcher.profile = profile;
|
||||||
|
AuthService.profile = profile;
|
||||||
|
LauncherEngine.clientParams = params;
|
||||||
|
Request.setSession(params.session);
|
||||||
|
checkJVMBitsAndVersion();
|
||||||
|
LauncherEngine.modulesManager.invokeEvent(new ClientLauncherInitPhase(null));
|
||||||
|
|
||||||
|
Path clientDir = Paths.get(params.clientDir);
|
||||||
|
Path assetDir = Paths.get(params.assetDir);
|
||||||
|
|
||||||
|
// Verify ClientLauncher sign and classpath
|
||||||
|
LogHelper.debug("Verifying ClientLauncher sign and classpath");
|
||||||
|
List<URL> classpath = new LinkedList<>();
|
||||||
|
resolveClassPathStream(clientDir, params.profile.getClassPath()).map(IOHelper::toURL).collect(Collectors.toCollection(() -> classpath));
|
||||||
|
|
||||||
|
params.profile.pushOptionalClassPath((opt) -> {
|
||||||
|
resolveClassPathStream(clientDir, opt).map(IOHelper::toURL).collect(Collectors.toCollection(() -> classpath));
|
||||||
|
});
|
||||||
|
classLoader = new ClientClassLoader(classpath.toArray(new URL[0]), ClassLoader.getSystemClassLoader());
|
||||||
|
Thread.currentThread().setContextClassLoader(classLoader);
|
||||||
|
classLoader.nativePath = clientDir.resolve("natives").toString();
|
||||||
|
// Start client with WatchService monitoring
|
||||||
|
boolean digest = !profile.isUpdateFastCheck();
|
||||||
|
LogHelper.debug("Restore sessions");
|
||||||
|
RestoreSessionRequest request = new RestoreSessionRequest(Request.getSession());
|
||||||
|
request.request();
|
||||||
|
Request.service.reconnectCallback = () ->
|
||||||
|
{
|
||||||
|
LogHelper.debug("WebSocket connect closed. Try reconnect");
|
||||||
|
try {
|
||||||
|
Request.service.open();
|
||||||
|
LogHelper.debug("Connect to %s", Launcher.getConfig().address);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogHelper.error(e);
|
||||||
|
throw new RequestException(String.format("Connect error: %s", e.getMessage() != null ? e.getMessage() : "null"));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
RestoreSessionRequest request1 = new RestoreSessionRequest(Request.getSession());
|
||||||
|
request1.request();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogHelper.error(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
AuthService.username = params.playerProfile.username;
|
||||||
|
AuthService.uuid = params.playerProfile.uuid;
|
||||||
|
ClientService.classLoader = classLoader;
|
||||||
|
ClientService.nativePath = classLoader.nativePath;
|
||||||
|
classLoader.addURL(IOHelper.getCodeSource(ClientLauncher.class).toUri().toURL());
|
||||||
|
//classForName(classLoader, "com.google.common.collect.ForwardingMultimap");
|
||||||
|
ClientService.baseURLs = classLoader.getURLs();
|
||||||
|
LogHelper.debug("Starting JVM and client WatchService");
|
||||||
|
FileNameMatcher assetMatcher = profile.getAssetUpdateMatcher();
|
||||||
|
FileNameMatcher clientMatcher = profile.getClientUpdateMatcher();
|
||||||
|
try (DirWatcher assetWatcher = new DirWatcher(assetDir, params.assetHDir, assetMatcher, digest);
|
||||||
|
DirWatcher clientWatcher = new DirWatcher(clientDir, params.clientHDir, clientMatcher, digest)) {
|
||||||
|
// Verify current state of all dirs
|
||||||
|
//verifyHDir(IOHelper.JVM_DIR, jvmHDir.object, null, digest);
|
||||||
|
//for (OptionalFile s : Launcher.profile.getOptional()) {
|
||||||
|
// if (params.updateOptional.contains(s)) s.mark = true;
|
||||||
|
// else hdir.removeR(s.file);
|
||||||
|
//}
|
||||||
|
Launcher.profile.pushOptionalFile(params.clientHDir, false);
|
||||||
|
// Start WatchService, and only then client
|
||||||
|
CommonHelper.newThread("Asset Directory Watcher", true, assetWatcher).start();
|
||||||
|
CommonHelper.newThread("Client Directory Watcher", true, clientWatcher).start();
|
||||||
|
verifyHDir(assetDir, params.assetHDir, assetMatcher, digest);
|
||||||
|
verifyHDir(clientDir, params.clientHDir, clientMatcher, digest);
|
||||||
|
launch(profile, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static void initGson(ClientModuleManager moduleManager) {
|
||||||
|
Launcher.gsonManager = new ClientGsonManager(moduleManager);
|
||||||
|
Launcher.gsonManager.initGson();
|
||||||
|
}
|
||||||
|
public static void verifyHDir(Path dir, HashedDir hdir, FileNameMatcher matcher, boolean digest) throws IOException {
|
||||||
|
//if (matcher != null)
|
||||||
|
// matcher = matcher.verifyOnly();
|
||||||
|
|
||||||
|
// Hash directory and compare (ignore update-only matcher entries, it will break offline-mode)
|
||||||
|
HashedDir currentHDir = new HashedDir(dir, matcher, true, digest);
|
||||||
|
HashedDir.Diff diff = hdir.diff(currentHDir, matcher);
|
||||||
|
if (!diff.isSame()) {
|
||||||
|
/*AtomicBoolean isFoundFile = new AtomicBoolean(false);
|
||||||
|
diff.extra.walk(File.separator, (e,k,v) -> {
|
||||||
|
if(v.getType().equals(HashedEntry.Type.FILE)) { LogHelper.error("Extra file %s", e); isFoundFile.set(true); }
|
||||||
|
else LogHelper.error("Extra %s", e);
|
||||||
|
});
|
||||||
|
diff.mismatch.walk(File.separator, (e,k,v) -> {
|
||||||
|
if(v.getType().equals(HashedEntry.Type.FILE)) { LogHelper.error("Mismatch file %s", e); isFoundFile.set(true); }
|
||||||
|
else LogHelper.error("Mismatch %s", e);
|
||||||
|
});
|
||||||
|
if(isFoundFile.get())*/
|
||||||
|
throw new SecurityException(String.format("Forbidden modification: '%s'", IOHelper.getFileName(dir)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void checkJVMBitsAndVersion() {
|
||||||
|
if (JVMHelper.JVM_BITS != JVMHelper.OS_BITS) {
|
||||||
|
String error = String.format("У Вас установлена Java %d, но Ваша система определена как %d. Установите Java правильной разрядности", JVMHelper.JVM_BITS, JVMHelper.OS_BITS);
|
||||||
|
LogHelper.error(error);
|
||||||
|
if (Launcher.getConfig().isWarningMissArchJava)
|
||||||
|
JOptionPane.showMessageDialog(null, error);
|
||||||
|
}
|
||||||
|
String jvmVersion = JVMHelper.RUNTIME_MXBEAN.getVmVersion();
|
||||||
|
LogHelper.info(jvmVersion);
|
||||||
|
if (jvmVersion.startsWith("10.") || jvmVersion.startsWith("9.") || jvmVersion.startsWith("11.")) {
|
||||||
|
String error = String.format("У Вас установлена Java %s. Для правильной работы необходима Java 8", JVMHelper.RUNTIME_MXBEAN.getVmVersion());
|
||||||
|
LogHelper.error(error);
|
||||||
|
if (Launcher.getConfig().isWarningMissArchJava)
|
||||||
|
JOptionPane.showMessageDialog(null, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static LinkedList<Path> resolveClassPathList(Path clientDir, String... classPath) throws IOException {
|
||||||
|
return resolveClassPathStream(clientDir, classPath).collect(Collectors.toCollection(LinkedList::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Stream<Path> resolveClassPathStream(Path clientDir, String... classPath) throws IOException {
|
||||||
|
Stream.Builder<Path> builder = Stream.builder();
|
||||||
|
for (String classPathEntry : classPath) {
|
||||||
|
Path path = clientDir.resolve(IOHelper.toPath(classPathEntry.replace(IOHelper.CROSS_SEPARATOR, IOHelper.PLATFORM_SEPARATOR)));
|
||||||
|
if (IOHelper.isDir(path)) { // Recursive walking and adding
|
||||||
|
IOHelper.walk(path, new ClassPathFileVisitor(builder), false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
builder.accept(path);
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
private static final class ClassPathFileVisitor extends SimpleFileVisitor<Path> {
|
||||||
|
private final Stream.Builder<Path> result;
|
||||||
|
|
||||||
|
private ClassPathFileVisitor(Stream.Builder<Path> result) {
|
||||||
|
this.result = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||||
|
if (IOHelper.hasExtension(file, "jar") || IOHelper.hasExtension(file, "zip"))
|
||||||
|
result.accept(file);
|
||||||
|
return super.visitFile(file, attrs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static void launch(ClientProfile profile, ClientLauncherProcess.ClientParams params) throws Throwable {
|
||||||
|
// Add client args
|
||||||
|
Collection<String> args = new LinkedList<>();
|
||||||
|
if (profile.getVersion().compareTo(ClientProfile.Version.MC164) >= 0)
|
||||||
|
params.addClientArgs(args);
|
||||||
|
else {
|
||||||
|
params.addClientLegacyArgs(args);
|
||||||
|
System.setProperty("minecraft.applet.TargetDirectory", params.clientDir);
|
||||||
|
}
|
||||||
|
Collections.addAll(args, profile.getClientArgs());
|
||||||
|
List<String> copy = new ArrayList<>(args);
|
||||||
|
for (int i = 0, l = copy.size(); i < l; i++) {
|
||||||
|
String s = copy.get(i);
|
||||||
|
if (i + 1 < l && ("--accessToken".equals(s) || "--session".equals(s))) {
|
||||||
|
copy.set(i + 1, "censored");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LogHelper.debug("Args: " + copy);
|
||||||
|
// Resolve main class and method
|
||||||
|
Class<?> mainClass = classLoader.loadClass(profile.getMainClass());
|
||||||
|
for(URL u : classLoader.getURLs())
|
||||||
|
{
|
||||||
|
LogHelper.info("ClassLoader URL: %s", u.toString());
|
||||||
|
}
|
||||||
|
FMLPatcher.apply();
|
||||||
|
MethodHandle mainMethod = MethodHandles.publicLookup().findStatic(mainClass, "main", MethodType.methodType(void.class, String[].class)).asFixedArity();
|
||||||
|
Launcher.LAUNCHED.set(true);
|
||||||
|
JVMHelper.fullGC();
|
||||||
|
// Invoke main method
|
||||||
|
try {
|
||||||
|
mainMethod.invokeWithArguments((Object) args.toArray(new String[0]));
|
||||||
|
LogHelper.debug("Main exit successful");
|
||||||
|
} catch (Throwable e) {
|
||||||
|
LogHelper.error(e);
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
LauncherEngine.exitLauncher(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,257 @@
|
||||||
|
package pro.gravit.launcher.client;
|
||||||
|
|
||||||
|
import pro.gravit.launcher.Launcher;
|
||||||
|
import pro.gravit.launcher.guard.LauncherGuardManager;
|
||||||
|
import pro.gravit.launcher.hasher.HashedDir;
|
||||||
|
import pro.gravit.launcher.profiles.ClientProfile;
|
||||||
|
import pro.gravit.launcher.profiles.PlayerProfile;
|
||||||
|
import pro.gravit.launcher.request.Request;
|
||||||
|
import pro.gravit.launcher.serialize.HOutput;
|
||||||
|
import pro.gravit.utils.Version;
|
||||||
|
import pro.gravit.utils.helper.EnvHelper;
|
||||||
|
import pro.gravit.utils.helper.IOHelper;
|
||||||
|
import pro.gravit.utils.helper.JVMHelper;
|
||||||
|
import pro.gravit.utils.helper.SecurityHelper;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class ClientLauncherProcess {
|
||||||
|
private transient Process process;
|
||||||
|
private final transient Boolean[] waitWriteParams = new Boolean[1];
|
||||||
|
public final Path executeFile;
|
||||||
|
public final Path workDir;
|
||||||
|
public final Path javaDir;
|
||||||
|
public final ClientParams params = new ClientParams();
|
||||||
|
public final List<String> jvmArgs = new LinkedList<>();
|
||||||
|
public final List<String> systemClientArgs = new LinkedList<>();
|
||||||
|
public final List<String> systemClassPath = new LinkedList<>();
|
||||||
|
public final Map<String, String> systemEnv = new HashMap<>();
|
||||||
|
public final String mainClass;
|
||||||
|
public boolean isStarted;
|
||||||
|
|
||||||
|
public ClientLauncherProcess(Path executeFile, Path workDir, Path javaDir, String mainClass) {
|
||||||
|
this.executeFile = executeFile;
|
||||||
|
this.workDir = workDir;
|
||||||
|
this.javaDir = javaDir;
|
||||||
|
this.mainClass = mainClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientLauncherProcess(Path clientDir, Path assetDir,
|
||||||
|
ClientProfile profile, PlayerProfile playerProfile, String accessToken,
|
||||||
|
HashedDir clientHDir, HashedDir assetHDir, HashedDir jvmHDir) {
|
||||||
|
this(clientDir, assetDir, clientDir.resolve("resourcepacks"), profile, playerProfile, accessToken, clientHDir, assetHDir, jvmHDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientLauncherProcess(Path clientDir, Path assetDir, Path resourcePackDir,
|
||||||
|
ClientProfile profile, PlayerProfile playerProfile, String accessToken,
|
||||||
|
HashedDir clientHDir, HashedDir assetHDir, HashedDir jvmHDir) {
|
||||||
|
this.executeFile = LauncherGuardManager.getGuardJavaBinPath();
|
||||||
|
this.workDir = clientDir.toAbsolutePath();
|
||||||
|
this.javaDir = Paths.get(System.getProperty("java.home"));
|
||||||
|
this.mainClass = ClientLauncherEntryPoint.class.getName();
|
||||||
|
this.params.clientDir = this.workDir.toString();
|
||||||
|
this.params.resourcePackDir = resourcePackDir.toAbsolutePath().toString();
|
||||||
|
this.params.assetDir = assetDir.toAbsolutePath().toString();
|
||||||
|
this.params.profile = profile;
|
||||||
|
this.params.playerProfile = playerProfile;
|
||||||
|
this.params.accessToken = accessToken;
|
||||||
|
this.params.assetHDir = assetHDir;
|
||||||
|
this.params.clientHDir = clientHDir;
|
||||||
|
this.params.javaHDir = jvmHDir;
|
||||||
|
applyClientProfile();
|
||||||
|
}
|
||||||
|
private void applyClientProfile()
|
||||||
|
{
|
||||||
|
this.systemClassPath.add(IOHelper.getCodeSource(ClientLauncherEntryPoint.class).toAbsolutePath().toString());
|
||||||
|
Collections.addAll(this.jvmArgs, this.params.profile.getJvmArgs());
|
||||||
|
this.systemEnv.put("JAVA_HOME", javaDir.toString());
|
||||||
|
Collections.addAll(this.systemClassPath, this.params.profile.getAlternativeClassPath());
|
||||||
|
if (params.ram > 0) {
|
||||||
|
this.jvmArgs.add("-Xms" + params.ram + 'M');
|
||||||
|
this.jvmArgs.add("-Xmx" + params.ram + 'M');
|
||||||
|
}
|
||||||
|
this.params.session = Request.getSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class ClientParams
|
||||||
|
{
|
||||||
|
public String assetDir;
|
||||||
|
|
||||||
|
public String clientDir;
|
||||||
|
|
||||||
|
public String resourcePackDir;
|
||||||
|
|
||||||
|
// Client params
|
||||||
|
|
||||||
|
public PlayerProfile playerProfile;
|
||||||
|
|
||||||
|
public ClientProfile profile;
|
||||||
|
|
||||||
|
public String accessToken;
|
||||||
|
|
||||||
|
//==Minecraft params==
|
||||||
|
|
||||||
|
public boolean autoEnter;
|
||||||
|
|
||||||
|
public boolean fullScreen;
|
||||||
|
|
||||||
|
public int ram;
|
||||||
|
|
||||||
|
public int width;
|
||||||
|
|
||||||
|
public int height;
|
||||||
|
|
||||||
|
//========
|
||||||
|
|
||||||
|
public long session;
|
||||||
|
|
||||||
|
public transient HashedDir assetHDir;
|
||||||
|
|
||||||
|
public transient HashedDir clientHDir;
|
||||||
|
|
||||||
|
public transient HashedDir javaHDir;
|
||||||
|
|
||||||
|
public void addClientArgs(Collection<String> args)
|
||||||
|
{
|
||||||
|
if (profile.getVersion().compareTo(ClientProfile.Version.MC164) >= 0)
|
||||||
|
addModernClientArgs(args);
|
||||||
|
else
|
||||||
|
addClientLegacyArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void addClientLegacyArgs(Collection<String> args) {
|
||||||
|
args.add(playerProfile.username);
|
||||||
|
args.add(accessToken);
|
||||||
|
|
||||||
|
// Add args for tweaker
|
||||||
|
Collections.addAll(args, "--version", profile.getVersion().name);
|
||||||
|
Collections.addAll(args, "--gameDir", clientDir.toString());
|
||||||
|
Collections.addAll(args, "--assetsDir", assetDir.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addModernClientArgs(Collection<String> args) {
|
||||||
|
|
||||||
|
// Add version-dependent args
|
||||||
|
ClientProfile.Version version = profile.getVersion();
|
||||||
|
Collections.addAll(args, "--username", playerProfile.username);
|
||||||
|
if (version.compareTo(ClientProfile.Version.MC172) >= 0) {
|
||||||
|
Collections.addAll(args, "--uuid", Launcher.toHash(playerProfile.uuid));
|
||||||
|
Collections.addAll(args, "--accessToken", accessToken);
|
||||||
|
|
||||||
|
// Add 1.7.10+ args (user properties, asset index)
|
||||||
|
if (version.compareTo(ClientProfile.Version.MC1710) >= 0) {
|
||||||
|
// Add user properties
|
||||||
|
Collections.addAll(args, "--userType", "mojang");
|
||||||
|
ClientLauncher.ClientUserProperties properties = new ClientLauncher.ClientUserProperties();
|
||||||
|
if (playerProfile.skin != null) {
|
||||||
|
properties.skinURL = new String[]{playerProfile.skin.url};
|
||||||
|
properties.skinDigest = new String[]{SecurityHelper.toHex(playerProfile.skin.digest)};
|
||||||
|
}
|
||||||
|
if (playerProfile.cloak != null) {
|
||||||
|
properties.cloakURL = new String[]{playerProfile.cloak.url};
|
||||||
|
properties.cloakDigest = new String[]{SecurityHelper.toHex(playerProfile.cloak.digest)};
|
||||||
|
}
|
||||||
|
Collections.addAll(args, "--userProperties", Launcher.gsonManager.gson.toJson(properties));
|
||||||
|
|
||||||
|
// Add asset index
|
||||||
|
Collections.addAll(args, "--assetIndex", profile.getAssetIndex());
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
Collections.addAll(args, "--session", accessToken);
|
||||||
|
|
||||||
|
// Add version and dirs args
|
||||||
|
Collections.addAll(args, "--version", profile.getVersion().name);
|
||||||
|
Collections.addAll(args, "--gameDir", clientDir);
|
||||||
|
Collections.addAll(args, "--assetsDir", assetDir);
|
||||||
|
Collections.addAll(args, "--resourcePackDir", resourcePackDir);
|
||||||
|
if (version.compareTo(ClientProfile.Version.MC194) >= 0)
|
||||||
|
Collections.addAll(args, "--versionType", "Launcher v" + Version.getVersion().getVersionString());
|
||||||
|
|
||||||
|
// Add server args
|
||||||
|
if (autoEnter) {
|
||||||
|
Collections.addAll(args, "--server", profile.getServerAddress());
|
||||||
|
Collections.addAll(args, "--port", Integer.toString(profile.getServerPort()));
|
||||||
|
}
|
||||||
|
profile.pushOptionalClientArgs(args);
|
||||||
|
// Add window size args
|
||||||
|
if (fullScreen)
|
||||||
|
Collections.addAll(args, "--fullscreen", Boolean.toString(true));
|
||||||
|
if (width > 0 && height > 0) {
|
||||||
|
Collections.addAll(args, "--width", Integer.toString(width));
|
||||||
|
Collections.addAll(args, "--height", Integer.toString(height));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void start(boolean pipeOutput) throws IOException, InterruptedException {
|
||||||
|
if(isStarted) throw new IllegalStateException("Process already started");
|
||||||
|
List<String> processArgs = new LinkedList<>();
|
||||||
|
processArgs.add(executeFile.toString());
|
||||||
|
processArgs.addAll(jvmArgs);
|
||||||
|
processArgs.add("-cp");
|
||||||
|
//ADD CLASSPATH
|
||||||
|
processArgs.add(String.join(getPathSeparator(), systemClassPath));
|
||||||
|
processArgs.addAll(systemClientArgs);
|
||||||
|
synchronized (waitWriteParams)
|
||||||
|
{
|
||||||
|
if(!waitWriteParams[0])
|
||||||
|
{
|
||||||
|
waitWriteParams.wait(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ProcessBuilder processBuilder = new ProcessBuilder(processArgs);
|
||||||
|
EnvHelper.addEnv(processBuilder);
|
||||||
|
processBuilder.environment().putAll(systemEnv);
|
||||||
|
processBuilder.directory(workDir.toFile());
|
||||||
|
processBuilder.inheritIO();
|
||||||
|
if (pipeOutput) {
|
||||||
|
processBuilder.redirectErrorStream(true);
|
||||||
|
processBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE);
|
||||||
|
}
|
||||||
|
process = processBuilder.start();
|
||||||
|
isStarted = true;
|
||||||
|
}
|
||||||
|
public void runWriteParams(SocketAddress address) throws IOException
|
||||||
|
{
|
||||||
|
try(ServerSocket serverSocket = new ServerSocket())
|
||||||
|
{
|
||||||
|
serverSocket.bind(address);
|
||||||
|
synchronized (waitWriteParams)
|
||||||
|
{
|
||||||
|
waitWriteParams[0] = true;
|
||||||
|
waitWriteParams.notifyAll();
|
||||||
|
}
|
||||||
|
Socket socket = serverSocket.accept();
|
||||||
|
try(HOutput output = new HOutput(socket.getOutputStream()))
|
||||||
|
{
|
||||||
|
byte[] serializedMainParams = Launcher.gsonManager.gson.toJson(params).getBytes(IOHelper.UNICODE_CHARSET);
|
||||||
|
output.writeByteArray(serializedMainParams, 0);
|
||||||
|
params.clientHDir.write(output);
|
||||||
|
params.assetHDir.write(output);
|
||||||
|
params.javaHDir.write(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Process getProcess() {
|
||||||
|
return process;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getPathSeparator()
|
||||||
|
{
|
||||||
|
if(JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE)
|
||||||
|
return ";";
|
||||||
|
else
|
||||||
|
return ":";
|
||||||
|
}
|
||||||
|
}
|
|
@ -128,6 +128,8 @@ public enum ClassLoaderConfig
|
||||||
@LauncherNetworkAPI
|
@LauncherNetworkAPI
|
||||||
private final List<String> classPath = new ArrayList<>();
|
private final List<String> classPath = new ArrayList<>();
|
||||||
@LauncherNetworkAPI
|
@LauncherNetworkAPI
|
||||||
|
private final List<String> altClassPath = new ArrayList<>();
|
||||||
|
@LauncherNetworkAPI
|
||||||
private final List<String> clientArgs = new ArrayList<>();
|
private final List<String> clientArgs = new ArrayList<>();
|
||||||
@LauncherNetworkAPI
|
@LauncherNetworkAPI
|
||||||
public SecurityManagerConfig securityManagerConfig = SecurityManagerConfig.CLIENT;
|
public SecurityManagerConfig securityManagerConfig = SecurityManagerConfig.CLIENT;
|
||||||
|
@ -154,6 +156,10 @@ public String[] getClassPath() {
|
||||||
return classPath.toArray(new String[0]);
|
return classPath.toArray(new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String[] getAlternativeClassPath() {
|
||||||
|
return classPath.toArray(new String[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public String[] getClientArgs() {
|
public String[] getClientArgs() {
|
||||||
return clientArgs.toArray(new String[0]);
|
return clientArgs.toArray(new String[0]);
|
||||||
|
@ -196,6 +202,7 @@ public String[] getJvmArgs() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public String getMainClass() {
|
public String getMainClass() {
|
||||||
return mainClass;
|
return mainClass;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue