diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f5cc7c9d..fc383772 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,41 +1,49 @@ -image: frekele/java +image: docker:latest +services: +- docker:dind + +variables: + DOCKER_DRIVER: overlay2 + CI_VERSION: '6.6.$CI_PIPELINE_IID' stages: - - build - - test - - deploy - -before_script: - # - echo `pwd` # debug - # - echo "$CI_BUILD_NAME, $CI_BUILD_REF_NAME $CI_BUILD_STAGE" # debug - - export GRADLE_USER_HOME=`pwd`/.gradle - - apt-get update -qq && apt-get install -y -qq git git-core - -cache: - key: ${CI_COMMIT_REF_NAME} - paths: - - .gradle/wrapper - - .gradle/caches +- build +- test build: + image: frekele/java stage: build + before_script: + - apt-get -y update + - apt-get -y install zip git + - export GRADLE_USER_HOME=`pwd`/.gradle + - chmod +x gradlew + - sed -i 's/git@github.com:/https:\/\/github.com\//' .gitmodules + - git submodule sync + - git submodule update --init --recursive script: - - sed -i 's/git@github.com:/https:\/\/github.com\//' .gitmodules - - git submodule sync - - git submodule update --init --recursive - - ./gradlew assemble - - mv LaunchServer/build/libs/*.jar LaunchServer - - mv ServerWrapper/build/libs/*.jar ServerWrapper - - mv modules/*_module/build/libs/*.jar modules - artifacts: + - ./gradlew assemble + after_script: + - mkdir -p artifacts/modules + - cd LaunchServer/build/libs/ + - zip -r -9 ../../../artifacts/libraries.zip * -x "LaunchServer.jar" -x "LaunchServer-clean.jar" + - mv LaunchServer.jar ../../../artifacts/LaunchServer.jar + - cd ../../../ServerWrapper/build/libs + - mv ServerWrapper.jar ../../../artifacts/ServerWrapper.jar + - cd ../../../ + - mv modules/*_module/build/libs/*.jar artifacts/modules + - mv modules/*_swmodule/build/libs/*.jar artifacts/modules + - mv modules/*_lmodule/build/libs/*.jar artifacts/modules + cache: paths: - - LaunchServer/*.jar - - ServerWrapper/*.jar - - modules/*.jar + - .gradle + artifacts: + expire_in: 6 week + paths: + - artifacts + test: + image: frekele/java stage: test script: - - ./gradlew check - -after_script: - - echo "End CI" + - ./gradlew check \ No newline at end of file diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/asm/ClassMetadataReader.java b/LaunchServer/src/main/java/pro/gravit/launchserver/asm/ClassMetadataReader.java index d347dea7..976bfb6d 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/asm/ClassMetadataReader.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/asm/ClassMetadataReader.java @@ -55,6 +55,15 @@ public void acceptVisitor(byte[] classData, ClassVisitor visitor) { public void acceptVisitor(String className, ClassVisitor visitor) throws IOException { acceptVisitor(getClassData(className), visitor); } + + public void acceptVisitor(byte[] classData, ClassVisitor visitor, int flags) { + new ClassReader(classData).accept(visitor, flags); + } + + public void acceptVisitor(String className, ClassVisitor visitor, int flags) throws IOException { + acceptVisitor(getClassData(className), visitor, flags); + } + public byte[] getClassData(String className) throws IOException { for (JarFile f : cp) { diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/asm/NodeUtils.java b/LaunchServer/src/main/java/pro/gravit/launchserver/asm/NodeUtils.java new file mode 100644 index 00000000..90ed2754 --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/asm/NodeUtils.java @@ -0,0 +1,169 @@ +package pro.gravit.launchserver.asm; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.AnnotationNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.InvokeDynamicInsnNode; +import org.objectweb.asm.tree.LdcInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; + +import pro.gravit.utils.helper.IOHelper; + +import static org.objectweb.asm.Opcodes.*; + +public final class NodeUtils { + private NodeUtils() { } + public static ClassNode forClass(Class cls, int flags) { + try (InputStream in = cls.getClassLoader().getResourceAsStream(cls.getName().replace('.', '/') + ".class")) { + ClassNode ret = new ClassNode(); + new ClassReader(IOHelper.read(in)).accept(ret, flags); + return ret; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static ClassNode forClass(String clazz, int flags, ClassMetadataReader r) { + try { + ClassNode ret = new ClassNode(); + r.acceptVisitor(clazz, ret, flags); + return ret; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static List annots(String clazz, String method, ClassMetadataReader r) { + if (clazz.startsWith("L")) clazz = Type.getType(clazz).getInternalName(); + try { + List ret = new ArrayList<>(); + ClassNode n = forClass(clazz, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG, r); + if (n.visibleAnnotations != null) ret.addAll(n.visibleAnnotations); + if (n.invisibleAnnotations != null) ret.addAll(n.invisibleAnnotations); + for (MethodNode m : n.methods) + if (method.equals(m.name)) { + if (m.visibleAnnotations != null) ret.addAll(m.visibleAnnotations); + if (m.invisibleAnnotations != null) ret.addAll(m.invisibleAnnotations); + } + return ret; + } catch (Throwable e) { + return Collections.emptyList(); + } + } + + private static int doMethodEmulation(String desc) { + int result = 0; + Type returnType = Type.getReturnType(desc); + + if (returnType.getSort() == Type.LONG || returnType.getSort() == Type.DOUBLE) + result++; + if (returnType.getSort() != Type.VOID) + result++; + + return result; + } + + public static int opcodeEmulation(AbstractInsnNode e) { + int stackSize = 0; + switch (e.getOpcode()) { + case NOP: + case LALOAD: // (index, arrayref) -> (long, long_top) + case DALOAD: // (index, arrayref) -> (double, double_top) + case SWAP: // (value1, value2) -> (value2, value1) + case INEG: + case LNEG: + case FNEG: + case DNEG: + case IINC: + case I2F: + case L2D: + case F2I: + case D2L: + case I2B: + case I2C: + case I2S: + case GOTO: + case RETURN: + case NEWARRAY: + case ANEWARRAY: + case ARRAYLENGTH: + case CHECKCAST: + case INSTANCEOF: + // Does nothing + break; + case ACONST_NULL: + case ICONST_M1: + case ICONST_0: + case ICONST_1: + case ICONST_2: + case ICONST_3: + case ICONST_4: + case ICONST_5: + case FCONST_0: + case FCONST_1: + case FCONST_2: + case BIPUSH: + case SIPUSH: + case ILOAD: + case FLOAD: + case ALOAD: + case DUP: + case DUP_X1: + case DUP_X2: + case I2L: + case I2D: + case F2L: + case F2D: + case NEW: + // Pushes one-word constant to stack + stackSize++; + break; + case LDC: + LdcInsnNode ldc = (LdcInsnNode) e; + if (ldc.cst instanceof Long || ldc.cst instanceof Double) + stackSize++; + + stackSize++; + break; + case LCONST_0: + case LCONST_1: + case DCONST_0: + case DCONST_1: + case LLOAD: + case DLOAD: + case DUP2: + case DUP2_X1: + case DUP2_X2: + // Pushes two-word constant or two one-word constants to stack + stackSize++; + stackSize++; + break; + case INVOKEVIRTUAL: + case INVOKESPECIAL: + case INVOKEINTERFACE: + stackSize += doMethodEmulation(((MethodInsnNode) e).desc); + break; + case INVOKESTATIC: + stackSize += doMethodEmulation(((MethodInsnNode) e).desc); + break; + case INVOKEDYNAMIC: + stackSize += doMethodEmulation(((InvokeDynamicInsnNode) e).desc); + break; + case JSR: + case RET: + throw new RuntimeException("Did not expect JSR/RET instructions"); + default: + break; + } + return stackSize; + } +} diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/binary/tasks/AdditionalFixesApplyTask.java b/LaunchServer/src/main/java/pro/gravit/launchserver/binary/tasks/AdditionalFixesApplyTask.java index 8dcd35e0..cc409840 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/binary/tasks/AdditionalFixesApplyTask.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/binary/tasks/AdditionalFixesApplyTask.java @@ -77,7 +77,7 @@ public static void apply(Path inputFile, Path addFile, ZipOutputStream output, L try { bytes = classFix(bytes, reader, srv.config.launcher.stripLineNumbers); } catch (Throwable t) { - LogHelper.subWarning("Error on fixing class: " + t); + LogHelper.error(t); } output.write(bytes); } else diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/command/dao/GetUserCommand.java b/LaunchServer/src/main/java/pro/gravit/launchserver/command/dao/GetUserCommand.java index d5788850..3956f6f1 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/command/dao/GetUserCommand.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/command/dao/GetUserCommand.java @@ -3,7 +3,6 @@ import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.command.Command; import pro.gravit.launchserver.dao.User; -import pro.gravit.launchserver.dao.UserHWID; import pro.gravit.utils.helper.LogHelper; public class GetUserCommand extends Command { @@ -31,9 +30,9 @@ public void invoke(String... args) throws Exception { return; } LogHelper.info("[%s] UUID: %s", user.username, user.uuid.toString()); - for(UserHWID hwid : user.hwids) - { - LogHelper.info("[%s] HWID: memory: %d | serial %s | hwdiskserial: %s | processorID %s | macAddr %s", user.username, hwid.totalMemory, hwid.serialNumber, hwid.HWDiskSerial, hwid.processorID, hwid.macAddr); - } + //for(UserHWID hwid : user.hwids) + //{ + // LogHelper.info("[%s] HWID: memory: %d | serial %s | hwdiskserial: %s | processorID %s | macAddr %s", user.username, hwid.totalMemory, hwid.serialNumber, hwid.HWDiskSerial, hwid.processorID, hwid.macAddr); + //} } } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/dao/User.java b/LaunchServer/src/main/java/pro/gravit/launchserver/dao/User.java index 6dedae53..ee09f5b5 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/dao/User.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/dao/User.java @@ -4,18 +4,13 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; -import java.util.Collection; import java.util.UUID; -import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; -import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.OneToMany; import javax.persistence.Table; import pro.gravit.launcher.ClientPermissions; @@ -39,12 +34,6 @@ public class User { public String serverID; private String password_salt; public long permissions; - //TODO: заменить EAGER на LASY и придумать способ сохранить сессию - // [ERROR] org.hibernate.LazyInitializationException: - // failed to lazily initialize a collection of role: pro.gravit.launchserver.dao.User.hwids, could not initialize proxy - no Session - @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) - @JoinColumn(name = "user_id") - public Collection hwids; public void setPassword(String password) { password_salt = SecurityHelper.randomStringAESKey(); diff --git a/LaunchServer/src/main/resources/pro/gravit/launchserver/defaults/profile1.14.4.cfg b/LaunchServer/src/main/resources/pro/gravit/launchserver/defaults/profile1.14.4.cfg new file mode 100644 index 00000000..e5ca1b4b --- /dev/null +++ b/LaunchServer/src/main/resources/pro/gravit/launchserver/defaults/profile1.14.4.cfg @@ -0,0 +1,40 @@ +{ + "version": "1.14.4", + "assetIndex": "1.14.4", + "assetDir": "asset1.14.4", + "dir": "HiTech", + "info": "Информация о сервере", + "sortIndex": 0, + "title": "xxxxxxxx", + "serverAddress": "localhost", + "serverPort": 25565, + "update": [ + "servers.dat" + ], + "updateExclusions": [], + "updateShared": [], + "updateVerify": [ + "libraries", + "natives", + "minecraft.jar" + ], + "updateOptional": [], + "updateFastCheck": true, + "useWhitelist": false, + "mainClass": "net.minecraft.client.main.Main", + "jvmArgs": [ + "-Dfml.ignorePatchDiscrepancies=true", + "-Dfml.ignoreInvalidMinecraftCertificates=rue", + "-XX:+UseConcMarkSweepGC", + "-XX:+CMSIncrementalMode", + "-XX:-UseAdaptiveSizePolicy", + "-Xmn128M", + "-XX:+DisableAttachMechanism" + ], + "classPath": [ + "minecraft.jar", + "libraries" + ], + "clientArgs": [], + "whitelist": [] +} diff --git a/LaunchServer/src/main/resources/pro/gravit/launchserver/defaults/proguard.cfg b/LaunchServer/src/main/resources/pro/gravit/launchserver/defaults/proguard.cfg index 0f109a1d..5a4eb1ef 100644 --- a/LaunchServer/src/main/resources/pro/gravit/launchserver/defaults/proguard.cfg +++ b/LaunchServer/src/main/resources/pro/gravit/launchserver/defaults/proguard.cfg @@ -20,9 +20,9 @@ -keepattributes Signature -adaptresourcefilecontents META-INF/MANIFEST.MF --keeppackagenames com.mojang.**,net.minecraftforge.fml.**,cpw.mods.fml.**,com.google.gson.**,pro.gravit.repackage.**,org.fusesource.** +-keeppackagenames com.mojang.**,net.minecraftforge.fml.**,cpw.mods.fml.**,com.google.gson.**,pro.gravit.repackage.**,org.fusesource.**, pro.gravit.launcher.api.** --keep class com.mojang.**,net.minecraftforge.fml.**,cpw.mods.fml.**,com.google.gson.**,pro.gravit.repackage.**,org.fusesource.** { +-keep class com.mojang.**,net.minecraftforge.fml.**,cpw.mods.fml.**,com.google.gson.**,pro.gravit.repackage.**,org.fusesource.**, pro.gravit.launcher.api.** { *; } diff --git a/Launcher/src/main/java/pro/gravit/launcher/ClientLauncherWrapper.java b/Launcher/src/main/java/pro/gravit/launcher/ClientLauncherWrapper.java index 97ec06bc..aaeba1ec 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/ClientLauncherWrapper.java +++ b/Launcher/src/main/java/pro/gravit/launcher/ClientLauncherWrapper.java @@ -1,5 +1,6 @@ package pro.gravit.launcher; +import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; @@ -8,6 +9,7 @@ import java.util.List; import pro.gravit.launcher.client.ClientLauncher; +import pro.gravit.launcher.client.ClientModuleManager; import pro.gravit.launcher.client.DirBridge; import pro.gravit.utils.helper.EnvHelper; import pro.gravit.utils.helper.IOHelper; @@ -17,7 +19,9 @@ public class ClientLauncherWrapper { public static final String MAGIC_ARG = "-Djdk.attach.allowAttachSelf"; public static final String WAIT_PROCESS_PROPERTY = "launcher.waitProcess"; + public static final String NO_JAVA9_CHECK_PROPERTY = "launcher.noJava9Check"; public static boolean waitProcess = Boolean.getBoolean(WAIT_PROCESS_PROPERTY); + public static boolean noJava9check = Boolean.getBoolean(NO_JAVA9_CHECK_PROPERTY); public static void main(String[] arguments) throws IOException, InterruptedException { LogHelper.printVersion("Launcher"); @@ -26,6 +30,9 @@ public static void main(String[] arguments) throws IOException, InterruptedExcep JVMHelper.verifySystemProperties(Launcher.class, true); EnvHelper.checkDangerousParams(); LauncherConfig config = Launcher.getConfig(); + LauncherEngine.modulesManager = new ClientModuleManager(); + LauncherConfig.getAutogenConfig().initModules(); + LogHelper.info("Launcher for project %s", config.projectname); if (config.environment.equals(LauncherConfig.LauncherEnvironment.PROD)) { if (System.getProperty(LogHelper.DEBUG_PROPERTY) != null) { @@ -53,12 +60,34 @@ public static void main(String[] arguments) throws IOException, InterruptedExcep JVMHelper.addSystemPropertyToArgs(args, DirBridge.CUSTOMDIR_PROPERTY); JVMHelper.addSystemPropertyToArgs(args, DirBridge.USE_CUSTOMDIR_PROPERTY); JVMHelper.addSystemPropertyToArgs(args, DirBridge.USE_OPTDIR_PROPERTY); + if (!noJava9check && !System.getProperty("java.version").startsWith("1.8")) + { + LogHelper.debug("Found Java 9+ ( %s )", System.getProperty("java.version")); + Collections.addAll(args, "--add-modules"); + Collections.addAll(args, "javafx.base,javafx.fxml,javafx.controls,jdk.unsupported"); + Path jvmDir = Paths.get(System.getProperty("java.home")); + String pathToFx = System.getenv("PATH_TO_FX"); + Path fxPath = pathToFx == null ? null : Paths.get(pathToFx); + StringBuilder builder = new StringBuilder(); + Path[] findPath = new Path[]{jvmDir, fxPath}; + tryAddModule(findPath, "javafx.base", builder); + tryAddModule(findPath, "javafx.graphics", builder); + tryAddModule(findPath, "javafx.fxml", builder); + tryAddModule(findPath, "javafx.controls", builder); + String modulePath = builder.toString(); + if(!modulePath.isEmpty()) + { + Collections.addAll(args, "--module-path"); + Collections.addAll(args, modulePath); + } + } Collections.addAll(args, MAGIC_ARG); Collections.addAll(args, "-XX:+DisableAttachMechanism"); - Collections.addAll(args, "-javaagent:".concat(pathLauncher).concat("=pr")); + Collections.addAll(args, "-javaagent:".concat(pathLauncher)); Collections.addAll(args, "-cp"); Collections.addAll(args, pathLauncher); Collections.addAll(args, LauncherEngine.class.getName()); + LauncherEngine.modulesManager.callWrapper(processBuilder, args); EnvHelper.addEnv(processBuilder); LogHelper.debug("Commandline: " + args); processBuilder.command(args); @@ -78,4 +107,30 @@ public static void main(String[] arguments) throws IOException, InterruptedExcep process.waitFor(); } } + public static Path tryFindModule(Path path, String moduleName) + { + Path result = path.resolve(moduleName.concat(".jar")); + LogHelper.dev("Try resolve %s", result.toString()); + if(!IOHelper.isFile(result)) + result = path.resolve("lib").resolve(moduleName.concat(".jar")); + else return result; + if(!IOHelper.isFile(result)) + return null; + else return result; + } + public static boolean tryAddModule(Path[] paths, String moduleName, StringBuilder args) + { + for(Path path : paths) + { + if(path == null) continue; + Path result = tryFindModule(path, moduleName); + if(result != null) + { + if(args.length() != 0) args.append(File.pathSeparatorChar); + args.append(result.toAbsolutePath().toString()); + return true; + } + } + return false; + } } diff --git a/Launcher/src/main/java/pro/gravit/launcher/LauncherAgent.java b/Launcher/src/main/java/pro/gravit/launcher/LauncherAgent.java index 01122aff..0d5d034e 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/LauncherAgent.java +++ b/Launcher/src/main/java/pro/gravit/launcher/LauncherAgent.java @@ -1,29 +1,14 @@ package pro.gravit.launcher; -import static org.objectweb.asm.Opcodes.ACONST_NULL; -import static org.objectweb.asm.Opcodes.ARETURN; - -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.lang.instrument.Instrumentation; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; import java.util.jar.JarFile; -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 cpw.mods.fml.SafeExitJVMLegacy; import net.minecraftforge.fml.SafeExitJVM; import pro.gravit.launcher.utils.NativeJVMHalt; -import pro.gravit.utils.helper.JVMHelper; import pro.gravit.utils.helper.LogHelper; @LauncherAPI @@ -52,138 +37,16 @@ public static void premain(String agentArgument, Instrumentation instrumentation SafeExitJVM.class.getName(); NativeJVMHalt.class.getName(); NativeJVMHalt.initFunc(); - isAgentStarted = true; - if (System.getProperty("java.vm.name").toUpperCase(Locale.US).contains("HOTSPOT")) - try { - if (JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE) { - 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; - } - } - replaceClasses(pb, rt); - } else replaceClasses(false, false); - } catch (Error e) { - NativeJVMHalt.haltA(294); - throw e; - } + boolean bad = false; + try { + for (StackTraceElement e : new Throwable().getStackTrace()) + if (Class.forName(e.getClassName()).getClassLoader() != Runtime.class.getClassLoader() && Class.forName(e.getClassName()) != LauncherAgent.class) bad = true; + } catch(Throwable e) { bad = true; } + if (bad) NativeJVMHalt.haltA(-17); + else isAgentStarted = true; } public static boolean isStarted() { return isAgentStarted; } - - private static void replaceClasses(boolean pb, boolean rt) { - java.awt.Robot.class.getName(); - List 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; - byte[] buffer = new byte[8192]; - while ((r = is.read(buffer)) >= 0) { - baos.write(buffer, 0, r); - } - return baos.toByteArray(); - } - } } diff --git a/Launcher/src/main/java/pro/gravit/launcher/api/AuthService.java b/Launcher/src/main/java/pro/gravit/launcher/api/AuthService.java new file mode 100644 index 00000000..d6d8da59 --- /dev/null +++ b/Launcher/src/main/java/pro/gravit/launcher/api/AuthService.java @@ -0,0 +1,19 @@ +package pro.gravit.launcher.api; + +import pro.gravit.launcher.ClientPermissions; + +import java.util.UUID; + +public class AuthService { + public static String username; + public static ClientPermissions permissions = new ClientPermissions(); + public static UUID uuid; + public static boolean isAdmin() + { + return permissions.canAdmin; + } + public static boolean isServer() + { + return permissions.canServer; + } +} diff --git a/Launcher/src/main/java/pro/gravit/launcher/api/ClientService.java b/Launcher/src/main/java/pro/gravit/launcher/api/ClientService.java new file mode 100644 index 00000000..be931ec8 --- /dev/null +++ b/Launcher/src/main/java/pro/gravit/launcher/api/ClientService.java @@ -0,0 +1,10 @@ +package pro.gravit.launcher.api; + +import java.lang.instrument.Instrumentation; +import java.net.URL; + +public class ClientService { + public static Instrumentation instrumentation; + public static ClassLoader classLoader; + public static URL[] baseURLs; +} diff --git a/Launcher/src/main/java/pro/gravit/launcher/client/ClientLauncher.java b/Launcher/src/main/java/pro/gravit/launcher/client/ClientLauncher.java index c0fb76a8..0ad7b597 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/client/ClientLauncher.java +++ b/Launcher/src/main/java/pro/gravit/launcher/client/ClientLauncher.java @@ -32,7 +32,11 @@ import pro.gravit.launcher.LauncherAgent; import pro.gravit.launcher.LauncherConfig; import pro.gravit.launcher.LauncherEngine; +import pro.gravit.launcher.api.AuthService; +import pro.gravit.launcher.api.ClientService; +import pro.gravit.launcher.client.events.ClientLaunchPhase; import pro.gravit.launcher.client.events.ClientLauncherInitPhase; +import pro.gravit.launcher.client.events.ClientLauncherPostInitPhase; import pro.gravit.launcher.guard.LauncherGuardManager; import pro.gravit.launcher.gui.JSRuntimeProvider; import pro.gravit.launcher.hasher.FileNameMatcher; @@ -40,7 +44,6 @@ import pro.gravit.launcher.hwid.HWIDProvider; import pro.gravit.launcher.managers.ClientGsonManager; import pro.gravit.launcher.managers.ClientHookManager; -import pro.gravit.launcher.modules.events.PostInitPhase; import pro.gravit.launcher.modules.events.PreConfigPhase; import pro.gravit.launcher.profiles.ClientProfile; import pro.gravit.launcher.profiles.PlayerProfile; @@ -51,6 +54,7 @@ import pro.gravit.launcher.serialize.HOutput; import pro.gravit.launcher.serialize.stream.StreamObject; import pro.gravit.launcher.utils.DirWatcher; +import pro.gravit.launcher.utils.NativeJVMHalt; import pro.gravit.utils.PublicURLClassLoader; import pro.gravit.utils.Version; import pro.gravit.utils.helper.CommonHelper; @@ -300,11 +304,11 @@ private static void launch(ClientProfile profile, Params params) throws Throwabl LogHelper.debug("Args: " + copy); // Resolve main class and method Class mainClass = classLoader.loadClass(profile.getMainClass()); - MethodHandle mainMethod = MethodHandles.publicLookup().findStatic(mainClass, "main", MethodType.methodType(void.class, String[].class)); + MethodHandle mainMethod = MethodHandles.publicLookup().findStatic(mainClass, "main", MethodType.methodType(void.class, String[].class)).asFixedArity(); Launcher.LAUNCHED.set(true); JVMHelper.fullGC(); // Invoke main method - mainMethod.invoke((Object) args.toArray(new String[0])); + mainMethod.invokeWithArguments((Object)args.toArray(new String[0])); } private static Process process = null; @@ -410,9 +414,16 @@ public static Process launch( builder.redirectErrorStream(true); builder.redirectOutput(Redirect.PIPE); } + List command = builder.command(); // Let's rock! ClientHookManager.preStartHook.hook(context, builder); process = builder.start(); + if (builder.command() != command) { + LogHelper.error("Something strange cheating..."); + System.exit(100); + clientStarted = false; + return null; + } if(ClientHookManager.postStartHook.hook(context, builder)) return process; if (!pipeOutput) { for (int i = 0; i < 50; ++i) { @@ -434,6 +445,21 @@ public static Process launch( clientStarted = false; return process; } + public static class ClientLaunchContext + { + public final Params params; + public final ClientProfile profile; + public final HashedDir assetHDir, clientHDir; + public DirWatcher assetWatcher, clientWatcher; + + + public ClientLaunchContext(Params params, ClientProfile profile, HashedDir assetHDir, HashedDir clientHDir) { + this.params = params; + this.profile = profile; + this.assetHDir = assetHDir; + this.clientHDir = clientHDir; + } + } @LauncherAPI public static void main(String... args) throws Throwable { @@ -444,6 +470,10 @@ public static void main(String... args) throws Throwable { LauncherEngine.modulesManager.initModules(null); initGson(LauncherEngine.modulesManager); //Launcher.modulesManager.preInitModules(); + if (!LauncherAgent.isStarted()) { + NativeJVMHalt.haltA(100); + return; + } LauncherEngine.modulesManager.invokeEvent(new PreConfigPhase()); JVMHelper.verifySystemProperties(ClientLauncher.class, true); EnvHelper.checkDangerousParams(); @@ -474,11 +504,12 @@ public static void main(String... args) throws Throwable { System.exit(-98); return; } + ClientLaunchContext context = new ClientLaunchContext(params, profile, assetHDir, clientHDir); Launcher.profile = profile; playerProfile = params.pp; Request.setSession(params.session); checkJVMBitsAndVersion(); - LauncherEngine.modulesManager.invokeEvent(new ClientLauncherInitPhase()); + LauncherEngine.modulesManager.invokeEvent(new ClientLauncherInitPhase(context)); // Verify ClientLauncher sign and classpath LogHelper.debug("Verifying ClientLauncher sign and classpath"); LinkedList classPath = resolveClassPathList(params.clientDir, profile.getClassPath()); @@ -518,6 +549,11 @@ public static void main(String... args) throws Throwable { LogHelper.error(e); } }; + AuthService.username = params.pp.username; + AuthService.uuid = params.pp.uuid; + ClientService.instrumentation = LauncherAgent.inst; + ClientService.classLoader = classLoader; + ClientService.baseURLs = classpathurls; LogHelper.debug("Starting JVM and client WatchService"); FileNameMatcher assetMatcher = profile.getAssetUpdateMatcher(); FileNameMatcher clientMatcher = profile.getClientUpdateMatcher(); @@ -529,13 +565,16 @@ public static void main(String... args) throws Throwable { // if (params.updateOptional.contains(s)) s.mark = true; // else hdir.removeR(s.file); //} + context.assetWatcher = assetWatcher; + context.clientWatcher = clientWatcher; Launcher.profile.pushOptionalFile(clientHDir, false); - LauncherEngine.modulesManager.invokeEvent(new PostInitPhase()); + LauncherEngine.modulesManager.invokeEvent(new ClientLauncherPostInitPhase(context)); // 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); + LauncherEngine.modulesManager.invokeEvent(new ClientLaunchPhase(context)); launch(profile, params); } } diff --git a/Launcher/src/main/java/pro/gravit/launcher/client/ClientModuleManager.java b/Launcher/src/main/java/pro/gravit/launcher/client/ClientModuleManager.java index 90e66237..a5f6343f 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/client/ClientModuleManager.java +++ b/Launcher/src/main/java/pro/gravit/launcher/client/ClientModuleManager.java @@ -2,6 +2,7 @@ import java.io.IOException; import java.nio.file.Path; +import java.util.Collection; import pro.gravit.launcher.modules.LauncherModule; import pro.gravit.launcher.modules.impl.SimpleModuleManager; @@ -25,4 +26,14 @@ public void autoload(Path dir) throws IOException { public LauncherModule loadModule(Path file) throws IOException { throw new UnsupportedOperationException(); } + public void callWrapper(ProcessBuilder processBuilder, Collection jvmArgs) + { + for(LauncherModule module : modules) + { + if(module instanceof ClientWrapperModule) + { + ((ClientWrapperModule) module).wrapperPhase(processBuilder, jvmArgs); + } + } + } } diff --git a/Launcher/src/main/java/pro/gravit/launcher/client/ClientWrapperModule.java b/Launcher/src/main/java/pro/gravit/launcher/client/ClientWrapperModule.java new file mode 100644 index 00000000..4381e6e0 --- /dev/null +++ b/Launcher/src/main/java/pro/gravit/launcher/client/ClientWrapperModule.java @@ -0,0 +1,7 @@ +package pro.gravit.launcher.client; + +import java.util.Collection; + +public interface ClientWrapperModule { + void wrapperPhase(ProcessBuilder processBuilder, Collection jvmArgs); +} diff --git a/Launcher/src/main/java/pro/gravit/launcher/client/FunctionalBridge.java b/Launcher/src/main/java/pro/gravit/launcher/client/FunctionalBridge.java index efa11a15..560b5abe 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/client/FunctionalBridge.java +++ b/Launcher/src/main/java/pro/gravit/launcher/client/FunctionalBridge.java @@ -7,6 +7,7 @@ import java.util.concurrent.atomic.AtomicReference; import pro.gravit.launcher.LauncherAPI; +import pro.gravit.launcher.api.AuthService; import pro.gravit.launcher.events.request.AuthRequestEvent; import pro.gravit.launcher.guard.LauncherGuardManager; import pro.gravit.launcher.hasher.FileNameMatcher; @@ -100,6 +101,12 @@ public static void setAuthParams(AuthRequestEvent event) { Request.setSession(event.session); } LauncherGuardManager.guard.setProtectToken(event.protectToken); + AuthService.permissions = event.permissions; + if(event.playerProfile != null) + { + AuthService.username = event.playerProfile.username; + AuthService.uuid = event.playerProfile.uuid; + } } @FunctionalInterface diff --git a/Launcher/src/main/java/pro/gravit/launcher/client/LauncherUpdateController.java b/Launcher/src/main/java/pro/gravit/launcher/client/LauncherUpdateController.java index 7918a20a..51eb2939 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/client/LauncherUpdateController.java +++ b/Launcher/src/main/java/pro/gravit/launcher/client/LauncherUpdateController.java @@ -76,6 +76,7 @@ public void postDiff(UpdateRequest request, UpdateRequestEvent e, HashedDir.Diff LogHelper.debug("Copy file %s to %s", ret.toAbsolutePath().toString(), source.toAbsolutePath().toString()); } //Let's go! + Files.deleteIfExists(source); Files.copy(ret, source); try (InputStream input = IOHelper.newInput(ret)) { IOHelper.transfer(input, source); diff --git a/Launcher/src/main/java/pro/gravit/launcher/client/events/ClientLaunchPhase.java b/Launcher/src/main/java/pro/gravit/launcher/client/events/ClientLaunchPhase.java new file mode 100644 index 00000000..10db8ba1 --- /dev/null +++ b/Launcher/src/main/java/pro/gravit/launcher/client/events/ClientLaunchPhase.java @@ -0,0 +1,12 @@ +package pro.gravit.launcher.client.events; + +import pro.gravit.launcher.client.ClientLauncher; +import pro.gravit.launcher.modules.LauncherModule; + +public class ClientLaunchPhase extends LauncherModule.Event { + public final ClientLauncher.ClientLaunchContext context; + + public ClientLaunchPhase(ClientLauncher.ClientLaunchContext context) { + this.context = context; + } +} diff --git a/Launcher/src/main/java/pro/gravit/launcher/client/events/ClientLauncherInitPhase.java b/Launcher/src/main/java/pro/gravit/launcher/client/events/ClientLauncherInitPhase.java index 4f10e732..83eea342 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/client/events/ClientLauncherInitPhase.java +++ b/Launcher/src/main/java/pro/gravit/launcher/client/events/ClientLauncherInitPhase.java @@ -1,6 +1,12 @@ package pro.gravit.launcher.client.events; +import pro.gravit.launcher.client.ClientLauncher; import pro.gravit.launcher.modules.events.InitPhase; public class ClientLauncherInitPhase extends InitPhase { + public final ClientLauncher.ClientLaunchContext context; + + public ClientLauncherInitPhase(ClientLauncher.ClientLaunchContext context) { + this.context = context; + } } diff --git a/Launcher/src/main/java/pro/gravit/launcher/client/events/ClientLauncherPostInitPhase.java b/Launcher/src/main/java/pro/gravit/launcher/client/events/ClientLauncherPostInitPhase.java new file mode 100644 index 00000000..952ad735 --- /dev/null +++ b/Launcher/src/main/java/pro/gravit/launcher/client/events/ClientLauncherPostInitPhase.java @@ -0,0 +1,12 @@ +package pro.gravit.launcher.client.events; + +import pro.gravit.launcher.client.ClientLauncher; +import pro.gravit.launcher.modules.events.PostInitPhase; + +public class ClientLauncherPostInitPhase extends PostInitPhase { + public final ClientLauncher.ClientLaunchContext context; + + public ClientLauncherPostInitPhase(ClientLauncher.ClientLaunchContext context) { + this.context = context; + } +} diff --git a/Launcher/src/main/java/pro/gravit/launcher/guard/LauncherGravitGuard.java b/Launcher/src/main/java/pro/gravit/launcher/guard/LauncherGravitGuard.java index 6552a68a..4052673a 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/guard/LauncherGravitGuard.java +++ b/Launcher/src/main/java/pro/gravit/launcher/guard/LauncherGravitGuard.java @@ -23,19 +23,15 @@ public class LauncherGravitGuard implements LauncherGuardInterface { @Override public String getName() { - return "wrapper"; + return "gravitguard"; } @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 if (ClientLauncher.getJavaBinPath() != null) { javaBinPath = ClientLauncher.getJavaBinPath(); String projectName = Launcher.getConfig().projectname; - String wrapperUnpackName = JVMHelper.JVM_BITS == 64 ? projectName.concat("64.exe") : projectName.concat("32.exe"); + String wrapperUnpackName = ( javaBinPath == null ? JVMHelper.JVM_BITS : JVMHelper.OS_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"))); @@ -43,22 +39,26 @@ public Path getJavaBinPath() { @Override public int getClientJVMBits() { - return JVMHelper.JVM_BITS; + //При использовании GravitGuard без своей джавы + //Если при запуске лаунчера используется 32 бит джава, а ОС 64бит + //То в окне настроек будет отображаться >1.5Гб доступной памяти + //Однако при выставлении >1.5Гб JVM x32 работать откажеться + return JVMHelper.OS_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)); + UnpackHelper.unpack(Launcher.getResourceURL("wrapper32.exe", "guard"), DirBridge.getGuardDir().resolve(projectName.concat("64.exe"))); + UnpackHelper.unpack(Launcher.getResourceURL("AntiInject64.dll", "guard"), DirBridge.getGuardDir().resolve("AntiInject64.dll")); + + UnpackHelper.unpack(Launcher.getResourceURL("wrapper32.exe", "guard"), DirBridge.getGuardDir().resolve(projectName.concat("32.exe"))); + UnpackHelper.unpack(Launcher.getResourceURL("AntiInject32.dll", "guard"), DirBridge.getGuardDir().resolve("AntiInject32.dll")); } catch (IOException e) { throw new SecurityException(e); } - if (clientInstance) GravitGuardBridge.callGuard(); + if (clientInstance && JVMHelper.OS_TYPE == JVMHelper.OS.MUSTDIE) GravitGuardBridge.callGuard(); } @Override diff --git a/Launcher/src/main/java/pro/gravit/launcher/managers/SettingsManager.java b/Launcher/src/main/java/pro/gravit/launcher/managers/SettingsManager.java index 6df99215..fcc5a95c 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/managers/SettingsManager.java +++ b/Launcher/src/main/java/pro/gravit/launcher/managers/SettingsManager.java @@ -1,7 +1,6 @@ package pro.gravit.launcher.managers; import java.io.IOException; -import java.lang.reflect.Type; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; @@ -102,9 +101,4 @@ public void loadHDirStore() throws IOException { public void saveHDirStore() throws IOException { saveHDirStore(DirBridge.dirProjectStore); } - - @Override - public void setType(Type type) { - super.setType(type); - } } diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/config/JsonConfigurable.java b/LauncherAPI/src/main/java/pro/gravit/launcher/config/JsonConfigurable.java index 6972e023..90258efc 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/config/JsonConfigurable.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/config/JsonConfigurable.java @@ -1,29 +1,13 @@ package pro.gravit.launcher.config; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.IOException; import java.lang.reflect.Type; import java.nio.file.Path; -import pro.gravit.launcher.Launcher; import pro.gravit.launcher.LauncherAPI; -import pro.gravit.utils.helper.IOHelper; -import pro.gravit.utils.helper.LogHelper; -public abstract class JsonConfigurable { - private Type type; - protected Path configPath; - - @LauncherAPI - public void saveConfig() throws IOException { - saveConfig(configPath); - } - - @LauncherAPI - public void loadConfig() throws IOException { - loadConfig(configPath); - } +public abstract class JsonConfigurable implements JsonConfigurableInterface { + private transient final Type type; + protected transient final Path configPath; @LauncherAPI public JsonConfigurable(Type type, Path configPath) { @@ -31,55 +15,14 @@ public JsonConfigurable(Type type, Path configPath) { this.configPath = configPath; } - @LauncherAPI - public void saveConfig(Path configPath) throws IOException { - try (BufferedWriter writer = IOHelper.newWriter(configPath)) { - Launcher.gsonManager.configGson.toJson(getConfig(), type, writer); - } + @Override + public Path getPath() { + return configPath; } - @LauncherAPI - public void loadConfig(Path configPath) throws IOException { - if (generateConfigIfNotExists(configPath)) return; - try (BufferedReader reader = IOHelper.newReader(configPath)) { - setConfig(Launcher.gsonManager.configGson.fromJson(reader, type)); - } catch (Exception e) - { - LogHelper.error(e); - resetConfig(configPath); - } - } - - @LauncherAPI - public void resetConfig() throws IOException { - setConfig(getDefaultConfig()); - saveConfig(); - } - - @LauncherAPI - public void resetConfig(Path newPath) throws IOException { - setConfig(getDefaultConfig()); - saveConfig(newPath); - } - - @LauncherAPI - public boolean generateConfigIfNotExists(Path path) throws IOException { - if (IOHelper.isFile(path)) - return false; - resetConfig(path); - return true; - } - - @LauncherAPI - public boolean generateConfigIfNotExists() throws IOException { - if (IOHelper.isFile(configPath)) - return false; - resetConfig(); - return true; - } - - protected void setType(Type type) { - this.type = type; + @Override + public Type getType() { + return type; } @LauncherAPI diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/config/JsonConfigurableInterface.java b/LauncherAPI/src/main/java/pro/gravit/launcher/config/JsonConfigurableInterface.java new file mode 100644 index 00000000..7b80756e --- /dev/null +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/config/JsonConfigurableInterface.java @@ -0,0 +1,84 @@ +package pro.gravit.launcher.config; + +import com.google.gson.Gson; +import pro.gravit.launcher.Launcher; +import pro.gravit.launcher.LauncherAPI; +import pro.gravit.utils.helper.IOHelper; +import pro.gravit.utils.helper.LogHelper; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.lang.reflect.Type; +import java.nio.file.Path; + +public interface JsonConfigurableInterface { + @LauncherAPI + default void saveConfig() throws IOException { + saveConfig(getPath()); + } + @LauncherAPI + default void loadConfig() throws IOException { + loadConfig(getPath()); + } + @LauncherAPI + default void saveConfig(Gson gson, Path configPath) throws IOException { + try (BufferedWriter writer = IOHelper.newWriter(configPath)) { + gson.toJson(getConfig(), getType(), writer); + } + } + @LauncherAPI + default void loadConfig(Gson gson, Path configPath) throws IOException { + if (generateConfigIfNotExists(configPath)) return; + try (BufferedReader reader = IOHelper.newReader(configPath)) { + setConfig(gson.fromJson(reader, getType())); + } catch (Exception e) + { + LogHelper.error(e); + resetConfig(configPath); + } + } + @LauncherAPI + default void saveConfig(Path configPath) throws IOException { + saveConfig(Launcher.gsonManager.configGson, configPath); + } + @LauncherAPI + default void loadConfig(Path configPath) throws IOException { + loadConfig(Launcher.gsonManager.configGson, configPath); + } + @LauncherAPI + default void resetConfig() throws IOException { + setConfig(getDefaultConfig()); + saveConfig(); + } + @LauncherAPI + default void resetConfig(Path newPath) throws IOException { + setConfig(getDefaultConfig()); + saveConfig(newPath); + } + @LauncherAPI + default boolean generateConfigIfNotExists(Path path) throws IOException { + if (IOHelper.isFile(path)) + return false; + resetConfig(path); + return true; + } + @LauncherAPI + default boolean generateConfigIfNotExists() throws IOException { + if (IOHelper.isFile(getPath())) + return false; + resetConfig(); + return true; + } + @LauncherAPI + T getConfig(); + @LauncherAPI + T getDefaultConfig(); + @LauncherAPI + void setConfig(T config); + @LauncherAPI + Path getPath(); + + Type getType(); + +} diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/config/SimpleConfig.java b/LauncherAPI/src/main/java/pro/gravit/launcher/config/SimpleConfig.java new file mode 100644 index 00000000..764cab2b --- /dev/null +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/config/SimpleConfig.java @@ -0,0 +1,39 @@ +package pro.gravit.launcher.config; + +import java.lang.reflect.Type; +import java.nio.file.Path; + +public abstract class SimpleConfig implements JsonConfigurableInterface { + private transient final Class type; + protected transient final Path configPath; + + protected SimpleConfig(Class type, Path configPath) { + this.type = type; + this.configPath = configPath; + } + @SuppressWarnings("unchecked") + @Override + public T getConfig() { + return (T) this; + } + + @Override + public T getDefaultConfig() { + try { + return type.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + return null; + } + } + + @Override + public Path getPath() { + return configPath; + } + + @Override + public Type getType() { + return type; + } + +} diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/config/SimpleConfigurable.java b/LauncherAPI/src/main/java/pro/gravit/launcher/config/SimpleConfigurable.java new file mode 100644 index 00000000..5027e9eb --- /dev/null +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/config/SimpleConfigurable.java @@ -0,0 +1,32 @@ +package pro.gravit.launcher.config; + +import java.nio.file.Path; + +public class SimpleConfigurable extends JsonConfigurable { + public T config; + private final Class tClass; + + public SimpleConfigurable(Class type, Path configPath) { + super(type, configPath); + tClass = type; + } + + @Override + public T getConfig() { + return config; + } + + @Override + public T getDefaultConfig() { + try { + return tClass.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + return null; + } + } + + @Override + public void setConfig(T config) { + this.config = config; + } +} diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/managers/SimpleModulesConfigManager.java b/LauncherAPI/src/main/java/pro/gravit/launcher/managers/SimpleModulesConfigManager.java index 54b1754c..a5379675 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/managers/SimpleModulesConfigManager.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/managers/SimpleModulesConfigManager.java @@ -4,6 +4,7 @@ import java.nio.file.Files; import java.nio.file.Path; +import pro.gravit.launcher.config.SimpleConfigurable; import pro.gravit.launcher.modules.ModulesConfigManager; import pro.gravit.utils.helper.IOHelper; import pro.gravit.utils.helper.LogHelper; @@ -34,4 +35,9 @@ public Path getModuleConfigDir(String moduleName) { } return configDir.resolve(moduleName); } + + @Override + public SimpleConfigurable getConfigurable(Class tClass, Path configPath) { + return new SimpleConfigurable<>(tClass, configPath); + } } diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/modules/ModulesConfigManager.java b/LauncherAPI/src/main/java/pro/gravit/launcher/modules/ModulesConfigManager.java index 3d5522e5..1016ac14 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/modules/ModulesConfigManager.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/modules/ModulesConfigManager.java @@ -1,5 +1,7 @@ package pro.gravit.launcher.modules; +import pro.gravit.launcher.config.SimpleConfigurable; + import java.nio.file.Path; public interface ModulesConfigManager { @@ -8,4 +10,16 @@ public interface ModulesConfigManager { Path getModuleConfig(String moduleName, String configName); Path getModuleConfigDir(String moduleName); + + SimpleConfigurable getConfigurable(Class tClass, Path configPath); + + default SimpleConfigurable getConfigurable(Class tClass, String moduleName) + { + return getConfigurable(tClass, getModuleConfig(moduleName)); + } + + default SimpleConfigurable getConfigurable(Class tClass, String moduleName, String configName) + { + return getConfigurable(tClass, getModuleConfig(moduleName, configName)); + } } diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/request/update/LauncherRequest.java b/LauncherAPI/src/main/java/pro/gravit/launcher/request/update/LauncherRequest.java index adefd1cc..92d03509 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/request/update/LauncherRequest.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/request/update/LauncherRequest.java @@ -1,6 +1,8 @@ package pro.gravit.launcher.request.update; import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; @@ -25,6 +27,9 @@ public final class LauncherRequest extends Request impleme public int launcher_type = EXE_BINARY ? 2 : 1; @LauncherAPI public static final Path BINARY_PATH = IOHelper.getCodeSource(Launcher.class); + + @LauncherAPI + public static final Path C_BINARY_PATH = BINARY_PATH.getParent().resolve(IOHelper.getFileName(BINARY_PATH) + ".tmp"); @LauncherAPI public static final boolean EXE_BINARY = IOHelper.hasExtension(BINARY_PATH, "exe"); @@ -52,7 +57,12 @@ public static void update(LauncherRequestEvent result) throws IOException { }*/ try { ListDownloader downloader = new ListDownloader(); - downloader.downloadOne(result.url, BINARY_PATH); + Files.deleteIfExists(C_BINARY_PATH); + downloader.downloadOne(result.url, C_BINARY_PATH); + try (InputStream in = IOHelper.newInput(C_BINARY_PATH)) { + IOHelper.transfer(in, BINARY_PATH); + } + Files.deleteIfExists(C_BINARY_PATH); } catch (Throwable e) { LogHelper.error(e); } diff --git a/LauncherAuthlib/src/main/java/com/mojang/authlib/minecraft/MinecraftProfileTexture.java b/LauncherAuthlib/src/main/java/com/mojang/authlib/minecraft/MinecraftProfileTexture.java index 721b69b7..3926a6e1 100644 --- a/LauncherAuthlib/src/main/java/com/mojang/authlib/minecraft/MinecraftProfileTexture.java +++ b/LauncherAuthlib/src/main/java/com/mojang/authlib/minecraft/MinecraftProfileTexture.java @@ -4,7 +4,7 @@ import java.util.EnumSet; import java.util.Set; -public final class MinecraftProfileTexture { +public class MinecraftProfileTexture { public enum Type { SKIN, CAPE, diff --git a/LauncherAuthlib/src/main/java/com/mojang/authlib/yggdrasil/CompatBridge.java b/LauncherAuthlib/src/main/java/com/mojang/authlib/yggdrasil/CompatBridge.java index 3d89315e..67a77fb5 100644 --- a/LauncherAuthlib/src/main/java/com/mojang/authlib/yggdrasil/CompatBridge.java +++ b/LauncherAuthlib/src/main/java/com/mojang/authlib/yggdrasil/CompatBridge.java @@ -14,7 +14,7 @@ // Used to bypass Launcher's class name obfuscation and access API @LauncherAPI -public final class CompatBridge { +public class CompatBridge { public static final int PROFILES_MAX_BATCH_SIZE = SerializeLimits.MAX_BATCH_SIZE; public static CompatProfile checkServer(String username, String serverID) throws Exception { diff --git a/LauncherAuthlib/src/main/java/com/mojang/authlib/yggdrasil/CompatProfile.java b/LauncherAuthlib/src/main/java/com/mojang/authlib/yggdrasil/CompatProfile.java index 199ee209..56fb51e2 100644 --- a/LauncherAuthlib/src/main/java/com/mojang/authlib/yggdrasil/CompatProfile.java +++ b/LauncherAuthlib/src/main/java/com/mojang/authlib/yggdrasil/CompatProfile.java @@ -8,7 +8,7 @@ import pro.gravit.utils.helper.SecurityHelper; @LauncherAPI -public final class CompatProfile { +public class CompatProfile { public static final String SKIN_URL_PROPERTY = Launcher.SKIN_URL_PROPERTY; public static final String SKIN_DIGEST_PROPERTY = Launcher.SKIN_DIGEST_PROPERTY; public static final String CLOAK_URL_PROPERTY = Launcher.CLOAK_URL_PROPERTY; diff --git a/LauncherAuthlib/src/main/java/com/mojang/authlib/yggdrasil/LegacyBridge.java b/LauncherAuthlib/src/main/java/com/mojang/authlib/yggdrasil/LegacyBridge.java index baa874e1..73ba24e1 100644 --- a/LauncherAuthlib/src/main/java/com/mojang/authlib/yggdrasil/LegacyBridge.java +++ b/LauncherAuthlib/src/main/java/com/mojang/authlib/yggdrasil/LegacyBridge.java @@ -9,7 +9,7 @@ // Used by 1.6.4 and below versions @LauncherAPI -public final class LegacyBridge { +public class LegacyBridge { public static boolean checkServer(String username, String serverID) throws Exception { LogHelper.debug("LegacyBridge.checkServer, Username: '%s', Server ID: %s", username, serverID); return new CheckServerRequest(username, serverID).request() != null; diff --git a/LauncherAuthlib/src/main/java/com/mojang/authlib/yggdrasil/YggdrasilAuthenticationService.java b/LauncherAuthlib/src/main/java/com/mojang/authlib/yggdrasil/YggdrasilAuthenticationService.java index d334f539..227952e2 100644 --- a/LauncherAuthlib/src/main/java/com/mojang/authlib/yggdrasil/YggdrasilAuthenticationService.java +++ b/LauncherAuthlib/src/main/java/com/mojang/authlib/yggdrasil/YggdrasilAuthenticationService.java @@ -10,7 +10,7 @@ import pro.gravit.utils.helper.LogHelper; -public final class YggdrasilAuthenticationService implements AuthenticationService { +public class YggdrasilAuthenticationService implements AuthenticationService { public YggdrasilAuthenticationService(Proxy proxy, String clientToken) { LogHelper.debug("Patched AuthenticationService created: '%s'", clientToken); } diff --git a/LauncherAuthlib/src/main/java/com/mojang/authlib/yggdrasil/YggdrasilGameProfileRepository.java b/LauncherAuthlib/src/main/java/com/mojang/authlib/yggdrasil/YggdrasilGameProfileRepository.java index 0d54ba97..97fd927a 100644 --- a/LauncherAuthlib/src/main/java/com/mojang/authlib/yggdrasil/YggdrasilGameProfileRepository.java +++ b/LauncherAuthlib/src/main/java/com/mojang/authlib/yggdrasil/YggdrasilGameProfileRepository.java @@ -14,7 +14,7 @@ import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.VerifyHelper; -public final class YggdrasilGameProfileRepository implements GameProfileRepository { +public class YggdrasilGameProfileRepository implements GameProfileRepository { private static final long BUSY_WAIT_MS = VerifyHelper.verifyLong( Long.parseLong(System.getProperty("launcher.com.mojang.authlib.busyWait", Long.toString(100L))), VerifyHelper.L_NOT_NEGATIVE, "launcher.com.mojang.authlib.busyWait can't be < 0"); diff --git a/LauncherAuthlib/src/main/java/com/mojang/authlib/yggdrasil/YggdrasilMinecraftSessionService.java b/LauncherAuthlib/src/main/java/com/mojang/authlib/yggdrasil/YggdrasilMinecraftSessionService.java index d7afaede..dfbad307 100644 --- a/LauncherAuthlib/src/main/java/com/mojang/authlib/yggdrasil/YggdrasilMinecraftSessionService.java +++ b/LauncherAuthlib/src/main/java/com/mojang/authlib/yggdrasil/YggdrasilMinecraftSessionService.java @@ -28,7 +28,7 @@ import pro.gravit.utils.helper.LogHelper; import pro.gravit.utils.helper.SecurityHelper; -public final class YggdrasilMinecraftSessionService extends BaseMinecraftSessionService { +public class YggdrasilMinecraftSessionService extends BaseMinecraftSessionService { public static final JsonParser JSON_PARSER = new JsonParser(); public static final boolean NO_TEXTURES = Boolean.parseBoolean("launcher.com.mojang.authlib.noTextures"); diff --git a/LauncherCore/src/main/java/pro/gravit/utils/Version.java b/LauncherCore/src/main/java/pro/gravit/utils/Version.java index 383bba32..37353c60 100644 --- a/LauncherCore/src/main/java/pro/gravit/utils/Version.java +++ b/LauncherCore/src/main/java/pro/gravit/utils/Version.java @@ -22,7 +22,7 @@ public final class Version { public final Type release; public static final int MAJOR = 5; public static final int MINOR = 0; - public static final int PATCH = 8; + public static final int PATCH = 9; public static final int BUILD = 1; public static final Version.Type RELEASE = Type.STABLE; diff --git a/build.gradle b/build.gradle index 63262bbb..f3dd636e 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ id 'signing' } group = 'pro.gravit.launcher' -version = '5.0.8' +version = '5.0.9' configure(subprojects.findAll { it.name != 'modules' }) { apply plugin: 'idea' diff --git a/modules b/modules index 88bd03c3..8d7a95d0 160000 --- a/modules +++ b/modules @@ -1 +1 @@ -Subproject commit 88bd03c38a2681e997a305e376eb63e446c99a7f +Subproject commit 8d7a95d0707539ab18fba83bef6ef9f09b70c0ad