From 4757612342c4fcf6ca3fbd5b42aed97cf62e9a28 Mon Sep 17 00:00:00 2001 From: Glavo Date: Tue, 21 Oct 2025 16:15:12 +0800 Subject: [PATCH 01/22] update --- .../jackhuang/hmcl/java/JavaInfoCache.java | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfoCache.java diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfoCache.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfoCache.java new file mode 100644 index 0000000000..7d95882730 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfoCache.java @@ -0,0 +1,82 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2025 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.java; + +import com.google.gson.*; +import com.google.gson.annotations.JsonAdapter; +import org.jackhuang.hmcl.util.gson.JsonSerializable; +import org.jackhuang.hmcl.util.gson.JsonUtils; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.lang.reflect.Type; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.LinkedHashMap; +import java.util.List; + +import static org.jackhuang.hmcl.util.logging.Logger.LOG; + +@JsonSerializable +@JsonAdapter(JavaInfoCache.Serializer.class) +public record JavaInfoCache(String realPath, String cacheKey, JavaInfo javaInfo) { + public static final long FORMAT_VERSION = 0L; + + public static LinkedHashMap loadCacheMap(Path cacheFile) throws IOException { + if (Files.notExists(cacheFile)) + throw new FileNotFoundException("Cache file does not exist: " + cacheFile); + + JsonObject jsonObject = JsonUtils.fromJsonFile(cacheFile, JsonObject.class); + + JsonElement fileVersion = jsonObject.get("version"); + if (!(fileVersion instanceof JsonPrimitive)) + throw new IOException("Invalid version JSON: " + fileVersion); + + int version = fileVersion.getAsJsonPrimitive().getAsInt(); + if (version != FORMAT_VERSION) + throw new IOException("Unsupported cache file, version: %d".formatted(version)); + + List caches = JsonUtils.GSON.fromJson( + jsonObject.getAsJsonArray("cache"), + JsonUtils.listTypeOf(JavaInfoCache.class)); + + LinkedHashMap result = new LinkedHashMap<>(); + for (JavaInfoCache item : caches) { + try { + Path path = Path.of(item.realPath); + result.put(path, item); + } catch (Exception e) { + LOG.warning("Java info cache invalid: " + item.realPath, e); + } + } + return result; + } + + public static final class Serializer implements JsonSerializer, JsonDeserializer { + + @Override + public JavaInfoCache deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + return null; + } + + @Override + public JsonElement serialize(JavaInfoCache javaInfoCache, Type type, JsonSerializationContext jsonSerializationContext) { + return null; + } + } +} From d1bede1a79734e2f160df55a6c76ef55f620c987 Mon Sep 17 00:00:00 2001 From: Glavo Date: Tue, 21 Oct 2025 16:27:13 +0800 Subject: [PATCH 02/22] update --- .../org/jackhuang/hmcl/java/JavaManager.java | 407 +++++++++--------- 1 file changed, 205 insertions(+), 202 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java index 4fa82750b4..2737552951 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java @@ -335,55 +335,52 @@ public static void initialize() { // search java private static Map searchPotentialJavaExecutables() { - Map javaRuntimes = new HashMap<>(); - searchAllJavaInRepository(javaRuntimes, Platform.SYSTEM_PLATFORM); + Searcher searcher = new Searcher(); + searcher.searchAllJavaInRepository(Platform.SYSTEM_PLATFORM); switch (OperatingSystem.CURRENT_OS) { case WINDOWS: if (Architecture.SYSTEM_ARCH == Architecture.X86_64) - searchAllJavaInRepository(javaRuntimes, Platform.WINDOWS_X86); + searcher.searchAllJavaInRepository(Platform.WINDOWS_X86); if (Architecture.SYSTEM_ARCH == Architecture.ARM64) { if (OperatingSystem.SYSTEM_BUILD_NUMBER >= 21277) - searchAllJavaInRepository(javaRuntimes, Platform.WINDOWS_X86_64); - searchAllJavaInRepository(javaRuntimes, Platform.WINDOWS_X86); + searcher.searchAllJavaInRepository(Platform.WINDOWS_X86_64); + searcher.searchAllJavaInRepository(Platform.WINDOWS_X86); } break; case MACOS: if (Architecture.SYSTEM_ARCH == Architecture.ARM64) - searchAllJavaInRepository(javaRuntimes, Platform.MACOS_X86_64); + searcher.searchAllJavaInRepository(Platform.MACOS_X86_64); break; } switch (OperatingSystem.CURRENT_OS) { case WINDOWS: - queryJavaInRegistryKey(javaRuntimes, WinReg.HKEY.HKEY_LOCAL_MACHINE, "SOFTWARE\\JavaSoft\\Java Runtime Environment"); - queryJavaInRegistryKey(javaRuntimes, WinReg.HKEY.HKEY_LOCAL_MACHINE, "SOFTWARE\\JavaSoft\\Java Development Kit"); - queryJavaInRegistryKey(javaRuntimes, WinReg.HKEY.HKEY_LOCAL_MACHINE, "SOFTWARE\\JavaSoft\\JRE"); - queryJavaInRegistryKey(javaRuntimes, WinReg.HKEY.HKEY_LOCAL_MACHINE, "SOFTWARE\\JavaSoft\\JDK"); + searcher.queryJavaInRegistryKey(WinReg.HKEY.HKEY_LOCAL_MACHINE, "SOFTWARE\\JavaSoft\\Java Runtime Environment"); + searcher.queryJavaInRegistryKey(WinReg.HKEY.HKEY_LOCAL_MACHINE, "SOFTWARE\\JavaSoft\\Java Development Kit"); + searcher.queryJavaInRegistryKey(WinReg.HKEY.HKEY_LOCAL_MACHINE, "SOFTWARE\\JavaSoft\\JRE"); + searcher.queryJavaInRegistryKey(WinReg.HKEY.HKEY_LOCAL_MACHINE, "SOFTWARE\\JavaSoft\\JDK"); - searchJavaInProgramFiles(javaRuntimes, "ProgramFiles", "C:\\Program Files"); - searchJavaInProgramFiles(javaRuntimes, "ProgramFiles(x86)", "C:\\Program Files (x86)"); - if (Architecture.SYSTEM_ARCH == Architecture.ARM64) { - searchJavaInProgramFiles(javaRuntimes, "ProgramFiles(ARM)", "C:\\Program Files (ARM)"); - } + searcher.searchJavaInProgramFiles("ProgramFiles", "C:\\Program Files"); + searcher.searchJavaInProgramFiles("ProgramFiles(x86)", "C:\\Program Files (x86)"); break; case LINUX: - searchAllJavaInDirectory(javaRuntimes, Paths.get("/usr/java")); // Oracle RPMs - searchAllJavaInDirectory(javaRuntimes, Paths.get("/usr/lib/jvm")); // General locations - searchAllJavaInDirectory(javaRuntimes, Paths.get("/usr/lib32/jvm")); // General locations - searchAllJavaInDirectory(javaRuntimes, Paths.get("/usr/lib64/jvm")); // General locations - searchAllJavaInDirectory(javaRuntimes, Paths.get(System.getProperty("user.home"), "/.sdkman/candidates/java")); // SDKMAN! + searcher.searchAllJavaInDirectory(Path.of("/usr/java")); // Oracle RPMs + searcher.searchAllJavaInDirectory(Path.of("/usr/lib/jvm")); // General locations + searcher.searchAllJavaInDirectory(Path.of("/usr/lib32/jvm")); // General locations + searcher.searchAllJavaInDirectory(Path.of("/usr/lib64/jvm")); // General locations + searcher.searchAllJavaInDirectory(Path.of(System.getProperty("user.home"), "/.sdkman/candidates/java")); // SDKMAN! break; case MACOS: - searchJavaInMacJavaVirtualMachines(javaRuntimes, Paths.get("/Library/Java/JavaVirtualMachines")); - searchJavaInMacJavaVirtualMachines(javaRuntimes, Paths.get(System.getProperty("user.home"), "/Library/Java/JavaVirtualMachines")); - tryAddJavaExecutable(javaRuntimes, Paths.get("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java")); - tryAddJavaExecutable(javaRuntimes, Paths.get("/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/MacOS/itms/java/bin/java")); + searcher.searchJavaInMacJavaVirtualMachines(Path.of("/Library/Java/JavaVirtualMachines")); + searcher.searchJavaInMacJavaVirtualMachines(Path.of(System.getProperty("user.home"), "/Library/Java/JavaVirtualMachines")); + searcher.tryAddJavaExecutable(Path.of("/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java")); + searcher.tryAddJavaExecutable(Path.of("/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/MacOS/itms/java/bin/java")); // Homebrew - tryAddJavaExecutable(javaRuntimes, Paths.get("/opt/homebrew/opt/java/bin/java")); - searchAllJavaInDirectory(javaRuntimes, Paths.get("/opt/homebrew/Cellar/openjdk")); - try (DirectoryStream dirs = Files.newDirectoryStream(Paths.get("/opt/homebrew/Cellar"), "openjdk@*")) { + searcher.tryAddJavaExecutable(Path.of("/opt/homebrew/opt/java/bin/java")); + searcher.searchAllJavaInDirectory(Path.of("/opt/homebrew/Cellar/openjdk")); + try (DirectoryStream dirs = Files.newDirectoryStream(Path.of("/opt/homebrew/Cellar"), "openjdk@*")) { for (Path dir : dirs) { - searchAllJavaInDirectory(javaRuntimes, dir); + searcher.searchAllJavaInDirectory(dir); } } catch (IOException e) { LOG.warning("Failed to get subdirectories of /opt/homebrew/Cellar"); @@ -397,16 +394,16 @@ private static Map searchPotentialJavaExecutables() { // Search Minecraft bundled runtimes if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS && Architecture.SYSTEM_ARCH.isX86()) { FileUtils.tryGetPath(System.getenv("localappdata"), "Packages\\Microsoft.4297127D64EC6_8wekyb3d8bbwe\\LocalCache\\Local\\runtime") - .ifPresent(it -> searchAllOfficialJava(javaRuntimes, it, false)); + .ifPresent(it -> searcher.searchAllOfficialJava(it, false)); FileUtils.tryGetPath(Lang.requireNonNullElse(System.getenv("ProgramFiles(x86)"), "C:\\Program Files (x86)"), "Minecraft Launcher\\runtime") - .ifPresent(it -> searchAllOfficialJava(javaRuntimes, it, false)); + .ifPresent(it -> searcher.searchAllOfficialJava(it, false)); } else if (OperatingSystem.CURRENT_OS == OperatingSystem.LINUX && Architecture.SYSTEM_ARCH == Architecture.X86_64) { - searchAllOfficialJava(javaRuntimes, Paths.get(System.getProperty("user.home"), ".minecraft/runtime"), false); + searcher.searchAllOfficialJava(Path.of(System.getProperty("user.home"), ".minecraft/runtime"), false); } else if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS) { - searchAllOfficialJava(javaRuntimes, Paths.get(System.getProperty("user.home"), "Library/Application Support/minecraft/runtime"), false); + searcher.searchAllOfficialJava(Path.of(System.getProperty("user.home"), "Library/Application Support/minecraft/runtime"), false); } - searchAllOfficialJava(javaRuntimes, CacheRepository.getInstance().getCacheDirectory().resolve("java"), true); + searcher.searchAllOfficialJava(CacheRepository.getInstance().getCacheDirectory().resolve("java"), true); // Search in PATH. if (System.getenv("PATH") != null) { @@ -420,7 +417,7 @@ private static Map searchPotentialJavaExecutables() { } try { - tryAddJavaExecutable(javaRuntimes, Path.of(path, OperatingSystem.CURRENT_OS.getJavaExecutable())); + searcher.tryAddJavaExecutable(Path.of(path, OperatingSystem.CURRENT_OS.getJavaExecutable())); } catch (InvalidPathException ignored) { } } @@ -430,16 +427,17 @@ private static Map searchPotentialJavaExecutables() { String[] paths = System.getenv("HMCL_JRES").split(File.pathSeparator); for (String path : paths) { try { - tryAddJavaHome(javaRuntimes, Paths.get(path)); + searcher.tryAddJavaHome(Path.of(path)); } catch (InvalidPathException ignored) { } } } - searchAllJavaInDirectory(javaRuntimes, Paths.get(System.getProperty("user.home"), ".jdks")); + + searcher.searchAllJavaInDirectory(Path.of(System.getProperty("user.home"), ".jdks")); for (String javaPath : ConfigHolder.globalConfig().getUserJava()) { try { - tryAddJavaExecutable(javaRuntimes, Paths.get(javaPath)); + searcher.tryAddJavaExecutable(Path.of(javaPath)); } catch (InvalidPathException e) { LOG.warning("Invalid Java path: " + javaPath); } @@ -447,233 +445,238 @@ private static Map searchPotentialJavaExecutables() { JavaRuntime currentJava = JavaRuntime.CURRENT_JAVA; if (currentJava != null - && !javaRuntimes.containsKey(currentJava.getBinary()) + && !searcher.javaRuntimes.containsKey(currentJava.getBinary()) && !ConfigHolder.globalConfig().getDisabledJava().contains(currentJava.getBinary().toString())) { - javaRuntimes.put(currentJava.getBinary(), currentJava); + searcher.javaRuntimes.put(currentJava.getBinary(), currentJava); } - LOG.trace(javaRuntimes.values().stream().sorted() + LOG.trace(searcher.javaRuntimes.values().stream().sorted() .map(it -> String.format(" - %s %s (%s, %s): %s", it.isJDK() ? "JDK" : "JRE", it.getVersion(), it.getPlatform().getArchitecture().getDisplayName(), Lang.requireNonNullElse(it.getVendor(), "Unknown"), it.getBinary())) - .collect(Collectors.joining("\n", "Finished Java lookup, found " + javaRuntimes.size() + "\n", ""))); + .collect(Collectors.joining("\n", "Finished Java lookup, found " + searcher.javaRuntimes.size() + "\n", ""))); - return javaRuntimes; + return searcher.javaRuntimes; } + + private static final class Searcher { + final Map javaRuntimes = new HashMap<>(); - private static void tryAddJavaHome(Map javaRuntimes, Path javaHome) { - Path executable = getExecutable(javaHome); - if (!Files.isRegularFile(executable)) { - return; - } + void tryAddJavaHome(Path javaHome) { + Path executable = getExecutable(javaHome); + if (!Files.isRegularFile(executable)) { + return; + } - try { - executable = executable.toRealPath(); - } catch (IOException e) { - LOG.warning("Failed to resolve path " + executable, e); - return; - } + try { + executable = executable.toRealPath(); + } catch (IOException e) { + LOG.warning("Failed to resolve path " + executable, e); + return; + } - if (javaRuntimes.containsKey(executable) || ConfigHolder.globalConfig().getDisabledJava().contains(executable.toString())) { - return; - } + if (javaRuntimes.containsKey(executable) || ConfigHolder.globalConfig().getDisabledJava().contains(executable.toString())) { + return; + } - JavaInfo info = null; + JavaInfo info = null; - Path releaseFile = javaHome.resolve("release"); - if (Files.exists(releaseFile)) { - try { - info = JavaInfo.fromReleaseFile(releaseFile); - } catch (IOException e) { - LOG.warning("Failed to read release file " + releaseFile, e); + Path releaseFile = javaHome.resolve("release"); + if (Files.exists(releaseFile)) { + try { + info = JavaInfo.fromReleaseFile(releaseFile); + } catch (IOException e) { + LOG.warning("Failed to read release file " + releaseFile, e); + } + } + + if (info == null) { + try { + info = JavaInfoUtils.fromExecutable(executable, false); + } catch (IOException e) { + LOG.warning("Failed to lookup Java executable at " + executable, e); + } } + + if (info != null && isCompatible(info.getPlatform())) + javaRuntimes.put(executable, JavaRuntime.of(executable, info, false)); } - if (info == null) { + void tryAddJavaExecutable(Path executable) { try { - info = JavaInfoUtils.fromExecutable(executable, false); + executable = executable.toRealPath(); } catch (IOException e) { - LOG.warning("Failed to lookup Java executable at " + executable, e); + return; } - } - - if (info != null && isCompatible(info.getPlatform())) - javaRuntimes.put(executable, JavaRuntime.of(executable, info, false)); - } - - private static void tryAddJavaExecutable(Map javaRuntimes, Path executable) { - try { - executable = executable.toRealPath(); - } catch (IOException e) { - return; - } - if (javaRuntimes.containsKey(executable) || ConfigHolder.globalConfig().getDisabledJava().contains(executable.toString())) { - return; - } + if (javaRuntimes.containsKey(executable) || ConfigHolder.globalConfig().getDisabledJava().contains(executable.toString())) { + return; + } - JavaInfo info = null; - try { - info = JavaInfoUtils.fromExecutable(executable, true); - } catch (IOException e) { - LOG.warning("Failed to lookup Java executable at " + executable, e); - } + JavaInfo info = null; + try { + info = JavaInfoUtils.fromExecutable(executable, true); + } catch (IOException e) { + LOG.warning("Failed to lookup Java executable at " + executable, e); + } - if (info != null && isCompatible(info.getPlatform())) { - javaRuntimes.put(executable, JavaRuntime.of(executable, info, false)); + if (info != null && isCompatible(info.getPlatform())) { + javaRuntimes.put(executable, JavaRuntime.of(executable, info, false)); + } } - } - private static void tryAddJavaInComponentDir(Map javaRuntimes, String platform, Path component, boolean verify) { - Path sha1File = component.resolve(platform).resolve(component.getFileName() + ".sha1"); - if (!Files.isRegularFile(sha1File)) - return; + void tryAddJavaInComponentDir(String platform, Path component, boolean verify) { + Path sha1File = component.resolve(platform).resolve(component.getFileName() + ".sha1"); + if (!Files.isRegularFile(sha1File)) + return; - Path dir = component.resolve(platform).resolve(component.getFileName()); + Path dir = component.resolve(platform).resolve(component.getFileName()); - if (verify) { - try (BufferedReader reader = Files.newBufferedReader(sha1File)) { - String line; - while ((line = reader.readLine()) != null) { - if (line.isEmpty()) continue; + if (verify) { + try (BufferedReader reader = Files.newBufferedReader(sha1File)) { + String line; + while ((line = reader.readLine()) != null) { + if (line.isEmpty()) continue; - int idx = line.indexOf(" /#//"); - if (idx <= 0) - throw new IOException("Illegal line: " + line); + int idx = line.indexOf(" /#//"); + if (idx <= 0) + throw new IOException("Illegal line: " + line); - Path file = dir.resolve(line.substring(0, idx)); + Path file = dir.resolve(line.substring(0, idx)); - // Should we check the sha1 of files? This will take a lot of time. - if (Files.notExists(file)) - throw new NoSuchFileException(file.toAbsolutePath().toString()); + // Should we check the sha1 of files? This will take a lot of time. + if (Files.notExists(file)) + throw new NoSuchFileException(file.toAbsolutePath().toString()); + } + } catch (IOException e) { + LOG.warning("Failed to verify Java in " + component, e); + return; } - } catch (IOException e) { - LOG.warning("Failed to verify Java in " + component, e); - return; } - } - - if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS) { - Path macPath = dir.resolve("jre.bundle/Contents/Home"); - if (Files.exists(macPath)) { - tryAddJavaHome(javaRuntimes, macPath); - return; - } else - LOG.warning("The Java is not in 'jre.bundle/Contents/Home'"); - } - tryAddJavaHome(javaRuntimes, dir); - } + if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS) { + Path macPath = dir.resolve("jre.bundle/Contents/Home"); + if (Files.exists(macPath)) { + tryAddJavaHome(macPath); + return; + } else + LOG.warning("The Java is not in 'jre.bundle/Contents/Home'"); + } - private static void searchAllJavaInRepository(Map javaRuntimes, Platform platform) { - for (JavaRuntime java : REPOSITORY.getAllJava(platform)) { - javaRuntimes.put(java.getBinary(), java); + tryAddJavaHome(dir); } - for (JavaRuntime java : LOCAL_REPOSITORY.getAllJava(platform)) { - javaRuntimes.put(java.getBinary(), java); + void searchAllJavaInRepository(Platform platform) { + for (JavaRuntime java : REPOSITORY.getAllJava(platform)) { + javaRuntimes.put(java.getBinary(), java); + } + + for (JavaRuntime java : LOCAL_REPOSITORY.getAllJava(platform)) { + javaRuntimes.put(java.getBinary(), java); + } } - } - private static void searchAllOfficialJava(Map javaRuntimes, Path directory, boolean verify) { - if (!Files.isDirectory(directory)) - return; - // 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 + void searchAllOfficialJava(Path directory, boolean verify) { + if (!Files.isDirectory(directory)) + return; + // 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 - String javaPlatform = getMojangJavaPlatform(Platform.SYSTEM_PLATFORM); - if (javaPlatform != null) { - searchAllOfficialJava(javaRuntimes, directory, javaPlatform, verify); - } + String javaPlatform = getMojangJavaPlatform(Platform.SYSTEM_PLATFORM); + if (javaPlatform != null) { + searchAllOfficialJava(directory, javaPlatform, verify); + } - if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) { - if (Architecture.SYSTEM_ARCH == Architecture.X86_64) { - searchAllOfficialJava(javaRuntimes, directory, getMojangJavaPlatform(Platform.WINDOWS_X86), verify); - } else if (Architecture.SYSTEM_ARCH == Architecture.ARM64) { - if (OperatingSystem.SYSTEM_BUILD_NUMBER >= 21277) { - searchAllOfficialJava(javaRuntimes, directory, getMojangJavaPlatform(Platform.WINDOWS_X86_64), verify); + if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) { + if (Architecture.SYSTEM_ARCH == Architecture.X86_64) { + searchAllOfficialJava(directory, getMojangJavaPlatform(Platform.WINDOWS_X86), verify); + } else if (Architecture.SYSTEM_ARCH == Architecture.ARM64) { + if (OperatingSystem.SYSTEM_BUILD_NUMBER >= 21277) { + searchAllOfficialJava(directory, getMojangJavaPlatform(Platform.WINDOWS_X86_64), verify); + } + searchAllOfficialJava(directory, getMojangJavaPlatform(Platform.WINDOWS_X86), verify); } - searchAllOfficialJava(javaRuntimes, directory, getMojangJavaPlatform(Platform.WINDOWS_X86), verify); + } else if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS && Architecture.CURRENT_ARCH == Architecture.ARM64) { + searchAllOfficialJava(directory, getMojangJavaPlatform(Platform.MACOS_X86_64), verify); } - } else if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS && Architecture.CURRENT_ARCH == Architecture.ARM64) { - searchAllOfficialJava(javaRuntimes, directory, getMojangJavaPlatform(Platform.MACOS_X86_64), verify); } - } - private static void searchAllOfficialJava(Map javaRuntimes, Path directory, String platform, boolean verify) { - try (DirectoryStream dir = Files.newDirectoryStream(directory)) { - // 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) { - tryAddJavaInComponentDir(javaRuntimes, platform, component, verify); + void searchAllOfficialJava(Path directory, String platform, boolean verify) { + try (DirectoryStream dir = Files.newDirectoryStream(directory)) { + // 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) { + tryAddJavaInComponentDir(platform, component, verify); + } + } catch (IOException e) { + LOG.warning("Failed to list java-runtime directory " + directory, e); } - } catch (IOException e) { - LOG.warning("Failed to list java-runtime directory " + directory, e); } - } - private static void searchAllJavaInDirectory(Map javaRuntimes, Path directory) { - if (!Files.isDirectory(directory)) { - return; - } + void searchAllJavaInDirectory(Path directory) { + if (!Files.isDirectory(directory)) { + return; + } - try (DirectoryStream stream = Files.newDirectoryStream(directory)) { - for (Path subDir : stream) { - tryAddJavaHome(javaRuntimes, subDir); + try (DirectoryStream stream = Files.newDirectoryStream(directory)) { + for (Path subDir : stream) { + tryAddJavaHome(subDir); + } + } catch (IOException e) { + LOG.warning("Failed to find Java in " + directory, e); } - } catch (IOException e) { - LOG.warning("Failed to find Java in " + directory, e); } - } - private static void searchJavaInProgramFiles(Map javaRuntimes, String env, String defaultValue) { - String programFiles = Lang.requireNonNullElse(System.getenv(env), defaultValue); - Path path; - try { - path = Paths.get(programFiles); - } catch (InvalidPathException ignored) { - return; - } + void searchJavaInProgramFiles(String env, String defaultValue) { + String programFiles = Lang.requireNonNullElse(System.getenv(env), defaultValue); + Path path; + try { + path = Path.of(programFiles); + } catch (InvalidPathException ignored) { + return; + } - for (String vendor : new String[]{"Java", "BellSoft", "AdoptOpenJDK", "Zulu", "Microsoft", "Eclipse Foundation", "Semeru"}) { - searchAllJavaInDirectory(javaRuntimes, path.resolve(vendor)); + for (String vendor : new String[]{"Java", "BellSoft", "AdoptOpenJDK", "Zulu", "Microsoft", "Eclipse Foundation", "Semeru"}) { + searchAllJavaInDirectory(path.resolve(vendor)); + } } - } - private static void searchJavaInMacJavaVirtualMachines(Map javaRuntimes, Path directory) { - if (!Files.isDirectory(directory)) { - return; - } + void searchJavaInMacJavaVirtualMachines(Path directory) { + if (!Files.isDirectory(directory)) { + return; + } - try (DirectoryStream stream = Files.newDirectoryStream(directory)) { - for (Path subDir : stream) { - tryAddJavaHome(javaRuntimes, subDir.resolve("Contents/Home")); + try (DirectoryStream stream = Files.newDirectoryStream(directory)) { + for (Path subDir : stream) { + tryAddJavaHome(subDir.resolve("Contents/Home")); + } + } catch (IOException e) { + LOG.warning("Failed to find Java in " + directory, e); } - } catch (IOException e) { - LOG.warning("Failed to find Java in " + directory, e); } - } - // ==== Windows Registry Support ==== - private static void queryJavaInRegistryKey(Map javaRuntimes, WinReg.HKEY hkey, String location) { - WinReg reg = WinReg.INSTANCE; - if (reg == null) - return; - - for (String java : reg.querySubKeys(hkey, location)) { - if (!reg.querySubKeys(hkey, java).contains(java + "\\MSI")) - continue; - Object home = reg.queryValue(hkey, java, "JavaHome"); - if (home instanceof String) { - try { - tryAddJavaHome(javaRuntimes, Paths.get((String) home)); - } catch (InvalidPathException e) { - LOG.warning("Invalid Java path in system registry: " + home); + // ==== Windows Registry Support ==== + void queryJavaInRegistryKey(WinReg.HKEY hkey, String location) { + WinReg reg = WinReg.INSTANCE; + if (reg == null) + return; + + for (String java : reg.querySubKeys(hkey, location)) { + if (!reg.querySubKeys(hkey, java).contains(java + "\\MSI")) + continue; + Object home = reg.queryValue(hkey, java, "JavaHome"); + if (home instanceof String) { + try { + tryAddJavaHome(Path.of((String) home)); + } catch (InvalidPathException e) { + LOG.warning("Invalid Java path in system registry: " + home); + } } } } + } } From c08b93639180d166238e9c141bcf1c0b8da65577 Mon Sep 17 00:00:00 2001 From: Glavo Date: Tue, 21 Oct 2025 16:28:48 +0800 Subject: [PATCH 03/22] update --- .../org/jackhuang/hmcl/java/JavaInfoCache.java | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfoCache.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfoCache.java index 7d95882730..a76e73db81 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfoCache.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfoCache.java @@ -27,17 +27,14 @@ import java.lang.reflect.Type; import java.nio.file.Files; import java.nio.file.Path; -import java.util.LinkedHashMap; import java.util.List; -import static org.jackhuang.hmcl.util.logging.Logger.LOG; - @JsonSerializable @JsonAdapter(JavaInfoCache.Serializer.class) public record JavaInfoCache(String realPath, String cacheKey, JavaInfo javaInfo) { public static final long FORMAT_VERSION = 0L; - public static LinkedHashMap loadCacheMap(Path cacheFile) throws IOException { + public static List loadCacheMap(Path cacheFile) throws IOException { if (Files.notExists(cacheFile)) throw new FileNotFoundException("Cache file does not exist: " + cacheFile); @@ -51,20 +48,9 @@ public static LinkedHashMap loadCacheMap(Path cacheFile) th if (version != FORMAT_VERSION) throw new IOException("Unsupported cache file, version: %d".formatted(version)); - List caches = JsonUtils.GSON.fromJson( + return JsonUtils.GSON.fromJson( jsonObject.getAsJsonArray("cache"), JsonUtils.listTypeOf(JavaInfoCache.class)); - - LinkedHashMap result = new LinkedHashMap<>(); - for (JavaInfoCache item : caches) { - try { - Path path = Path.of(item.realPath); - result.put(path, item); - } catch (Exception e) { - LOG.warning("Java info cache invalid: " + item.realPath, e); - } - } - return result; } public static final class Serializer implements JsonSerializer, JsonDeserializer { From f95c79f5a21d73ec9171499256f67c8026cfa342 Mon Sep 17 00:00:00 2001 From: Glavo Date: Tue, 21 Oct 2025 16:30:55 +0800 Subject: [PATCH 04/22] update --- .../java/org/jackhuang/hmcl/java/JavaManager.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java index 2737552951..629e0ba909 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java @@ -55,6 +55,16 @@ public final class JavaManager { private JavaManager() { } + private static final String[] KNOWN_VENDOR_DIRECTORIES = { + "Java", + "BellSoft", + "AdoptOpenJDK", + "Zulu", + "Microsoft", + "Eclipse Foundation", + "Semeru" + }; + public static final HMCLJavaRepository REPOSITORY = new HMCLJavaRepository(Metadata.HMCL_GLOBAL_DIRECTORY.resolve("java")); public static final HMCLJavaRepository LOCAL_REPOSITORY = new HMCLJavaRepository(Metadata.HMCL_CURRENT_DIRECTORY.resolve("java")); @@ -461,7 +471,7 @@ private static Map searchPotentialJavaExecutables() { return searcher.javaRuntimes; } - + private static final class Searcher { final Map javaRuntimes = new HashMap<>(); @@ -639,7 +649,7 @@ void searchJavaInProgramFiles(String env, String defaultValue) { return; } - for (String vendor : new String[]{"Java", "BellSoft", "AdoptOpenJDK", "Zulu", "Microsoft", "Eclipse Foundation", "Semeru"}) { + for (String vendor : KNOWN_VENDOR_DIRECTORIES) { searchAllJavaInDirectory(path.resolve(vendor)); } } From 8256e129853cba224f18306d460f08a27642f698 Mon Sep 17 00:00:00 2001 From: Glavo Date: Fri, 24 Oct 2025 16:11:26 +0800 Subject: [PATCH 05/22] update --- .../org/jackhuang/hmcl/java/JavaManager.java | 59 +++++++++++++++++++ .../jackhuang/hmcl/java/JavaInfoCache.java | 37 ------------ 2 files changed, 59 insertions(+), 37 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java index 629e0ba909..b3227d3257 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java @@ -17,6 +17,8 @@ */ package org.jackhuang.hmcl.java; +import com.google.gson.*; +import com.google.gson.annotations.JsonAdapter; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import org.jackhuang.hmcl.Metadata; @@ -31,6 +33,8 @@ import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.util.CacheRepository; import org.jackhuang.hmcl.util.Lang; +import org.jackhuang.hmcl.util.gson.JsonSerializable; +import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.platform.*; import org.jackhuang.hmcl.util.platform.windows.WinReg; @@ -40,6 +44,7 @@ import java.io.BufferedReader; import java.io.File; import java.io.IOException; +import java.lang.reflect.Type; import java.nio.file.*; import java.util.*; import java.util.concurrent.CountDownLatch; @@ -474,6 +479,40 @@ private static Map searchPotentialJavaExecutables() { private static final class Searcher { final Map javaRuntimes = new HashMap<>(); + private final LinkedHashMap caches = new LinkedHashMap<>(); + private boolean needRefreshCache = false; + + void loadCache(Path cacheFile) { + if (Files.notExists(cacheFile)) { + return; + } + + try { + JsonObject jsonObject = JsonUtils.fromJsonFile(cacheFile, JsonObject.class); + JsonElement fileVersion = jsonObject.get("version"); + if (!(fileVersion instanceof JsonPrimitive)) + throw new IOException("Invalid version JSON: " + fileVersion); + + int version = fileVersion.getAsJsonPrimitive().getAsInt(); + if (version != JavaInfoCache.FORMAT_VERSION) + throw new IOException("Unsupported cache file, version: %d".formatted(version)); + + for (JavaInfoCache cache : JsonUtils.GSON.fromJson( + jsonObject.getAsJsonArray("cache"), + JsonUtils.listTypeOf(JavaInfoCache.class))) { + try { + Path realPath = Path.of(cache.realPath).toRealPath(); + caches.put(realPath, cache); + } catch (Exception e) { + LOG.warning("Invalid cache: " + cache); + needRefreshCache = true; + } + } + } catch (Exception ex) { + LOG.warning("Failed to load cache file: " + cacheFile); + needRefreshCache = true; + } + } void tryAddJavaHome(Path javaHome) { Path executable = getExecutable(javaHome); @@ -689,4 +728,24 @@ void queryJavaInRegistryKey(WinReg.HKEY hkey, String location) { } } + + @JsonSerializable + @JsonAdapter(JavaInfoCache.Serializer.class) + private record JavaInfoCache(String realPath, String cacheKey, JavaInfo javaInfo) { + public static final long FORMAT_VERSION = 0L; + + public static final class Serializer implements JsonSerializer, JsonDeserializer { + + @Override + public JavaInfoCache deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { + return null; + } + + @Override + public JsonElement serialize(JavaInfoCache javaInfoCache, Type type, JsonSerializationContext jsonSerializationContext) { + return null; + } + } + } + } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfoCache.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfoCache.java index a76e73db81..3d3909f38a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfoCache.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfoCache.java @@ -29,40 +29,3 @@ import java.nio.file.Path; import java.util.List; -@JsonSerializable -@JsonAdapter(JavaInfoCache.Serializer.class) -public record JavaInfoCache(String realPath, String cacheKey, JavaInfo javaInfo) { - public static final long FORMAT_VERSION = 0L; - - public static List loadCacheMap(Path cacheFile) throws IOException { - if (Files.notExists(cacheFile)) - throw new FileNotFoundException("Cache file does not exist: " + cacheFile); - - JsonObject jsonObject = JsonUtils.fromJsonFile(cacheFile, JsonObject.class); - - JsonElement fileVersion = jsonObject.get("version"); - if (!(fileVersion instanceof JsonPrimitive)) - throw new IOException("Invalid version JSON: " + fileVersion); - - int version = fileVersion.getAsJsonPrimitive().getAsInt(); - if (version != FORMAT_VERSION) - throw new IOException("Unsupported cache file, version: %d".formatted(version)); - - return JsonUtils.GSON.fromJson( - jsonObject.getAsJsonArray("cache"), - JsonUtils.listTypeOf(JavaInfoCache.class)); - } - - public static final class Serializer implements JsonSerializer, JsonDeserializer { - - @Override - public JavaInfoCache deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { - return null; - } - - @Override - public JsonElement serialize(JavaInfoCache javaInfoCache, Type type, JsonSerializationContext jsonSerializationContext) { - return null; - } - } -} From 350651fb666a49983410e3d2a15703f8eee5330b Mon Sep 17 00:00:00 2001 From: Glavo Date: Fri, 24 Oct 2025 16:37:12 +0800 Subject: [PATCH 06/22] update --- .../org/jackhuang/hmcl/java/JavaManager.java | 53 ++++++++++++++++--- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java index b3227d3257..6a71460102 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java @@ -19,6 +19,7 @@ import com.google.gson.*; import com.google.gson.annotations.JsonAdapter; +import com.google.gson.stream.JsonWriter; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import org.jackhuang.hmcl.Metadata; @@ -44,7 +45,9 @@ import java.io.BufferedReader; import java.io.File; import java.io.IOException; +import java.io.OutputStreamWriter; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.util.*; import java.util.concurrent.CountDownLatch; @@ -350,7 +353,7 @@ public static void initialize() { // search java private static Map searchPotentialJavaExecutables() { - Searcher searcher = new Searcher(); + Searcher searcher = new Searcher(Metadata.HMCL_GLOBAL_DIRECTORY.resolve("javaCache.json")); searcher.searchAllJavaInRepository(Platform.SYSTEM_PLATFORM); switch (OperatingSystem.CURRENT_OS) { case WINDOWS: @@ -465,6 +468,8 @@ private static Map searchPotentialJavaExecutables() { searcher.javaRuntimes.put(currentJava.getBinary(), currentJava); } + searcher.saveCache(); + LOG.trace(searcher.javaRuntimes.values().stream().sorted() .map(it -> String.format(" - %s %s (%s, %s): %s", it.isJDK() ? "JDK" : "JRE", @@ -473,19 +478,20 @@ private static Map searchPotentialJavaExecutables() { Lang.requireNonNullElse(it.getVendor(), "Unknown"), it.getBinary())) .collect(Collectors.joining("\n", "Finished Java lookup, found " + searcher.javaRuntimes.size() + "\n", ""))); - return searcher.javaRuntimes; } private static final class Searcher { + private final Path cacheFile; final Map javaRuntimes = new HashMap<>(); private final LinkedHashMap caches = new LinkedHashMap<>(); private boolean needRefreshCache = false; - void loadCache(Path cacheFile) { - if (Files.notExists(cacheFile)) { + Searcher(Path cacheFile) { + this.cacheFile = cacheFile; + + if (Files.notExists(cacheFile)) return; - } try { JsonObject jsonObject = JsonUtils.fromJsonFile(cacheFile, JsonObject.class); @@ -498,7 +504,7 @@ void loadCache(Path cacheFile) { throw new IOException("Unsupported cache file, version: %d".formatted(version)); for (JavaInfoCache cache : JsonUtils.GSON.fromJson( - jsonObject.getAsJsonArray("cache"), + jsonObject.getAsJsonArray("caches"), JsonUtils.listTypeOf(JavaInfoCache.class))) { try { Path realPath = Path.of(cache.realPath).toRealPath(); @@ -514,6 +520,41 @@ void loadCache(Path cacheFile) { } } + void saveCache() { + if (!needRefreshCache) + return; + + try { + FileUtils.saveSafely(cacheFile, output -> { + try (var writer = new JsonWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8))) { + writer.beginObject(); + + writer.name("version").value(JavaInfoCache.FORMAT_VERSION); + + writer.name("caches"); + writer.beginArray(); + for (Map.Entry entry : caches.entrySet()) { + Path realPath = entry.getKey(); + JavaInfoCache cache = entry.getValue(); + + writer.beginObject(); + + writer.name("realPath").value(realPath.toString()); + writer.name("cacheKey").value(cache.cacheKey()); + writer.name("javaInfo").jsonValue(JsonUtils.GSON.toJson(cache.javaInfo().toString())); + + writer.endObject(); + } + writer.endArray(); + + writer.endObject(); + } + }); + } catch (Exception e) { + LOG.warning("Failed to save cache file: " + cacheFile); + } + } + void tryAddJavaHome(Path javaHome) { Path executable = getExecutable(javaHome); if (!Files.isRegularFile(executable)) { From 678d7583ffdd52fbf8987f75f864bc6369c167a8 Mon Sep 17 00:00:00 2001 From: Glavo Date: Fri, 24 Oct 2025 16:41:59 +0800 Subject: [PATCH 07/22] update --- .../java/org/jackhuang/hmcl/java/JavaManager.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java index 6a71460102..f01587a41b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java @@ -555,6 +555,10 @@ void saveCache() { } } + private boolean loadFromCache(Path realPath) { + return false; // TODO + } + void tryAddJavaHome(Path javaHome) { Path executable = getExecutable(javaHome); if (!Files.isRegularFile(executable)) { @@ -568,7 +572,9 @@ void tryAddJavaHome(Path javaHome) { return; } - if (javaRuntimes.containsKey(executable) || ConfigHolder.globalConfig().getDisabledJava().contains(executable.toString())) { + if (javaRuntimes.containsKey(executable) + || ConfigHolder.globalConfig().getDisabledJava().contains(executable.toString()) + || loadFromCache(executable)) { return; } @@ -602,7 +608,9 @@ void tryAddJavaExecutable(Path executable) { return; } - if (javaRuntimes.containsKey(executable) || ConfigHolder.globalConfig().getDisabledJava().contains(executable.toString())) { + if (javaRuntimes.containsKey(executable) + || ConfigHolder.globalConfig().getDisabledJava().contains(executable.toString()) + || loadFromCache(executable)) { return; } From eb0e4539a871741fd054b85ce0d37907bcc30f76 Mon Sep 17 00:00:00 2001 From: Glavo Date: Fri, 24 Oct 2025 16:47:25 +0800 Subject: [PATCH 08/22] update --- .../org/jackhuang/hmcl/java/JavaManager.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java index f01587a41b..74f7770094 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java @@ -465,7 +465,7 @@ private static Map searchPotentialJavaExecutables() { if (currentJava != null && !searcher.javaRuntimes.containsKey(currentJava.getBinary()) && !ConfigHolder.globalConfig().getDisabledJava().contains(currentJava.getBinary().toString())) { - searcher.javaRuntimes.put(currentJava.getBinary(), currentJava); + searcher.addResult(currentJava.getBinary(), currentJava); } searcher.saveCache(); @@ -556,9 +556,17 @@ void saveCache() { } private boolean loadFromCache(Path realPath) { + JavaInfoCache cache = caches.get(realPath); + if (cache == null) + return false; + return false; // TODO } + void addResult(Path realPath, JavaRuntime javaRuntime) { + javaRuntimes.put(realPath, javaRuntime); + } + void tryAddJavaHome(Path javaHome) { Path executable = getExecutable(javaHome); if (!Files.isRegularFile(executable)) { @@ -598,7 +606,7 @@ void tryAddJavaHome(Path javaHome) { } if (info != null && isCompatible(info.getPlatform())) - javaRuntimes.put(executable, JavaRuntime.of(executable, info, false)); + addResult(executable, JavaRuntime.of(executable, info, false)); } void tryAddJavaExecutable(Path executable) { @@ -622,7 +630,7 @@ void tryAddJavaExecutable(Path executable) { } if (info != null && isCompatible(info.getPlatform())) { - javaRuntimes.put(executable, JavaRuntime.of(executable, info, false)); + addResult(executable, JavaRuntime.of(executable, info, false)); } } @@ -669,11 +677,11 @@ void tryAddJavaInComponentDir(String platform, Path component, boolean verify) { void searchAllJavaInRepository(Platform platform) { for (JavaRuntime java : REPOSITORY.getAllJava(platform)) { - javaRuntimes.put(java.getBinary(), java); + addResult(java.getBinary(), java); } for (JavaRuntime java : LOCAL_REPOSITORY.getAllJava(platform)) { - javaRuntimes.put(java.getBinary(), java); + addResult(java.getBinary(), java); } } From f14ad0d82b796c7cafecfe9200d287cf66cfc94f Mon Sep 17 00:00:00 2001 From: Glavo Date: Fri, 24 Oct 2025 16:57:40 +0800 Subject: [PATCH 09/22] update --- .../hmcl/java/HMCLJavaRepository.java | 2 +- .../jackhuang/hmcl/java/JavaInfoUtils.java | 11 +------ .../org/jackhuang/hmcl/java/JavaManager.java | 33 ++++++++++++++++--- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/HMCLJavaRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/HMCLJavaRepository.java index b0100219ea..3231793385 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/HMCLJavaRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/HMCLJavaRepository.java @@ -163,7 +163,7 @@ public Task getDownloadJavaTask(DownloadProvider downloadProvider, JavaInfo info; if (JavaManager.isCompatible(platform)) - info = JavaInfoUtils.fromExecutable(executable, false); + info = JavaInfoUtils.fromExecutable(executable); else info = new JavaInfo(platform, result.download.getVersion().getName(), null); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaInfoUtils.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaInfoUtils.java index f67e4b1639..0dc66eaaee 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaInfoUtils.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaInfoUtils.java @@ -55,19 +55,10 @@ private static Path tryFindReleaseFile(Path executable) { return null; } - public static @NotNull JavaInfo fromExecutable(Path executable, boolean tryFindReleaseFile) throws IOException { + public static @NotNull JavaInfo fromExecutable(Path executable) throws IOException { assert executable.isAbsolute(); - Path releaseFile; - if (tryFindReleaseFile && (releaseFile = tryFindReleaseFile(executable)) != null) { - try { - return JavaInfo.fromReleaseFile(releaseFile); - } catch (IOException ignored) { - } - } - Path thisPath = JarUtils.thisJarPath(); - if (thisPath == null) { throw new IOException("Failed to find current HMCL location"); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java index 74f7770094..3aaf0e4bd4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java @@ -49,6 +49,8 @@ import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributeView; +import java.nio.file.attribute.BasicFileAttributes; import java.util.*; import java.util.concurrent.CountDownLatch; import java.util.stream.Collectors; @@ -178,7 +180,7 @@ public static JavaRuntime getJava(Path executable) throws IOException, Interrupt return javaRuntime; } - JavaInfo info = JavaInfoUtils.fromExecutable(executable, true); + JavaInfo info = JavaInfoUtils.fromExecutable(executable); return JavaRuntime.of(executable, info, false); } @@ -485,6 +487,7 @@ private static final class Searcher { private final Path cacheFile; final Map javaRuntimes = new HashMap<>(); private final LinkedHashMap caches = new LinkedHashMap<>(); + private final Set failed = new HashSet<>(); private boolean needRefreshCache = false; Searcher(Path cacheFile) { @@ -555,12 +558,32 @@ void saveCache() { } } + private static String getCacheKey(Path realPath) throws IOException { + StringBuilder builder = new StringBuilder(); + + + + BasicFileAttributes attributes = Files.getFileAttributeView(realPath, BasicFileAttributeView.class).readAttributes(); + + + + + return builder.toString(); // TODO + } + private boolean loadFromCache(Path realPath) { JavaInfoCache cache = caches.get(realPath); if (cache == null) return false; - return false; // TODO + try { + getCacheKey(realPath); + } catch (Exception e) { + } + + caches.remove(realPath); + needRefreshCache = true; + return false; } void addResult(Path realPath, JavaRuntime javaRuntime) { @@ -581,6 +604,7 @@ void tryAddJavaHome(Path javaHome) { } if (javaRuntimes.containsKey(executable) + || failed.contains(executable) || ConfigHolder.globalConfig().getDisabledJava().contains(executable.toString()) || loadFromCache(executable)) { return; @@ -599,7 +623,7 @@ void tryAddJavaHome(Path javaHome) { if (info == null) { try { - info = JavaInfoUtils.fromExecutable(executable, false); + info = JavaInfoUtils.fromExecutable(executable); } catch (IOException e) { LOG.warning("Failed to lookup Java executable at " + executable, e); } @@ -617,6 +641,7 @@ void tryAddJavaExecutable(Path executable) { } if (javaRuntimes.containsKey(executable) + || failed.contains(executable) || ConfigHolder.globalConfig().getDisabledJava().contains(executable.toString()) || loadFromCache(executable)) { return; @@ -624,7 +649,7 @@ void tryAddJavaExecutable(Path executable) { JavaInfo info = null; try { - info = JavaInfoUtils.fromExecutable(executable, true); + info = JavaInfoUtils.fromExecutable(executable); } catch (IOException e) { LOG.warning("Failed to lookup Java executable at " + executable, e); } From 9d392e9b86c160db376590fe67ad75dfb336c79b Mon Sep 17 00:00:00 2001 From: Glavo Date: Fri, 24 Oct 2025 21:32:58 +0800 Subject: [PATCH 10/22] update --- .../jackhuang/hmcl/java/JavaInfoUtils.java | 17 ---- .../org/jackhuang/hmcl/java/JavaManager.java | 96 +++++++++++++------ 2 files changed, 69 insertions(+), 44 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaInfoUtils.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaInfoUtils.java index 0dc66eaaee..2ec7b1f618 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaInfoUtils.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaInfoUtils.java @@ -27,7 +27,6 @@ import org.jetbrains.annotations.NotNull; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; /** @@ -39,22 +38,6 @@ public final class JavaInfoUtils { private JavaInfoUtils() { } - private static Path tryFindReleaseFile(Path executable) { - Path parent = executable.getParent(); - if (parent != null && parent.getFileName() != null && parent.getFileName().toString().equals("bin")) { - Path javaHome = parent.getParent(); - if (javaHome != null && javaHome.getFileName() != null) { - Path releaseFile = javaHome.resolve("release"); - String javaHomeName = javaHome.getFileName().toString(); - if ((javaHomeName.contains("jre") || javaHomeName.contains("jdk") || javaHomeName.contains("openj9")) - && Files.isRegularFile(releaseFile)) { - return releaseFile; - } - } - } - return null; - } - public static @NotNull JavaInfo fromExecutable(Path executable) throws IOException { assert executable.isAbsolute(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java index 3aaf0e4bd4..ab23f41a65 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java @@ -33,6 +33,7 @@ import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.util.CacheRepository; +import org.jackhuang.hmcl.util.DigestUtils; import org.jackhuang.hmcl.util.Lang; import org.jackhuang.hmcl.util.gson.JsonSerializable; import org.jackhuang.hmcl.util.gson.JsonUtils; @@ -49,7 +50,6 @@ import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.nio.file.*; -import java.nio.file.attribute.BasicFileAttributeView; import java.nio.file.attribute.BasicFileAttributes; import java.util.*; import java.util.concurrent.CountDownLatch; @@ -487,7 +487,7 @@ private static final class Searcher { private final Path cacheFile; final Map javaRuntimes = new HashMap<>(); private final LinkedHashMap caches = new LinkedHashMap<>(); - private final Set failed = new HashSet<>(); + private final Set failed = new HashSet<>(); private boolean needRefreshCache = false; Searcher(Path cacheFile) { @@ -527,6 +527,7 @@ void saveCache() { if (!needRefreshCache) return; + needRefreshCache = false; try { FileUtils.saveSafely(cacheFile, output -> { try (var writer = new JsonWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8))) { @@ -558,17 +559,69 @@ void saveCache() { } } - private static String getCacheKey(Path realPath) throws IOException { - StringBuilder builder = new StringBuilder(); + private static @Nullable String createCacheKey(Path realPath) { + Path binDir = realPath.getParent(); + if (binDir == null || !FileUtils.getName(binDir).equals("bin")) + return null; + + if (Files.isRegularFile(realPath.resolveSibling("ikvm.properties"))) + return null; + + Path javaHome = binDir.getParent(); + if (javaHome == null) + return null; + + String javaHomeName = javaHome.getFileName().toString(); + if (!javaHomeName.contains("java") + && !javaHomeName.contains("jre") + && !javaHomeName.contains("jdk") + && !javaHomeName.contains("openj9")) + return null; + + Path libDir = javaHome.resolve("lib"); + if (!Files.isDirectory(libDir)) + return null; + BasicFileAttributes launcherAttributes; + String releaseHash = null; + BasicFileAttributes coreLibsAttributes = null; - BasicFileAttributes attributes = Files.getFileAttributeView(realPath, BasicFileAttributeView.class).readAttributes(); + try { + launcherAttributes = Files.readAttributes(realPath, BasicFileAttributes.class); + + Path releaseFile = libDir.resolve("release"); + if (Files.exists(releaseFile)) { + releaseHash = DigestUtils.digestToString("SHA-1", releaseFile); + } else { + Path coreLibsFile = libDir.resolve("rt.jar"); + if (!Files.isRegularFile(coreLibsFile)) { + coreLibsFile = javaHome.resolve("jre/lib/rt.jar"); + if (!Files.isRegularFile(coreLibsFile)) + return null; + + coreLibsAttributes = Files.readAttributes(coreLibsFile, BasicFileAttributes.class); + } + } + } catch (Exception e) { + LOG.warning("Failed to create cache key for " + realPath, e); + return null; + } + StringJoiner joiner = new StringJoiner(","); + joiner.add("sz:" + launcherAttributes.size()); + joiner.add("lm:" + launcherAttributes.lastModifiedTime().toMillis()); + if (releaseHash != null) + joiner.add(releaseHash); - return builder.toString(); // TODO + if (coreLibsAttributes != null) { + joiner.add("rsz:" + coreLibsAttributes.size()); + joiner.add("rlm:" + coreLibsAttributes.lastModifiedTime().toMillis()); + } + + return joiner.toString(); } private boolean loadFromCache(Path realPath) { @@ -576,9 +629,10 @@ private boolean loadFromCache(Path realPath) { if (cache == null) return false; - try { - getCacheKey(realPath); - } catch (Exception e) { + String cacheKey = createCacheKey(realPath); + if (cacheKey != null && cacheKey.equals(cache.cacheKey())) { + javaRuntimes.put(realPath, JavaRuntime.of(realPath, cache.javaInfo, false)); + return true; } caches.remove(realPath); @@ -612,21 +666,10 @@ void tryAddJavaHome(Path javaHome) { JavaInfo info = null; - Path releaseFile = javaHome.resolve("release"); - if (Files.exists(releaseFile)) { - try { - info = JavaInfo.fromReleaseFile(releaseFile); - } catch (IOException e) { - LOG.warning("Failed to read release file " + releaseFile, e); - } - } - - if (info == null) { - try { - info = JavaInfoUtils.fromExecutable(executable); - } catch (IOException e) { - LOG.warning("Failed to lookup Java executable at " + executable, e); - } + try { + info = JavaInfoUtils.fromExecutable(executable); + } catch (IOException e) { + LOG.warning("Failed to lookup Java executable at " + executable, e); } if (info != null && isCompatible(info.getPlatform())) @@ -798,10 +841,9 @@ void queryJavaInRegistryKey(WinReg.HKEY hkey, String location) { for (String java : reg.querySubKeys(hkey, location)) { if (!reg.querySubKeys(hkey, java).contains(java + "\\MSI")) continue; - Object home = reg.queryValue(hkey, java, "JavaHome"); - if (home instanceof String) { + if (reg.queryValue(hkey, java, "JavaHome") instanceof String home) { try { - tryAddJavaHome(Path.of((String) home)); + tryAddJavaHome(Path.of(home)); } catch (InvalidPathException e) { LOG.warning("Invalid Java path in system registry: " + home); } From 9bff8253ee0194f61ba28cb350183f2248f7dea0 Mon Sep 17 00:00:00 2001 From: Glavo Date: Fri, 24 Oct 2025 21:53:49 +0800 Subject: [PATCH 11/22] update --- .../org/jackhuang/hmcl/java/JavaManager.java | 122 ++++++------------ 1 file changed, 41 insertions(+), 81 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java index ab23f41a65..0790df843b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java @@ -18,7 +18,6 @@ package org.jackhuang.hmcl.java; import com.google.gson.*; -import com.google.gson.annotations.JsonAdapter; import com.google.gson.stream.JsonWriter; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; @@ -35,7 +34,6 @@ import org.jackhuang.hmcl.util.CacheRepository; import org.jackhuang.hmcl.util.DigestUtils; import org.jackhuang.hmcl.util.Lang; -import org.jackhuang.hmcl.util.gson.JsonSerializable; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.platform.*; @@ -47,7 +45,6 @@ import java.io.File; import java.io.IOException; import java.io.OutputStreamWriter; -import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; @@ -492,7 +489,14 @@ private static final class Searcher { Searcher(Path cacheFile) { this.cacheFile = cacheFile; + } + + private static final long CACHE_VERSION = 0L; + + private record JavaInfoCache(String key, JavaInfo info) { + } + void loadCache() { if (Files.notExists(cacheFile)) return; @@ -503,17 +507,22 @@ private static final class Searcher { throw new IOException("Invalid version JSON: " + fileVersion); int version = fileVersion.getAsJsonPrimitive().getAsInt(); - if (version != JavaInfoCache.FORMAT_VERSION) + if (version != CACHE_VERSION) throw new IOException("Unsupported cache file, version: %d".formatted(version)); - for (JavaInfoCache cache : JsonUtils.GSON.fromJson( - jsonObject.getAsJsonArray("caches"), - JsonUtils.listTypeOf(JavaInfoCache.class))) { + JsonArray cachesArray = jsonObject.getAsJsonArray("caches"); + + for (JsonElement element : cachesArray) { try { - Path realPath = Path.of(cache.realPath).toRealPath(); - caches.put(realPath, cache); + var obj = (JsonObject) element; + + Path realPath = Path.of(obj.getAsJsonPrimitive("path").getAsString()).toRealPath(); + String key = obj.getAsJsonPrimitive("key").getAsString(); + JavaInfo info = JsonUtils.GSON.fromJson(obj.getAsJsonObject("info"), JavaInfo.class); + + caches.put(realPath, new JavaInfoCache(key, info)); } catch (Exception e) { - LOG.warning("Invalid cache: " + cache); + LOG.warning("Invalid cache: " + element); needRefreshCache = true; } } @@ -533,7 +542,7 @@ void saveCache() { try (var writer = new JsonWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8))) { writer.beginObject(); - writer.name("version").value(JavaInfoCache.FORMAT_VERSION); + writer.name("version").value(CACHE_VERSION); writer.name("caches"); writer.beginArray(); @@ -544,8 +553,8 @@ void saveCache() { writer.beginObject(); writer.name("realPath").value(realPath.toString()); - writer.name("cacheKey").value(cache.cacheKey()); - writer.name("javaInfo").jsonValue(JsonUtils.GSON.toJson(cache.javaInfo().toString())); + writer.name("key").value(cache.key()); + writer.name("info").jsonValue(JsonUtils.GSON.toJson(cache.info().toString())); writer.endObject(); } @@ -624,56 +633,12 @@ void saveCache() { return joiner.toString(); } - private boolean loadFromCache(Path realPath) { - JavaInfoCache cache = caches.get(realPath); - if (cache == null) - return false; - - String cacheKey = createCacheKey(realPath); - if (cacheKey != null && cacheKey.equals(cache.cacheKey())) { - javaRuntimes.put(realPath, JavaRuntime.of(realPath, cache.javaInfo, false)); - return true; - } - - caches.remove(realPath); - needRefreshCache = true; - return false; - } - void addResult(Path realPath, JavaRuntime javaRuntime) { javaRuntimes.put(realPath, javaRuntime); } void tryAddJavaHome(Path javaHome) { - Path executable = getExecutable(javaHome); - if (!Files.isRegularFile(executable)) { - return; - } - - try { - executable = executable.toRealPath(); - } catch (IOException e) { - LOG.warning("Failed to resolve path " + executable, e); - return; - } - - if (javaRuntimes.containsKey(executable) - || failed.contains(executable) - || ConfigHolder.globalConfig().getDisabledJava().contains(executable.toString()) - || loadFromCache(executable)) { - return; - } - - JavaInfo info = null; - - try { - info = JavaInfoUtils.fromExecutable(executable); - } catch (IOException e) { - LOG.warning("Failed to lookup Java executable at " + executable, e); - } - - if (info != null && isCompatible(info.getPlatform())) - addResult(executable, JavaRuntime.of(executable, info, false)); + tryAddJavaExecutable(getExecutable(javaHome)); } void tryAddJavaExecutable(Path executable) { @@ -685,11 +650,26 @@ void tryAddJavaExecutable(Path executable) { if (javaRuntimes.containsKey(executable) || failed.contains(executable) - || ConfigHolder.globalConfig().getDisabledJava().contains(executable.toString()) - || loadFromCache(executable)) { + || ConfigHolder.globalConfig().getDisabledJava().contains(executable.toString())) { return; } + String cacheKey = createCacheKey(executable); + if (cacheKey != null) { + JavaInfoCache cache = caches.get(executable); + if (cache != null) { + if (cacheKey.equals(cache.key())) { + javaRuntimes.put(executable, JavaRuntime.of(executable, cache.info(), false)); + return; + } else { + caches.remove(executable); + needRefreshCache = true; + } + } + } else if (caches.remove(executable) != null) { + needRefreshCache = true; + } + JavaInfo info = null; try { info = JavaInfoUtils.fromExecutable(executable); @@ -698,7 +678,7 @@ void tryAddJavaExecutable(Path executable) { } if (info != null && isCompatible(info.getPlatform())) { - addResult(executable, JavaRuntime.of(executable, info, false)); + javaRuntimes.put(executable, JavaRuntime.of(executable, info, false)); } } @@ -852,24 +832,4 @@ void queryJavaInRegistryKey(WinReg.HKEY hkey, String location) { } } - - @JsonSerializable - @JsonAdapter(JavaInfoCache.Serializer.class) - private record JavaInfoCache(String realPath, String cacheKey, JavaInfo javaInfo) { - public static final long FORMAT_VERSION = 0L; - - public static final class Serializer implements JsonSerializer, JsonDeserializer { - - @Override - public JavaInfoCache deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { - return null; - } - - @Override - public JsonElement serialize(JavaInfoCache javaInfoCache, Type type, JsonSerializationContext jsonSerializationContext) { - return null; - } - } - } - } From 74eceb192884c90536df85cde10e745a3fc8a6b1 Mon Sep 17 00:00:00 2001 From: Glavo Date: Fri, 24 Oct 2025 21:59:28 +0800 Subject: [PATCH 12/22] update --- .../jackhuang/hmcl/java/JavaInfoUtils.java | 1 - .../org/jackhuang/hmcl/java/JavaManager.java | 10 +++--- .../org/jackhuang/hmcl/java/JavaInfo.java | 35 +++++-------------- 3 files changed, 14 insertions(+), 32 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaInfoUtils.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaInfoUtils.java index 2ec7b1f618..d8c97a7bf4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaInfoUtils.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaInfoUtils.java @@ -68,7 +68,6 @@ private JavaInfoUtils() { ? architecture : Architecture.SYSTEM_ARCH); - return new JavaInfo(platform, result.javaVersion, result.javaVendor); } catch (IOException e) { throw e; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java index 0790df843b..03d1a07d76 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java @@ -547,12 +547,12 @@ void saveCache() { writer.name("caches"); writer.beginArray(); for (Map.Entry entry : caches.entrySet()) { - Path realPath = entry.getKey(); + Path path = entry.getKey(); JavaInfoCache cache = entry.getValue(); writer.beginObject(); - writer.name("realPath").value(realPath.toString()); + writer.name("path").value(path.toString()); writer.name("key").value(cache.key()); writer.name("info").jsonValue(JsonUtils.GSON.toJson(cache.info().toString())); @@ -591,7 +591,6 @@ void saveCache() { if (!Files.isDirectory(libDir)) return null; - BasicFileAttributes launcherAttributes; String releaseHash = null; BasicFileAttributes coreLibsAttributes = null; @@ -658,7 +657,7 @@ void tryAddJavaExecutable(Path executable) { if (cacheKey != null) { JavaInfoCache cache = caches.get(executable); if (cache != null) { - if (cacheKey.equals(cache.key())) { + if (isCompatible(cache.info().getPlatform()) && cacheKey.equals(cache.key())) { javaRuntimes.put(executable, JavaRuntime.of(executable, cache.info(), false)); return; } else { @@ -675,9 +674,10 @@ void tryAddJavaExecutable(Path executable) { info = JavaInfoUtils.fromExecutable(executable); } catch (IOException e) { LOG.warning("Failed to lookup Java executable at " + executable, e); + failed.add(executable); } - if (info != null && isCompatible(info.getPlatform())) { + if (info != null) { javaRuntimes.put(executable, JavaRuntime.of(executable, info, false)); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfo.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfo.java index 7f28b7464d..22833f9263 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfo.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfo.java @@ -31,8 +31,6 @@ import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.Map; /** @@ -85,12 +83,6 @@ public static JavaInfo fromReleaseFile(BufferedReader reader) throws IOException return new JavaInfo(Platform.getPlatform(os, arch), javaVersion, vendor); } - public static JavaInfo fromReleaseFile(Path releaseFile) throws IOException { - try (BufferedReader reader = Files.newBufferedReader(releaseFile)) { - return fromReleaseFile(reader); - } - } - public static JavaInfo fromArchive(ArchiveFileTree tree) throws IOException { if (tree.getRoot().getSubDirs().size() != 1 || !tree.getRoot().getFiles().isEmpty()) throw new IOException(); @@ -116,24 +108,15 @@ public static String normalizeVendor(String vendor) { if (vendor == null) return null; - switch (vendor) { - case "N/A": - return null; - case "Oracle Corporation": - return "Oracle"; - case "Azul Systems, Inc.": - return "Azul"; - case "IBM Corporation": - case "International Business Machines Corporation": - case "Eclipse OpenJ9": - return "IBM"; - case "Eclipse Adoptium": - return "Adoptium"; - case "Amazon.com Inc.": - return "Amazon"; - default: - return vendor; - } + return switch (vendor) { + case "N/A" -> null; + case "Oracle Corporation" -> "Oracle"; + case "Azul Systems, Inc." -> "Azul"; + case "IBM Corporation", "International Business Machines Corporation", "Eclipse OpenJ9" -> "IBM"; + case "Eclipse Adoptium" -> "Adoptium"; + case "Amazon.com Inc." -> "Amazon"; + default -> vendor; + }; } public static final JavaInfo CURRENT_ENVIRONMENT = new JavaInfo(Platform.CURRENT_PLATFORM, System.getProperty("java.version"), System.getProperty("java.vendor")); From 9e021b837c5bfef06de77f2888d6692953ea68a2 Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 25 Oct 2025 15:40:45 +0800 Subject: [PATCH 13/22] update --- .../main/java/org/jackhuang/hmcl/java/JavaManager.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java index 03d1a07d76..8e483d24b9 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java @@ -182,7 +182,7 @@ public static JavaRuntime getJava(Path executable) throws IOException, Interrupt } public static void refresh() { - Task.supplyAsync(JavaManager::searchPotentialJavaExecutables).whenComplete(Schedulers.javafx(), (result, exception) -> { + Task.supplyAsync(() -> searchPotentialJavaExecutables(false)).whenComplete(Schedulers.javafx(), (result, exception) -> { if (result != null) { LATCH.await(); allJava = result; @@ -343,7 +343,7 @@ public static JavaRuntime findSuitableJava(Collection javaRuntimes, } public static void initialize() { - Map allJava = searchPotentialJavaExecutables(); + Map allJava = searchPotentialJavaExecutables(true); JavaManager.allJava = allJava; LATCH.countDown(); FXUtils.runInFX(() -> updateAllJavaProperty(allJava)); @@ -351,8 +351,11 @@ public static void initialize() { // search java - private static Map searchPotentialJavaExecutables() { + private static Map searchPotentialJavaExecutables(boolean useCache) { Searcher searcher = new Searcher(Metadata.HMCL_GLOBAL_DIRECTORY.resolve("javaCache.json")); + if (useCache) + searcher.loadCache(); + searcher.searchAllJavaInRepository(Platform.SYSTEM_PLATFORM); switch (OperatingSystem.CURRENT_OS) { case WINDOWS: From 0f39a66f3c7e1951611821dbf84f7f87a676b1f6 Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 25 Oct 2025 16:00:26 +0800 Subject: [PATCH 14/22] update --- .../org/jackhuang/hmcl/java/JavaManager.java | 44 ++++++++++++++----- .../org/jackhuang/hmcl/java/JavaInfo.java | 27 ++++++++++++ 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java index 8e483d24b9..cf04ea8687 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java @@ -21,6 +21,7 @@ import com.google.gson.stream.JsonWriter; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; +import javafx.css.Match; import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.download.LibraryAnalyzer; @@ -50,6 +51,8 @@ import java.nio.file.attribute.BasicFileAttributes; import java.util.*; import java.util.concurrent.CountDownLatch; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import static org.jackhuang.hmcl.util.logging.Logger.LOG; @@ -494,7 +497,9 @@ private static final class Searcher { this.cacheFile = cacheFile; } - private static final long CACHE_VERSION = 0L; + private static final Pattern CACHE_VERSION_PATTERN = Pattern.compile("(?\\d+)(?:\\.(?\\d+))?"); + private static final int CACHE_MAJOR_VERSION = 0; + private static final int CACHE_MINOR_VERSION = 0; private record JavaInfoCache(String key, JavaInfo info) { } @@ -504,16 +509,23 @@ void loadCache() { return; try { - JsonObject jsonObject = JsonUtils.fromJsonFile(cacheFile, JsonObject.class); - JsonElement fileVersion = jsonObject.get("version"); - if (!(fileVersion instanceof JsonPrimitive)) - throw new IOException("Invalid version JSON: " + fileVersion); + JsonObject jsonFile = JsonUtils.fromJsonFile(cacheFile, JsonObject.class); + JsonElement fileVersion = jsonFile.get("version"); + + Matcher matcher; + if (jsonFile.get("version") instanceof JsonPrimitive version + && (matcher = CACHE_VERSION_PATTERN.matcher(version.getAsString())).matches()) { + int major = Integer.parseInt(matcher.group("major")); - int version = fileVersion.getAsJsonPrimitive().getAsInt(); - if (version != CACHE_VERSION) - throw new IOException("Unsupported cache file, version: %d".formatted(version)); + String minorString = matcher.group("minor"); + int minor = minorString != null ? Integer.parseInt(minorString) : 0; + + if (major != CACHE_MAJOR_VERSION || minor < CACHE_MINOR_VERSION) + throw new IOException("Unsupported cache file, version: %s".formatted(version.getAsString())); + } else + throw new IOException("Invalid version JSON: " + fileVersion); - JsonArray cachesArray = jsonObject.getAsJsonArray("caches"); + JsonArray cachesArray = jsonFile.getAsJsonArray("caches"); for (JsonElement element : cachesArray) { try { @@ -521,9 +533,17 @@ void loadCache() { Path realPath = Path.of(obj.getAsJsonPrimitive("path").getAsString()).toRealPath(); String key = obj.getAsJsonPrimitive("key").getAsString(); - JavaInfo info = JsonUtils.GSON.fromJson(obj.getAsJsonObject("info"), JavaInfo.class); - caches.put(realPath, new JavaInfoCache(key, info)); + OperatingSystem osName = OperatingSystem.parseOSName(obj.getAsJsonPrimitive("os.name").getAsString()); + Architecture osArch = Architecture.parseArchName(obj.getAsJsonPrimitive("os.arch").getAsString()); + String javaVersion = obj.getAsJsonPrimitive("java.version").getAsString(); + + JavaInfo.Builder infoBuilder = JavaInfo.newBuilder(Platform.getPlatform(osName, osArch), javaVersion); + + if (obj.get("java.vendor") instanceof JsonPrimitive vendor) + infoBuilder.setVendor(vendor.getAsString()); + + caches.put(realPath, new JavaInfoCache(key, infoBuilder.build())); } catch (Exception e) { LOG.warning("Invalid cache: " + element); needRefreshCache = true; @@ -545,7 +565,7 @@ void saveCache() { try (var writer = new JsonWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8))) { writer.beginObject(); - writer.name("version").value(CACHE_VERSION); + writer.name("version").value(CACHE_MAJOR_VERSION); writer.name("caches"); writer.beginArray(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfo.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfo.java index 22833f9263..16c8f2d717 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfo.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfo.java @@ -25,6 +25,8 @@ import org.jackhuang.hmcl.util.platform.Platform; import org.jackhuang.hmcl.util.tree.ArchiveFileTree; import org.jackhuang.hmcl.util.versioning.VersionNumber; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.BufferedReader; @@ -119,6 +121,31 @@ public static String normalizeVendor(String vendor) { }; } + public static final class Builder { + private final Platform platform; + private final String version; + private @Nullable String vendor; + + public Builder(Platform platform, String version) { + this.platform = platform; + this.version = version; + } + + @Contract("_ -> this") + public Builder setVendor(@Nullable String vendor) { + this.vendor = vendor; + return this; + } + + public JavaInfo build() { + return new JavaInfo(platform, version, vendor); + } + } + + public static Builder newBuilder(@NotNull Platform platform, @NotNull String version) { + return new Builder(platform, version); + } + public static final JavaInfo CURRENT_ENVIRONMENT = new JavaInfo(Platform.CURRENT_PLATFORM, System.getProperty("java.version"), System.getProperty("java.vendor")); private final Platform platform; From d567535685ac9009b6560a18177563a8ee684b76 Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 25 Oct 2025 16:16:32 +0800 Subject: [PATCH 15/22] update --- .../hmcl/java/HMCLJavaRepository.java | 17 ++++-------- .../org/jackhuang/hmcl/java/JavaManager.java | 26 +++++++++++++++---- .../jackhuang/hmcl/java/JavaRepository.java | 2 +- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/HMCLJavaRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/HMCLJavaRepository.java index 3231793385..1593e3a82d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/HMCLJavaRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/HMCLJavaRepository.java @@ -97,7 +97,7 @@ public boolean isInstalled(Platform platform, GameJavaVersion gameJavaVersion) { return getJavaExecutable(platform, MOJANG_JAVA_PREFIX + gameJavaVersion.getComponent()); } - private static void getAllJava(List list, Platform platform, Path platformRoot, boolean isManaged) { + private static void getAllJava(List list, Platform platform, Path platformRoot) { try (DirectoryStream stream = Files.newDirectoryStream(platformRoot)) { for (Path file : stream) { try { @@ -115,8 +115,7 @@ private static void getAllJava(List list, Platform platform, Path p } if (Files.isDirectory(javaDir)) { - JavaManifest manifest = JsonUtils.fromJsonFile(file, JavaManifest.class); - list.add(JavaRuntime.of(executable, manifest.getInfo(), isManaged)); + list.add(executable); } } } catch (Throwable e) { @@ -129,20 +128,14 @@ private static void getAllJava(List list, Platform platform, Path p } @Override - public Collection getAllJava(Platform platform) { + public Collection getAllJava(Platform platform) { Path platformRoot = getPlatformRoot(platform); if (!Files.isDirectory(platformRoot)) return Collections.emptyList(); - ArrayList list = new ArrayList<>(); - - getAllJava(list, platform, platformRoot, true); - if (platform.getOperatingSystem() == OperatingSystem.MACOS) { - platformRoot = root.resolve(platform.getOperatingSystem().getMojangName() + "-" + platform.getArchitecture().getCheckedName()); - if (Files.isDirectory(platformRoot)) - getAllJava(list, platform, platformRoot, false); - } + ArrayList list = new ArrayList<>(); + getAllJava(list, platform, platformRoot); return list; } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java index cf04ea8687..0ec3e0e6c3 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java @@ -572,12 +572,18 @@ void saveCache() { for (Map.Entry entry : caches.entrySet()) { Path path = entry.getKey(); JavaInfoCache cache = entry.getValue(); + JavaInfo info = cache.info(); writer.beginObject(); writer.name("path").value(path.toString()); writer.name("key").value(cache.key()); - writer.name("info").jsonValue(JsonUtils.GSON.toJson(cache.info().toString())); + + writer.name("os.name").value(info.getPlatform().os().getCheckedName()); + writer.name("os.arch").value(info.getPlatform().arch().getCheckedName()); + writer.name("java.version").value(info.getVersion()); + if (info.getVendor() != null) + writer.name("java.vendor").value(info.getVendor()); writer.endObject(); } @@ -664,6 +670,10 @@ void tryAddJavaHome(Path javaHome) { } void tryAddJavaExecutable(Path executable) { + tryAddJavaExecutable(executable, false); + } + + void tryAddJavaExecutable(Path executable, boolean isManaged) { try { executable = executable.toRealPath(); } catch (IOException e) { @@ -747,12 +757,18 @@ void tryAddJavaInComponentDir(String platform, Path component, boolean verify) { } void searchAllJavaInRepository(Platform platform) { - for (JavaRuntime java : REPOSITORY.getAllJava(platform)) { - addResult(java.getBinary(), java); + for (Path java : REPOSITORY.getAllJava(platform)) { + tryAddJavaExecutable(java, true); + } + + for (Path java : LOCAL_REPOSITORY.getAllJava(platform)) { + tryAddJavaExecutable(java, true); } - for (JavaRuntime java : LOCAL_REPOSITORY.getAllJava(platform)) { - addResult(java.getBinary(), java); + if (platform.os() == OperatingSystem.MACOS) { + // In the past, we used 'osx' as the checked name for macOS + Path platformRoot = REPOSITORY.getPlatformRoot(platform).resolveSibling("osx-" + platform.getArchitecture().getCheckedName()); + searchAllJavaInDirectory(platformRoot); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaRepository.java index 2fda58be71..4b5f35e4b7 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaRepository.java @@ -34,7 +34,7 @@ public interface JavaRepository { Path getManifestFile(Platform platform, String name); - Collection getAllJava(Platform platform); + Collection getAllJava(Platform platform); Task getDownloadJavaTask(DownloadProvider downloadProvider, Platform platform, GameJavaVersion gameJavaVersion); From cbb66d6821a727b2da38f0e427d7406f38ccef87 Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 25 Oct 2025 16:23:53 +0800 Subject: [PATCH 16/22] update --- .../main/java/org/jackhuang/hmcl/java/JavaManager.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java index 0ec3e0e6c3..07421f2b71 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java @@ -691,7 +691,7 @@ void tryAddJavaExecutable(Path executable, boolean isManaged) { JavaInfoCache cache = caches.get(executable); if (cache != null) { if (isCompatible(cache.info().getPlatform()) && cacheKey.equals(cache.key())) { - javaRuntimes.put(executable, JavaRuntime.of(executable, cache.info(), false)); + javaRuntimes.put(executable, JavaRuntime.of(executable, cache.info(), isManaged)); return; } else { caches.remove(executable); @@ -711,7 +711,12 @@ void tryAddJavaExecutable(Path executable, boolean isManaged) { } if (info != null) { - javaRuntimes.put(executable, JavaRuntime.of(executable, info, false)); + if (cacheKey != null) { + caches.put(executable, new JavaInfoCache(cacheKey, info)); + needRefreshCache = true; + } + + javaRuntimes.put(executable, JavaRuntime.of(executable, info, isManaged)); } } From 9253160b70eb4b7c7543b9e7f22d84f90a4fc135 Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 25 Oct 2025 16:24:50 +0800 Subject: [PATCH 17/22] update --- .../org/jackhuang/hmcl/java/JavaManager.java | 1 - .../jackhuang/hmcl/java/JavaInfoCache.java | 31 ------------------- 2 files changed, 32 deletions(-) delete mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfoCache.java diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java index 07421f2b71..e8e654fc4b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java @@ -21,7 +21,6 @@ import com.google.gson.stream.JsonWriter; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; -import javafx.css.Match; import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.download.LibraryAnalyzer; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfoCache.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfoCache.java deleted file mode 100644 index 3d3909f38a..0000000000 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfoCache.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Hello Minecraft! Launcher - * Copyright (C) 2025 huangyuhui and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.jackhuang.hmcl.java; - -import com.google.gson.*; -import com.google.gson.annotations.JsonAdapter; -import org.jackhuang.hmcl.util.gson.JsonSerializable; -import org.jackhuang.hmcl.util.gson.JsonUtils; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.lang.reflect.Type; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; - From 2be76e266ffa9061aaff0966fbfb10127e55093c Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 25 Oct 2025 16:27:17 +0800 Subject: [PATCH 18/22] update --- .../org/jackhuang/hmcl/java/JavaInfoUtils.java | 14 +++++--------- .../java/org/jackhuang/hmcl/java/JavaManager.java | 15 +++++++-------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaInfoUtils.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaInfoUtils.java index d8c97a7bf4..5ac6e91414 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaInfoUtils.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaInfoUtils.java @@ -18,6 +18,7 @@ package org.jackhuang.hmcl.java; import com.google.gson.annotations.SerializedName; +import org.jackhuang.hmcl.util.gson.JsonSerializable; import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.JarUtils; import org.jackhuang.hmcl.util.platform.Architecture; @@ -76,14 +77,9 @@ private JavaInfoUtils() { } } - private static final class Result { - @SerializedName("os.name") - public String osName; - @SerializedName("os.arch") - public String osArch; - @SerializedName("java.version") - public String javaVersion; - @SerializedName("java.vendor") - public String javaVendor; + @JsonSerializable + private record Result(@SerializedName("os.name") String osName, @SerializedName("os.arch") String osArch, + @SerializedName("java.version") String javaVersion, + @SerializedName("java.vendor") String javaVendor) { } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java index e8e654fc4b..6cc6c7ef5f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java @@ -701,22 +701,21 @@ void tryAddJavaExecutable(Path executable, boolean isManaged) { needRefreshCache = true; } - JavaInfo info = null; + JavaInfo info; try { info = JavaInfoUtils.fromExecutable(executable); } catch (IOException e) { LOG.warning("Failed to lookup Java executable at " + executable, e); failed.add(executable); + return; } - if (info != null) { - if (cacheKey != null) { - caches.put(executable, new JavaInfoCache(cacheKey, info)); - needRefreshCache = true; - } - - javaRuntimes.put(executable, JavaRuntime.of(executable, info, isManaged)); + if (cacheKey != null) { + caches.put(executable, new JavaInfoCache(cacheKey, info)); + needRefreshCache = true; } + + javaRuntimes.put(executable, JavaRuntime.of(executable, info, isManaged)); } void tryAddJavaInComponentDir(String platform, Path component, boolean verify) { From ac4117f5e1eab9c67ed0f17b5664e064e8410591 Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 25 Oct 2025 16:28:49 +0800 Subject: [PATCH 19/22] update --- .../src/main/java/org/jackhuang/hmcl/java/JavaManager.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java index 6cc6c7ef5f..25e91800a2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java @@ -608,13 +608,6 @@ void saveCache() { if (javaHome == null) return null; - String javaHomeName = javaHome.getFileName().toString(); - if (!javaHomeName.contains("java") - && !javaHomeName.contains("jre") - && !javaHomeName.contains("jdk") - && !javaHomeName.contains("openj9")) - return null; - Path libDir = javaHome.resolve("lib"); if (!Files.isDirectory(libDir)) return null; From ce4d00ca703efdefb1553ba3362550ccf930142f Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 25 Oct 2025 16:31:13 +0800 Subject: [PATCH 20/22] update --- HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java index 25e91800a2..85b2ca0609 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java @@ -564,7 +564,7 @@ void saveCache() { try (var writer = new JsonWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8))) { writer.beginObject(); - writer.name("version").value(CACHE_MAJOR_VERSION); + writer.name("version").value("%d.%d".formatted(CACHE_MAJOR_VERSION, CACHE_MINOR_VERSION)); writer.name("caches"); writer.beginArray(); From 70cbd347512a3bdc9bb4d094ca12eab75583141b Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 25 Oct 2025 16:32:42 +0800 Subject: [PATCH 21/22] update --- HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java index 85b2ca0609..efb4a39496 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java @@ -619,7 +619,7 @@ void saveCache() { try { launcherAttributes = Files.readAttributes(realPath, BasicFileAttributes.class); - Path releaseFile = libDir.resolve("release"); + Path releaseFile = javaHome.resolve("release"); if (Files.exists(releaseFile)) { releaseHash = DigestUtils.digestToString("SHA-1", releaseFile); } else { From c14ad85c8889ad94b7e19fd6d0e1adb3ec23d1f1 Mon Sep 17 00:00:00 2001 From: Glavo Date: Sat, 4 Apr 2026 21:38:56 +0800 Subject: [PATCH 22/22] refactor: reuse `fileVersion` in JavaManager version check logic --- HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java index 2750cb5e56..0cdf27ec84 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/java/JavaManager.java @@ -512,7 +512,7 @@ void loadCache() { JsonElement fileVersion = jsonFile.get("version"); Matcher matcher; - if (jsonFile.get("version") instanceof JsonPrimitive version + if (fileVersion instanceof JsonPrimitive version && (matcher = CACHE_VERSION_PATTERN.matcher(version.getAsString())).matches()) { int major = Integer.parseInt(matcher.group("major"));