diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java index 00731ed8c..9d2e9545d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/Controllers.java @@ -31,7 +31,6 @@ import javafx.stage.Stage; import javafx.stage.StageStyle; import org.jackhuang.hmcl.Launcher; import org.jackhuang.hmcl.Metadata; -import org.jackhuang.hmcl.download.java.JavaRepository; import org.jackhuang.hmcl.game.ModpackHelper; import org.jackhuang.hmcl.setting.Accounts; import org.jackhuang.hmcl.setting.EnumCommonDirectory; @@ -173,7 +172,7 @@ public final class Controllers { dialog(i18n("launcher.cache_directory.invalid")); } - Task.runAsync(JavaVersion::initialize).thenRunAsync(JavaRepository::initialize).start(); + Task.runAsync(JavaVersion::initialize).start(); scene = new Scene(decorator.getDecorator()); scene.setFill(Color.TRANSPARENT); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/JavaRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/JavaRepository.java index e2e323d9d..214872a7e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/JavaRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/JavaRepository.java @@ -4,6 +4,7 @@ import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.game.GameJavaVersion; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.CacheRepository; +import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.platform.Architecture; import org.jackhuang.hmcl.util.platform.JavaVersion; import org.jackhuang.hmcl.util.platform.OperatingSystem; @@ -12,8 +13,11 @@ import java.io.IOException; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import java.util.logging.Level; +import java.util.stream.Stream; import static org.jackhuang.hmcl.util.Logging.LOG; @@ -42,26 +46,48 @@ public final class JavaRepository { throw new IOException("Incorrect java home " + javaHome); } - public static void initialize() throws IOException, InterruptedException { - Optional platformOptional = getSystemJavaPlatform(); - if (platformOptional.isPresent()) { - String platform = platformOptional.get(); - Path javaStoragePath = getJavaStoragePath(); - if (Files.isDirectory(javaStoragePath)) { - try (DirectoryStream dirStream = Files.newDirectoryStream(javaStoragePath)) { - for (Path component : dirStream) { - Path javaHome = component.resolve(platform).resolve(component.getFileName()); - try { - addJava(javaHome); - } catch (IOException e) { - LOG.log(Level.WARNING, "Failed to determine Java at " + javaHome, e); - } - } - } - } + public static Stream> findMinecraftRuntimeDirs() { + switch (OperatingSystem.CURRENT_OS) { + case WINDOWS: + return Stream.of( + FileUtils.tryGetPath(System.getenv("localappdata"), + "Packages\\Microsoft.4297127D64EC6_8wekyb3d8bbwe\\LocalCache\\Local\\runtime"), + FileUtils.tryGetPath( + Optional.ofNullable(System.getenv("ProgramFiles(x86)")).orElse("C:\\Program Files (x86)"), + "Minecraft Launcher\\runtime")); + case LINUX: + return Stream.of(FileUtils.tryGetPath(System.getProperty("user.home", ".minecraft/runtime"))); + case OSX: + return Stream.of(FileUtils.tryGetPath("/Library/Application Support/minecraft/runtime"), + FileUtils.tryGetPath(System.getProperty("user.home"), "/Library/Application Support/minecraft/runtime")); + default: + return Stream.empty(); } } + public static Stream findJavaHomeInMinecraftRuntimeDir(Path runtimeDir) { + // Examples: + // $HOME/Library/Application Support/minecraft/runtime/java-runtime-beta/mac-os/java-runtime-beta/jre.bundle/Contents/Home + // $HOME/.minecraft/runtime/java-runtime-beta/linux/java-runtime-beta + Optional platformOptional = getSystemJavaPlatform(); + if (!platformOptional.isPresent()) return Stream.empty(); + String platform = platformOptional.get(); + List javaHomes = new ArrayList<>(); + try (DirectoryStream dir = Files.newDirectoryStream(runtimeDir)) { + // component can be jre-legacy, java-runtime-alpha, java-runtime-beta, java-runtime-gamma or any other being added in the future. + for (Path component : dir) { + Path javaHome = component.resolve(platform).resolve(component.getFileName()); + if (OperatingSystem.CURRENT_OS == OperatingSystem.OSX) { + javaHomes.add(javaHome.resolve("jre.bundle/Contents/Home")); + } + javaHomes.add(javaHome); + } + } catch (IOException e) { + LOG.log(Level.WARNING, "Failed to list java-runtime directory " + runtimeDir, e); + } + return javaHomes.stream(); + } + public static Optional getSystemJavaPlatform() { if (OperatingSystem.CURRENT_OS == OperatingSystem.LINUX) { if (Architecture.SYSTEM_ARCH == Architecture.X86) { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/JavaVersion.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/JavaVersion.java index 23526eecb..e89666707 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/JavaVersion.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/platform/JavaVersion.java @@ -17,6 +17,7 @@ */ package org.jackhuang.hmcl.util.platform; +import org.jackhuang.hmcl.download.java.JavaRepository; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.StringUtils; @@ -32,7 +33,6 @@ import java.util.concurrent.*; import java.util.logging.Level; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import java.util.stream.Stream; import static java.util.stream.Collectors.toList; @@ -305,13 +305,8 @@ public final class JavaVersion { // 2. Minecraft-installed locations // 3. PATH List> javaExecutables = new ArrayList<>(); - // Can be not present -- we check at the last part - List> runtimeDirs = new ArrayList<>(); - // Is this necessary? Can't we just do listDirectory(...).map(x -> x.resolve(...))? - // lookupJavas() should take care of it... - List runtimeOSArch = new ArrayList<>(); - String pathSep = System.getProperty("path.separator"); - String javaExec = OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS ? "java.exe" : "java"; + + String javaExe = OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS ? "java.exe" : "java"; switch (OperatingSystem.CURRENT_OS) { case WINDOWS: @@ -332,26 +327,13 @@ public final class JavaVersion { javaExecutables.add(listDirectory(programFiles.get().resolve(vendor)).map(JavaVersion::getExecutable)); } } - - runtimeDirs.add(FileUtils.tryGetPath(System.getenv("localappdata"), - "Packages\\Microsoft.4297127D64EC6_8wekyb3d8bbwe\\LocalCache\\Local\\runtime")); - runtimeDirs.add(FileUtils.tryGetPath( - Optional.ofNullable(System.getenv("ProgramFiles(x86)")).orElse("C:\\Program Files (x86)"), - "Minecraft Launcher\\runtime")); - - runtimeOSArch.add("windows-x64"); - runtimeOSArch.add("windows-x86"); break; case LINUX: javaExecutables.add(listDirectory(Paths.get("/usr/java")).map(JavaVersion::getExecutable)); // Oracle RPMs javaExecutables.add(listDirectory(Paths.get("/usr/lib/jvm")).map(JavaVersion::getExecutable)); // General locations javaExecutables.add(listDirectory(Paths.get("/usr/lib32/jvm")).map(JavaVersion::getExecutable)); // General locations - - runtimeDirs.add(FileUtils.tryGetPath(System.getProperty("user.home", ".minecraft/runtime"))); - runtimeOSArch.add("linux"); break; - case OSX: javaExecutables.add(listDirectory(Paths.get("/Library/Java/JavaVirtualMachines")) @@ -362,40 +344,29 @@ public final class JavaVersion { .map(JavaVersion::getExecutable)); javaExecutables.add(Stream.of(Paths.get("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java"))); javaExecutables.add(Stream.of(Paths.get("/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/MacOS/itms/java/bin/java"))); - - runtimeDirs.add(FileUtils.tryGetPath("/Library/Application Support/minecraft/runtime")); - runtimeDirs.add(FileUtils.tryGetPath(System.getProperty("user.home"), "/Library/Application Support/minecraft/runtime")); - - runtimeOSArch.add("mac-os"); break; - + default: break; } - // Do MC runtimes, given the OS-specific info we have. - for (Optional runtimeDir : runtimeDirs) { - if (!runtimeDir.isPresent()) - continue; - - for (String osArch : runtimeOSArch) { - javaExecutables.add(Stream.of( - runtimeDir.get().resolve("jre-legacy").resolve(osArch).resolve("jre-legacy"), - runtimeDir.get().resolve("java-runtime-alpha").resolve(osArch).resolve("java-runtime-alpha"), - runtimeDir.get().resolve("java-runtime-beta").resolve(osArch).resolve("java-runtime-beta")) - .map(x -> OperatingSystem.CURRENT_OS == OperatingSystem.OSX ? x.resolve("jre.bundle/Contents/Home") : x) - .map(JavaVersion::getExecutable)); - } + // Search Minecraft bundled runtimes. + javaExecutables.add(Stream.concat(Stream.of(Optional.of(JavaRepository.getJavaStoragePath())), JavaRepository.findMinecraftRuntimeDirs()) + .flatMap(Lang::toStream) + .flatMap(JavaRepository::findJavaHomeInMinecraftRuntimeDir) + .map(JavaVersion::getExecutable)); + + // Search in PATH. + if (System.getenv("PATH") != null) { + javaExecutables.add(Arrays.stream(System.getenv("PATH").split(OperatingSystem.PATH_SEPARATOR)) + .flatMap(path -> Lang.toStream(FileUtils.tryGetPath(path, javaExe)))); } - // Do PATH. - if (System.getenv("PATH") != null) { - javaExecutables.add(Arrays.stream(System.getenv("PATH").split(pathSep)) - .flatMap(path -> Lang.toStream(FileUtils.tryGetPath(path, javaExec)))); - } + // Search in HMCL_JRES, convenient environment variable for users to add JRE in global + // May be removed when we implement global Java configuration. if (System.getenv("HMCL_JRES") != null) { - javaExecutables.add(Arrays.stream(System.getenv("HMCL_JRES").split(pathSep)) - .flatMap(path -> Lang.toStream(FileUtils.tryGetPath(path, "bin", javaExec)))); + javaExecutables.add(Arrays.stream(System.getenv("HMCL_JRES").split(OperatingSystem.PATH_SEPARATOR)) + .flatMap(path -> Lang.toStream(FileUtils.tryGetPath(path, "bin", javaExe)))); } return javaExecutables.parallelStream().flatMap(stream -> stream); }