From 93d4917ed88bf48b23ac5698eda81b798c8d3468 Mon Sep 17 00:00:00 2001 From: CalbootOnceMore Date: Sun, 3 May 2026 12:49:56 +0800 Subject: [PATCH 01/17] =?UTF-8?q?=E5=90=8E=E5=8F=B0=E4=B8=8B=E8=BD=BD?= =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/jackhuang/hmcl/setting/Config.java | 15 ++++++++++ .../jackhuang/hmcl/ui/main/SettingsPage.java | 29 +++++++++++++++---- .../jackhuang/hmcl/upgrade/RemoteVersion.java | 28 ++++++++++++++++-- .../jackhuang/hmcl/upgrade/UpdateChannel.java | 2 +- .../jackhuang/hmcl/upgrade/UpdateChecker.java | 5 ++-- .../jackhuang/hmcl/upgrade/UpdateHandler.java | 29 +++++++++++-------- .../resources/assets/lang/I18N.properties | 2 ++ .../resources/assets/lang/I18N_zh.properties | 2 ++ .../assets/lang/I18N_zh_CN.properties | 2 ++ 9 files changed, 91 insertions(+), 23 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java index 0a845929b50..7ad231457e5 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java @@ -237,6 +237,21 @@ public void setDisableAutoShowUpdateDialog(boolean disableAutoShowUpdateDialog) this.disableAutoShowUpdateDialog.set(disableAutoShowUpdateDialog); } + @SerializedName("autoDownloadUpdate") + private final BooleanProperty autoDownloadUpdate = new SimpleBooleanProperty(false); + + public BooleanProperty autoDownloadUpdateProperty() { + return autoDownloadUpdate; + } + + public boolean isAutoDownloadUpdate() { + return autoDownloadUpdate.get(); + } + + public void setAutoDownloadUpdate(boolean autoDownloadUpdate) { + this.autoDownloadUpdate.set(autoDownloadUpdate); + } + @SerializedName("disableAprilFools") private final BooleanProperty disableAprilFools = new SimpleBooleanProperty(false); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java index 9d80ebcefd0..4e00ff0d01f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java @@ -21,6 +21,7 @@ import javafx.beans.InvalidationListener; import javafx.beans.WeakInvalidationListener; import javafx.beans.binding.Bindings; +import javafx.beans.property.BooleanProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.StringProperty; import javafx.css.PseudoClass; @@ -173,17 +174,13 @@ protected int getTrailingTextIndex() { settingsPane.getContent().add(updatePane); } + BooleanProperty preview; { LineToggleButton previewPane = new LineToggleButton(); previewPane.setTitle(i18n("update.preview")); previewPane.setSubtitle(i18n("update.preview.subtitle")); previewPane.selectedProperty().bindBidirectional(config().acceptPreviewUpdateProperty()); - - InvalidationListener checkUpdateListener = e -> { - UpdateChecker.requestCheckUpdate(updateChannel.get(), previewPane.isSelected()); - }; - updateChannel.addListener(checkUpdateListener); - previewPane.selectedProperty().addListener(checkUpdateListener); + preview = previewPane.selectedProperty(); settingsPane.getContent().add(previewPane); } @@ -196,6 +193,26 @@ protected int getTrailingTextIndex() { settingsPane.getContent().add(disableAutoShowUpdateDialogPane); } + BooleanProperty autoDownloadUpdate; + { + LineToggleButton autoDownloadUpdatePane = new LineToggleButton(); + autoDownloadUpdatePane.setTitle(i18n("update.auto_download")); + autoDownloadUpdatePane.setSubtitle(i18n("update.auto_download.subtitle")); + autoDownloadUpdatePane.selectedProperty().bindBidirectional(config().autoDownloadUpdateProperty()); + autoDownloadUpdate = autoDownloadUpdatePane.selectedProperty(); + + settingsPane.getContent().add(autoDownloadUpdatePane); + } + + { + InvalidationListener checkUpdateListener = e -> { + UpdateChecker.requestCheckUpdate(updateChannel.get(), preview.get(), autoDownloadUpdate.get()); + }; + updateChannel.addListener(checkUpdateListener); + preview.addListener(checkUpdateListener); + autoDownloadUpdate.addListener(checkUpdateListener); + } + if (AprilFools.isShowAprilFoolsSettings()) { LineToggleButton disableAprilFools = new LineToggleButton(); disableAprilFools.setTitle(i18n("settings.launcher.disable_april_fools")); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java index b24ce19491a..5e0a1602e7a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java @@ -24,12 +24,17 @@ import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Optional; +import static org.jackhuang.hmcl.util.logging.Logger.LOG; + public record RemoteVersion(UpdateChannel channel, String version, String url, Type type, IntegrityCheck integrityCheck, - boolean preview, boolean force) { + boolean preview, boolean force, @Nullable Path downloadedFile) { public static RemoteVersion fetch(UpdateChannel channel, boolean preview, String url) throws IOException { try { @@ -39,7 +44,7 @@ public static RemoteVersion fetch(UpdateChannel channel, boolean preview, String String jarHash = Optional.ofNullable(response.get("jarsha1")).map(JsonElement::getAsString).orElse(null); boolean force = Optional.ofNullable(response.get("force")).map(JsonElement::getAsBoolean).orElse(false); if (jarUrl != null && jarHash != null) { - return new RemoteVersion(channel, version, jarUrl, Type.JAR, new IntegrityCheck("SHA-1", jarHash), preview, force); + return new RemoteVersion(channel, version, jarUrl, Type.JAR, new IntegrityCheck("SHA-1", jarHash), preview, force, null); } else { throw new IOException("No download url is available"); } @@ -53,6 +58,25 @@ public static RemoteVersion fetch(UpdateChannel channel, boolean preview, String return "[" + version + " from " + url + "]"; } + public RemoteVersion tryDownload() { + if (downloadedFile() != null) return this; + Path downloaded; + try { + downloaded = Files.createTempFile("hmcl-update-", ".jar"); + } catch (IOException e) { + LOG.warning("Failed to create temp file", e); + return this; + } + + var executor = new HMCLDownloadTask(this, downloaded).executor(); + if (executor.test()) { + return new RemoteVersion(channel(), version(), url(), type(), integrityCheck(), preview(), force(), downloaded); + } else { + LOG.warning("Failed to download update for " + this, executor.getException()); + return this; + } + } + public enum Type { JAR } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChannel.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChannel.java index 998a3da7d2e..b040ac9ba5d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChannel.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChannel.java @@ -22,7 +22,7 @@ public enum UpdateChannel { STABLE("stable"), DEVELOPMENT("dev"), - NIGHTLY("nightly"); + NIGHTLY("dev"); public final String channelName; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java index df15a3fac11..f8f5b767daf 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java @@ -56,7 +56,7 @@ private UpdateChecker() { private static final ReadOnlyBooleanWrapper checkingUpdate = new ReadOnlyBooleanWrapper(false); public static void init() { - requestCheckUpdate(UpdateChannel.getChannel(), config().isAcceptPreviewUpdate()); + requestCheckUpdate(UpdateChannel.getChannel(), config().isAcceptPreviewUpdate(), config().isAutoDownloadUpdate()); } public static RemoteVersion getLatestVersion() { @@ -101,7 +101,7 @@ private static boolean isDevelopmentVersion(String version) { version.contains("SNAPSHOT"); // eg. 3.5.SNAPSHOT } - public static void requestCheckUpdate(UpdateChannel channel, boolean preview) { + public static void requestCheckUpdate(UpdateChannel channel, boolean preview, boolean download) { Platform.runLater(() -> { if (isCheckingUpdate()) return; @@ -112,6 +112,7 @@ public static void requestCheckUpdate(UpdateChannel channel, boolean preview) { try { result = checkUpdate(channel, preview); LOG.info("Latest version (" + channel + ", preview=" + preview + ") is " + result); + if (download) result = result.tryDownload(); } catch (Throwable e) { LOG.warning("Failed to check for update", e); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateHandler.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateHandler.java index 95c8f1b772c..769b025fa23 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateHandler.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateHandler.java @@ -106,18 +106,23 @@ public static void updateFrom(RemoteVersion version) { } Controllers.dialog(new UpgradeDialog(version, () -> { - Path downloaded; - try { - downloaded = Files.createTempFile("hmcl-update-", ".jar"); - } catch (IOException e) { - LOG.warning("Failed to create temp file", e); - return; - } - - Task task = new HMCLDownloadTask(version, downloaded); + Path downloaded = version.downloadedFile(); + TaskExecutor executor; + if (downloaded != null && Files.isRegularFile(downloaded)) { + executor = Task.completed(null).executor(); + } else { + try { + downloaded = Files.createTempFile("hmcl-update-", ".jar"); + } catch (IOException e) { + LOG.warning("Failed to create temp file", e); + return; + } - TaskExecutor executor = task.executor(); - Controllers.taskDialog(executor, i18n("message.downloading"), TaskCancellationAction.NORMAL); + Task task = new HMCLDownloadTask(version, downloaded); + executor = task.executor(); + Controllers.taskDialog(executor, i18n("message.downloading"), TaskCancellationAction.NORMAL); + } + final Path finalDownloaded = downloaded; thread(() -> { boolean success = executor.test(); @@ -150,7 +155,7 @@ public static void updateFrom(RemoteVersion version) { // Ignore } - requestUpdate(downloaded, getCurrentLocation()); + requestUpdate(finalDownloaded, getCurrentLocation()); EntryPoint.exit(0); } catch (IOException e) { LOG.warning("Failed to update to " + version, e); diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index e13e61bd67b..6f33c43ad45 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -1627,6 +1627,8 @@ unofficial.hint=You are using an unofficial build of HMCL. We cannot guarantee i update=Update update.accept=Update +update.auto_download=Auto download update +update.auto_download.subtitle=Automatically download (but not installing) launcher update in the background. update.changelog=Changelog update.channel.dev=Beta update.channel.dev.hint=You are currently using a Beta channel build of the launcher. While it may include some extra features, it is also sometimes less stable than the Stable channel builds.\n\ diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 555f135af8d..2b6a3328cb0 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -1417,6 +1417,8 @@ unofficial.hint=你正在使用第三方提供的 HMCL。我們無法保證其 update=啟動器更新 update.accept=更新 +update.auto_download=自動下載更新 +update.auto_download.subtitle=自動在後臺下載更新(但不安裝) update.changelog=更新日誌 update.channel.dev=開發版 update.channel.dev.hint=你正在使用 HMCL 開發版。開發版包含一些未在穩定版中包含的測試性功能,僅用於體驗新功能。開發版功能未受充分驗證,使用起來可能不穩定!\n\ diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index b251db3d68a..cbd381a6b64 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -1422,6 +1422,8 @@ unofficial.hint=你正在使用非官方构建的 HMCL。我们无法保证其 update=启动器更新 update.accept=更新 +update.auto_download=自动下载更新 +update.auto_download.subtitle=自动在后台下载更新(但不安装) update.changelog=更新日志 update.channel.dev=开发版 update.channel.dev.hint=你正在使用 HMCL 开发版。开发版包含一些未在稳定版中包含的测试性功能,仅用于体验新功能。开发版功能未受充分验证,使用起来可能不稳定!下载稳定版\n\ From e8f258c5c8ae78b1167f32ddc56033700a0cca81 Mon Sep 17 00:00:00 2001 From: CalbootOnceMore Date: Sun, 3 May 2026 13:29:09 +0800 Subject: [PATCH 02/17] update --- HMCL/src/main/resources/assets/lang/I18N_zh.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 2b6a3328cb0..b436335d08a 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -1418,7 +1418,7 @@ unofficial.hint=你正在使用第三方提供的 HMCL。我們無法保證其 update=啟動器更新 update.accept=更新 update.auto_download=自動下載更新 -update.auto_download.subtitle=自動在後臺下載更新(但不安裝) +update.auto_download.subtitle=自動在後臺下載更新 (但不安裝) update.changelog=更新日誌 update.channel.dev=開發版 update.channel.dev.hint=你正在使用 HMCL 開發版。開發版包含一些未在穩定版中包含的測試性功能,僅用於體驗新功能。開發版功能未受充分驗證,使用起來可能不穩定!\n\ diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index cbd381a6b64..5b6d0f85efc 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -1423,7 +1423,7 @@ unofficial.hint=你正在使用非官方构建的 HMCL。我们无法保证其 update=启动器更新 update.accept=更新 update.auto_download=自动下载更新 -update.auto_download.subtitle=自动在后台下载更新(但不安装) +update.auto_download.subtitle=自动在后台下载更新 (但不安装) update.changelog=更新日志 update.channel.dev=开发版 update.channel.dev.hint=你正在使用 HMCL 开发版。开发版包含一些未在稳定版中包含的测试性功能,仅用于体验新功能。开发版功能未受充分验证,使用起来可能不稳定!下载稳定版\n\ From 38c1abe2f80acddf12718539b7b182ccb0125afe Mon Sep 17 00:00:00 2001 From: CalbootOnceMore Date: Mon, 4 May 2026 10:48:11 +0800 Subject: [PATCH 03/17] Apply suggestions from code review Co-authored-by: 3gf8jv4dv <3gf8jv4dv@gmail.com> --- HMCL/src/main/resources/assets/lang/I18N.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_zh.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 6f33c43ad45..65b88f7d1f4 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -1628,7 +1628,7 @@ unofficial.hint=You are using an unofficial build of HMCL. We cannot guarantee i update=Update update.accept=Update update.auto_download=Auto download update -update.auto_download.subtitle=Automatically download (but not installing) launcher update in the background. +update.auto_download.subtitle=Automatically download launcher updates and notify when ready to install. update.changelog=Changelog update.channel.dev=Beta update.channel.dev.hint=You are currently using a Beta channel build of the launcher. While it may include some extra features, it is also sometimes less stable than the Stable channel builds.\n\ diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index b436335d08a..d58a6da30b5 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -1418,7 +1418,7 @@ unofficial.hint=你正在使用第三方提供的 HMCL。我們無法保證其 update=啟動器更新 update.accept=更新 update.auto_download=自動下載更新 -update.auto_download.subtitle=自動在後臺下載更新 (但不安裝) +update.auto_download.subtitle=自動在背景下載更新,下載完成後顯示通知 update.changelog=更新日誌 update.channel.dev=開發版 update.channel.dev.hint=你正在使用 HMCL 開發版。開發版包含一些未在穩定版中包含的測試性功能,僅用於體驗新功能。開發版功能未受充分驗證,使用起來可能不穩定!\n\ diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 5b6d0f85efc..e17ea940286 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -1423,7 +1423,7 @@ unofficial.hint=你正在使用非官方构建的 HMCL。我们无法保证其 update=启动器更新 update.accept=更新 update.auto_download=自动下载更新 -update.auto_download.subtitle=自动在后台下载更新 (但不安装) +update.auto_download.subtitle=自动在后台下载更新,下载完成后显示通知 update.changelog=更新日志 update.channel.dev=开发版 update.channel.dev.hint=你正在使用 HMCL 开发版。开发版包含一些未在稳定版中包含的测试性功能,仅用于体验新功能。开发版功能未受充分验证,使用起来可能不稳定!下载稳定版\n\ From 43a7ee1e93e5195417bc03a3f2e3e11d41cd8157 Mon Sep 17 00:00:00 2001 From: CalbootOnceMore Date: Mon, 4 May 2026 12:24:07 +0800 Subject: [PATCH 04/17] using cache --- .../jackhuang/hmcl/upgrade/RemoteVersion.java | 22 +++++++++++-------- .../jackhuang/hmcl/upgrade/UpdateChecker.java | 2 +- .../jackhuang/hmcl/upgrade/UpdateHandler.java | 10 ++++----- .../jackhuang/hmcl/util/CacheRepository.java | 10 +-------- .../org/jackhuang/hmcl/util/io/FileUtils.java | 14 ++++++++++++ 5 files changed, 34 insertions(+), 24 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java index 5e0a1602e7a..8fa744fba7b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java @@ -22,19 +22,23 @@ import com.google.gson.JsonParseException; import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck; import org.jackhuang.hmcl.util.gson.JsonUtils; +import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; import java.util.Optional; import static org.jackhuang.hmcl.util.logging.Logger.LOG; public record RemoteVersion(UpdateChannel channel, String version, String url, Type type, IntegrityCheck integrityCheck, - boolean preview, boolean force, @Nullable Path downloadedFile) { + boolean preview, boolean force) { + + public static final Map downloadCache = new HashMap<>(); public static RemoteVersion fetch(UpdateChannel channel, boolean preview, String url) throws IOException { try { @@ -44,7 +48,7 @@ public static RemoteVersion fetch(UpdateChannel channel, boolean preview, String String jarHash = Optional.ofNullable(response.get("jarsha1")).map(JsonElement::getAsString).orElse(null); boolean force = Optional.ofNullable(response.get("force")).map(JsonElement::getAsBoolean).orElse(false); if (jarUrl != null && jarHash != null) { - return new RemoteVersion(channel, version, jarUrl, Type.JAR, new IntegrityCheck("SHA-1", jarHash), preview, force, null); + return new RemoteVersion(channel, version, jarUrl, Type.JAR, new IntegrityCheck("SHA-1", jarHash), preview, force); } else { throw new IOException("No download url is available"); } @@ -58,22 +62,22 @@ public static RemoteVersion fetch(UpdateChannel channel, boolean preview, String return "[" + version + " from " + url + "]"; } - public RemoteVersion tryDownload() { - if (downloadedFile() != null) return this; - Path downloaded; + public void tryDownload() { + Path downloaded = downloadCache.get(this); + if (downloaded != null && FileUtils.verifyHash(downloaded, integrityCheck().algorithm(), integrityCheck().checksum())) return; + try { downloaded = Files.createTempFile("hmcl-update-", ".jar"); } catch (IOException e) { LOG.warning("Failed to create temp file", e); - return this; + return; } var executor = new HMCLDownloadTask(this, downloaded).executor(); if (executor.test()) { - return new RemoteVersion(channel(), version(), url(), type(), integrityCheck(), preview(), force(), downloaded); + downloadCache.put(this, downloaded); } else { LOG.warning("Failed to download update for " + this, executor.getException()); - return this; } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java index f8f5b767daf..a86f1624f2f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java @@ -112,7 +112,7 @@ public static void requestCheckUpdate(UpdateChannel channel, boolean preview, bo try { result = checkUpdate(channel, preview); LOG.info("Latest version (" + channel + ", preview=" + preview + ") is " + result); - if (download) result = result.tryDownload(); + if (download) result.tryDownload(); } catch (Throwable e) { LOG.warning("Failed to check for update", e); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateHandler.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateHandler.java index 769b025fa23..3a682470e7a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateHandler.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateHandler.java @@ -33,6 +33,7 @@ import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.SwingUtils; import org.jackhuang.hmcl.util.TaskCancellationAction; +import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.io.JarUtils; import org.jackhuang.hmcl.util.platform.OperatingSystem; @@ -106,9 +107,9 @@ public static void updateFrom(RemoteVersion version) { } Controllers.dialog(new UpgradeDialog(version, () -> { - Path downloaded = version.downloadedFile(); + Path downloaded = RemoteVersion.downloadCache.get(version); TaskExecutor executor; - if (downloaded != null && Files.isRegularFile(downloaded)) { + if (downloaded != null && FileUtils.verifyHash(downloaded, version.integrityCheck().algorithm(), version.integrityCheck().checksum())) { executor = Task.completed(null).executor(); } else { try { @@ -124,9 +125,8 @@ public static void updateFrom(RemoteVersion version) { } final Path finalDownloaded = downloaded; thread(() -> { - boolean success = executor.test(); - - if (success) { + if (executor.test()) { + RemoteVersion.downloadCache.put(version, finalDownloaded); try { if (!IntegrityChecker.isSelfVerified() && !IntegrityChecker.DISABLE_SELF_INTEGRITY_CHECK) { throw new IOException("Current JAR is not verified"); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/CacheRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/CacheRepository.java index 3e08b993b2b..1e2659af3bc 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/CacheRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/CacheRepository.java @@ -103,15 +103,7 @@ protected Path getFile(String algorithm, String hash) { protected boolean fileExists(String algorithm, String hash) { if (hash == null) return false; Path file = getFile(algorithm, hash); - if (Files.exists(file)) { - try { - return DigestUtils.digestToString(algorithm, file).equalsIgnoreCase(hash); - } catch (IOException e) { - return false; - } - } else { - return false; - } + return FileUtils.verifyHash(file, algorithm, hash); } public void tryCacheFile(Path path, String algorithm, String hash) throws IOException { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java index bce609927db..dd3070f39e9 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java @@ -20,6 +20,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import org.glavo.chardet.DetectedCharset; import org.glavo.chardet.UniversalDetector; +import org.jackhuang.hmcl.util.DigestUtils; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.function.ExceptionalConsumer; import org.jackhuang.hmcl.util.platform.OperatingSystem; @@ -559,4 +560,17 @@ public static EnumSet parsePosixFilePermission(int unixMode return permissions; } + + public static boolean verifyHash(Path file, String algorithm, String hash) { + if (Files.exists(file)) { + try { + return DigestUtils.digestToString(algorithm, file).equalsIgnoreCase(hash); + } catch (IOException e) { + LOG.warning("Failed to verify hash for file " + file, e); + return false; + } + } else { + return false; + } + } } From 34360346793be3a874e92f1add2c18dae0800b40 Mon Sep 17 00:00:00 2001 From: CalbootOnceMore Date: Mon, 4 May 2026 19:34:41 +0800 Subject: [PATCH 05/17] update --- .../src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java index 8fa744fba7b..ab286a81425 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java @@ -63,7 +63,7 @@ public static RemoteVersion fetch(UpdateChannel channel, boolean preview, String } public void tryDownload() { - Path downloaded = downloadCache.get(this); + Path downloaded = downloadCache.get(this); if (downloaded != null && FileUtils.verifyHash(downloaded, integrityCheck().algorithm(), integrityCheck().checksum())) return; try { From f29058eb2600d12508bcfcca3940103c60d79900 Mon Sep 17 00:00:00 2001 From: ToobLac Date: Fri, 19 Jun 2026 12:12:54 +0800 Subject: [PATCH 06/17] merge --- HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java | 0 .../java/org/jackhuang/hmcl/setting/LauncherSettings.java | 8 -------- .../java/org/jackhuang/hmcl/ui/main/SettingsPage.java | 2 +- .../java/org/jackhuang/hmcl/upgrade/UpdateChecker.java | 3 ++- 4 files changed, 3 insertions(+), 10 deletions(-) delete mode 100644 HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/LauncherSettings.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/LauncherSettings.java index abce42da5e9..1b1db4c527d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/LauncherSettings.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/LauncherSettings.java @@ -185,14 +185,6 @@ public BooleanProperty autoDownloadUpdateProperty() { return autoDownloadUpdate; } - public boolean isAutoDownloadUpdate() { - return autoDownloadUpdate.get(); - } - - public void setAutoDownloadUpdate(boolean autoDownloadUpdate) { - this.autoDownloadUpdate.set(autoDownloadUpdate); - } - /// Whether April Fools features are disabled. @SerializedName("disableAprilFools") private final BooleanProperty disableAprilFools = new SimpleBooleanProperty(false); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java index 9e012ae8f0d..27046cc4877 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java @@ -163,7 +163,7 @@ protected int getTrailingTextIndex() { LineToggleButton autoDownloadUpdatePane = new LineToggleButton(); autoDownloadUpdatePane.setTitle(i18n("update.auto_download")); autoDownloadUpdatePane.setSubtitle(i18n("update.auto_download.subtitle")); - autoDownloadUpdatePane.selectedProperty().bindBidirectional(config().autoDownloadUpdateProperty()); + autoDownloadUpdatePane.selectedProperty().bindBidirectional(settings().autoDownloadUpdateProperty()); autoDownloadUpdate = autoDownloadUpdatePane.selectedProperty(); updatePaneList.getContent().add(autoDownloadUpdatePane); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java index 331fe9a210f..9c894d53abb 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java @@ -23,6 +23,7 @@ import javafx.beans.property.*; import javafx.beans.value.ObservableBooleanValue; import org.jackhuang.hmcl.Metadata; +import org.jackhuang.hmcl.setting.SettingsManager; import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jackhuang.hmcl.util.versioning.VersionNumber; @@ -56,7 +57,7 @@ private UpdateChecker() { private static final ReadOnlyBooleanWrapper checkingUpdate = new ReadOnlyBooleanWrapper(false); public static void init() { - requestCheckUpdate(UpdateChannel.getChannel(), settings().acceptPreviewUpdateProperty().get(), settings().isAutoDownloadUpdate()); + requestCheckUpdate(UpdateChannel.getChannel(), settings().acceptPreviewUpdateProperty().get(), settings().autoDownloadUpdateProperty().get()); } public static RemoteVersion getLatestVersion() { From 2d2d789cf9dea873be640bbaf500c16df091975a Mon Sep 17 00:00:00 2001 From: ToobLac Date: Fri, 19 Jun 2026 12:18:30 +0800 Subject: [PATCH 07/17] update --- HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java | 1 - 1 file changed, 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java index 9c894d53abb..115103f8dc0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java @@ -23,7 +23,6 @@ import javafx.beans.property.*; import javafx.beans.value.ObservableBooleanValue; import org.jackhuang.hmcl.Metadata; -import org.jackhuang.hmcl.setting.SettingsManager; import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jackhuang.hmcl.util.versioning.VersionNumber; From 68376b3868ea54769a37de122f48d06ebd48d5ba Mon Sep 17 00:00:00 2001 From: ToobLac Date: Fri, 19 Jun 2026 21:41:49 +0800 Subject: [PATCH 08/17] update --- .../src/main/java/org/jackhuang/hmcl/upgrade/UpdateChannel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChannel.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChannel.java index b040ac9ba5d..998a3da7d2e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChannel.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChannel.java @@ -22,7 +22,7 @@ public enum UpdateChannel { STABLE("stable"), DEVELOPMENT("dev"), - NIGHTLY("dev"); + NIGHTLY("nightly"); public final String channelName; From b3e78c2c146626434f4b028c0f664cc1f27e30bc Mon Sep 17 00:00:00 2001 From: ToobLac Date: Fri, 19 Jun 2026 21:45:51 +0800 Subject: [PATCH 09/17] update --- .../jackhuang/hmcl/upgrade/RemoteVersion.java | 4 +-- .../jackhuang/hmcl/upgrade/UpdateChecker.java | 36 +++++++++---------- .../org/jackhuang/hmcl/util/io/FileUtils.java | 2 +- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java index ab286a81425..b2d6fd1c813 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java @@ -29,16 +29,16 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.HashMap; import java.util.Map; import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; import static org.jackhuang.hmcl.util.logging.Logger.LOG; public record RemoteVersion(UpdateChannel channel, String version, String url, Type type, IntegrityCheck integrityCheck, boolean preview, boolean force) { - public static final Map downloadCache = new HashMap<>(); + public static final Map downloadCache = new ConcurrentHashMap<>(); public static RemoteVersion fetch(UpdateChannel channel, boolean preview, String url) throws IOException { try { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java index 115103f8dc0..2d886c5b0bd 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java @@ -18,10 +18,7 @@ package org.jackhuang.hmcl.upgrade; import javafx.application.Platform; -import javafx.beans.binding.Bindings; -import javafx.beans.binding.BooleanBinding; import javafx.beans.property.*; -import javafx.beans.value.ObservableBooleanValue; import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jackhuang.hmcl.util.versioning.VersionNumber; @@ -38,21 +35,7 @@ private UpdateChecker() { } private static final ObjectProperty latestVersion = new SimpleObjectProperty<>(); - private static final BooleanBinding outdated = Bindings.createBooleanBinding( - () -> { - RemoteVersion latest = latestVersion.get(); - if (latest == null || isDevelopmentVersion(Metadata.VERSION)) { - return false; - } else if (latest.force() - || Metadata.isNightly() - || latest.channel() == UpdateChannel.NIGHTLY - || latest.channel() != UpdateChannel.getChannel()) { - return !latest.version().equals(Metadata.VERSION); - } else { - return VersionNumber.compare(Metadata.VERSION, latest.version()) < 0; - } - }, - latestVersion); + private static final ReadOnlyBooleanWrapper outdated = new ReadOnlyBooleanWrapper(false); private static final ReadOnlyBooleanWrapper checkingUpdate = new ReadOnlyBooleanWrapper(false); public static void init() { @@ -71,7 +54,7 @@ public static boolean isOutdated() { return outdated.get(); } - public static ObservableBooleanValue outdatedProperty() { + public static ReadOnlyBooleanProperty outdatedProperty() { return outdated; } @@ -101,6 +84,19 @@ private static boolean isDevelopmentVersion(String version) { version.contains("SNAPSHOT"); // eg. 3.5.SNAPSHOT } + private static boolean checkOutdated(RemoteVersion latest) { + if (latest == null || isDevelopmentVersion(Metadata.VERSION)) { + return false; + } else if (latest.force() + || Metadata.isNightly() + || latest.channel() == UpdateChannel.NIGHTLY + || latest.channel() != UpdateChannel.getChannel()) { + return !latest.version().equals(Metadata.VERSION); + } else { + return VersionNumber.compare(Metadata.VERSION, latest.version()) < 0; + } + } + public static void requestCheckUpdate(UpdateChannel channel, boolean preview, boolean download) { Platform.runLater(() -> { if (isCheckingUpdate()) @@ -121,7 +117,9 @@ public static void requestCheckUpdate(UpdateChannel channel, boolean preview, bo Platform.runLater(() -> { checkingUpdate.set(false); if (finalResult != null) { + boolean isOutdated = checkOutdated(finalResult); latestVersion.set(finalResult); + outdated.set(isOutdated); } }); }, "Update Checker", true); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java index 40a7aa7a342..6f8603c8427 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java @@ -576,7 +576,7 @@ public static EnumSet parsePosixFilePermission(int unixMode } public static boolean verifyHash(Path file, String algorithm, String hash) { - if (Files.exists(file)) { + if (Files.isRegularFile(file)) { try { return DigestUtils.digestToString(algorithm, file).equalsIgnoreCase(hash); } catch (IOException e) { From 2511138c8bf856eb1560efa34ecb35b8bff03a0d Mon Sep 17 00:00:00 2001 From: ToobLac Date: Thu, 25 Jun 2026 16:51:22 +0800 Subject: [PATCH 10/17] fix --- .../main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java index feacf798004..9ef79e9ac19 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java @@ -105,18 +105,20 @@ public static void requestCheckUpdate(UpdateChannel channel, boolean preview, bo thread(() -> { RemoteVersion result = null; + boolean b = false; try { result = checkUpdate(channel, preview); + b = checkOutdated(result); LOG.info("Latest version (" + channel + ", preview=" + preview + ") is " + result); - if (download) result.tryDownload(); + if (download && b) result.tryDownload(); } catch (Throwable e) { LOG.warning("Failed to check for update", e); } + boolean isOutdated = b; RemoteVersion finalResult = result; Platform.runLater(() -> { if (finalResult != null) { - boolean isOutdated = checkOutdated(finalResult); latestVersion.set(finalResult); outdated.set(isOutdated); } From 6e732bf3070f09865b9f317da3d5ec6eea9a2d80 Mon Sep 17 00:00:00 2001 From: ToobLac Date: Fri, 26 Jun 2026 22:17:08 +0800 Subject: [PATCH 11/17] update --- .../main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java index b2d6fd1c813..f6ee7de0420 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java @@ -78,6 +78,10 @@ public void tryDownload() { downloadCache.put(this, downloaded); } else { LOG.warning("Failed to download update for " + this, executor.getException()); + try { + Files.deleteIfExists(downloaded); + } catch (IOException ignored) { + } } } From bcf7608c4061b85853351acd54313ee2edd4ceab Mon Sep 17 00:00:00 2001 From: ToobLac Date: Fri, 26 Jun 2026 22:29:40 +0800 Subject: [PATCH 12/17] update --- .../src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java index 27046cc4877..4830e1b4075 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java @@ -171,11 +171,10 @@ protected int getTrailingTextIndex() { { InvalidationListener checkUpdateListener = e -> { - UpdateChecker.requestCheckUpdate(updateChannel.get(), preview.get(), autoDownloadUpdate.get()); + UpdateChecker.requestCheckUpdate(updateChannel.get(), preview.get(), false); }; updateChannel.addListener(checkUpdateListener); preview.addListener(checkUpdateListener); - autoDownloadUpdate.addListener(checkUpdateListener); } rootPane.getChildren().addAll(ComponentList.createComponentListTitle(i18n("update")), updatePaneList); From 636cca3eb8d13dfeec258df80dabf85d0e333e8c Mon Sep 17 00:00:00 2001 From: ToobLac Date: Fri, 26 Jun 2026 22:30:22 +0800 Subject: [PATCH 13/17] update --- HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java index 4830e1b4075..4ecf95c47e1 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java @@ -158,13 +158,11 @@ protected int getTrailingTextIndex() { updatePaneList.getContent().add(disableAutoShowUpdateDialogPane); } - BooleanProperty autoDownloadUpdate; { LineToggleButton autoDownloadUpdatePane = new LineToggleButton(); autoDownloadUpdatePane.setTitle(i18n("update.auto_download")); autoDownloadUpdatePane.setSubtitle(i18n("update.auto_download.subtitle")); autoDownloadUpdatePane.selectedProperty().bindBidirectional(settings().autoDownloadUpdateProperty()); - autoDownloadUpdate = autoDownloadUpdatePane.selectedProperty(); updatePaneList.getContent().add(autoDownloadUpdatePane); } From aeeabb4c6ce69b511f918e94bd85113cc6e3f664 Mon Sep 17 00:00:00 2001 From: ToobLac Date: Fri, 26 Jun 2026 22:40:16 +0800 Subject: [PATCH 14/17] update --- .../jackhuang/hmcl/upgrade/UpdateChecker.java | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java index 9ef79e9ac19..daf2a134dce 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java @@ -18,6 +18,8 @@ package org.jackhuang.hmcl.upgrade; import javafx.application.Platform; +import javafx.beans.binding.Bindings; +import javafx.beans.binding.BooleanBinding; import javafx.beans.property.*; import org.jackhuang.hmcl.Metadata; import org.jackhuang.hmcl.util.io.NetworkUtils; @@ -36,7 +38,10 @@ private UpdateChecker() { private static final ObjectProperty latestVersion = new SimpleObjectProperty<>(); private static final ReadOnlyBooleanWrapper outdated = new ReadOnlyBooleanWrapper(false); - private static final ReadOnlyBooleanWrapper checkingUpdate = new ReadOnlyBooleanWrapper(false); + private static final IntegerProperty runningThreads = new SimpleIntegerProperty(0); + private static final BooleanBinding checkingUpdate = Bindings.createBooleanBinding(() -> runningThreads.get() > 0, runningThreads); + + private static UpdateChannel desiredChannel = null; public static void init() { requestCheckUpdate(UpdateChannel.getChannel(), settings().acceptPreviewUpdateProperty().get(), settings().autoDownloadUpdateProperty().get()); @@ -62,8 +67,8 @@ public static boolean isCheckingUpdate() { return checkingUpdate.get(); } - public static ReadOnlyBooleanProperty checkingUpdateProperty() { - return checkingUpdate.getReadOnlyProperty(); + public static BooleanBinding checkingUpdateProperty() { + return checkingUpdate; } private static RemoteVersion checkUpdate(UpdateChannel channel, boolean preview) throws IOException { @@ -99,9 +104,10 @@ private static boolean checkOutdated(RemoteVersion latest) { public static void requestCheckUpdate(UpdateChannel channel, boolean preview, boolean download) { Platform.runLater(() -> { - if (isCheckingUpdate()) + if (isCheckingUpdate() && desiredChannel == channel) return; - checkingUpdate.set(true); + runningThreads.set(runningThreads.get() + 1); + desiredChannel = channel; thread(() -> { RemoteVersion result = null; @@ -118,11 +124,11 @@ public static void requestCheckUpdate(UpdateChannel channel, boolean preview, bo RemoteVersion finalResult = result; Platform.runLater(() -> { - if (finalResult != null) { + if (finalResult != null && desiredChannel == channel) { latestVersion.set(finalResult); outdated.set(isOutdated); } - checkingUpdate.set(false); + runningThreads.set(runningThreads.get() - 1); }); }, "Update Checker", true); }); From d5e1508935cf47e205161be5366313482a8fc8d2 Mon Sep 17 00:00:00 2001 From: ToobLac Date: Fri, 26 Jun 2026 22:44:07 +0800 Subject: [PATCH 15/17] update --- .../main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java index daf2a134dce..6b54caee096 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java @@ -42,6 +42,7 @@ private UpdateChecker() { private static final BooleanBinding checkingUpdate = Bindings.createBooleanBinding(() -> runningThreads.get() > 0, runningThreads); private static UpdateChannel desiredChannel = null; + private static Boolean desiredPreview = null; public static void init() { requestCheckUpdate(UpdateChannel.getChannel(), settings().acceptPreviewUpdateProperty().get(), settings().autoDownloadUpdateProperty().get()); @@ -104,10 +105,11 @@ private static boolean checkOutdated(RemoteVersion latest) { public static void requestCheckUpdate(UpdateChannel channel, boolean preview, boolean download) { Platform.runLater(() -> { - if (isCheckingUpdate() && desiredChannel == channel) + if (isCheckingUpdate() && desiredChannel == channel && desiredPreview != null && desiredPreview.equals(preview)) return; runningThreads.set(runningThreads.get() + 1); desiredChannel = channel; + desiredPreview = preview; thread(() -> { RemoteVersion result = null; @@ -124,7 +126,7 @@ public static void requestCheckUpdate(UpdateChannel channel, boolean preview, bo RemoteVersion finalResult = result; Platform.runLater(() -> { - if (finalResult != null && desiredChannel == channel) { + if (finalResult != null && desiredChannel == channel && desiredPreview.equals(preview)) { latestVersion.set(finalResult); outdated.set(isOutdated); } From d098e170e4fb3556f79a541d31f85c1f4da2a391 Mon Sep 17 00:00:00 2001 From: ToobLac Date: Fri, 26 Jun 2026 22:54:08 +0800 Subject: [PATCH 16/17] update --- .../jackhuang/hmcl/upgrade/UpdateChecker.java | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java index 6b54caee096..9e796446c49 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java @@ -41,8 +41,7 @@ private UpdateChecker() { private static final IntegerProperty runningThreads = new SimpleIntegerProperty(0); private static final BooleanBinding checkingUpdate = Bindings.createBooleanBinding(() -> runningThreads.get() > 0, runningThreads); - private static UpdateChannel desiredChannel = null; - private static Boolean desiredPreview = null; + private static UpdateTarget desiredTarget = null; public static void init() { requestCheckUpdate(UpdateChannel.getChannel(), settings().acceptPreviewUpdateProperty().get(), settings().autoDownloadUpdateProperty().get()); @@ -104,21 +103,24 @@ private static boolean checkOutdated(RemoteVersion latest) { } public static void requestCheckUpdate(UpdateChannel channel, boolean preview, boolean download) { + requestCheckUpdate(new UpdateTarget(channel, preview, download)); + } + + private static void requestCheckUpdate(UpdateTarget target) { Platform.runLater(() -> { - if (isCheckingUpdate() && desiredChannel == channel && desiredPreview != null && desiredPreview.equals(preview)) + if (isCheckingUpdate() && target.concealedBy(desiredTarget)) return; runningThreads.set(runningThreads.get() + 1); - desiredChannel = channel; - desiredPreview = preview; + desiredTarget = target; thread(() -> { RemoteVersion result = null; boolean b = false; try { - result = checkUpdate(channel, preview); + result = checkUpdate(target.channel(), target.preview()); b = checkOutdated(result); - LOG.info("Latest version (" + channel + ", preview=" + preview + ") is " + result); - if (download && b) result.tryDownload(); + LOG.info("Latest version (" + target.channel() + ", preview=" + target.preview() + ") is " + result); + if (target.download() && b) result.tryDownload(); } catch (Throwable e) { LOG.warning("Failed to check for update", e); } @@ -126,7 +128,7 @@ public static void requestCheckUpdate(UpdateChannel channel, boolean preview, bo RemoteVersion finalResult = result; Platform.runLater(() -> { - if (finalResult != null && desiredChannel == channel && desiredPreview.equals(preview)) { + if (finalResult != null && target.concealedBy(desiredTarget)) { latestVersion.set(finalResult); outdated.set(isOutdated); } @@ -135,4 +137,11 @@ public static void requestCheckUpdate(UpdateChannel channel, boolean preview, bo }, "Update Checker", true); }); } + + private record UpdateTarget(UpdateChannel channel, boolean preview, boolean download) { + public boolean concealedBy(UpdateTarget other) { + if (other == null) return false; + return other.channel == channel && other.preview == preview && (!download || other.download); + } + } } From 1fbab4d565f894894f2914c30bc3dede6aaa47ce Mon Sep 17 00:00:00 2001 From: ToobLac Date: Sat, 27 Jun 2026 07:29:15 +0800 Subject: [PATCH 17/17] reverse --- .../main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java index 4ecf95c47e1..27046cc4877 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java @@ -158,21 +158,24 @@ protected int getTrailingTextIndex() { updatePaneList.getContent().add(disableAutoShowUpdateDialogPane); } + BooleanProperty autoDownloadUpdate; { LineToggleButton autoDownloadUpdatePane = new LineToggleButton(); autoDownloadUpdatePane.setTitle(i18n("update.auto_download")); autoDownloadUpdatePane.setSubtitle(i18n("update.auto_download.subtitle")); autoDownloadUpdatePane.selectedProperty().bindBidirectional(settings().autoDownloadUpdateProperty()); + autoDownloadUpdate = autoDownloadUpdatePane.selectedProperty(); updatePaneList.getContent().add(autoDownloadUpdatePane); } { InvalidationListener checkUpdateListener = e -> { - UpdateChecker.requestCheckUpdate(updateChannel.get(), preview.get(), false); + UpdateChecker.requestCheckUpdate(updateChannel.get(), preview.get(), autoDownloadUpdate.get()); }; updateChannel.addListener(checkUpdateListener); preview.addListener(checkUpdateListener); + autoDownloadUpdate.addListener(checkUpdateListener); } rootPane.getChildren().addAll(ComponentList.createComponentListTitle(i18n("update")), updatePaneList);