diff --git a/launcher/build.gradle b/launcher/build.gradle index 5ef3b76..1525fac 100644 --- a/launcher/build.gradle +++ b/launcher/build.gradle @@ -15,6 +15,7 @@ compile 'com.beust:jcommander:1.32' compile 'com.miglayout:miglayout:3.7.4' compile 'com.google.code.findbugs:jsr305:3.0.0' + compile 'com.googlecode.plist:dd-plist:1.23' implementation 'net.java.dev.jna:jna-platform:5.10.0' } diff --git a/launcher/src/main/java/com/skcraft/launcher/launch/JavaRuntimeFinder.java b/launcher/src/main/java/com/skcraft/launcher/launch/JavaRuntimeFinder.java index 54846bd..9f0701d 100644 --- a/launcher/src/main/java/com/skcraft/launcher/launch/JavaRuntimeFinder.java +++ b/launcher/src/main/java/com/skcraft/launcher/launch/JavaRuntimeFinder.java @@ -6,6 +6,10 @@ package com.skcraft.launcher.launch; +import com.dd.plist.NSArray; +import com.dd.plist.NSDictionary; +import com.dd.plist.NSObject; +import com.dd.plist.PropertyListParser; import com.skcraft.launcher.model.minecraft.JavaVersion; import com.skcraft.launcher.util.Environment; import com.skcraft.launcher.util.EnvironmentParser; @@ -36,29 +40,32 @@ public static List getAvailableRuntimes() { Environment env = Environment.getInstance(); Set entries = new HashSet<>(); - File launcherDir; + Set launcherDirs = new HashSet<>(); if (env.getPlatform() == Platform.WINDOWS) { try { String launcherPath = WinRegistry.readString(WinReg.HKEY_CURRENT_USER, "SOFTWARE\\Mojang\\InstalledProducts\\Minecraft Launcher", "InstallLocation"); - launcherDir = new File(launcherPath); + launcherDirs.add(new File(launcherPath)); } catch (Throwable err) { log.log(Level.WARNING, "Failed to read launcher location from registry", err); - - String programFiles = Objects.equals(env.getArchBits(), "64") - ? System.getenv("ProgramFiles(x86)") - : System.getenv("ProgramFiles"); - - launcherDir = new File(programFiles, "Minecraft Launcher"); } + String programFiles = Objects.equals(env.getArchBits(), "64") + ? System.getenv("ProgramFiles(x86)") + : System.getenv("ProgramFiles"); + + // Mojang likes to move the java runtime directory + launcherDirs.add(new File(programFiles, "Minecraft")); + launcherDirs.add(new File(programFiles, "Minecraft Launcher")); + launcherDirs.add(new File(System.getenv("LOCALAPPDATA"), "Packages\\Microsoft.4297127D64EC6_8wekyb3d8bbwe\\LocalCache\\Local")); + getEntriesFromRegistry(entries, "SOFTWARE\\JavaSoft\\Java Runtime Environment"); getEntriesFromRegistry(entries, "SOFTWARE\\JavaSoft\\Java Development Kit"); getEntriesFromRegistry(entries, "SOFTWARE\\JavaSoft\\JDK"); } else if (env.getPlatform() == Platform.LINUX) { - launcherDir = new File(System.getenv("HOME"), ".minecraft"); + launcherDirs.add(new File(System.getenv("HOME"), ".minecraft")); String javaHome = System.getenv("JAVA_HOME"); if (javaHome != null) { @@ -75,38 +82,57 @@ } }).distinct().forEach(file -> entries.add(getRuntimeFromPath(file.getAbsolutePath()))); } + } else if (env.getPlatform() == Platform.MAC_OS_X) { + launcherDirs.add(new File(System.getenv("HOME"), "Library/Application Support/minecraft")); + + try { + Process p = Runtime.getRuntime().exec("/usr/libexec/java_home -X"); + NSArray root = (NSArray) PropertyListParser.parse(p.getInputStream()); + NSObject[] arr = root.getArray(); + for (NSObject obj : arr) { + NSDictionary dict = (NSDictionary) obj; + entries.add(new JavaRuntime( + new File(dict.objectForKey("JVMHomePath").toString()).getAbsoluteFile(), + dict.objectForKey("JVMVersion").toString(), + isArch64Bit(dict.objectForKey("JVMArch").toString()) + )); + } + } catch (Throwable err) { + log.log(Level.WARNING, "Failed to parse java_home command", err); + } } else { return Collections.emptyList(); } - File runtimes = new File(launcherDir, "runtime"); - File[] runtimeList = runtimes.listFiles(); - if (runtimeList != null) { - for (File potential : runtimeList) { - if (potential.getName().startsWith("jre-x")) { - boolean is64Bit = potential.getName().equals("jre-x64"); - - JavaRuntime runtime = new JavaRuntime(potential.getAbsoluteFile(), readVersionFromRelease(potential), is64Bit); - runtime.setMinecraftBundled(true); - entries.add(runtime); - } else { + for (File install : launcherDirs) { + File runtimes = new File(install, "runtime"); + File[] runtimeList = runtimes.listFiles(); + if (runtimeList != null) { + for (File potential : runtimeList) { String runtimeName = potential.getName(); + if (runtimeName.startsWith("jre-x")) { + boolean is64Bit = runtimeName.equals("jre-x64"); - String[] children = potential.list(); - if (children == null || children.length == 0) continue; - String platformName = children[0]; + JavaRuntime runtime = new JavaRuntime(potential.getAbsoluteFile(), readVersionFromRelease(potential), is64Bit); + runtime.setMinecraftBundled(true); + entries.add(runtime); + } else { + String[] children = potential.list((dir, name) -> new File(dir, name).isDirectory()); + if (children == null || children.length != 1) continue; + String platformName = children[0]; - String[] parts = platformName.split("-"); - if (parts.length < 2) continue; + File javaDir = new File(potential, String.format("%s/%s", platformName, runtimeName)); + if (env.getPlatform() == Platform.MAC_OS_X) { + javaDir = new File(javaDir, "jre.bundle/Contents/Home"); + } - String arch = parts[1]; - boolean is64Bit = arch.equals("x64"); + String arch = readArchFromRelease(javaDir); + boolean is64Bit = isArch64Bit(arch); - File javaDir = new File(potential, String.format("%s/%s", platformName, runtimeName)); - JavaRuntime runtime = new JavaRuntime(javaDir.getAbsoluteFile(), readVersionFromRelease(javaDir), is64Bit); - runtime.setMinecraftBundled(true); - - entries.add(runtime); + JavaRuntime runtime = new JavaRuntime(javaDir.getAbsoluteFile(), readVersionFromRelease(javaDir), is64Bit); + runtime.setMinecraftBundled(true); + entries.add(runtime); + } } } } @@ -146,6 +172,9 @@ if (target.isFile()) { // Probably referring directly to bin/java, back up two levels target = target.getParentFile().getParentFile(); + } else if (target.getName().equals("bin")) { + // Probably copied the bin directory that java.exe is in + target = target.getParentFile(); } { @@ -193,6 +222,10 @@ } } + private static boolean isArch64Bit(String string) { + return string == null || string.matches("x64|x86_64|amd64|aarch64"); + } + private static String readVersionFromRelease(File javaPath) { File releaseFile = new File(javaPath, "release"); if (releaseFile.exists()) { @@ -208,4 +241,20 @@ return null; } + + private static String readArchFromRelease(File javaPath) { + File releaseFile = new File(javaPath, "release"); + if (releaseFile.exists()) { + try { + Map releaseDetails = EnvironmentParser.parse(releaseFile); + + return releaseDetails.get("OS_ARCH"); + } catch (IOException e) { + log.log(Level.WARNING, "Failed to read release file " + releaseFile.getAbsolutePath(), e); + return null; + } + } + + return null; + } }