fix update overlay

This commit is contained in:
DrLeonardo 2019-05-21 00:33:03 +03:00
parent a13cc2ed0f
commit b9ee6cbc49
37 changed files with 2 additions and 2707 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,7 +0,0 @@
package ru.gravit.launcher.client;
import ru.gravit.utils.ProviderMap;
public class UserSettings {
public static ProviderMap<UserSettings> providers = new ProviderMap<>();
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

@ -1 +1 @@
Subproject commit 60fa1c6694b570dda50056b1e2fe18fcdb0f8be0 Subproject commit 07581407e7b214b7dff8a256247dde62bba1697d

@ -1 +1 @@
Subproject commit b599517693c9d1c9a9045982ffa8068e1ed620c4 Subproject commit c338164496249a17eea2079ed560b09132831f35