diff --git a/launcher/src/main/java/com/skcraft/launcher/Configuration.java b/launcher/src/main/java/com/skcraft/launcher/Configuration.java index 934a69b..8365feb 100644 --- a/launcher/src/main/java/com/skcraft/launcher/Configuration.java +++ b/launcher/src/main/java/com/skcraft/launcher/Configuration.java @@ -7,8 +7,8 @@ package com.skcraft.launcher; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.skcraft.launcher.launch.JavaRuntime; -import com.skcraft.launcher.launch.JavaRuntimeFinder; +import com.skcraft.launcher.launch.runtime.JavaRuntime; +import com.skcraft.launcher.launch.runtime.JavaRuntimeFinder; import lombok.Data; /** diff --git a/launcher/src/main/java/com/skcraft/launcher/InstanceSettings.java b/launcher/src/main/java/com/skcraft/launcher/InstanceSettings.java index f42c6c7..a320361 100644 --- a/launcher/src/main/java/com/skcraft/launcher/InstanceSettings.java +++ b/launcher/src/main/java/com/skcraft/launcher/InstanceSettings.java @@ -1,8 +1,8 @@ package com.skcraft.launcher; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.skcraft.launcher.launch.JavaRuntime; import com.skcraft.launcher.launch.MemorySettings; +import com.skcraft.launcher.launch.runtime.JavaRuntime; import lombok.Data; @Data diff --git a/launcher/src/main/java/com/skcraft/launcher/dialog/ConfigurationDialog.java b/launcher/src/main/java/com/skcraft/launcher/dialog/ConfigurationDialog.java index 06f4d6c..e0025bc 100644 --- a/launcher/src/main/java/com/skcraft/launcher/dialog/ConfigurationDialog.java +++ b/launcher/src/main/java/com/skcraft/launcher/dialog/ConfigurationDialog.java @@ -9,9 +9,9 @@ package com.skcraft.launcher.dialog; import com.skcraft.launcher.Configuration; import com.skcraft.launcher.Launcher; import com.skcraft.launcher.dialog.component.BetterComboBox; -import com.skcraft.launcher.launch.AddJavaRuntime; -import com.skcraft.launcher.launch.JavaRuntime; -import com.skcraft.launcher.launch.JavaRuntimeFinder; +import com.skcraft.launcher.launch.runtime.AddJavaRuntime; +import com.skcraft.launcher.launch.runtime.JavaRuntime; +import com.skcraft.launcher.launch.runtime.JavaRuntimeFinder; import com.skcraft.launcher.persistence.Persistence; import com.skcraft.launcher.swing.*; import com.skcraft.launcher.util.SharedLocale; diff --git a/launcher/src/main/java/com/skcraft/launcher/dialog/InstanceSettingsDialog.java b/launcher/src/main/java/com/skcraft/launcher/dialog/InstanceSettingsDialog.java index 7ef41fe..f261e95 100644 --- a/launcher/src/main/java/com/skcraft/launcher/dialog/InstanceSettingsDialog.java +++ b/launcher/src/main/java/com/skcraft/launcher/dialog/InstanceSettingsDialog.java @@ -3,9 +3,9 @@ package com.skcraft.launcher.dialog; import com.skcraft.launcher.Instance; import com.skcraft.launcher.InstanceSettings; import com.skcraft.launcher.dialog.component.BetterComboBox; -import com.skcraft.launcher.launch.JavaRuntime; -import com.skcraft.launcher.launch.JavaRuntimeFinder; import com.skcraft.launcher.launch.MemorySettings; +import com.skcraft.launcher.launch.runtime.JavaRuntime; +import com.skcraft.launcher.launch.runtime.JavaRuntimeFinder; import com.skcraft.launcher.persistence.Persistence; import com.skcraft.launcher.swing.FormPanel; import com.skcraft.launcher.swing.LinedBoxPanel; diff --git a/launcher/src/main/java/com/skcraft/launcher/launch/JavaProcessBuilder.java b/launcher/src/main/java/com/skcraft/launcher/launch/JavaProcessBuilder.java index c28ab35..7b7e40e 100644 --- a/launcher/src/main/java/com/skcraft/launcher/launch/JavaProcessBuilder.java +++ b/launcher/src/main/java/com/skcraft/launcher/launch/JavaProcessBuilder.java @@ -6,6 +6,7 @@ package com.skcraft.launcher.launch; +import com.skcraft.launcher.launch.runtime.JavaRuntime; import lombok.Getter; import lombok.Setter; import lombok.ToString; diff --git a/launcher/src/main/java/com/skcraft/launcher/launch/JavaRuntimeFinder.java b/launcher/src/main/java/com/skcraft/launcher/launch/JavaRuntimeFinder.java deleted file mode 100644 index 9f0701d..0000000 --- a/launcher/src/main/java/com/skcraft/launcher/launch/JavaRuntimeFinder.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * SK's Minecraft Launcher - * Copyright (C) 2010-2014 Albert Pham and contributors - * Please see LICENSE.txt for license information. - */ - -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; -import com.skcraft.launcher.util.Platform; -import com.skcraft.launcher.util.WinRegistry; -import com.sun.jna.platform.win32.WinReg; -import lombok.extern.java.Log; - -import java.io.File; -import java.io.IOException; -import java.util.*; -import java.util.logging.Level; -import java.util.stream.Collectors; - -/** - * Finds the best Java runtime to use. - */ -@Log -public final class JavaRuntimeFinder { - - private JavaRuntimeFinder() { - } - - /** - * Get all available Java runtimes on the system - * @return List of available Java runtimes sorted by newest first - */ - public static List getAvailableRuntimes() { - Environment env = Environment.getInstance(); - Set entries = new HashSet<>(); - Set launcherDirs = new HashSet<>(); - - if (env.getPlatform() == Platform.WINDOWS) { - try { - String launcherPath = WinRegistry.readString(WinReg.HKEY_CURRENT_USER, - "SOFTWARE\\Mojang\\InstalledProducts\\Minecraft Launcher", "InstallLocation"); - - 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"); - - // 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) { - launcherDirs.add(new File(System.getenv("HOME"), ".minecraft")); - - String javaHome = System.getenv("JAVA_HOME"); - if (javaHome != null) { - entries.add(getRuntimeFromPath(javaHome)); - } - - File[] runtimesList = new File("/usr/lib/jvm").listFiles(); - if (runtimesList != null) { - Arrays.stream(runtimesList).map(file -> { - try { - return file.getCanonicalFile(); - } catch (IOException exception) { - return file; - } - }).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(); - } - - 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"); - - 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]; - - 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 = readArchFromRelease(javaDir); - boolean is64Bit = isArch64Bit(arch); - - JavaRuntime runtime = new JavaRuntime(javaDir.getAbsoluteFile(), readVersionFromRelease(javaDir), is64Bit); - runtime.setMinecraftBundled(true); - entries.add(runtime); - } - } - } - } - - return entries.stream().sorted().collect(Collectors.toList()); - } - - /** - * Return the path to the best found JVM location. - * - * @return the JVM location, or null - */ - public static File findBestJavaPath() { - List entries = getAvailableRuntimes(); - if (entries.size() > 0) { - return new File(entries.get(0).getDir(), "bin"); - } - - return null; - } - - public static Optional findBestJavaRuntime(JavaVersion targetVersion) { - List entries = getAvailableRuntimes(); - - return entries.stream().sorted() - .filter(runtime -> runtime.getMajorVersion() == targetVersion.getMajorVersion()) - .findFirst(); - } - - public static Optional findAnyJavaRuntime() { - return getAvailableRuntimes().stream().sorted().findFirst(); - } - - public static JavaRuntime getRuntimeFromPath(String path) { - File target = new File(path); - - 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(); - } - - { - File jre = new File(target, "jre/release"); - if (jre.isFile()) { - target = jre.getParentFile(); - } - } - - return new JavaRuntime(target, readVersionFromRelease(target), guessIf64BitWindows(target)); - } - - private static void getEntriesFromRegistry(Collection entries, String basePath) - throws IllegalArgumentException { - try { - List subKeys = WinRegistry.readStringSubKeys(WinReg.HKEY_LOCAL_MACHINE, basePath); - for (String subKey : subKeys) { - JavaRuntime entry = getEntryFromRegistry(basePath, subKey); - if (entry != null) { - entries.add(entry); - } - } - } catch (Throwable err) { - log.log(Level.INFO, "Failed to read Java locations from registry in " + basePath); - } - } - - private static JavaRuntime getEntryFromRegistry(String basePath, String version) { - String regPath = basePath + "\\" + version; - String path = WinRegistry.readString(WinReg.HKEY_LOCAL_MACHINE, regPath, "JavaHome"); - File dir = new File(path); - if (dir.exists() && new File(dir, "bin/java.exe").exists()) { - return new JavaRuntime(dir, version, guessIf64BitWindows(dir)); - } else { - return null; - } - } - - private static boolean guessIf64BitWindows(File path) { - try { - String programFilesX86 = System.getenv("ProgramFiles(x86)"); - return programFilesX86 == null || !path.getCanonicalPath().startsWith(new File(programFilesX86).getCanonicalPath()); - } catch (IOException ignored) { - return false; - } - } - - 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()) { - try { - Map releaseDetails = EnvironmentParser.parse(releaseFile); - - return releaseDetails.get("JAVA_VERSION"); - } catch (IOException e) { - log.log(Level.WARNING, "Failed to read release file " + releaseFile.getAbsolutePath(), e); - return null; - } - } - - 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; - } -} diff --git a/launcher/src/main/java/com/skcraft/launcher/launch/Runner.java b/launcher/src/main/java/com/skcraft/launcher/launch/Runner.java index 0fcbd87..27d98e7 100644 --- a/launcher/src/main/java/com/skcraft/launcher/launch/Runner.java +++ b/launcher/src/main/java/com/skcraft/launcher/launch/Runner.java @@ -16,6 +16,8 @@ import com.skcraft.concurrency.ProgressObservable; import com.skcraft.launcher.*; import com.skcraft.launcher.auth.Session; import com.skcraft.launcher.install.ZipExtract; +import com.skcraft.launcher.launch.runtime.JavaRuntime; +import com.skcraft.launcher.launch.runtime.JavaRuntimeFinder; import com.skcraft.launcher.model.minecraft.*; import com.skcraft.launcher.persistence.Persistence; import com.skcraft.launcher.util.Environment; diff --git a/launcher/src/main/java/com/skcraft/launcher/launch/AddJavaRuntime.java b/launcher/src/main/java/com/skcraft/launcher/launch/runtime/AddJavaRuntime.java similarity index 90% rename from launcher/src/main/java/com/skcraft/launcher/launch/AddJavaRuntime.java rename to launcher/src/main/java/com/skcraft/launcher/launch/runtime/AddJavaRuntime.java index ebe81c2..06265ed 100644 --- a/launcher/src/main/java/com/skcraft/launcher/launch/AddJavaRuntime.java +++ b/launcher/src/main/java/com/skcraft/launcher/launch/runtime/AddJavaRuntime.java @@ -1,4 +1,4 @@ -package com.skcraft.launcher.launch; +package com.skcraft.launcher.launch.runtime; import java.io.File; diff --git a/launcher/src/main/java/com/skcraft/launcher/launch/runtime/JavaReleaseFile.java b/launcher/src/main/java/com/skcraft/launcher/launch/runtime/JavaReleaseFile.java new file mode 100644 index 0000000..b7cee3b --- /dev/null +++ b/launcher/src/main/java/com/skcraft/launcher/launch/runtime/JavaReleaseFile.java @@ -0,0 +1,47 @@ +package com.skcraft.launcher.launch.runtime; + +import com.skcraft.launcher.util.EnvironmentParser; +import lombok.extern.java.Log; + +import java.io.File; +import java.io.IOException; +import java.util.Map; +import java.util.logging.Level; + +@Log +public class JavaReleaseFile { + private Map backingMap; + + private JavaReleaseFile(Map releaseDetails) { + this.backingMap = releaseDetails; + } + + public String getVersion() { + return backingMap.get("JAVA_VERSION"); + } + + public String getArch() { + return backingMap.get("OS_ARCH"); + } + + public boolean isArch64Bit() { + return getArch() == null || getArch().matches("x64|x86_64|amd64|aarch64"); + } + + public static JavaReleaseFile parseFromRelease(File javaPath) { + File releaseFile = new File(javaPath, "release"); + + if (releaseFile.exists()) { + try { + Map releaseDetails = EnvironmentParser.parse(releaseFile); + + return new JavaReleaseFile(releaseDetails); + } catch (IOException e) { + log.log(Level.WARNING, "Failed to read release file " + releaseFile.getAbsolutePath(), e); + return null; + } + } + + return null; + } +} diff --git a/launcher/src/main/java/com/skcraft/launcher/launch/JavaRuntime.java b/launcher/src/main/java/com/skcraft/launcher/launch/runtime/JavaRuntime.java similarity index 98% rename from launcher/src/main/java/com/skcraft/launcher/launch/JavaRuntime.java rename to launcher/src/main/java/com/skcraft/launcher/launch/runtime/JavaRuntime.java index 7dffcb5..7d92730 100644 --- a/launcher/src/main/java/com/skcraft/launcher/launch/JavaRuntime.java +++ b/launcher/src/main/java/com/skcraft/launcher/launch/runtime/JavaRuntime.java @@ -1,4 +1,4 @@ -package com.skcraft.launcher.launch; +package com.skcraft.launcher.launch.runtime; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; diff --git a/launcher/src/main/java/com/skcraft/launcher/launch/runtime/JavaRuntimeFinder.java b/launcher/src/main/java/com/skcraft/launcher/launch/runtime/JavaRuntimeFinder.java new file mode 100644 index 0000000..7af48c8 --- /dev/null +++ b/launcher/src/main/java/com/skcraft/launcher/launch/runtime/JavaRuntimeFinder.java @@ -0,0 +1,112 @@ +/* + * SK's Minecraft Launcher + * Copyright (C) 2010-2014 Albert Pham and contributors + * Please see LICENSE.txt for license information. + */ + +package com.skcraft.launcher.launch.runtime; + +import com.skcraft.launcher.model.minecraft.JavaVersion; +import com.skcraft.launcher.util.Environment; +import lombok.extern.java.Log; + +import java.io.File; +import java.util.*; +import java.util.stream.Collectors; + +/** + * Finds the best Java runtime to use. + */ +@Log +public final class JavaRuntimeFinder { + + private JavaRuntimeFinder() { + } + + /** + * Get all available Java runtimes on the system + * @return List of available Java runtimes sorted by newest first + */ + public static List getAvailableRuntimes() { + Environment env = Environment.getInstance(); + PlatformRuntimeFinder runtimeFinder = getRuntimeFinder(env); + + if (runtimeFinder == null) { + return Collections.emptyList(); + } + + // Add Minecraft javas + List mcRuntimes = MinecraftJavaFinder.scanLauncherDirectories(env, + runtimeFinder.getLauncherDirectories(env)); + Set entries = new HashSet<>(mcRuntimes); + + // Add system Javas + runtimeFinder.getCandidateJavaLocations().stream() + .map(JavaRuntimeFinder::getRuntimeFromPath) + .forEach(entries::add); + + // Add extra runtimes + entries.addAll(runtimeFinder.getExtraRuntimes()); + + return entries.stream().sorted().collect(Collectors.toList()); + } + + /** + * Find the best runtime for a given Java version + * @param targetVersion Version to match + * @return Java runtime if available, empty Optional otherwise + */ + public static Optional findBestJavaRuntime(JavaVersion targetVersion) { + List entries = getAvailableRuntimes(); + + return entries.stream().sorted() + .filter(runtime -> runtime.getMajorVersion() == targetVersion.getMajorVersion()) + .findFirst(); + } + + public static Optional findAnyJavaRuntime() { + return getAvailableRuntimes().stream().sorted().findFirst(); + } + + public static JavaRuntime getRuntimeFromPath(String path) { + return getRuntimeFromPath(new File(path)); + } + + public static JavaRuntime getRuntimeFromPath(File target) { + 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(); + } + + { + File jre = new File(target, "jre/release"); + if (jre.isFile()) { + target = jre.getParentFile(); + } + } + + JavaReleaseFile release = JavaReleaseFile.parseFromRelease(target); + if (release == null) { + // Make some assumptions... + return new JavaRuntime(target, null, true); + } + + return new JavaRuntime(target, release.getVersion(), release.isArch64Bit()); + } + + private static PlatformRuntimeFinder getRuntimeFinder(Environment env) { + switch (env.getPlatform()) { + case WINDOWS: + return new WindowsRuntimeFinder(); + case MAC_OS_X: + return new MacRuntimeFinder(); + case LINUX: + return new LinuxRuntimeFinder(); + default: + return null; + } + } +} diff --git a/launcher/src/main/java/com/skcraft/launcher/launch/runtime/LinuxRuntimeFinder.java b/launcher/src/main/java/com/skcraft/launcher/launch/runtime/LinuxRuntimeFinder.java new file mode 100644 index 0000000..1ab0df6 --- /dev/null +++ b/launcher/src/main/java/com/skcraft/launcher/launch/runtime/LinuxRuntimeFinder.java @@ -0,0 +1,44 @@ +package com.skcraft.launcher.launch.runtime; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.skcraft.launcher.util.Environment; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +public class LinuxRuntimeFinder implements PlatformRuntimeFinder { + @Override + public Set getLauncherDirectories(Environment env) { + return ImmutableSet.of(new File(System.getenv("HOME"), ".minecraft")); + } + + @Override + public List getCandidateJavaLocations() { + ArrayList entries = Lists.newArrayList(); + + String javaHome = System.getenv("JAVA_HOME"); + if (javaHome != null) { + entries.add(new File(javaHome)); + } + + File[] runtimesList = new File("/usr/lib/jvm").listFiles(); + if (runtimesList != null) { + Arrays.stream(runtimesList).map(file -> { + try { + return file.getCanonicalFile(); + } catch (IOException exception) { + return file; + } + }).distinct().forEach(entries::add); + } + + return entries; + } + + @Override + public List getExtraRuntimes() { + return Collections.emptyList(); + } +} diff --git a/launcher/src/main/java/com/skcraft/launcher/launch/runtime/MacRuntimeFinder.java b/launcher/src/main/java/com/skcraft/launcher/launch/runtime/MacRuntimeFinder.java new file mode 100644 index 0000000..22a21df --- /dev/null +++ b/launcher/src/main/java/com/skcraft/launcher/launch/runtime/MacRuntimeFinder.java @@ -0,0 +1,57 @@ +package com.skcraft.launcher.launch.runtime; + +import com.dd.plist.NSArray; +import com.dd.plist.NSDictionary; +import com.dd.plist.NSObject; +import com.dd.plist.PropertyListParser; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.skcraft.launcher.util.Environment; +import lombok.extern.java.Log; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; + +@Log +public class MacRuntimeFinder implements PlatformRuntimeFinder { + @Override + public Set getLauncherDirectories(Environment env) { + return ImmutableSet.of(new File(System.getenv("HOME"), "Library/Application Support/minecraft")); + } + + @Override + public List getCandidateJavaLocations() { + return Collections.emptyList(); + } + + @Override + public List getExtraRuntimes() { + ArrayList entries = Lists.newArrayList(); + + 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); + } + + return entries; + } + + private static boolean isArch64Bit(String string) { + return string == null || string.matches("x64|x86_64|amd64|aarch64"); + } +} diff --git a/launcher/src/main/java/com/skcraft/launcher/launch/runtime/MinecraftJavaFinder.java b/launcher/src/main/java/com/skcraft/launcher/launch/runtime/MinecraftJavaFinder.java new file mode 100644 index 0000000..5dcbf6b --- /dev/null +++ b/launcher/src/main/java/com/skcraft/launcher/launch/runtime/MinecraftJavaFinder.java @@ -0,0 +1,66 @@ +package com.skcraft.launcher.launch.runtime; + +import com.skcraft.launcher.util.Environment; +import com.skcraft.launcher.util.Platform; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Scans Minecraft bundled Java directories + */ +public class MinecraftJavaFinder { + public static List scanLauncherDirectories(Environment env, Collection launcherDirs) { + ArrayList entries = new ArrayList<>(); + + for (File install : launcherDirs) { + File runtimes = new File(install, "runtime"); + File[] runtimeList = runtimes.listFiles(); + if (runtimeList != null) { + for (File potential : runtimeList) { + JavaRuntime runtime = scanPotentialRuntime(env, potential); + + if (runtime != null) { + entries.add(runtime); + } + } + } + } + + return entries; + } + + private static JavaRuntime scanPotentialRuntime(Environment env, File potential) { + String runtimeName = potential.getName(); + if (runtimeName.startsWith("jre-x")) { + boolean is64Bit = runtimeName.equals("jre-x64"); + + JavaReleaseFile release = JavaReleaseFile.parseFromRelease(potential); + String version = release != null ? release.getVersion() : null; + + JavaRuntime runtime = new JavaRuntime(potential.getAbsoluteFile(), version, is64Bit); + runtime.setMinecraftBundled(true); + return runtime; + } else { + String[] children = potential.list((dir, name) -> new File(dir, name).isDirectory()); + if (children == null || children.length != 1) return null; + String platformName = children[0]; + + 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"); + } + + JavaReleaseFile release = JavaReleaseFile.parseFromRelease(javaDir); + if (release == null) { + return null; + } + + JavaRuntime runtime = new JavaRuntime(javaDir.getAbsoluteFile(), release.getVersion(), release.isArch64Bit()); + runtime.setMinecraftBundled(true); + return runtime; + } + } +} diff --git a/launcher/src/main/java/com/skcraft/launcher/launch/runtime/PlatformRuntimeFinder.java b/launcher/src/main/java/com/skcraft/launcher/launch/runtime/PlatformRuntimeFinder.java new file mode 100644 index 0000000..1d66636 --- /dev/null +++ b/launcher/src/main/java/com/skcraft/launcher/launch/runtime/PlatformRuntimeFinder.java @@ -0,0 +1,31 @@ +package com.skcraft.launcher.launch.runtime; + +import com.skcraft.launcher.util.Environment; + +import java.io.File; +import java.util.List; +import java.util.Set; + +public interface PlatformRuntimeFinder { + /** + * Get the list of possible launcher locations for this platform + * @return List of possible launcher locations + */ + Set getLauncherDirectories(Environment env); + + /** + * Get a list of candidate folders to check for Java runtimes. + * The returned folders will be checked for "release" files which describe the version and architecture. + * + * @return List of folders that may contain Java runtimes + */ + List getCandidateJavaLocations(); + + /** + * Get a list of extra runtimes obtained using platform-specific logic. + * e.g. on Windows, registry entries are returned + * + * @return List of extra Java runtimes + */ + List getExtraRuntimes(); +} diff --git a/launcher/src/main/java/com/skcraft/launcher/launch/runtime/WindowsRuntimeFinder.java b/launcher/src/main/java/com/skcraft/launcher/launch/runtime/WindowsRuntimeFinder.java new file mode 100644 index 0000000..1308965 --- /dev/null +++ b/launcher/src/main/java/com/skcraft/launcher/launch/runtime/WindowsRuntimeFinder.java @@ -0,0 +1,91 @@ +package com.skcraft.launcher.launch.runtime; + +import com.google.common.collect.Lists; +import com.skcraft.launcher.util.Environment; +import com.skcraft.launcher.util.WinRegistry; +import com.sun.jna.platform.win32.WinReg; +import lombok.extern.java.Log; + +import java.io.File; +import java.io.IOException; +import java.util.*; +import java.util.logging.Level; + +@Log +public class WindowsRuntimeFinder implements PlatformRuntimeFinder { + @Override + public Set getLauncherDirectories(Environment env) { + HashSet launcherDirs = new HashSet<>(); + + try { + String launcherPath = WinRegistry.readString(WinReg.HKEY_CURRENT_USER, + "SOFTWARE\\Mojang\\InstalledProducts\\Minecraft Launcher", "InstallLocation"); + + 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"); + + // 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")); + + return launcherDirs; + } + + @Override + public List getCandidateJavaLocations() { + return Collections.emptyList(); + } + + @Override + public List getExtraRuntimes() { + ArrayList entries = Lists.newArrayList(); + + getEntriesFromRegistry(entries, "SOFTWARE\\JavaSoft\\Java Runtime Environment"); + getEntriesFromRegistry(entries, "SOFTWARE\\JavaSoft\\Java Development Kit"); + getEntriesFromRegistry(entries, "SOFTWARE\\JavaSoft\\JDK"); + + return entries; + } + + private static void getEntriesFromRegistry(Collection entries, String basePath) + throws IllegalArgumentException { + try { + List subKeys = WinRegistry.readStringSubKeys(WinReg.HKEY_LOCAL_MACHINE, basePath); + for (String subKey : subKeys) { + JavaRuntime entry = getEntryFromRegistry(basePath, subKey); + if (entry != null) { + entries.add(entry); + } + } + } catch (Throwable err) { + log.log(Level.INFO, "Failed to read Java locations from registry in " + basePath); + } + } + + private static JavaRuntime getEntryFromRegistry(String basePath, String version) { + String regPath = basePath + "\\" + version; + String path = WinRegistry.readString(WinReg.HKEY_LOCAL_MACHINE, regPath, "JavaHome"); + File dir = new File(path); + if (dir.exists() && new File(dir, "bin/java.exe").exists()) { + return new JavaRuntime(dir, version, guessIf64BitWindows(dir)); + } else { + return null; + } + } + + private static boolean guessIf64BitWindows(File path) { + try { + String programFilesX86 = System.getenv("ProgramFiles(x86)"); + return programFilesX86 == null || !path.getCanonicalPath().startsWith(new File(programFilesX86).getCanonicalPath()); + } catch (IOException ignored) { + return false; + } + } +}