mirror of
https://github.com/GravitLauncher/Launcher
synced 2025-04-19 14:33:04 +03:00
fix update overlay
This commit is contained in:
parent
a13cc2ed0f
commit
b9ee6cbc49
37 changed files with 2 additions and 2707 deletions
|
@ -1,60 +0,0 @@
|
||||||
apply plugin: 'com.github.johnrengelman.shadow'
|
|
||||||
|
|
||||||
String mainClassName = "ru.gravit.launcher.ClientLauncherWrapper"
|
|
||||||
String mainAgentName = "ru.gravit.launcher.LauncherAgent"
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
maven {
|
|
||||||
url "http://repo.spring.io/plugins-release/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceCompatibility = '1.8'
|
|
||||||
targetCompatibility = '1.8'
|
|
||||||
|
|
||||||
configurations {
|
|
||||||
bundle
|
|
||||||
pack
|
|
||||||
compile.extendsFrom bundle, pack
|
|
||||||
}
|
|
||||||
|
|
||||||
jar {
|
|
||||||
classifier = 'clean'
|
|
||||||
manifest.attributes("Main-Class": mainClassName,
|
|
||||||
"Premain-Class": mainAgentName,
|
|
||||||
"Can-Redefine-Classes": "true",
|
|
||||||
"Can-Retransform-Classes": "true",
|
|
||||||
"Can-Set-Native-Method-Prefix": "true")
|
|
||||||
}
|
|
||||||
|
|
||||||
shadowJar {
|
|
||||||
classifier = null
|
|
||||||
relocate 'org.objectweb.asm', 'ru.gravit.repackage.org.objectweb.asm'
|
|
||||||
relocate 'io.netty', 'ru.gravit.repackage.io.netty'
|
|
||||||
configurations = [project.configurations.pack]
|
|
||||||
exclude 'module-info.class'
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
pack project(':LauncherAuthlib')
|
|
||||||
bundle 'com.github.oshi:oshi-core:3.13.0'
|
|
||||||
bundle 'com.jfoenix:jfoenix:8.0.8'
|
|
||||||
bundle 'de.jensd:fontawesomefx:8.9'
|
|
||||||
bundle 'org.apache.httpcomponents:httpclient:4.5.7'
|
|
||||||
pack 'io.netty:netty-all:4.1.36.Final'
|
|
||||||
pack 'org.ow2.asm:asm-tree:7.1'
|
|
||||||
}
|
|
||||||
|
|
||||||
task genRuntimeJS(type: Zip) {
|
|
||||||
archiveName = "runtime.zip"
|
|
||||||
destinationDir = file("${buildDir}/tmp")
|
|
||||||
from "runtime/"
|
|
||||||
}
|
|
||||||
|
|
||||||
task dumpLibs(type: Copy) {
|
|
||||||
into "$buildDir/libs/libraries"
|
|
||||||
from configurations.bundle
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
build.dependsOn tasks.genRuntimeJS, tasks.dumpLibs, tasks.shadowJar
|
|
|
@ -3,7 +3,6 @@ Button, CheckBox, ComboBox, RadioButton {
|
||||||
-fx-cursor: hand;
|
-fx-cursor: hand;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Backgrounds */
|
/* Backgrounds */
|
||||||
#layout {
|
#layout {
|
||||||
-fx-background-color: transparent;
|
-fx-background-color: transparent;
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
package cpw.mods.fml;
|
|
||||||
|
|
||||||
import ru.gravit.utils.helper.JVMHelper;
|
|
||||||
|
|
||||||
// FMLSecurityManager запрещает делать System.exit из классов
|
|
||||||
// Не входящих в пакеты самого Forge
|
|
||||||
public class SafeExitJVMLegacy {
|
|
||||||
public static void exit(int code) {
|
|
||||||
try {
|
|
||||||
JVMHelper.RUNTIME.halt(code);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
System.exit(code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
package net.minecraftforge.fml;
|
|
||||||
|
|
||||||
import ru.gravit.utils.helper.JVMHelper;
|
|
||||||
|
|
||||||
// FMLSecurityManager запрещает делать System.exit из классов
|
|
||||||
// Не входящих в пакеты самого Forge
|
|
||||||
public class SafeExitJVM {
|
|
||||||
public static void exit(int code) {
|
|
||||||
try {
|
|
||||||
JVMHelper.RUNTIME.halt(code);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
System.exit(code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
package ru.gravit.launcher;
|
|
||||||
|
|
||||||
import ru.gravit.launcher.client.ClientLauncher;
|
|
||||||
import ru.gravit.launcher.client.DirBridge;
|
|
||||||
import ru.gravit.utils.helper.EnvHelper;
|
|
||||||
import ru.gravit.utils.helper.IOHelper;
|
|
||||||
import ru.gravit.utils.helper.JVMHelper;
|
|
||||||
import ru.gravit.utils.helper.LogHelper;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class ClientLauncherWrapper {
|
|
||||||
public static final String MAGIC_ARG = "-Djdk.attach.allowAttachSelf";
|
|
||||||
public static final String WAIT_PROCESS_PROPERTY = "launcher.waitProcess";
|
|
||||||
public static boolean waitProcess = Boolean.getBoolean(WAIT_PROCESS_PROPERTY);
|
|
||||||
|
|
||||||
public static void main(String[] arguments) throws IOException, InterruptedException {
|
|
||||||
LogHelper.printVersion("Launcher");
|
|
||||||
LogHelper.printLicense("Launcher");
|
|
||||||
JVMHelper.checkStackTrace(ClientLauncherWrapper.class);
|
|
||||||
JVMHelper.verifySystemProperties(Launcher.class, true);
|
|
||||||
EnvHelper.checkDangerousParams();
|
|
||||||
LauncherConfig config = Launcher.getConfig();
|
|
||||||
LogHelper.info("Launcher for project %s", config.projectname);
|
|
||||||
if (config.environment.equals(LauncherConfig.LauncherEnvironment.PROD)) {
|
|
||||||
if (System.getProperty(LogHelper.DEBUG_PROPERTY) != null) {
|
|
||||||
LogHelper.warning("Found -Dlauncher.debug=true");
|
|
||||||
}
|
|
||||||
if (System.getProperty(LogHelper.STACKTRACE_PROPERTY) != null) {
|
|
||||||
LogHelper.warning("Found -Dlauncher.stacktrace=true");
|
|
||||||
}
|
|
||||||
LogHelper.info("Debug mode disabled (found env PRODUCTION)");
|
|
||||||
} else {
|
|
||||||
LogHelper.info("If need debug output use -Dlauncher.debug=true");
|
|
||||||
LogHelper.info("If need stacktrace output use -Dlauncher.stacktrace=true");
|
|
||||||
if (LogHelper.isDebugEnabled()) waitProcess = true;
|
|
||||||
}
|
|
||||||
LogHelper.info("Restart Launcher with JavaAgent...");
|
|
||||||
ProcessBuilder processBuilder = new ProcessBuilder();
|
|
||||||
if (waitProcess) processBuilder.inheritIO();
|
|
||||||
Path javaBin = IOHelper.resolveJavaBin(Paths.get(System.getProperty("java.home")));
|
|
||||||
List<String> args = new LinkedList<>();
|
|
||||||
args.add(javaBin.toString());
|
|
||||||
String pathLauncher = IOHelper.getCodeSource(ClientLauncher.class).toString();
|
|
||||||
args.add(JVMHelper.jvmProperty(LogHelper.DEBUG_PROPERTY, Boolean.toString(LogHelper.isDebugEnabled())));
|
|
||||||
args.add(JVMHelper.jvmProperty(LogHelper.STACKTRACE_PROPERTY, Boolean.toString(LogHelper.isStacktraceEnabled())));
|
|
||||||
args.add(JVMHelper.jvmProperty(LogHelper.DEV_PROPERTY, Boolean.toString(LogHelper.isDevEnabled())));
|
|
||||||
JVMHelper.addSystemPropertyToArgs(args, DirBridge.CUSTOMDIR_PROPERTY);
|
|
||||||
JVMHelper.addSystemPropertyToArgs(args, DirBridge.USE_CUSTOMDIR_PROPERTY);
|
|
||||||
JVMHelper.addSystemPropertyToArgs(args, DirBridge.USE_OPTDIR_PROPERTY);
|
|
||||||
Collections.addAll(args, MAGIC_ARG);
|
|
||||||
Collections.addAll(args, "-XX:+DisableAttachMechanism");
|
|
||||||
Collections.addAll(args, "-javaagent:".concat(pathLauncher).concat("=pr"));
|
|
||||||
Collections.addAll(args, "-cp");
|
|
||||||
Collections.addAll(args, pathLauncher);
|
|
||||||
Collections.addAll(args, LauncherEngine.class.getName());
|
|
||||||
EnvHelper.addEnv(processBuilder);
|
|
||||||
LogHelper.debug("Commandline: " + args);
|
|
||||||
processBuilder.command(args);
|
|
||||||
Process process = processBuilder.start();
|
|
||||||
if (!waitProcess) {
|
|
||||||
Thread.sleep(3000);
|
|
||||||
if (!process.isAlive()) {
|
|
||||||
int errorcode = process.exitValue();
|
|
||||||
if (errorcode != 0)
|
|
||||||
LogHelper.error("Process exit with error code: %d", errorcode);
|
|
||||||
else
|
|
||||||
LogHelper.info("Process exit with code 0");
|
|
||||||
} else {
|
|
||||||
LogHelper.debug("Process started success");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
process.waitFor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package ru.gravit.launcher;
|
|
||||||
|
|
||||||
import javafx.application.Application;
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
public abstract class JSApplication extends Application {
|
|
||||||
private static final AtomicReference<JSApplication> INSTANCE = new AtomicReference<>();
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static JSApplication getInstance() {
|
|
||||||
return INSTANCE.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public JSApplication() {
|
|
||||||
INSTANCE.set(this);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,178 +0,0 @@
|
||||||
package ru.gravit.launcher;
|
|
||||||
|
|
||||||
import cpw.mods.fml.SafeExitJVMLegacy;
|
|
||||||
import net.minecraftforge.fml.SafeExitJVM;
|
|
||||||
import org.objectweb.asm.ClassReader;
|
|
||||||
import org.objectweb.asm.ClassWriter;
|
|
||||||
import org.objectweb.asm.tree.ClassNode;
|
|
||||||
import org.objectweb.asm.tree.InsnNode;
|
|
||||||
import org.objectweb.asm.tree.MethodNode;
|
|
||||||
import ru.gravit.launcher.utils.NativeJVMHalt;
|
|
||||||
import ru.gravit.utils.helper.LogHelper;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.lang.instrument.Instrumentation;
|
|
||||||
import java.lang.management.ManagementFactory;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.jar.JarFile;
|
|
||||||
|
|
||||||
import static org.objectweb.asm.Opcodes.ACONST_NULL;
|
|
||||||
import static org.objectweb.asm.Opcodes.ARETURN;
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public final class LauncherAgent {
|
|
||||||
private static boolean isAgentStarted = false;
|
|
||||||
public static Instrumentation inst;
|
|
||||||
|
|
||||||
public static void addJVMClassPath(String path) throws IOException {
|
|
||||||
LogHelper.debug("Launcher Agent addJVMClassPath");
|
|
||||||
inst.appendToSystemClassLoaderSearch(new JarFile(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAgentStarted() {
|
|
||||||
return isAgentStarted;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void premain(String agentArgument, Instrumentation instrumentation) {
|
|
||||||
System.out.println("Launcher Agent");
|
|
||||||
inst = instrumentation;
|
|
||||||
SafeExitJVMLegacy.class.getName();
|
|
||||||
SafeExitJVM.class.getName();
|
|
||||||
NativeJVMHalt.class.getName();
|
|
||||||
NativeJVMHalt.initFunc();
|
|
||||||
isAgentStarted = true;
|
|
||||||
boolean pb = true;
|
|
||||||
boolean rt = true;
|
|
||||||
if (agentArgument != null) {
|
|
||||||
String trimmedArg = agentArgument.trim();
|
|
||||||
if (!trimmedArg.isEmpty()) {
|
|
||||||
if (trimmedArg.contains("p")) pb = false;
|
|
||||||
if (trimmedArg.contains("r")) rt = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (ManagementFactory.getOperatingSystemMXBean().getName().startsWith("Windows")) replaceClasses(pb, rt);
|
|
||||||
else replaceClasses(false, false);
|
|
||||||
} catch (Error e) {
|
|
||||||
NativeJVMHalt.haltA(294);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isStarted() {
|
|
||||||
return isAgentStarted;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void replaceClasses(boolean pb, boolean rt) {
|
|
||||||
java.awt.Robot.class.getName();
|
|
||||||
List<java.lang.instrument.ClassDefinition> defs = new ArrayList<>();
|
|
||||||
if (rt) {
|
|
||||||
try {
|
|
||||||
defs.add(new java.lang.instrument.ClassDefinition(java.lang.Runtime.class, transformClass(java.lang.Runtime.class.getName(), getClassFile(java.lang.Runtime.class))));
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new Error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (pb) {
|
|
||||||
try {
|
|
||||||
defs.add(new java.lang.instrument.ClassDefinition(java.lang.ProcessBuilder.class, transformClass(java.lang.ProcessBuilder.class.getName(), getClassFile(java.lang.ProcessBuilder.class))));
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new Error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
defs.add(new java.lang.instrument.ClassDefinition(java.awt.Robot.class, transformClass(java.awt.Robot.class.getName(), getClassFile(java.awt.Robot.class))));
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new Error(e);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
inst.redefineClasses(defs.toArray(new java.lang.instrument.ClassDefinition[0]));
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new Error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author https://github.com/Konloch/JVM-Sandbox
|
|
||||||
* Use ASM to modify the byte array
|
|
||||||
*/
|
|
||||||
private static byte[] transformClass(String className, byte[] classBytes) {
|
|
||||||
switch (className) {
|
|
||||||
case "java.lang.Runtime": {
|
|
||||||
ClassReader cr = new ClassReader(classBytes);
|
|
||||||
ClassNode cn = new ClassNode();
|
|
||||||
cr.accept(cn, ClassReader.EXPAND_FRAMES);
|
|
||||||
|
|
||||||
for (Object o : cn.methods.toArray()) {
|
|
||||||
MethodNode m = (MethodNode) o;
|
|
||||||
if (m.name.equals("exec")) {
|
|
||||||
m.instructions.insert(new InsnNode(ARETURN));
|
|
||||||
m.instructions.insert(new InsnNode(ACONST_NULL));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
|
||||||
cn.accept(cw);
|
|
||||||
return cw.toByteArray();
|
|
||||||
}
|
|
||||||
case "java.lang.ProcessBuilder": {
|
|
||||||
ClassReader cr = new ClassReader(classBytes);
|
|
||||||
ClassNode cn = new ClassNode();
|
|
||||||
cr.accept(cn, ClassReader.EXPAND_FRAMES);
|
|
||||||
|
|
||||||
for (Object o : cn.methods.toArray()) {
|
|
||||||
MethodNode m = (MethodNode) o;
|
|
||||||
if (m.name.equals("start")) {
|
|
||||||
m.instructions.insert(new InsnNode(ARETURN));
|
|
||||||
m.instructions.insert(new InsnNode(ACONST_NULL));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
|
||||||
cn.accept(cw);
|
|
||||||
return cw.toByteArray();
|
|
||||||
}
|
|
||||||
case "java.awt.Robot": {
|
|
||||||
ClassReader cr = new ClassReader(classBytes);
|
|
||||||
ClassNode cn = new ClassNode();
|
|
||||||
cr.accept(cn, ClassReader.EXPAND_FRAMES);
|
|
||||||
|
|
||||||
for (Object o : cn.methods.toArray()) {
|
|
||||||
MethodNode m = (MethodNode) o;
|
|
||||||
if (m.name.equals("createScreenCapture") || m.name.equals("getPixelColor") ||
|
|
||||||
m.name.equals("keyPress") || m.name.equals("keyRelease") ||
|
|
||||||
m.name.equals("mouseMove") || m.name.equals("mousePress") ||
|
|
||||||
m.name.equals("mouseWheel")) {
|
|
||||||
m.instructions.insert(new InsnNode(ARETURN));
|
|
||||||
m.instructions.insert(new InsnNode(ACONST_NULL));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
|
||||||
cn.accept(cw);
|
|
||||||
return cw.toByteArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return classBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param clazz
|
|
||||||
* @return array, respending this class in bytecode.
|
|
||||||
* @throws IOException
|
|
||||||
* @author https://github.com/Konloch/JVM-Sandbox
|
|
||||||
* Do not remove this method. Do not to cause classloading!
|
|
||||||
* Grab the byte array from the loaded Class object
|
|
||||||
*/
|
|
||||||
private static byte[] getClassFile(Class<?> clazz) throws IOException {
|
|
||||||
try (InputStream is = clazz.getResourceAsStream("/" + clazz.getName().replace('.', '/') + ".class");
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
|
||||||
int r = 0;
|
|
||||||
byte[] buffer = new byte[8192];
|
|
||||||
while ((r = is.read(buffer)) >= 0) {
|
|
||||||
baos.write(buffer, 0, r);
|
|
||||||
}
|
|
||||||
return baos.toByteArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,112 +0,0 @@
|
||||||
package ru.gravit.launcher;
|
|
||||||
|
|
||||||
import ru.gravit.launcher.client.ClientModuleManager;
|
|
||||||
import ru.gravit.launcher.client.DirBridge;
|
|
||||||
import ru.gravit.launcher.client.FunctionalBridge;
|
|
||||||
import ru.gravit.launcher.guard.LauncherGuardManager;
|
|
||||||
import ru.gravit.launcher.gui.JSRuntimeProvider;
|
|
||||||
import ru.gravit.launcher.gui.RuntimeProvider;
|
|
||||||
import ru.gravit.launcher.managers.ClientGsonManager;
|
|
||||||
import ru.gravit.launcher.managers.ConsoleManager;
|
|
||||||
import ru.gravit.launcher.request.Request;
|
|
||||||
import ru.gravit.launcher.request.RequestException;
|
|
||||||
import ru.gravit.launcher.request.auth.RestoreSessionRequest;
|
|
||||||
import ru.gravit.launcher.request.websockets.StandartClientWebSocketService;
|
|
||||||
import ru.gravit.utils.helper.CommonHelper;
|
|
||||||
import ru.gravit.utils.helper.EnvHelper;
|
|
||||||
import ru.gravit.utils.helper.JVMHelper;
|
|
||||||
import ru.gravit.utils.helper.LogHelper;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
public class LauncherEngine {
|
|
||||||
|
|
||||||
public static void main(String... args) throws Throwable {
|
|
||||||
JVMHelper.checkStackTrace(LauncherEngine.class);
|
|
||||||
JVMHelper.verifySystemProperties(Launcher.class, true);
|
|
||||||
EnvHelper.checkDangerousParams();
|
|
||||||
//if(!LauncherAgent.isStarted()) throw new SecurityException("JavaAgent not set");
|
|
||||||
LogHelper.printVersion("Launcher");
|
|
||||||
LogHelper.printLicense("Launcher");
|
|
||||||
// Start Launcher
|
|
||||||
initGson();
|
|
||||||
ConsoleManager.initConsole();
|
|
||||||
LauncherConfig config = Launcher.getConfig();
|
|
||||||
if (config.environment.equals(LauncherConfig.LauncherEnvironment.PROD)) {
|
|
||||||
if (!LauncherAgent.isStarted()) throw new SecurityException("LauncherAgent must started");
|
|
||||||
}
|
|
||||||
long startTime = System.currentTimeMillis();
|
|
||||||
try {
|
|
||||||
new LauncherEngine().start(args);
|
|
||||||
} catch (Exception e) {
|
|
||||||
LogHelper.error(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
long endTime = System.currentTimeMillis();
|
|
||||||
LogHelper.debug("Launcher started in %dms", endTime - startTime);
|
|
||||||
//Request.service.close();
|
|
||||||
//FunctionalBridge.close();
|
|
||||||
System.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void initGson() {
|
|
||||||
Launcher.gsonManager = new ClientGsonManager();
|
|
||||||
Launcher.gsonManager.initGson();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instance
|
|
||||||
private final AtomicBoolean started = new AtomicBoolean(false);
|
|
||||||
public RuntimeProvider runtimeProvider;
|
|
||||||
|
|
||||||
private LauncherEngine() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public void start(String... args) throws Throwable {
|
|
||||||
Launcher.modulesManager = new ClientModuleManager(this);
|
|
||||||
LauncherConfig.getAutogenConfig().initModules();
|
|
||||||
Launcher.modulesManager.preInitModules();
|
|
||||||
if (runtimeProvider == null) runtimeProvider = new JSRuntimeProvider();
|
|
||||||
runtimeProvider.init(false);
|
|
||||||
runtimeProvider.preLoad();
|
|
||||||
if (Request.service == null) {
|
|
||||||
String address = Launcher.getConfig().address;
|
|
||||||
LogHelper.debug("Start async connection to %s", address);
|
|
||||||
Request.service = StandartClientWebSocketService.initWebSockets(address, true);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
LauncherGuardManager.initGuard(false);
|
|
||||||
Objects.requireNonNull(args, "args");
|
|
||||||
if (started.getAndSet(true))
|
|
||||||
throw new IllegalStateException("Launcher has been already started");
|
|
||||||
Launcher.modulesManager.initModules();
|
|
||||||
runtimeProvider.preLoad();
|
|
||||||
FunctionalBridge.getHWID = CommonHelper.newThread("GetHWID Thread", true, FunctionalBridge::getHWID);
|
|
||||||
FunctionalBridge.getHWID.start();
|
|
||||||
LogHelper.debug("Dir: %s", DirBridge.dir);
|
|
||||||
runtimeProvider.run(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static LauncherEngine clientInstance() {
|
|
||||||
return new LauncherEngine();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
package ru.gravit.launcher;
|
|
||||||
|
|
||||||
import ru.gravit.launcher.client.UserSettings;
|
|
||||||
import ru.gravit.launcher.hasher.HashedDir;
|
|
||||||
import ru.gravit.launcher.profiles.ClientProfile;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public class NewLauncherSettings {
|
|
||||||
@LauncherAPI
|
|
||||||
public String login;
|
|
||||||
@LauncherAPI
|
|
||||||
public String auth;
|
|
||||||
@LauncherAPI
|
|
||||||
public byte[] rsaPassword;
|
|
||||||
@LauncherAPI
|
|
||||||
public int profile;
|
|
||||||
@LauncherAPI
|
|
||||||
public transient Path updatesDir;
|
|
||||||
@LauncherAPI
|
|
||||||
public String updatesDirPath;
|
|
||||||
@LauncherAPI
|
|
||||||
public boolean autoEnter;
|
|
||||||
@LauncherAPI
|
|
||||||
public boolean debug;
|
|
||||||
@LauncherAPI
|
|
||||||
public boolean fullScreen;
|
|
||||||
@LauncherAPI
|
|
||||||
public boolean offline;
|
|
||||||
@LauncherAPI
|
|
||||||
public int ram;
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public byte[] lastDigest;
|
|
||||||
@LauncherAPI
|
|
||||||
public List<ClientProfile> lastProfiles = new LinkedList<>();
|
|
||||||
@LauncherAPI
|
|
||||||
public Map<String, UserSettings> userSettings = new HashMap<>();
|
|
||||||
|
|
||||||
public static class HashedStoreEntry {
|
|
||||||
@LauncherAPI
|
|
||||||
public HashedDir hdir;
|
|
||||||
@LauncherAPI
|
|
||||||
public String name;
|
|
||||||
@LauncherAPI
|
|
||||||
public String fullPath;
|
|
||||||
|
|
||||||
public HashedStoreEntry(HashedDir hdir, String name, String fullPath) {
|
|
||||||
this.hdir = hdir;
|
|
||||||
this.name = name;
|
|
||||||
this.fullPath = fullPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public transient List<HashedStoreEntry> lastHDirs = new ArrayList<>(16);
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public void putHDir(String name, Path path, HashedDir dir) {
|
|
||||||
String fullPath = path.toAbsolutePath().toString();
|
|
||||||
for (HashedStoreEntry e : lastHDirs) {
|
|
||||||
if (e.fullPath.equals(fullPath) && e.name.equals(name)) return;
|
|
||||||
}
|
|
||||||
lastHDirs.add(new HashedStoreEntry(dir, name, fullPath));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,561 +0,0 @@
|
||||||
package ru.gravit.launcher.client;
|
|
||||||
|
|
||||||
import ru.gravit.launcher.*;
|
|
||||||
import ru.gravit.launcher.guard.LauncherGuardManager;
|
|
||||||
import ru.gravit.launcher.gui.JSRuntimeProvider;
|
|
||||||
import ru.gravit.launcher.hasher.FileNameMatcher;
|
|
||||||
import ru.gravit.launcher.hasher.HashedDir;
|
|
||||||
import ru.gravit.launcher.managers.ClientGsonManager;
|
|
||||||
import ru.gravit.launcher.profiles.ClientProfile;
|
|
||||||
import ru.gravit.launcher.profiles.PlayerProfile;
|
|
||||||
import ru.gravit.launcher.request.Request;
|
|
||||||
import ru.gravit.launcher.request.RequestException;
|
|
||||||
import ru.gravit.launcher.request.auth.RestoreSessionRequest;
|
|
||||||
import ru.gravit.launcher.serialize.HInput;
|
|
||||||
import ru.gravit.launcher.serialize.HOutput;
|
|
||||||
import ru.gravit.launcher.serialize.stream.StreamObject;
|
|
||||||
import ru.gravit.launcher.utils.DirWatcher;
|
|
||||||
import ru.gravit.utils.PublicURLClassLoader;
|
|
||||||
import ru.gravit.utils.Version;
|
|
||||||
import ru.gravit.utils.helper.*;
|
|
||||||
import ru.gravit.utils.helper.JVMHelper.OS;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.ProcessBuilder.Redirect;
|
|
||||||
import java.lang.invoke.MethodHandle;
|
|
||||||
import java.lang.invoke.MethodHandles;
|
|
||||||
import java.lang.invoke.MethodType;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.net.ServerSocket;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.nio.file.FileVisitResult;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.SimpleFileVisitor;
|
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
|
||||||
import java.nio.file.attribute.PosixFilePermission;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public final class ClientLauncher {
|
|
||||||
private static final class ClassPathFileVisitor extends SimpleFileVisitor<Path> {
|
|
||||||
private final Collection<Path> result;
|
|
||||||
|
|
||||||
private ClassPathFileVisitor(Collection<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.add(file);
|
|
||||||
return super.visitFile(file, attrs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Params extends StreamObject {
|
|
||||||
// Client paths
|
|
||||||
@LauncherAPI
|
|
||||||
public final Path assetDir;
|
|
||||||
@LauncherAPI
|
|
||||||
public final Path clientDir;
|
|
||||||
|
|
||||||
// Client params
|
|
||||||
@LauncherAPI
|
|
||||||
public final PlayerProfile pp;
|
|
||||||
@LauncherAPI
|
|
||||||
public final String accessToken;
|
|
||||||
@LauncherAPI
|
|
||||||
public final boolean autoEnter;
|
|
||||||
@LauncherAPI
|
|
||||||
public final boolean fullScreen;
|
|
||||||
@LauncherAPI
|
|
||||||
public final int ram;
|
|
||||||
@LauncherAPI
|
|
||||||
public final int width;
|
|
||||||
@LauncherAPI
|
|
||||||
public final int height;
|
|
||||||
@LauncherAPI
|
|
||||||
public final long session;
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public Params(byte[] launcherDigest, Path assetDir, Path clientDir, PlayerProfile pp, String accessToken,
|
|
||||||
boolean autoEnter, boolean fullScreen, int ram, int width, int height) {
|
|
||||||
// Client paths
|
|
||||||
this.assetDir = assetDir;
|
|
||||||
this.clientDir = clientDir;
|
|
||||||
// Client params
|
|
||||||
this.pp = pp;
|
|
||||||
this.accessToken = SecurityHelper.verifyToken(accessToken);
|
|
||||||
this.autoEnter = autoEnter;
|
|
||||||
this.fullScreen = fullScreen;
|
|
||||||
this.ram = ram;
|
|
||||||
this.width = width;
|
|
||||||
this.height = height;
|
|
||||||
this.session = Request.getSession();
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public Params(HInput input) throws Exception {
|
|
||||||
session = input.readLong();
|
|
||||||
// Client paths
|
|
||||||
assetDir = IOHelper.toPath(input.readString(0));
|
|
||||||
clientDir = IOHelper.toPath(input.readString(0));
|
|
||||||
// Client params
|
|
||||||
pp = new PlayerProfile(input);
|
|
||||||
byte[] encryptedAccessToken = input.readByteArray(SecurityHelper.CRYPTO_MAX_LENGTH);
|
|
||||||
String accessTokenD = new String(SecurityHelper.decrypt(Launcher.getConfig().secretKeyClient.getBytes(), encryptedAccessToken));
|
|
||||||
accessToken = SecurityHelper.verifyToken(accessTokenD);
|
|
||||||
autoEnter = input.readBoolean();
|
|
||||||
fullScreen = input.readBoolean();
|
|
||||||
ram = input.readVarInt();
|
|
||||||
width = input.readVarInt();
|
|
||||||
height = input.readVarInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(HOutput output) throws IOException {
|
|
||||||
output.writeLong(session);
|
|
||||||
// Client paths
|
|
||||||
output.writeString(assetDir.toString(), 0);
|
|
||||||
output.writeString(clientDir.toString(), 0);
|
|
||||||
pp.write(output);
|
|
||||||
try {
|
|
||||||
output.writeByteArray(SecurityHelper.encrypt(Launcher.getConfig().secretKeyClient.getBytes(), accessToken.getBytes()), SecurityHelper.CRYPTO_MAX_LENGTH);
|
|
||||||
} catch (Exception e) {
|
|
||||||
LogHelper.error(e);
|
|
||||||
}
|
|
||||||
output.writeBoolean(autoEnter);
|
|
||||||
output.writeBoolean(fullScreen);
|
|
||||||
output.writeVarInt(ram);
|
|
||||||
output.writeVarInt(width);
|
|
||||||
output.writeVarInt(height);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String[] EMPTY_ARRAY = new String[0];
|
|
||||||
private static final String SOCKET_HOST = "127.0.0.1";
|
|
||||||
private static final int SOCKET_PORT = Launcher.getConfig().clientPort;
|
|
||||||
private static final String MAGICAL_INTEL_OPTION = "-XX:HeapDumpPath=ThisTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump";
|
|
||||||
|
|
||||||
private static Path JavaBinPath;
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private static final Set<PosixFilePermission> BIN_POSIX_PERMISSIONS = Collections.unmodifiableSet(EnumSet.of(
|
|
||||||
PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE, // Owner
|
|
||||||
PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_EXECUTE, // Group
|
|
||||||
PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_EXECUTE // Others
|
|
||||||
));
|
|
||||||
// Constants
|
|
||||||
private static final Path NATIVES_DIR = IOHelper.toPath("natives");
|
|
||||||
private static final Path RESOURCEPACKS_DIR = IOHelper.toPath("resourcepacks");
|
|
||||||
private static PublicURLClassLoader classLoader;
|
|
||||||
|
|
||||||
public static class ClientUserProperties {
|
|
||||||
@LauncherAPI
|
|
||||||
String[] skinURL;
|
|
||||||
@LauncherAPI
|
|
||||||
String[] skinDigest;
|
|
||||||
@LauncherAPI
|
|
||||||
String[] cloakURL;
|
|
||||||
@LauncherAPI
|
|
||||||
String[] cloakDigest;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Path getJavaBinPath() {
|
|
||||||
return JavaBinPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void addClientArgs(Collection<String> args, ClientProfile profile, Params params) {
|
|
||||||
PlayerProfile pp = params.pp;
|
|
||||||
|
|
||||||
// Add version-dependent args
|
|
||||||
ClientProfile.Version version = profile.getVersion();
|
|
||||||
Collections.addAll(args, "--username", pp.username);
|
|
||||||
if (version.compareTo(ClientProfile.Version.MC172) >= 0) {
|
|
||||||
Collections.addAll(args, "--uuid", Launcher.toHash(pp.uuid));
|
|
||||||
Collections.addAll(args, "--accessToken", params.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");
|
|
||||||
ClientUserProperties properties = new ClientUserProperties();
|
|
||||||
if (pp.skin != null) {
|
|
||||||
properties.skinURL = new String[]{pp.skin.url};
|
|
||||||
properties.skinDigest = new String[]{SecurityHelper.toHex(pp.skin.digest)};
|
|
||||||
}
|
|
||||||
if (pp.cloak != null) {
|
|
||||||
properties.cloakURL = new String[]{pp.cloak.url};
|
|
||||||
properties.cloakDigest = new String[]{SecurityHelper.toHex(pp.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", params.accessToken);
|
|
||||||
|
|
||||||
// Add version and dirs args
|
|
||||||
Collections.addAll(args, "--version", profile.getVersion().name);
|
|
||||||
Collections.addAll(args, "--gameDir", params.clientDir.toString());
|
|
||||||
Collections.addAll(args, "--assetsDir", params.assetDir.toString());
|
|
||||||
Collections.addAll(args, "--resourcePackDir", params.clientDir.resolve(RESOURCEPACKS_DIR).toString());
|
|
||||||
if (version.compareTo(ClientProfile.Version.MC194) >= 0)
|
|
||||||
Collections.addAll(args, "--versionType", "Launcher v" + Version.getVersion().getVersionString());
|
|
||||||
|
|
||||||
// Add server args
|
|
||||||
if (params.autoEnter) {
|
|
||||||
Collections.addAll(args, "--server", profile.getServerAddress());
|
|
||||||
Collections.addAll(args, "--port", Integer.toString(profile.getServerPort()));
|
|
||||||
}
|
|
||||||
profile.pushOptionalClientArgs(args);
|
|
||||||
// Add window size args
|
|
||||||
if (params.fullScreen)
|
|
||||||
Collections.addAll(args, "--fullscreen", Boolean.toString(true));
|
|
||||||
if (params.width > 0 && params.height > 0) {
|
|
||||||
Collections.addAll(args, "--width", Integer.toString(params.width));
|
|
||||||
Collections.addAll(args, "--height", Integer.toString(params.height));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static void setJavaBinPath(Path javaBinPath) {
|
|
||||||
JavaBinPath = javaBinPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void addClientLegacyArgs(Collection<String> args, ClientProfile profile, Params params) {
|
|
||||||
args.add(params.pp.username);
|
|
||||||
args.add(params.accessToken);
|
|
||||||
|
|
||||||
// Add args for tweaker
|
|
||||||
Collections.addAll(args, "--version", profile.getVersion().name);
|
|
||||||
Collections.addAll(args, "--gameDir", params.clientDir.toString());
|
|
||||||
Collections.addAll(args, "--assetsDir", params.assetDir.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static boolean isLaunched() {
|
|
||||||
return Launcher.LAUNCHED.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void launch(ClientProfile profile, Params params) throws Throwable {
|
|
||||||
// Add client args
|
|
||||||
Collection<String> args = new LinkedList<>();
|
|
||||||
if (profile.getVersion().compareTo(ClientProfile.Version.MC164) >= 0)
|
|
||||||
addClientArgs(args, profile, params);
|
|
||||||
else
|
|
||||||
addClientLegacyArgs(args, profile, params);
|
|
||||||
Collections.addAll(args, profile.getClientArgs());
|
|
||||||
LogHelper.debug("Args: " + args);
|
|
||||||
// Resolve main class and method
|
|
||||||
Class<?> mainClass = classLoader.loadClass(profile.getMainClass());
|
|
||||||
MethodHandle mainMethod = MethodHandles.publicLookup().findStatic(mainClass, "main", MethodType.methodType(void.class, String[].class));
|
|
||||||
// Invoke main method with exception wrapping
|
|
||||||
Launcher.LAUNCHED.set(true);
|
|
||||||
JVMHelper.fullGC();
|
|
||||||
System.setProperty("minecraft.applet.TargetDirectory", params.clientDir.toString()); // For 1.5.2
|
|
||||||
mainMethod.invoke((Object) args.toArray(EMPTY_ARRAY));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Process process = null;
|
|
||||||
private static boolean clientStarted = false;
|
|
||||||
private static Thread writeParamsThread;
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static Process launch(
|
|
||||||
HashedDir assetHDir, HashedDir clientHDir,
|
|
||||||
ClientProfile profile, Params params, boolean pipeOutput) throws Throwable {
|
|
||||||
LogHelper.debug("Writing ClientLauncher params");
|
|
||||||
ClientLauncherContext context = new ClientLauncherContext();
|
|
||||||
clientStarted = false;
|
|
||||||
if (writeParamsThread != null && writeParamsThread.isAlive()) {
|
|
||||||
writeParamsThread.interrupt();
|
|
||||||
}
|
|
||||||
writeParamsThread = CommonHelper.newThread("Client params writter", true, () ->
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
try (ServerSocket socket = new ServerSocket()) {
|
|
||||||
|
|
||||||
socket.setReuseAddress(true);
|
|
||||||
socket.setSoTimeout(30000);
|
|
||||||
socket.bind(new InetSocketAddress(SOCKET_HOST, SOCKET_PORT));
|
|
||||||
Socket client = socket.accept();
|
|
||||||
if (process == null) {
|
|
||||||
LogHelper.error("Process is null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!process.isAlive()) {
|
|
||||||
LogHelper.error("Process is not alive");
|
|
||||||
JOptionPane.showMessageDialog(null, "Client Process crashed", "Launcher", JOptionPane.ERROR_MESSAGE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try (HOutput output = new HOutput(client.getOutputStream())) {
|
|
||||||
params.write(output);
|
|
||||||
output.writeString(Launcher.gsonManager.gson.toJson(profile), 0);
|
|
||||||
assetHDir.write(output);
|
|
||||||
clientHDir.write(output);
|
|
||||||
}
|
|
||||||
clientStarted = true;
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
LogHelper.error(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
writeParamsThread.start();
|
|
||||||
checkJVMBitsAndVersion();
|
|
||||||
LogHelper.debug("Resolving JVM binary");
|
|
||||||
Path javaBin = LauncherGuardManager.getGuardJavaBinPath();
|
|
||||||
context.javaBin = javaBin;
|
|
||||||
context.clientProfile = profile;
|
|
||||||
context.playerProfile = params.pp;
|
|
||||||
context.args.add(javaBin.toString());
|
|
||||||
context.args.add(MAGICAL_INTEL_OPTION);
|
|
||||||
if (params.ram > 0 && params.ram <= FunctionalBridge.getJVMTotalMemory()) {
|
|
||||||
context.args.add("-Xms" + params.ram + 'M');
|
|
||||||
context.args.add("-Xmx" + params.ram + 'M');
|
|
||||||
}
|
|
||||||
context.args.add(JVMHelper.jvmProperty(LogHelper.DEBUG_PROPERTY, Boolean.toString(LogHelper.isDebugEnabled())));
|
|
||||||
context.args.add(JVMHelper.jvmProperty(LogHelper.STACKTRACE_PROPERTY, Boolean.toString(LogHelper.isStacktraceEnabled())));
|
|
||||||
context.args.add(JVMHelper.jvmProperty(LogHelper.DEV_PROPERTY, Boolean.toString(LogHelper.isDevEnabled())));
|
|
||||||
context.args.add(JVMHelper.jvmProperty(LogHelper.NO_JANSI_PROPERTY, "true")); // Отключаем JAnsi для нормального вывода в DEBUG окно
|
|
||||||
JVMHelper.addSystemPropertyToArgs(context.args, DirBridge.CUSTOMDIR_PROPERTY);
|
|
||||||
JVMHelper.addSystemPropertyToArgs(context.args, DirBridge.USE_CUSTOMDIR_PROPERTY);
|
|
||||||
JVMHelper.addSystemPropertyToArgs(context.args, DirBridge.USE_OPTDIR_PROPERTY);
|
|
||||||
if (JVMHelper.OS_TYPE == OS.MUSTDIE) {
|
|
||||||
if (JVMHelper.OS_VERSION.startsWith("10.")) {
|
|
||||||
LogHelper.debug("MustDie 10 fix is applied");
|
|
||||||
context.args.add(JVMHelper.jvmProperty("os.name", "Windows 10"));
|
|
||||||
context.args.add(JVMHelper.jvmProperty("os.version", "10.0"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Add classpath and main class
|
|
||||||
String pathLauncher = IOHelper.getCodeSource(ClientLauncher.class).toString();
|
|
||||||
context.pathLauncher = pathLauncher;
|
|
||||||
Collections.addAll(context.args, ClientLauncherWrapper.MAGIC_ARG);
|
|
||||||
Collections.addAll(context.args, profile.getJvmArgs());
|
|
||||||
profile.pushOptionalJvmArgs(context.args);
|
|
||||||
Collections.addAll(context.args, "-Djava.library.path=".concat(params.clientDir.resolve(NATIVES_DIR).toString())); // Add Native Path
|
|
||||||
Collections.addAll(context.args, "-javaagent:".concat(pathLauncher));
|
|
||||||
LauncherGuardManager.guard.addCustomParams(context);
|
|
||||||
Collections.addAll(context.args, ClientLauncher.class.getName());
|
|
||||||
|
|
||||||
// Print commandline debug message
|
|
||||||
LogHelper.debug("Commandline: " + context.args);
|
|
||||||
|
|
||||||
// Build client process
|
|
||||||
LogHelper.debug("Launching client instance");
|
|
||||||
ProcessBuilder builder = new ProcessBuilder(context.args);
|
|
||||||
context.builder = builder;
|
|
||||||
LauncherGuardManager.guard.addCustomEnv(context);
|
|
||||||
//else
|
|
||||||
//builder.environment().put("CLASSPATH", classPathString.toString());
|
|
||||||
EnvHelper.addEnv(builder);
|
|
||||||
builder.directory(params.clientDir.toFile());
|
|
||||||
builder.inheritIO();
|
|
||||||
if (pipeOutput) {
|
|
||||||
builder.redirectErrorStream(true);
|
|
||||||
builder.redirectOutput(Redirect.PIPE);
|
|
||||||
}
|
|
||||||
// Let's rock!
|
|
||||||
process = builder.start();
|
|
||||||
if (!pipeOutput) {
|
|
||||||
for (int i = 0; i < 50; ++i) {
|
|
||||||
if (!process.isAlive()) {
|
|
||||||
int exitCode = process.exitValue();
|
|
||||||
LogHelper.error("Process exit code %d", exitCode);
|
|
||||||
if (writeParamsThread != null && writeParamsThread.isAlive()) writeParamsThread.interrupt();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (clientStarted) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Thread.sleep(200);
|
|
||||||
}
|
|
||||||
if (!clientStarted) {
|
|
||||||
LogHelper.error("Write Client Params not successful. Using debug mode for more information");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
clientStarted = false;
|
|
||||||
return process;
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static void main(String... args) throws Throwable {
|
|
||||||
LauncherEngine engine = LauncherEngine.clientInstance();
|
|
||||||
Launcher.modulesManager = new ClientModuleManager(engine);
|
|
||||||
LauncherConfig.getAutogenConfig().initModules(); //INIT
|
|
||||||
initGson();
|
|
||||||
Launcher.modulesManager.preInitModules();
|
|
||||||
JVMHelper.verifySystemProperties(ClientLauncher.class, true);
|
|
||||||
EnvHelper.checkDangerousParams();
|
|
||||||
JVMHelper.checkStackTrace(ClientLauncher.class);
|
|
||||||
LogHelper.printVersion("Client Launcher");
|
|
||||||
if (engine.runtimeProvider == null) engine.runtimeProvider = new JSRuntimeProvider();
|
|
||||||
engine.runtimeProvider.init(true);
|
|
||||||
engine.runtimeProvider.preLoad();
|
|
||||||
LauncherGuardManager.initGuard(true);
|
|
||||||
LogHelper.debug("Reading ClientLauncher params");
|
|
||||||
Params params;
|
|
||||||
ClientProfile profile;
|
|
||||||
HashedDir assetHDir, clientHDir;
|
|
||||||
try {
|
|
||||||
try (Socket socket = IOHelper.newSocket()) {
|
|
||||||
socket.connect(new InetSocketAddress(SOCKET_HOST, SOCKET_PORT));
|
|
||||||
try (HInput input = new HInput(socket.getInputStream())) {
|
|
||||||
params = new Params(input);
|
|
||||||
profile = Launcher.gsonManager.gson.fromJson(input.readString(0), ClientProfile.class);
|
|
||||||
assetHDir = new HashedDir(input);
|
|
||||||
clientHDir = new HashedDir(input);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException ex) {
|
|
||||||
LogHelper.error(ex);
|
|
||||||
System.exit(-98);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Launcher.profile = profile;
|
|
||||||
Request.setSession(params.session);
|
|
||||||
checkJVMBitsAndVersion();
|
|
||||||
Launcher.modulesManager.initModules();
|
|
||||||
// Verify ClientLauncher sign and classpath
|
|
||||||
LogHelper.debug("Verifying ClientLauncher sign and classpath");
|
|
||||||
LinkedList<Path> classPath = resolveClassPathList(params.clientDir, profile.getClassPath());
|
|
||||||
for (Path classpathURL : classPath) {
|
|
||||||
LauncherAgent.addJVMClassPath(classpathURL.toAbsolutePath().toString());
|
|
||||||
}
|
|
||||||
profile.pushOptionalClassPath(cp -> {
|
|
||||||
LinkedList<Path> optionalClassPath = resolveClassPathList(params.clientDir, cp);
|
|
||||||
for (Path classpathURL : optionalClassPath) {
|
|
||||||
LauncherAgent.addJVMClassPath(classpathURL.toAbsolutePath().toString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
URL[] classpathurls = resolveClassPath(params.clientDir, profile.getClassPath());
|
|
||||||
classLoader = new PublicURLClassLoader(classpathurls, ClassLoader.getSystemClassLoader());
|
|
||||||
Thread.currentThread().setContextClassLoader(classLoader);
|
|
||||||
classLoader.nativePath = params.clientDir.resolve(NATIVES_DIR).toString();
|
|
||||||
PublicURLClassLoader.systemclassloader = classLoader;
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
LogHelper.debug("Starting JVM and client WatchService");
|
|
||||||
FileNameMatcher assetMatcher = profile.getAssetUpdateMatcher();
|
|
||||||
FileNameMatcher clientMatcher = profile.getClientUpdateMatcher();
|
|
||||||
try (DirWatcher assetWatcher = new DirWatcher(params.assetDir, assetHDir, assetMatcher, digest);
|
|
||||||
DirWatcher clientWatcher = new DirWatcher(params.clientDir, 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(clientHDir, false);
|
|
||||||
Launcher.modulesManager.postInitModules();
|
|
||||||
// Start WatchService, and only then client
|
|
||||||
CommonHelper.newThread("Asset Directory Watcher", true, assetWatcher).start();
|
|
||||||
CommonHelper.newThread("Client Directory Watcher", true, clientWatcher).start();
|
|
||||||
verifyHDir(params.assetDir, assetHDir, assetMatcher, digest);
|
|
||||||
verifyHDir(params.clientDir, clientHDir, clientMatcher, digest);
|
|
||||||
launch(profile, params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static URL[] resolveClassPath(Path clientDir, String... classPath) throws IOException {
|
|
||||||
Collection<Path> result = new LinkedList<>();
|
|
||||||
for (String classPathEntry : classPath) {
|
|
||||||
Path path = clientDir.resolve(IOHelper.toPath(classPathEntry));
|
|
||||||
if (IOHelper.isDir(path)) { // Recursive walking and adding
|
|
||||||
IOHelper.walk(path, new ClassPathFileVisitor(result), false);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
result.add(path);
|
|
||||||
}
|
|
||||||
return result.stream().map(IOHelper::toURL).toArray(URL[]::new);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static LinkedList<Path> resolveClassPathList(Path clientDir, String... classPath) throws IOException {
|
|
||||||
LinkedList<Path> result = new LinkedList<>();
|
|
||||||
for (String classPathEntry : classPath) {
|
|
||||||
Path path = clientDir.resolve(IOHelper.toPath(classPathEntry));
|
|
||||||
if (IOHelper.isDir(path)) { // Recursive walking and adding
|
|
||||||
IOHelper.walk(path, new ClassPathFileVisitor(result), false);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
result.add(path);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void initGson() {
|
|
||||||
Launcher.gsonManager = new ClientGsonManager();
|
|
||||||
Launcher.gsonManager.initGson();
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static void setProfile(ClientProfile profile) {
|
|
||||||
Launcher.profile = profile;
|
|
||||||
LogHelper.debug("New Profile name: %s", profile.getTitle());
|
|
||||||
}
|
|
||||||
|
|
||||||
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)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ClientLauncher() {
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
package ru.gravit.launcher.client;
|
|
||||||
|
|
||||||
import ru.gravit.launcher.profiles.ClientProfile;
|
|
||||||
import ru.gravit.launcher.profiles.PlayerProfile;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class ClientLauncherContext {
|
|
||||||
public Path javaBin;
|
|
||||||
public List<String> args = new LinkedList<>();
|
|
||||||
public String pathLauncher;
|
|
||||||
public ProcessBuilder builder;
|
|
||||||
public ClientProfile clientProfile;
|
|
||||||
public PlayerProfile playerProfile;
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
package ru.gravit.launcher.client;
|
|
||||||
|
|
||||||
import ru.gravit.launcher.Launcher;
|
|
||||||
import ru.gravit.launcher.LauncherEngine;
|
|
||||||
import ru.gravit.launcher.modules.ModuleContext;
|
|
||||||
import ru.gravit.launcher.modules.ModulesConfigManager;
|
|
||||||
import ru.gravit.launcher.modules.ModulesManager;
|
|
||||||
|
|
||||||
public class ClientModuleContext implements ModuleContext {
|
|
||||||
public final LauncherEngine engine;
|
|
||||||
|
|
||||||
ClientModuleContext(LauncherEngine engine) {
|
|
||||||
this.engine = engine;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Type getType() {
|
|
||||||
return Type.CLIENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ModulesManager getModulesManager() {
|
|
||||||
return Launcher.modulesManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ModulesConfigManager getModulesConfigManager() {
|
|
||||||
return null; // ClientModuleContext не поддерживает modulesConfigManager
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
package ru.gravit.launcher.client;
|
|
||||||
|
|
||||||
import ru.gravit.launcher.LauncherEngine;
|
|
||||||
import ru.gravit.launcher.managers.SimpleModuleManager;
|
|
||||||
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public class ClientModuleManager extends SimpleModuleManager {
|
|
||||||
public ClientModuleManager(LauncherEngine engine) {
|
|
||||||
context = new ClientModuleContext(engine);
|
|
||||||
modules = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void loadModule(URL jarpath, String classname) {
|
|
||||||
throw new SecurityException("Custom JAR's load not allowed here");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void loadModuleFull(URL jarpath) {
|
|
||||||
throw new SecurityException("Custom JAR's load not allowed here");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
package ru.gravit.launcher.client;
|
|
||||||
|
|
||||||
import ru.gravit.launcher.LauncherAPI;
|
|
||||||
import ru.gravit.utils.helper.IOHelper;
|
|
||||||
import ru.gravit.utils.helper.JVMHelper;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
|
|
||||||
public class DirBridge {
|
|
||||||
|
|
||||||
public static final String USE_CUSTOMDIR_PROPERTY = "launcher.usecustomdir";
|
|
||||||
public static final String CUSTOMDIR_PROPERTY = "launcher.customdir";
|
|
||||||
public static final String USE_OPTDIR_PROPERTY = "launcher.useoptdir";
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static Path dir;
|
|
||||||
@LauncherAPI
|
|
||||||
public static Path dirStore;
|
|
||||||
@LauncherAPI
|
|
||||||
public static Path dirProjectStore;
|
|
||||||
@LauncherAPI
|
|
||||||
public static Path dirUpdates;
|
|
||||||
@LauncherAPI
|
|
||||||
public static Path defaultUpdatesDir;
|
|
||||||
@LauncherAPI
|
|
||||||
public static boolean useLegacyDir;
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static void move(Path newDir) throws IOException {
|
|
||||||
IOHelper.move(dirUpdates, newDir);
|
|
||||||
dirUpdates = newDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static Path getAppDataDir() throws IOException {
|
|
||||||
boolean isCustomDir = Boolean.getBoolean(System.getProperty(USE_CUSTOMDIR_PROPERTY, "false"));
|
|
||||||
if (isCustomDir) {
|
|
||||||
return Paths.get(System.getProperty(CUSTOMDIR_PROPERTY));
|
|
||||||
}
|
|
||||||
if (JVMHelper.OS_TYPE == JVMHelper.OS.LINUX) {
|
|
||||||
boolean isOpt = Boolean.getBoolean(System.getProperty(USE_OPTDIR_PROPERTY, "false"));
|
|
||||||
if (isOpt) {
|
|
||||||
Path opt = Paths.get("/").resolve("opt");
|
|
||||||
if (!IOHelper.isDir(opt)) Files.createDirectories(opt);
|
|
||||||
return opt;
|
|
||||||
} else {
|
|
||||||
Path local = IOHelper.HOME_DIR.resolve(".minecraftlauncher");
|
|
||||||
if (!IOHelper.isDir(local)) Files.createDirectories(local);
|
|
||||||
return local;
|
|
||||||
}
|
|
||||||
} else if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE) {
|
|
||||||
Path appdata = IOHelper.HOME_DIR.resolve("AppData").resolve("Roaming");
|
|
||||||
if (!IOHelper.isDir(appdata)) Files.createDirectories(appdata);
|
|
||||||
return appdata;
|
|
||||||
} else if (JVMHelper.OS_TYPE == JVMHelper.OS.MACOSX) {
|
|
||||||
Path minecraft = IOHelper.HOME_DIR.resolve("minecraft");
|
|
||||||
if (!IOHelper.isDir(minecraft)) Files.createDirectories(minecraft);
|
|
||||||
return minecraft;
|
|
||||||
} else {
|
|
||||||
return IOHelper.HOME_DIR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static Path getLauncherDir(String projectname) throws IOException {
|
|
||||||
return getAppDataDir().resolve(projectname);
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static Path getStoreDir(String projectname) throws IOException {
|
|
||||||
if (JVMHelper.OS_TYPE == JVMHelper.OS.LINUX)
|
|
||||||
return getAppDataDir().resolve("store");
|
|
||||||
else if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE)
|
|
||||||
return getAppDataDir().resolve("GravitLauncherStore");
|
|
||||||
else
|
|
||||||
return getAppDataDir().resolve("minecraftStore");
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static Path getProjectStoreDir(String projectname) throws IOException {
|
|
||||||
return getStoreDir(projectname).resolve(projectname);
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static Path getGuardDir() {
|
|
||||||
return dir.resolve("guard");
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static Path getLegacyLauncherDir(String projectname) {
|
|
||||||
return IOHelper.HOME_DIR.resolve(projectname);
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static void setUseLegacyDir(boolean b) {
|
|
||||||
useLegacyDir = b;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,115 +0,0 @@
|
||||||
package ru.gravit.launcher.client;
|
|
||||||
|
|
||||||
import ru.gravit.launcher.HWID;
|
|
||||||
import ru.gravit.launcher.LauncherAPI;
|
|
||||||
import ru.gravit.launcher.events.request.AuthRequestEvent;
|
|
||||||
import ru.gravit.launcher.guard.LauncherGuardManager;
|
|
||||||
import ru.gravit.launcher.hasher.FileNameMatcher;
|
|
||||||
import ru.gravit.launcher.hasher.HashedDir;
|
|
||||||
import ru.gravit.launcher.hwid.OshiHWIDProvider;
|
|
||||||
import ru.gravit.launcher.managers.ConsoleManager;
|
|
||||||
import ru.gravit.launcher.managers.HasherManager;
|
|
||||||
import ru.gravit.launcher.managers.HasherStore;
|
|
||||||
import ru.gravit.launcher.request.Request;
|
|
||||||
import ru.gravit.launcher.serialize.signed.SignedObjectHolder;
|
|
||||||
import ru.gravit.utils.helper.LogHelper;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
public class FunctionalBridge {
|
|
||||||
@LauncherAPI
|
|
||||||
public static ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(0);
|
|
||||||
@LauncherAPI
|
|
||||||
public static OshiHWIDProvider hwidProvider = new OshiHWIDProvider();
|
|
||||||
@LauncherAPI
|
|
||||||
public static AtomicReference<HWID> hwid = new AtomicReference<>();
|
|
||||||
@LauncherAPI
|
|
||||||
public static Thread getHWID = null;
|
|
||||||
|
|
||||||
private static long cachedMemorySize = -1;
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static HashedDirRunnable offlineUpdateRequest(String dirName, Path dir, SignedObjectHolder<HashedDir> hdir, FileNameMatcher matcher, boolean digest) {
|
|
||||||
return () -> {
|
|
||||||
if (hdir == null) {
|
|
||||||
Request.requestError(java.lang.String.format("Директории '%s' нет в кэше", dirName));
|
|
||||||
}
|
|
||||||
ClientLauncher.verifyHDir(dir, hdir.object, matcher, digest);
|
|
||||||
return hdir;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static void startTask(Runnable task) {
|
|
||||||
threadPool.execute(task);
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static HWID getHWID() {
|
|
||||||
HWID hhwid = hwid.get();
|
|
||||||
if (hhwid == null) hwid.set(hwidProvider.getHWID());
|
|
||||||
return hhwid;
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static int getTotalMemory() {
|
|
||||||
if (cachedMemorySize > 0) return (int) cachedMemorySize;
|
|
||||||
return (int) (cachedMemorySize = hwidProvider.getTotalMemory() >> 20);
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static int getClientJVMBits() {
|
|
||||||
return LauncherGuardManager.guard.getClientJVMBits();
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static int getJVMTotalMemory() {
|
|
||||||
if (getClientJVMBits() == 32) {
|
|
||||||
return Math.min(getTotalMemory(), 1536);
|
|
||||||
} else {
|
|
||||||
return getTotalMemory();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static HasherStore getDefaultHasherStore() {
|
|
||||||
return HasherManager.getDefaultStore();
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static void registerUserSettings(String typename, Class<? extends UserSettings> clazz) {
|
|
||||||
UserSettings.providers.register(typename, clazz);
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static void close() throws Exception {
|
|
||||||
threadPool.awaitTermination(2, TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static void setAuthParams(AuthRequestEvent event) {
|
|
||||||
if (event.session != 0) {
|
|
||||||
Request.setSession(event.session);
|
|
||||||
}
|
|
||||||
LauncherGuardManager.guard.setProtectToken(event.protectToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface HashedDirRunnable {
|
|
||||||
SignedObjectHolder<HashedDir> run() throws Exception;
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static void evalCommand(String cmd) {
|
|
||||||
ConsoleManager.handler.eval(cmd, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static void addPlainOutput(LogHelper.Output output) {
|
|
||||||
LogHelper.addOutput(output, LogHelper.OutputTypes.PLAIN);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,227 +0,0 @@
|
||||||
package ru.gravit.launcher.client;
|
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import com.google.gson.JsonParser;
|
|
||||||
import ru.gravit.launcher.LauncherAPI;
|
|
||||||
import ru.gravit.launcher.profiles.ClientProfile;
|
|
||||||
import ru.gravit.launcher.serialize.HInput;
|
|
||||||
import ru.gravit.launcher.serialize.HOutput;
|
|
||||||
import ru.gravit.utils.helper.IOHelper;
|
|
||||||
import ru.gravit.utils.helper.LogHelper;
|
|
||||||
import ru.gravit.utils.helper.VerifyHelper;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
public final class ServerPinger {
|
|
||||||
private JsonParser parser = new JsonParser();
|
|
||||||
|
|
||||||
public static final class Result {
|
|
||||||
@LauncherAPI
|
|
||||||
public final int onlinePlayers;
|
|
||||||
@LauncherAPI
|
|
||||||
public final int maxPlayers;
|
|
||||||
@LauncherAPI
|
|
||||||
public final String raw;
|
|
||||||
|
|
||||||
public Result(int onlinePlayers, int maxPlayers, String raw) {
|
|
||||||
this.onlinePlayers = VerifyHelper.verifyInt(onlinePlayers,
|
|
||||||
VerifyHelper.NOT_NEGATIVE, "onlinePlayers can't be < 0");
|
|
||||||
this.maxPlayers = VerifyHelper.verifyInt(maxPlayers,
|
|
||||||
VerifyHelper.NOT_NEGATIVE, "maxPlayers can't be < 0");
|
|
||||||
this.raw = raw;
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public boolean isOverfilled() {
|
|
||||||
return onlinePlayers >= maxPlayers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constants
|
|
||||||
private static final String LEGACY_PING_HOST_MAGIC = "§1";
|
|
||||||
private static final String LEGACY_PING_HOST_CHANNEL = "MC|PingHost";
|
|
||||||
private static final Pattern LEGACY_PING_HOST_DELIMETER = Pattern.compile("\0", Pattern.LITERAL);
|
|
||||||
|
|
||||||
private static final int PACKET_LENGTH = 65535;
|
|
||||||
|
|
||||||
private static String readUTF16String(HInput input) throws IOException {
|
|
||||||
int length = input.readUnsignedShort() << 1;
|
|
||||||
byte[] encoded = input.readByteArray(-length);
|
|
||||||
return new String(encoded, StandardCharsets.UTF_16BE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void writeUTF16String(HOutput output, String s) throws IOException {
|
|
||||||
output.writeShort((short) s.length());
|
|
||||||
output.stream.write(s.getBytes(StandardCharsets.UTF_16BE));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instance
|
|
||||||
private final InetSocketAddress address;
|
|
||||||
private final ClientProfile.Version version;
|
|
||||||
// Cache
|
|
||||||
private final Object cacheLock = new Object();
|
|
||||||
|
|
||||||
private Result cache = null;
|
|
||||||
|
|
||||||
private Exception cacheException = null;
|
|
||||||
|
|
||||||
private Instant cacheTime = null;
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public ServerPinger(ClientProfile profile) {
|
|
||||||
this.address = Objects.requireNonNull(profile.getServerSocketAddress(), "address");
|
|
||||||
this.version = Objects.requireNonNull(profile.getVersion(), "version");
|
|
||||||
}
|
|
||||||
|
|
||||||
private Result doPing() throws IOException {
|
|
||||||
try (Socket socket = IOHelper.newSocket()) {
|
|
||||||
socket.connect(IOHelper.resolve(address), IOHelper.SOCKET_TIMEOUT);
|
|
||||||
try (HInput input = new HInput(socket.getInputStream());
|
|
||||||
HOutput output = new HOutput(socket.getOutputStream())) {
|
|
||||||
return version.compareTo(ClientProfile.Version.MC172) >= 0 ? modernPing(input, output) : legacyPing(input, output, version.compareTo(ClientProfile.Version.MC164) >= 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Result legacyPing(HInput input, HOutput output, boolean mc16) throws IOException {
|
|
||||||
output.writeUnsignedByte(0xFE); // 254 packet ID, Server list ping
|
|
||||||
output.writeUnsignedByte(0x01); // Server ping payload
|
|
||||||
if (mc16) {
|
|
||||||
output.writeUnsignedByte(0xFA); // 250 packet ID, Custom payload
|
|
||||||
writeUTF16String(output, LEGACY_PING_HOST_CHANNEL); // Custom payload name
|
|
||||||
|
|
||||||
// Prepare custom payload packet
|
|
||||||
byte[] customPayloadPacket;
|
|
||||||
try (ByteArrayOutputStream packetArray = IOHelper.newByteArrayOutput()) {
|
|
||||||
try (HOutput packetOutput = new HOutput(packetArray)) {
|
|
||||||
packetOutput.writeUnsignedByte(version.protocol); // Protocol version
|
|
||||||
writeUTF16String(packetOutput, address.getHostString()); // Server address
|
|
||||||
packetOutput.writeInt(address.getPort()); // Server port
|
|
||||||
}
|
|
||||||
customPayloadPacket = packetArray.toByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write custom payload packet
|
|
||||||
output.writeShort((short) customPayloadPacket.length);
|
|
||||||
output.stream.write(customPayloadPacket);
|
|
||||||
}
|
|
||||||
output.flush();
|
|
||||||
|
|
||||||
// Raed kick (response) packet
|
|
||||||
int kickPacketID = input.readUnsignedByte();
|
|
||||||
if (kickPacketID != 0xFF)
|
|
||||||
throw new IOException("Illegal kick packet ID: " + kickPacketID);
|
|
||||||
|
|
||||||
// Read and parse response
|
|
||||||
String response = readUTF16String(input);
|
|
||||||
LogHelper.debug("Ping response (legacy): '%s'", response);
|
|
||||||
String[] splitted = LEGACY_PING_HOST_DELIMETER.split(response);
|
|
||||||
if (splitted.length != 6)
|
|
||||||
throw new IOException("Tokens count mismatch");
|
|
||||||
|
|
||||||
// Verify all parts
|
|
||||||
String magic = splitted[0];
|
|
||||||
if (!magic.equals(LEGACY_PING_HOST_MAGIC))
|
|
||||||
throw new IOException("Magic file mismatch: " + magic);
|
|
||||||
int protocol = Integer.parseInt(splitted[1]);
|
|
||||||
if (protocol != version.protocol)
|
|
||||||
throw new IOException("Protocol mismatch: " + protocol);
|
|
||||||
String clientVersion = splitted[2];
|
|
||||||
if (!clientVersion.equals(version.name))
|
|
||||||
throw new IOException(String.format("Version mismatch: '%s'", clientVersion));
|
|
||||||
int onlinePlayers = VerifyHelper.verifyInt(Integer.parseInt(splitted[4]),
|
|
||||||
VerifyHelper.NOT_NEGATIVE, "onlinePlayers can't be < 0");
|
|
||||||
int maxPlayers = VerifyHelper.verifyInt(Integer.parseInt(splitted[5]),
|
|
||||||
VerifyHelper.NOT_NEGATIVE, "maxPlayers can't be < 0");
|
|
||||||
|
|
||||||
// Return ping status
|
|
||||||
return new Result(onlinePlayers, maxPlayers, response);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Result modernPing(HInput input, HOutput output) throws IOException {
|
|
||||||
// Prepare handshake packet
|
|
||||||
byte[] handshakePacket;
|
|
||||||
try (ByteArrayOutputStream packetArray = IOHelper.newByteArrayOutput()) {
|
|
||||||
try (HOutput packetOutput = new HOutput(packetArray)) {
|
|
||||||
packetOutput.writeVarInt(0x0); // Handshake packet ID
|
|
||||||
packetOutput.writeVarInt(version.protocol); // Protocol version
|
|
||||||
packetOutput.writeString(address.getHostString(), 0); // Server address
|
|
||||||
packetOutput.writeShort((short) address.getPort()); // Server port
|
|
||||||
packetOutput.writeVarInt(0x1); // Next state - status
|
|
||||||
}
|
|
||||||
handshakePacket = packetArray.toByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write handshake packet
|
|
||||||
output.writeByteArray(handshakePacket, PACKET_LENGTH);
|
|
||||||
|
|
||||||
// Request status packet
|
|
||||||
output.writeVarInt(1); // Status packet size (single byte)
|
|
||||||
output.writeVarInt(0x0); // Status packet ID
|
|
||||||
output.flush();
|
|
||||||
|
|
||||||
// ab is a dirty fix for some servers (noticed KCauldron 1.7.10)
|
|
||||||
int ab = 0;
|
|
||||||
while (ab <= 0)
|
|
||||||
ab = IOHelper.verifyLength(input.readVarInt(), PACKET_LENGTH);
|
|
||||||
|
|
||||||
// Read outer status response packet ID
|
|
||||||
String response;
|
|
||||||
byte[] statusPacket = input.readByteArray(-ab);
|
|
||||||
try (HInput packetInput = new HInput(statusPacket)) {
|
|
||||||
int statusPacketID = packetInput.readVarInt();
|
|
||||||
if (statusPacketID != 0x0)
|
|
||||||
throw new IOException("Illegal status packet ID: " + statusPacketID);
|
|
||||||
response = packetInput.readString(PACKET_LENGTH);
|
|
||||||
LogHelper.debug("Ping response (modern): '%s'", response);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse JSON response
|
|
||||||
JsonObject object = parser.parse(response).getAsJsonObject();
|
|
||||||
JsonObject playersObject = object.get("players").getAsJsonObject();
|
|
||||||
int online = playersObject.get("online").getAsInt();
|
|
||||||
int max = playersObject.get("max").getAsInt();
|
|
||||||
|
|
||||||
// Return ping status
|
|
||||||
return new Result(online, max, response);
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public Result ping() throws IOException {
|
|
||||||
Instant now = Instant.now();
|
|
||||||
synchronized (cacheLock) {
|
|
||||||
// Update ping cache
|
|
||||||
if (cacheTime == null || Duration.between(now, cacheTime).toMillis() >= IOHelper.SOCKET_TIMEOUT) {
|
|
||||||
cacheTime = now;
|
|
||||||
try {
|
|
||||||
cache = doPing();
|
|
||||||
cacheException = null;
|
|
||||||
} catch (IOException | IllegalArgumentException /* Protocol error */ e) {
|
|
||||||
cache = null;
|
|
||||||
cacheException = e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify is result available
|
|
||||||
if (cache == null) {
|
|
||||||
if (cacheException instanceof IOException)
|
|
||||||
throw (IOException) cacheException;
|
|
||||||
if (cacheException instanceof IllegalArgumentException)
|
|
||||||
throw (IllegalArgumentException) cacheException;
|
|
||||||
cacheException = new IOException("Unavailable");
|
|
||||||
throw (IOException) cacheException;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We're done
|
|
||||||
return cache;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
package ru.gravit.launcher.client;
|
|
||||||
|
|
||||||
import ru.gravit.utils.ProviderMap;
|
|
||||||
|
|
||||||
public class UserSettings {
|
|
||||||
public static ProviderMap<UserSettings> providers = new ProviderMap<>();
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
package ru.gravit.launcher.console;
|
|
||||||
|
|
||||||
import ru.gravit.launcher.managers.ConsoleManager;
|
|
||||||
import ru.gravit.utils.command.Command;
|
|
||||||
import ru.gravit.utils.helper.LogHelper;
|
|
||||||
|
|
||||||
public class UnlockCommand extends Command {
|
|
||||||
@Override
|
|
||||||
public String getArgsDescription() {
|
|
||||||
return "[key]";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getUsageDescription() {
|
|
||||||
return "Unlock console commands";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void invoke(String... args) throws Exception {
|
|
||||||
verifyArgs(args, 1);
|
|
||||||
if (ConsoleManager.checkUnlockKey(args[0])) {
|
|
||||||
LogHelper.info("Unlock successful");
|
|
||||||
ConsoleManager.unlock();
|
|
||||||
ConsoleManager.handler.unregisterCommand("unlock");
|
|
||||||
} else {
|
|
||||||
LogHelper.error("Unlock key incorrect");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
package ru.gravit.launcher.console.admin;
|
|
||||||
|
|
||||||
import ru.gravit.launcher.events.request.ExecCommandRequestEvent;
|
|
||||||
import ru.gravit.launcher.request.admin.ExecCommandRequest;
|
|
||||||
import ru.gravit.utils.command.Command;
|
|
||||||
import ru.gravit.utils.helper.LogHelper;
|
|
||||||
|
|
||||||
public class ExecCommand extends Command {
|
|
||||||
@Override
|
|
||||||
public String getArgsDescription() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getUsageDescription() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void invoke(String... args) throws Exception {
|
|
||||||
ExecCommandRequestEvent request = new ExecCommandRequest(String.join(" ", args)).request();
|
|
||||||
if (!request.success) LogHelper.error("Error executing command");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
package ru.gravit.launcher.console.admin;
|
|
||||||
|
|
||||||
import ru.gravit.launcher.LauncherNetworkAPI;
|
|
||||||
import ru.gravit.launcher.events.request.LogEvent;
|
|
||||||
import ru.gravit.launcher.request.Request;
|
|
||||||
import ru.gravit.launcher.request.websockets.RequestInterface;
|
|
||||||
import ru.gravit.utils.command.Command;
|
|
||||||
import ru.gravit.utils.helper.LogHelper;
|
|
||||||
|
|
||||||
public class LogListenerCommand extends Command {
|
|
||||||
public class LogListenerRequest implements RequestInterface {
|
|
||||||
@LauncherNetworkAPI
|
|
||||||
public LogHelper.OutputTypes outputType;
|
|
||||||
|
|
||||||
public LogListenerRequest(LogHelper.OutputTypes outputType) {
|
|
||||||
this.outputType = outputType;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getType() {
|
|
||||||
return "addLogListener";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getArgsDescription() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getUsageDescription() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void invoke(String... args) throws Exception {
|
|
||||||
LogHelper.info("Send log listener request");
|
|
||||||
Request.service.sendObject(new LogListenerRequest(LogHelper.JANSI ? LogHelper.OutputTypes.JANSI : LogHelper.OutputTypes.PLAIN));
|
|
||||||
LogHelper.info("Add log handler");
|
|
||||||
Request.service.registerHandler((result) -> {
|
|
||||||
if (result instanceof LogEvent) {
|
|
||||||
LogHelper.rawLog(() -> ((LogEvent) result).string, () -> ((LogEvent) result).string, () -> ((LogEvent) result).string);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
package ru.gravit.launcher.guard;
|
|
||||||
|
|
||||||
import ru.gravit.launcher.client.ClientLauncherContext;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
public interface LauncherGuardInterface {
|
|
||||||
String getName();
|
|
||||||
|
|
||||||
Path getJavaBinPath();
|
|
||||||
|
|
||||||
int getClientJVMBits();
|
|
||||||
|
|
||||||
void init(boolean clientInstance);
|
|
||||||
|
|
||||||
void addCustomParams(ClientLauncherContext context);
|
|
||||||
|
|
||||||
void addCustomEnv(ClientLauncherContext context);
|
|
||||||
|
|
||||||
void setProtectToken(String token);
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
package ru.gravit.launcher.guard;
|
|
||||||
|
|
||||||
import ru.gravit.launcher.Launcher;
|
|
||||||
import ru.gravit.launcher.LauncherConfig;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
public class LauncherGuardManager {
|
|
||||||
public static LauncherGuardInterface guard;
|
|
||||||
|
|
||||||
public static void initGuard(boolean clientInstance) {
|
|
||||||
LauncherConfig config = Launcher.getConfig();
|
|
||||||
switch (config.guardType) {
|
|
||||||
case "wrapper": {
|
|
||||||
guard = new LauncherWrapperGuard();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "java": {
|
|
||||||
guard = new LauncherJavaGuard();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
guard = new LauncherNoGuard();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
guard.init(clientInstance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Path getGuardJavaBinPath() {
|
|
||||||
return guard.getJavaBinPath();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
package ru.gravit.launcher.guard;
|
|
||||||
|
|
||||||
import ru.gravit.launcher.client.ClientLauncher;
|
|
||||||
import ru.gravit.launcher.client.ClientLauncherContext;
|
|
||||||
import ru.gravit.utils.helper.IOHelper;
|
|
||||||
import ru.gravit.utils.helper.JVMHelper;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
public class LauncherJavaGuard implements LauncherGuardInterface {
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "java";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Path getJavaBinPath() {
|
|
||||||
if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE)
|
|
||||||
return IOHelper.resolveJavaBin(ClientLauncher.getJavaBinPath());
|
|
||||||
else
|
|
||||||
return IOHelper.resolveJavaBin(Paths.get(System.getProperty("java.home")));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getClientJVMBits() {
|
|
||||||
return JVMHelper.OS_BITS;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(boolean clientInstance) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addCustomParams(ClientLauncherContext context) {
|
|
||||||
Collections.addAll(context.args, "-cp");
|
|
||||||
Collections.addAll(context.args, context.pathLauncher);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addCustomEnv(ClientLauncherContext context) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setProtectToken(String token) {
|
|
||||||
//Skip
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
package ru.gravit.launcher.guard;
|
|
||||||
|
|
||||||
import ru.gravit.launcher.client.ClientLauncherContext;
|
|
||||||
import ru.gravit.utils.helper.IOHelper;
|
|
||||||
import ru.gravit.utils.helper.JVMHelper;
|
|
||||||
import ru.gravit.utils.helper.LogHelper;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
public class LauncherNoGuard implements LauncherGuardInterface {
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "noGuard";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Path getJavaBinPath() {
|
|
||||||
return IOHelper.resolveJavaBin(Paths.get(System.getProperty("java.home")));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getClientJVMBits() {
|
|
||||||
return JVMHelper.JVM_BITS;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(boolean clientInstance) {
|
|
||||||
LogHelper.warning("Using noGuard interface");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addCustomParams(ClientLauncherContext context) {
|
|
||||||
Collections.addAll(context.args, "-cp");
|
|
||||||
Collections.addAll(context.args, context.pathLauncher);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addCustomEnv(ClientLauncherContext context) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setProtectToken(String token) {
|
|
||||||
//Skip
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
package ru.gravit.launcher.guard;
|
|
||||||
|
|
||||||
import ru.gravit.launcher.Launcher;
|
|
||||||
import ru.gravit.launcher.LauncherConfig;
|
|
||||||
import ru.gravit.launcher.client.ClientLauncherContext;
|
|
||||||
import ru.gravit.launcher.client.DirBridge;
|
|
||||||
import ru.gravit.utils.helper.IOHelper;
|
|
||||||
import ru.gravit.utils.helper.JVMHelper;
|
|
||||||
import ru.gravit.utils.helper.UnpackHelper;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class LauncherWrapperGuard implements LauncherGuardInterface {
|
|
||||||
|
|
||||||
public String protectToken;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "wrapper";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Path getJavaBinPath() {
|
|
||||||
if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE) {
|
|
||||||
String projectName = Launcher.getConfig().projectname;
|
|
||||||
String wrapperUnpackName = JVMHelper.JVM_BITS == 64 ? projectName.concat("64.exe") : projectName.concat("32.exe");
|
|
||||||
return DirBridge.getGuardDir().resolve(wrapperUnpackName);
|
|
||||||
} else
|
|
||||||
return IOHelper.resolveJavaBin(Paths.get(System.getProperty("java.home")));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getClientJVMBits() {
|
|
||||||
return JVMHelper.JVM_BITS;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(boolean clientInstance) {
|
|
||||||
try {
|
|
||||||
String wrapperName = JVMHelper.JVM_BITS == 64 ? "wrapper64.exe" : "wrapper32.exe";
|
|
||||||
String projectName = Launcher.getConfig().projectname;
|
|
||||||
String wrapperUnpackName = JVMHelper.JVM_BITS == 64 ? projectName.concat("64.exe") : projectName.concat("32.exe");
|
|
||||||
String antiInjectName = JVMHelper.JVM_BITS == 64 ? "AntiInject64.dll" : "AntiInject32.dll";
|
|
||||||
UnpackHelper.unpack(Launcher.getResourceURL(wrapperName, "guard"), DirBridge.getGuardDir().resolve(wrapperUnpackName));
|
|
||||||
UnpackHelper.unpack(Launcher.getResourceURL(antiInjectName, "guard"), DirBridge.getGuardDir().resolve(antiInjectName));
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new SecurityException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addCustomParams(ClientLauncherContext context) {
|
|
||||||
Collections.addAll(context.args, "-Djava.class.path=".concat(context.pathLauncher));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addCustomEnv(ClientLauncherContext context) {
|
|
||||||
Map<String, String> env = context.builder.environment();
|
|
||||||
env.put("JAVA_HOME", System.getProperty("java.home"));
|
|
||||||
LauncherConfig config = Launcher.getConfig();
|
|
||||||
env.put("GUARD_USERNAME", context.playerProfile.username);
|
|
||||||
env.put("GUARD_PUBLICKEY", config.publicKey.getModulus().toString(16));
|
|
||||||
env.put("GUARD_PROJECTNAME", config.projectname);
|
|
||||||
if (protectToken != null)
|
|
||||||
env.put("GUARD_TOKEN", protectToken);
|
|
||||||
if (config.guardLicenseName != null)
|
|
||||||
env.put("GUARD_LICENSE_NAME", config.guardLicenseName);
|
|
||||||
if (config.guardLicenseKey != null) {
|
|
||||||
env.put("GUARD_LICENSE_KEY", config.guardLicenseKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setProtectToken(String token) {
|
|
||||||
protectToken = token;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,160 +0,0 @@
|
||||||
package ru.gravit.launcher.gui;
|
|
||||||
|
|
||||||
import ru.gravit.launcher.*;
|
|
||||||
import ru.gravit.launcher.client.*;
|
|
||||||
import ru.gravit.launcher.hasher.FileNameMatcher;
|
|
||||||
import ru.gravit.launcher.hasher.HashedDir;
|
|
||||||
import ru.gravit.launcher.hasher.HashedEntry;
|
|
||||||
import ru.gravit.launcher.hasher.HashedFile;
|
|
||||||
import ru.gravit.launcher.managers.SettingsManager;
|
|
||||||
import ru.gravit.launcher.profiles.ClientProfile;
|
|
||||||
import ru.gravit.launcher.profiles.PlayerProfile;
|
|
||||||
import ru.gravit.launcher.profiles.Texture;
|
|
||||||
import ru.gravit.launcher.profiles.optional.OptionalFile;
|
|
||||||
import ru.gravit.launcher.request.PingRequest;
|
|
||||||
import ru.gravit.launcher.request.Request;
|
|
||||||
import ru.gravit.launcher.request.RequestException;
|
|
||||||
import ru.gravit.launcher.request.RequestType;
|
|
||||||
import ru.gravit.launcher.request.auth.*;
|
|
||||||
import ru.gravit.launcher.request.update.LauncherRequest;
|
|
||||||
import ru.gravit.launcher.request.update.ProfilesRequest;
|
|
||||||
import ru.gravit.launcher.request.update.UpdateRequest;
|
|
||||||
import ru.gravit.launcher.request.uuid.BatchProfileByUsernameRequest;
|
|
||||||
import ru.gravit.launcher.request.uuid.ProfileByUUIDRequest;
|
|
||||||
import ru.gravit.launcher.request.uuid.ProfileByUsernameRequest;
|
|
||||||
import ru.gravit.launcher.serialize.HInput;
|
|
||||||
import ru.gravit.launcher.serialize.HOutput;
|
|
||||||
import ru.gravit.launcher.serialize.signed.SignedBytesHolder;
|
|
||||||
import ru.gravit.launcher.serialize.signed.SignedObjectHolder;
|
|
||||||
import ru.gravit.launcher.serialize.stream.EnumSerializer;
|
|
||||||
import ru.gravit.launcher.serialize.stream.StreamObject;
|
|
||||||
import ru.gravit.utils.HTTPRequest;
|
|
||||||
import ru.gravit.utils.helper.*;
|
|
||||||
|
|
||||||
import javax.script.*;
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class JSRuntimeProvider implements RuntimeProvider {
|
|
||||||
|
|
||||||
private final ScriptEngine engine = CommonHelper.newScriptEngine();
|
|
||||||
private boolean isPreLoaded = false;
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static void addLauncherClassBindings(Map<String, Object> bindings) {
|
|
||||||
bindings.put("LauncherClass", Launcher.class);
|
|
||||||
bindings.put("LauncherConfigClass", LauncherConfig.class);
|
|
||||||
bindings.put("HTTPRequestClass", HTTPRequest.class);
|
|
||||||
bindings.put("SettingsManagerClass", SettingsManager.class);
|
|
||||||
bindings.put("NewLauncherSettingsClass", NewLauncherSettings.class);
|
|
||||||
|
|
||||||
// Set client class bindings
|
|
||||||
bindings.put("PlayerProfileClass", PlayerProfile.class);
|
|
||||||
bindings.put("PlayerProfileTextureClass", Texture.class);
|
|
||||||
bindings.put("ClientProfileClass", ClientProfile.class);
|
|
||||||
bindings.put("ClientProfileVersionClass", ClientProfile.Version.class);
|
|
||||||
bindings.put("ClientLauncherClass", ClientLauncher.class);
|
|
||||||
bindings.put("ClientLauncherParamsClass", ClientLauncher.Params.class);
|
|
||||||
bindings.put("ServerPingerClass", ServerPinger.class);
|
|
||||||
|
|
||||||
// Set request class bindings
|
|
||||||
bindings.put("RequestClass", Request.class);
|
|
||||||
bindings.put("RequestTypeClass", RequestType.class);
|
|
||||||
bindings.put("RequestExceptionClass", RequestException.class);
|
|
||||||
bindings.put("PingRequestClass", PingRequest.class);
|
|
||||||
bindings.put("AuthRequestClass", AuthRequest.class);
|
|
||||||
bindings.put("JoinServerRequestClass", JoinServerRequest.class);
|
|
||||||
bindings.put("CheckServerRequestClass", CheckServerRequest.class);
|
|
||||||
bindings.put("UpdateRequestClass", UpdateRequest.class);
|
|
||||||
bindings.put("LauncherRequestClass", LauncherRequest.class);
|
|
||||||
bindings.put("SetProfileRequestClass", SetProfileRequest.class);
|
|
||||||
bindings.put("ProfilesRequestClass", ProfilesRequest.class);
|
|
||||||
bindings.put("ProfileByUsernameRequestClass", ProfileByUsernameRequest.class);
|
|
||||||
bindings.put("ProfileByUUIDRequestClass", ProfileByUUIDRequest.class);
|
|
||||||
bindings.put("BatchProfileByUsernameRequestClass", BatchProfileByUsernameRequest.class);
|
|
||||||
bindings.put("GetAvailabilityAuthRequestClass", GetAvailabilityAuthRequest.class);
|
|
||||||
|
|
||||||
// Set hasher class bindings
|
|
||||||
bindings.put("FileNameMatcherClass", FileNameMatcher.class);
|
|
||||||
bindings.put("HashedDirClass", HashedDir.class);
|
|
||||||
bindings.put("HashedFileClass", HashedFile.class);
|
|
||||||
bindings.put("HashedEntryTypeClass", HashedEntry.Type.class);
|
|
||||||
|
|
||||||
// Set serialization class bindings
|
|
||||||
bindings.put("HInputClass", HInput.class);
|
|
||||||
bindings.put("HOutputClass", HOutput.class);
|
|
||||||
bindings.put("StreamObjectClass", StreamObject.class);
|
|
||||||
bindings.put("StreamObjectAdapterClass", StreamObject.Adapter.class);
|
|
||||||
bindings.put("SignedBytesHolderClass", SignedBytesHolder.class);
|
|
||||||
bindings.put("SignedObjectHolderClass", SignedObjectHolder.class);
|
|
||||||
bindings.put("EnumSerializerClass", EnumSerializer.class);
|
|
||||||
bindings.put("OptionalFileClass", OptionalFile.class);
|
|
||||||
bindings.put("UserSettingsClass", UserSettings.class);
|
|
||||||
|
|
||||||
// Set helper class bindings
|
|
||||||
bindings.put("CommonHelperClass", CommonHelper.class);
|
|
||||||
bindings.put("IOHelperClass", IOHelper.class);
|
|
||||||
bindings.put("EnvHelperClass", EnvHelper.class);
|
|
||||||
bindings.put("JVMHelperClass", JVMHelper.class);
|
|
||||||
bindings.put("JVMHelperOSClass", JVMHelper.OS.class);
|
|
||||||
bindings.put("LogHelperClass", LogHelper.class);
|
|
||||||
bindings.put("LogHelperOutputClass", LogHelper.Output.class);
|
|
||||||
bindings.put("SecurityHelperClass", SecurityHelper.class);
|
|
||||||
bindings.put("DigestAlgorithmClass", SecurityHelper.DigestAlgorithm.class);
|
|
||||||
bindings.put("VerifyHelperClass", VerifyHelper.class);
|
|
||||||
bindings.put("DirBridgeClass", DirBridge.class);
|
|
||||||
bindings.put("FunctionalBridgeClass", FunctionalBridge.class);
|
|
||||||
|
|
||||||
// Load JS API if available
|
|
||||||
try {
|
|
||||||
Class.forName("javafx.application.Application");
|
|
||||||
bindings.put("JSApplicationClass", JSApplication.class);
|
|
||||||
} catch (ClassNotFoundException ignored) {
|
|
||||||
LogHelper.warning("JavaFX API isn't available");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public Object loadScript(String path) throws IOException, ScriptException {
|
|
||||||
URL url = Launcher.getResourceURL(path);
|
|
||||||
LogHelper.debug("Loading script: '%s'", url);
|
|
||||||
try (BufferedReader reader = IOHelper.newReader(url)) {
|
|
||||||
return engine.eval(reader, engine.getBindings(ScriptContext.ENGINE_SCOPE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setScriptBindings() {
|
|
||||||
LogHelper.info("Setting up script engine bindings");
|
|
||||||
Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
|
|
||||||
bindings.put("launcher", this);
|
|
||||||
|
|
||||||
// Add launcher class bindings
|
|
||||||
JSRuntimeProvider.addLauncherClassBindings(bindings);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run(String[] args) throws ScriptException, NoSuchMethodException, IOException {
|
|
||||||
preLoad();
|
|
||||||
loadScript(Launcher.INIT_SCRIPT_FILE);
|
|
||||||
LogHelper.info("Invoking start() function");
|
|
||||||
Launcher.modulesManager.postInitModules();
|
|
||||||
((Invocable) engine).invokeFunction("start", (Object) args);
|
|
||||||
Launcher.modulesManager.finishModules();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void preLoad() throws IOException, ScriptException {
|
|
||||||
if (!isPreLoaded) {
|
|
||||||
loadScript(Launcher.API_SCRIPT_FILE);
|
|
||||||
loadScript(Launcher.CONFIG_SCRIPT_FILE);
|
|
||||||
isPreLoaded = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(boolean clientInstance) {
|
|
||||||
setScriptBindings();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
package ru.gravit.launcher.gui;
|
|
||||||
|
|
||||||
public interface RuntimeProvider {
|
|
||||||
void run(String[] args) throws Exception;
|
|
||||||
|
|
||||||
void preLoad() throws Exception;
|
|
||||||
|
|
||||||
void init(boolean clientInstance) throws Exception;
|
|
||||||
}
|
|
|
@ -1,141 +0,0 @@
|
||||||
package ru.gravit.launcher.hwid;
|
|
||||||
|
|
||||||
import oshi.SystemInfo;
|
|
||||||
import oshi.hardware.*;
|
|
||||||
import ru.gravit.launcher.HWID;
|
|
||||||
import ru.gravit.launcher.LauncherHWIDInterface;
|
|
||||||
import ru.gravit.launcher.OshiHWID;
|
|
||||||
import ru.gravit.utils.helper.LogHelper;
|
|
||||||
|
|
||||||
import java.net.NetworkInterface;
|
|
||||||
|
|
||||||
public class OshiHWIDProvider implements LauncherHWIDInterface {
|
|
||||||
public SystemInfo systemInfo;
|
|
||||||
public HardwareAbstractionLayer hardware;
|
|
||||||
public boolean noHWID;
|
|
||||||
|
|
||||||
public OshiHWIDProvider() {
|
|
||||||
try {
|
|
||||||
systemInfo = new SystemInfo();
|
|
||||||
noHWID = false;
|
|
||||||
} catch (Throwable e) {
|
|
||||||
LogHelper.error(e);
|
|
||||||
noHWID = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSerial() {
|
|
||||||
try {
|
|
||||||
if (hardware == null) hardware = systemInfo.getHardware();
|
|
||||||
return hardware.getComputerSystem().getSerialNumber();
|
|
||||||
} catch (Exception e) {
|
|
||||||
LogHelper.error(e);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getProcessorID() {
|
|
||||||
try {
|
|
||||||
if (hardware == null) hardware = systemInfo.getHardware();
|
|
||||||
return hardware.getProcessor().getProcessorID();
|
|
||||||
} catch (Exception e) {
|
|
||||||
LogHelper.error(e);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getHWDisk() {
|
|
||||||
try {
|
|
||||||
if (hardware == null) hardware = systemInfo.getHardware();
|
|
||||||
HWDiskStore store = null;
|
|
||||||
long size = 0;
|
|
||||||
for (HWDiskStore s : hardware.getDiskStores()) {
|
|
||||||
if (size < s.getSize()) {
|
|
||||||
store = s;
|
|
||||||
size = s.getSize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return store == null ? "" : store.getSerial();
|
|
||||||
} catch (Exception e) {
|
|
||||||
LogHelper.error(e);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSoundCardInfo() {
|
|
||||||
for (SoundCard soundcard : hardware.getSoundCards()) {
|
|
||||||
return soundcard.getName();
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMacAddr() {
|
|
||||||
for (NetworkIF networkIF : hardware.getNetworkIFs()) {
|
|
||||||
for (String ipv4 : networkIF.getIPv4addr()) {
|
|
||||||
if (ipv4.startsWith("127.")) continue;
|
|
||||||
if (ipv4.startsWith("10.")) continue;
|
|
||||||
return networkIF.getMacaddr();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getTotalMemory() {
|
|
||||||
if (noHWID) return 1024 << 20;
|
|
||||||
if (hardware == null) hardware = systemInfo.getHardware();
|
|
||||||
return hardware.getMemory().getTotal();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getAvailableMemory() {
|
|
||||||
if (noHWID) return -1;
|
|
||||||
if (hardware == null) hardware = systemInfo.getHardware();
|
|
||||||
return hardware.getMemory().getAvailable();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void printHardwareInformation() {
|
|
||||||
try {
|
|
||||||
if (hardware == null) hardware = systemInfo.getHardware();
|
|
||||||
ComputerSystem computerSystem = hardware.getComputerSystem();
|
|
||||||
LogHelper.debug("ComputerSystem Model: %s Serial: %s", computerSystem.getModel(), computerSystem.getSerialNumber());
|
|
||||||
for (HWDiskStore s : hardware.getDiskStores()) {
|
|
||||||
LogHelper.debug("HWDiskStore Serial: %s Model: %s Size: %d", s.getSerial(), s.getModel(), s.getSize());
|
|
||||||
}
|
|
||||||
for (UsbDevice s : hardware.getUsbDevices(true)) {
|
|
||||||
LogHelper.debug("USBDevice Serial: %s Name: %s", s.getSerialNumber(), s.getName());
|
|
||||||
}
|
|
||||||
for (NetworkIF networkIF : hardware.getNetworkIFs()) {
|
|
||||||
LogHelper.debug("Network Interface: %s mac: %s", networkIF.getName(), networkIF.getMacaddr());
|
|
||||||
NetworkInterface network = networkIF.getNetworkInterface();
|
|
||||||
if (network.isLoopback() || network.isVirtual()) continue;
|
|
||||||
LogHelper.debug("Network Interface display: %s name: %s", network.getDisplayName(), network.getName());
|
|
||||||
for (String ipv4 : networkIF.getIPv4addr()) {
|
|
||||||
if (ipv4.startsWith("127.")) continue;
|
|
||||||
if (ipv4.startsWith("10.")) continue;
|
|
||||||
LogHelper.subDebug("IPv4: %s", ipv4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (SoundCard soundcard : hardware.getSoundCards()) {
|
|
||||||
LogHelper.debug("SoundCard %s", soundcard.getName());
|
|
||||||
}
|
|
||||||
CentralProcessor processor = hardware.getProcessor();
|
|
||||||
LogHelper.debug("Processor Model: %s ID: %s", processor.getModel(), processor.getProcessorID());
|
|
||||||
} catch (Throwable e) {
|
|
||||||
LogHelper.error(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HWID getHWID() {
|
|
||||||
OshiHWID hwid = new OshiHWID();
|
|
||||||
hwid.serialNumber = getSerial();
|
|
||||||
hwid.totalMemory = getTotalMemory();
|
|
||||||
hwid.HWDiskSerial = getHWDisk();
|
|
||||||
hwid.processorID = getProcessorID();
|
|
||||||
hwid.macAddr = getMacAddr();
|
|
||||||
printHardwareInformation();
|
|
||||||
return hwid;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
package ru.gravit.launcher.managers;
|
|
||||||
|
|
||||||
import com.google.gson.GsonBuilder;
|
|
||||||
import ru.gravit.launcher.client.UserSettings;
|
|
||||||
import ru.gravit.utils.UniversalJsonAdapter;
|
|
||||||
|
|
||||||
public class ClientGsonManager extends GsonManager {
|
|
||||||
@Override
|
|
||||||
public void registerAdapters(GsonBuilder builder) {
|
|
||||||
super.registerAdapters(builder);
|
|
||||||
builder.registerTypeAdapter(UserSettings.class, new UniversalJsonAdapter<>(UserSettings.providers));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
package ru.gravit.launcher.managers;
|
|
||||||
|
|
||||||
import ru.gravit.launcher.console.UnlockCommand;
|
|
||||||
import ru.gravit.launcher.console.admin.ExecCommand;
|
|
||||||
import ru.gravit.launcher.console.admin.LogListenerCommand;
|
|
||||||
import ru.gravit.utils.command.BaseCommandCategory;
|
|
||||||
import ru.gravit.utils.command.CommandHandler;
|
|
||||||
import ru.gravit.utils.command.JLineCommandHandler;
|
|
||||||
import ru.gravit.utils.command.StdCommandHandler;
|
|
||||||
import ru.gravit.utils.command.basic.ClearCommand;
|
|
||||||
import ru.gravit.utils.command.basic.DebugCommand;
|
|
||||||
import ru.gravit.utils.command.basic.GCCommand;
|
|
||||||
import ru.gravit.utils.command.basic.HelpCommand;
|
|
||||||
import ru.gravit.utils.helper.CommonHelper;
|
|
||||||
import ru.gravit.utils.helper.LogHelper;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class ConsoleManager {
|
|
||||||
public static CommandHandler handler;
|
|
||||||
public static Thread thread;
|
|
||||||
|
|
||||||
public static void initConsole() throws IOException {
|
|
||||||
CommandHandler localCommandHandler;
|
|
||||||
try {
|
|
||||||
Class.forName("org.jline.terminal.Terminal");
|
|
||||||
|
|
||||||
// JLine2 available
|
|
||||||
localCommandHandler = new JLineCommandHandler();
|
|
||||||
LogHelper.info("JLine2 terminal enabled");
|
|
||||||
} catch (ClassNotFoundException ignored) {
|
|
||||||
localCommandHandler = new StdCommandHandler(true);
|
|
||||||
LogHelper.warning("JLine2 isn't in classpath, using std");
|
|
||||||
}
|
|
||||||
handler = localCommandHandler;
|
|
||||||
registerCommands();
|
|
||||||
thread = CommonHelper.newThread("Launcher Console", true, handler);
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void registerCommands() {
|
|
||||||
handler.registerCommand("help", new HelpCommand(handler));
|
|
||||||
handler.registerCommand("gc", new GCCommand());
|
|
||||||
handler.registerCommand("clear", new ClearCommand(handler));
|
|
||||||
handler.registerCommand("unlock", new UnlockCommand());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean checkUnlockKey(String key) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void unlock() {
|
|
||||||
handler.registerCommand("debug", new DebugCommand());
|
|
||||||
BaseCommandCategory admin = new BaseCommandCategory();
|
|
||||||
admin.registerCommand("exec", new ExecCommand());
|
|
||||||
admin.registerCommand("logListen", new LogListenerCommand());
|
|
||||||
handler.registerCategory(new CommandHandler.Category(admin, "admin", "Server admin commands"));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
package ru.gravit.launcher.managers;
|
|
||||||
|
|
||||||
public class HasherManager {
|
|
||||||
public static final HasherStore defaultStore = new HasherStore();
|
|
||||||
|
|
||||||
public static HasherStore getDefaultStore() {
|
|
||||||
return defaultStore;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
package ru.gravit.launcher.managers;
|
|
||||||
|
|
||||||
import ru.gravit.launcher.LauncherAPI;
|
|
||||||
import ru.gravit.launcher.hasher.FileNameMatcher;
|
|
||||||
import ru.gravit.launcher.hasher.HashedDir;
|
|
||||||
import ru.gravit.launcher.hasher.HashedEntry;
|
|
||||||
import ru.gravit.launcher.profiles.ClientProfile;
|
|
||||||
import ru.gravit.utils.helper.IOHelper;
|
|
||||||
import ru.gravit.utils.helper.LogHelper;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class HasherStore {
|
|
||||||
public Map<String, HasherStoreEnity> store;
|
|
||||||
|
|
||||||
public class HasherStoreEnity {
|
|
||||||
@LauncherAPI
|
|
||||||
public HashedDir hdir;
|
|
||||||
@LauncherAPI
|
|
||||||
public Path dir;
|
|
||||||
@LauncherAPI
|
|
||||||
public Collection<String> shared;
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public void addProfileUpdateDir(ClientProfile profile, Path dir, HashedDir hdir) {
|
|
||||||
HasherStoreEnity e = new HasherStoreEnity();
|
|
||||||
e.hdir = hdir;
|
|
||||||
e.dir = dir;
|
|
||||||
e.shared = profile.getShared();
|
|
||||||
|
|
||||||
store.put(profile.getTitle(), e);
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public void copyCompareFilesTo(String name, Path targetDir, HashedDir targetHDir, String[] shared) {
|
|
||||||
store.forEach((key, e) -> {
|
|
||||||
if (key.equals(name)) return;
|
|
||||||
FileNameMatcher nm = new FileNameMatcher(shared, null, null);
|
|
||||||
HashedDir compare = targetHDir.sideCompare(e.hdir, nm, new LinkedList<>(), true);
|
|
||||||
compare.map().forEach((arg1, arg2) -> recurseCopy(arg1, arg2, name, targetDir, e.dir));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public void recurseCopy(String filename, HashedEntry entry, String name, Path targetDir, Path sourceDir) {
|
|
||||||
if (!IOHelper.isDir(targetDir)) {
|
|
||||||
try {
|
|
||||||
Files.createDirectories(targetDir);
|
|
||||||
} catch (IOException e1) {
|
|
||||||
LogHelper.error(e1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (entry.getType().equals(HashedEntry.Type.DIR)) {
|
|
||||||
((HashedDir) entry).map().forEach((arg1, arg2) -> recurseCopy(arg1, arg2, name, targetDir.resolve(filename), sourceDir.resolve(filename)));
|
|
||||||
} else if (entry.getType().equals(HashedEntry.Type.FILE)) {
|
|
||||||
try {
|
|
||||||
IOHelper.copy(sourceDir.resolve(filename), targetDir.resolve(filename));
|
|
||||||
} catch (IOException e) {
|
|
||||||
LogHelper.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
package ru.gravit.launcher.managers;
|
|
||||||
|
|
||||||
import ru.gravit.launcher.LauncherAPI;
|
|
||||||
import ru.gravit.launcher.NewLauncherSettings;
|
|
||||||
import ru.gravit.launcher.client.DirBridge;
|
|
||||||
import ru.gravit.launcher.config.JsonConfigurable;
|
|
||||||
import ru.gravit.launcher.hasher.HashedDir;
|
|
||||||
import ru.gravit.launcher.serialize.HInput;
|
|
||||||
import ru.gravit.launcher.serialize.HOutput;
|
|
||||||
import ru.gravit.utils.helper.IOHelper;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.nio.file.*;
|
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
|
||||||
|
|
||||||
public class SettingsManager extends JsonConfigurable<NewLauncherSettings> {
|
|
||||||
public class StoreFileVisitor extends SimpleFileVisitor<Path> {
|
|
||||||
@Override
|
|
||||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
|
|
||||||
throws IOException {
|
|
||||||
try (HInput input = new HInput(IOHelper.newInput(file))) {
|
|
||||||
String dirName = input.readString(128);
|
|
||||||
String fullPath = input.readString(1024);
|
|
||||||
HashedDir dir = new HashedDir(input);
|
|
||||||
settings.lastHDirs.add(new NewLauncherSettings.HashedStoreEntry(dir, dirName, fullPath));
|
|
||||||
}
|
|
||||||
return super.visitFile(file, attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public static NewLauncherSettings settings;
|
|
||||||
|
|
||||||
public SettingsManager() {
|
|
||||||
super(NewLauncherSettings.class, DirBridge.dir.resolve("settings.json"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
@Override
|
|
||||||
public NewLauncherSettings getConfig() {
|
|
||||||
if (settings.updatesDir != null)
|
|
||||||
settings.updatesDirPath = settings.updatesDir.toString();
|
|
||||||
return settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
@Override
|
|
||||||
public NewLauncherSettings getDefaultConfig() {
|
|
||||||
return new NewLauncherSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
@Override
|
|
||||||
public void setConfig(NewLauncherSettings config) {
|
|
||||||
settings = config;
|
|
||||||
if (settings.updatesDirPath != null)
|
|
||||||
settings.updatesDir = Paths.get(settings.updatesDirPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public void loadHDirStore(Path storePath) throws IOException {
|
|
||||||
Files.createDirectories(storePath);
|
|
||||||
IOHelper.walk(storePath, new StoreFileVisitor(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public void saveHDirStore(Path storeProjectPath) throws IOException {
|
|
||||||
Files.createDirectories(storeProjectPath);
|
|
||||||
for (NewLauncherSettings.HashedStoreEntry e : settings.lastHDirs) {
|
|
||||||
Path file = storeProjectPath.resolve(e.name.concat(".bin"));
|
|
||||||
if (!Files.exists(file)) Files.createFile(file);
|
|
||||||
try (HOutput output = new HOutput(IOHelper.newOutput(file))) {
|
|
||||||
output.writeString(e.name, 128);
|
|
||||||
output.writeString(e.fullPath, 1024);
|
|
||||||
e.hdir.write(output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public void loadHDirStore() throws IOException {
|
|
||||||
loadHDirStore(DirBridge.dirStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public void saveHDirStore() throws IOException {
|
|
||||||
saveHDirStore(DirBridge.dirProjectStore);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setType(Type type) {
|
|
||||||
super.setType(type);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,149 +0,0 @@
|
||||||
package ru.gravit.launcher.utils;
|
|
||||||
|
|
||||||
import ru.gravit.launcher.LauncherAPI;
|
|
||||||
import ru.gravit.launcher.hasher.FileNameMatcher;
|
|
||||||
import ru.gravit.launcher.hasher.HashedDir;
|
|
||||||
import ru.gravit.launcher.hasher.HashedEntry;
|
|
||||||
import ru.gravit.launcher.hasher.HashedEntry.Type;
|
|
||||||
import ru.gravit.launcher.hasher.HashedFile;
|
|
||||||
import ru.gravit.utils.helper.IOHelper;
|
|
||||||
import ru.gravit.utils.helper.JVMHelper;
|
|
||||||
import ru.gravit.utils.helper.JVMHelper.OS;
|
|
||||||
import ru.gravit.utils.helper.LogHelper;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.*;
|
|
||||||
import java.nio.file.WatchEvent.Kind;
|
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
|
||||||
import java.util.Deque;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public final class DirWatcher implements Runnable, AutoCloseable {
|
|
||||||
private final class RegisterFileVisitor extends SimpleFileVisitor<Path> {
|
|
||||||
private final Deque<String> path = new LinkedList<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
|
|
||||||
FileVisitResult result = super.postVisitDirectory(dir, exc);
|
|
||||||
if (!DirWatcher.this.dir.equals(dir))
|
|
||||||
path.removeLast();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
|
|
||||||
FileVisitResult result = super.preVisitDirectory(dir, attrs);
|
|
||||||
if (DirWatcher.this.dir.equals(dir)) {
|
|
||||||
dir.register(service, KINDS);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Maybe it's unnecessary to go deeper
|
|
||||||
path.add(IOHelper.getFileName(dir));
|
|
||||||
//if (matcher != null && !matcher.shouldVerify(path)) {
|
|
||||||
// return FileVisitResult.SKIP_SUBTREE;
|
|
||||||
//}
|
|
||||||
|
|
||||||
// Register
|
|
||||||
dir.register(service, KINDS);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final boolean FILE_TREE_SUPPORTED = JVMHelper.OS_TYPE == OS.MUSTDIE;
|
|
||||||
|
|
||||||
// Constants
|
|
||||||
private static final Kind<?>[] KINDS = {
|
|
||||||
StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE
|
|
||||||
};
|
|
||||||
|
|
||||||
private static void handleError(Throwable e) {
|
|
||||||
LogHelper.error(e);
|
|
||||||
NativeJVMHalt.haltA(-123);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Deque<String> toPath(Iterable<Path> path) {
|
|
||||||
Deque<String> result = new LinkedList<>();
|
|
||||||
for (Path pe : path)
|
|
||||||
result.add(pe.toString());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instance
|
|
||||||
private final Path dir;
|
|
||||||
private final HashedDir hdir;
|
|
||||||
|
|
||||||
private final FileNameMatcher matcher;
|
|
||||||
|
|
||||||
private final WatchService service;
|
|
||||||
|
|
||||||
private final boolean digest;
|
|
||||||
|
|
||||||
@LauncherAPI
|
|
||||||
public DirWatcher(Path dir, HashedDir hdir, FileNameMatcher matcher, boolean digest) throws IOException {
|
|
||||||
this.dir = Objects.requireNonNull(dir, "dir");
|
|
||||||
this.hdir = Objects.requireNonNull(hdir, "hdir");
|
|
||||||
this.matcher = matcher;
|
|
||||||
this.digest = digest;
|
|
||||||
service = dir.getFileSystem().newWatchService();
|
|
||||||
|
|
||||||
// Register dirs recursively
|
|
||||||
IOHelper.walk(dir, new RegisterFileVisitor(), true);
|
|
||||||
LogHelper.subInfo("DirWatcher %s", dir.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@LauncherAPI
|
|
||||||
public void close() throws IOException {
|
|
||||||
service.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processKey(WatchKey key) throws IOException {
|
|
||||||
Path watchDir = (Path) key.watchable();
|
|
||||||
for (WatchEvent<?> event : key.pollEvents()) {
|
|
||||||
Kind<?> kind = event.kind();
|
|
||||||
if (kind.equals(StandardWatchEventKinds.OVERFLOW)) {
|
|
||||||
if (Boolean.getBoolean("launcher.dirwatcher.ignoreOverflows"))
|
|
||||||
continue; // Sometimes it's better to ignore than interrupt fair playing
|
|
||||||
throw new IOException("Overflow");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve paths and verify is not exclusion
|
|
||||||
Path path = watchDir.resolve((Path) event.context());
|
|
||||||
Deque<String> stringPath = toPath(dir.relativize(path));
|
|
||||||
if (matcher != null && !matcher.shouldVerify(stringPath))
|
|
||||||
continue; // Exclusion; should not be verified
|
|
||||||
// Verify is REALLY modified (not just attributes)
|
|
||||||
if (kind.equals(StandardWatchEventKinds.ENTRY_MODIFY)) {
|
|
||||||
HashedEntry entry = hdir.resolve(stringPath);
|
|
||||||
if (entry != null && (entry.getType() != Type.FILE || ((HashedFile) entry).isSame(path, digest)))
|
|
||||||
continue; // Modified attributes, not need to worry :D
|
|
||||||
}
|
|
||||||
|
|
||||||
// Forbidden modification!
|
|
||||||
throw new SecurityException(String.format("Forbidden modification (%s, %d times): '%s'", kind, event.count(), path));
|
|
||||||
}
|
|
||||||
key.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processLoop() throws IOException, InterruptedException {
|
|
||||||
LogHelper.debug("WatchService start processing");
|
|
||||||
while (!Thread.interrupted())
|
|
||||||
processKey(service.take());
|
|
||||||
LogHelper.debug("WatchService closed");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@LauncherAPI
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
processLoop();
|
|
||||||
} catch (InterruptedException | ClosedWatchServiceException ignored) {
|
|
||||||
LogHelper.debug("WatchService closed 2");
|
|
||||||
// Do nothing (closed etc)
|
|
||||||
} catch (Throwable exc) {
|
|
||||||
handleError(exc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
package ru.gravit.launcher.utils;
|
|
||||||
|
|
||||||
import cpw.mods.fml.SafeExitJVMLegacy;
|
|
||||||
import net.minecraftforge.fml.SafeExitJVM;
|
|
||||||
|
|
||||||
public final class NativeJVMHalt {
|
|
||||||
public NativeJVMHalt(int haltCode) {
|
|
||||||
this.haltCode = haltCode;
|
|
||||||
System.out.println("JVM exit code " + haltCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int haltCode;
|
|
||||||
|
|
||||||
public native void aaabbb38C_D();
|
|
||||||
|
|
||||||
@SuppressWarnings("null")
|
|
||||||
private boolean aaabBooleanC_D() {
|
|
||||||
return (boolean) (Boolean) null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void haltA(int code) {
|
|
||||||
NativeJVMHalt halt = new NativeJVMHalt(code);
|
|
||||||
try {
|
|
||||||
SafeExitJVMLegacy.exit(code);
|
|
||||||
} catch (Throwable ignored) {
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
SafeExitJVM.exit(code);
|
|
||||||
} catch (Throwable ignored) {
|
|
||||||
}
|
|
||||||
halt.aaabbb38C_D();
|
|
||||||
boolean a = halt.aaabBooleanC_D();
|
|
||||||
System.out.println(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean initFunc() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
2
Radon
2
Radon
|
@ -1 +1 @@
|
||||||
Subproject commit 60fa1c6694b570dda50056b1e2fe18fcdb0f8be0
|
Subproject commit 07581407e7b214b7dff8a256247dde62bba1697d
|
2
modules
2
modules
|
@ -1 +1 @@
|
||||||
Subproject commit b599517693c9d1c9a9045982ffa8068e1ed620c4
|
Subproject commit c338164496249a17eea2079ed560b09132831f35
|
Loading…
Reference in a new issue