Skip to content

[Enhancement] 后台自动下载更新#6092

Closed
ToobLac wants to merge 24 commits into
HMCL-dev:mainfrom
ToobLac:auto-update-download
Closed

[Enhancement] 后台自动下载更新#6092
ToobLac wants to merge 24 commits into
HMCL-dev:mainfrom
ToobLac:auto-update-download

Conversation

@ToobLac

@ToobLac ToobLac commented May 16, 2026

Copy link
Copy Markdown
Contributor

Closes #4352

目前默认关闭,开启后在检查更新成功后自动下载更新然后再弹窗

@Minecraft269

Copy link
Copy Markdown

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces an automatic update download feature, allowing the launcher to download updates in the background and cache them for installation. It adds the corresponding UI settings, localization strings, and a reusable hash verification utility. The review feedback highlights several critical improvements: implementing a version check to prevent downloading updates when the launcher is already up-to-date, using a thread-safe ConcurrentHashMap for the download cache to avoid concurrency issues, reverting an apparently erroneous change to the NIGHTLY channel name, and using Files.isRegularFile instead of Files.exists to prevent attempting to hash directories.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java
Comment thread HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java Outdated
Comment thread HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java Outdated
Comment thread HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChannel.java Outdated
Comment thread HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/FileUtils.java Outdated
@Minecraft269

Copy link
Copy Markdown

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces an automatic update download feature, allowing launcher updates to be downloaded in the background once detected. It adds a new setting toggle in the UI, caches downloaded updates, and refactors hash verification into a reusable utility. Feedback highlights two key issues: first, running the download synchronously within the update checker thread blocks the update-checking UI state, which should be resolved by offloading the download to a separate thread; second, failed downloads leave orphaned temporary files, which should be cleaned up upon failure.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java Outdated
Comment thread HMCL/src/main/java/org/jackhuang/hmcl/upgrade/RemoteVersion.java
@ToobLac ToobLac force-pushed the auto-update-download branch from 22201ad to aeeabb4 Compare June 26, 2026 14:41
@Minecraft269

Copy link
Copy Markdown

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces an "Auto download update" feature to automatically download launcher updates in the background. It adds a configuration property, a UI toggle in the settings page, caching of downloaded updates, and refactors hash verification into a reusable utility method. The review feedback points out that the auto-download setting is ignored (hardcoded to false) when the update channel or preview settings are changed, and suggests respecting the user's preference.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java Outdated
@Minecraft269

Copy link
Copy Markdown

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces an automatic background download feature for launcher updates, adding a new setting toggle in the UI, caching downloaded updates, and refactoring hash verification into a reusable utility method. The review feedback highlights a potential issue in UpdateChecker where valid update information might be discarded due to overly strict target matching, a concurrency issue in RemoteVersion.tryDownload() that could lead to duplicate downloads under race conditions, and a recommendation to add defensive null checks to the new FileUtils.verifyHash method.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

RemoteVersion finalResult = result;
Platform.runLater(() -> {
if (finalResult != null) {
if (finalResult != null && target.concealedBy(desiredTarget)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

此处使用 target.concealedBy(desiredTarget) 来判断是否更新 UI 上的版本信息过于严格。

如果前一个请求 target1 启用了下载(download=true),而最新的请求 target2 未启用下载(download=false),那么当 target1 的线程执行完毕时,target1.concealedBy(target2) 将返回 false。这会导致 target1 成功获取到的最新版本信息被丢弃,无法更新到 UI 上。

实际上,只要 channelpreview 相同,获取到的最新版本信息就是完全一致且有效的。建议在 UpdateTarget 中增加一个 isSameSource 方法,仅比较 channelpreview,并在此处使用它。

Suggested change
if (finalResult != null && target.concealedBy(desiredTarget)) {
if (finalResult != null && target.isSameSource(desiredTarget)) {

Comment on lines +141 to +146
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);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

建议在 UpdateTarget 中增加 isSameSource 方法,用于在不考虑 download 属性的情况下,判断两个更新目标是否指向相同的更新通道和预览设置。这可以避免在更新 UI 版本信息时,因 download 属性不同而错误地丢弃有效的更新结果。

    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);
        }

        public boolean isSameSource(UpdateTarget other) {
            if (other == null) return false;
            return other.channel == channel && other.preview == preview;
        }
    }

Comment on lines +65 to +86
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;
}

var executor = new HMCLDownloadTask(this, downloaded).executor();
if (executor.test()) {
downloadCache.put(this, downloaded);
} else {
LOG.warning("Failed to download update for " + this, executor.getException());
try {
Files.deleteIfExists(downloaded);
} catch (IOException ignored) {
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

在后台自动下载更新时,tryDownload() 可能会被并发调用(例如,用户在短时间内多次手动触发更新检查,或者多个模块同时请求检查)。

由于 tryDownload() 中的“检查缓存 -> 创建临时文件 -> 开始下载”操作不是原子的,可能会导致多个线程同时为同一个 RemoteVersion 创建临时文件并重复下载,造成网络资源和磁盘空间的浪费。

建议引入一个并发安全的集合(例如 ConcurrentHashMap.newKeySet())来记录当前正在下载的 RemoteVersion,从而避免重复的并发下载。

    private static final java.util.Set<RemoteVersion> downloadingVersions = ConcurrentHashMap.newKeySet();

    public void tryDownload() {
        Path downloaded = downloadCache.get(this);
        if (downloaded != null && FileUtils.verifyHash(downloaded, integrityCheck().algorithm(), integrityCheck().checksum())) return;

        if (!downloadingVersions.add(this)) return;
        try {
            try {
                downloaded = Files.createTempFile("hmcl-update-", ".jar");
            } catch (IOException e) {
                LOG.warning("Failed to create temp file", e);
                return;
            }

            var executor = new HMCLDownloadTask(this, downloaded).executor();
            if (executor.test()) {
                downloadCache.put(this, downloaded);
            } else {
                LOG.warning("Failed to download update for " + this, executor.getException());
                try {
                    Files.deleteIfExists(downloaded);
                } catch (IOException ignored) {
                }
            }
        } finally {
            downloadingVersions.remove(this);
        }
    }

Comment on lines +578 to +589
public static boolean verifyHash(Path file, String algorithm, String hash) {
if (Files.isRegularFile(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;
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

为了提高工具类方法的健壮性,建议在 verifyHash 方法中添加对 filealgorithmhash 的防御性空检查(Null Checks),避免在传入 null 时抛出 NullPointerException

    public static boolean verifyHash(Path file, String algorithm, String hash) {
        if (file == null || algorithm == null || hash == null) {
            return false;
        }
        if (Files.isRegularFile(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;
        }
    }

@ToobLac

ToobLac commented Jun 28, 2026

Copy link
Copy Markdown
Contributor Author

Replaced by #6255

@ToobLac ToobLac closed this Jun 28, 2026
@ToobLac

ToobLac commented Jun 28, 2026

Copy link
Copy Markdown
Contributor Author

Replaced by #6255

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] 添加“后台自动下载更新”功能设置

2 participants