Skip to content

修复清理游戏资源 / 库操作在 FXThread 运行的问题#5301

Merged
Glavo merged 5 commits intoHMCL-dev:mainfrom
CiiLu:固江河成帝业立国家终归于乱光阴逝千载过功成者都付笑谈间
Mar 30, 2026

Hidden character warning

The head ref may contain hidden characters: "\u56fa\u6c5f\u6cb3\u6210\u5e1d\u4e1a\u7acb\u56fd\u5bb6\u7ec8\u5f52\u4e8e\u4e71\u5149\u9634\u901d\u5343\u8f7d\u8fc7\u529f\u6210\u8005\u90fd\u4ed8\u7b11\u8c08\u95f4"
Merged

修复清理游戏资源 / 库操作在 FXThread 运行的问题#5301
Glavo merged 5 commits intoHMCL-dev:mainfrom
CiiLu:固江河成帝业立国家终归于乱光阴逝千载过功成者都付笑谈间

Conversation

@CiiLu
Copy link
Copy Markdown
Contributor

@CiiLu CiiLu commented Jan 24, 2026

改为和删除实例一样的逻辑

@burningtnt
Copy link
Copy Markdown
Member

image

你在搞什么鬼

@CiiLu CiiLu closed this Jan 24, 2026
@CiiLu CiiLu deleted the 固江河成帝业立国家终归于乱光阴逝千载过功成者都付笑谈间 branch January 24, 2026 02:25
@CiiLu CiiLu restored the 固江河成帝业立国家终归于乱光阴逝千载过功成者都付笑谈间 branch January 24, 2026 02:26
@CiiLu CiiLu reopened this Jan 24, 2026
@Glavo Glavo requested a review from Copilot January 29, 2026 13:18
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

该 PR 旨在修复“清理游戏资源/库文件时在 FXThread 执行导致卡 UI”的问题,将清理操作改为与删除实例类似的异步执行逻辑。

Changes:

  • 将清理 libraries 目录的删除操作迁移到 Schedulers.io() 异步执行,并在 Schedulers.javafx() 回调中处理异常提示
  • 将清理 assets 与实例 resources 目录的删除操作迁移到 Schedulers.io() 异步执行,并在 Schedulers.javafx() 回调中处理异常提示
  • 增加相关 Task/调度/异常展示所需的 import

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +170 to +174
FileUtils.deleteDirectoryQuietly(getProfile().getRepository().getBaseDirectory().resolve("libraries"));
}).whenComplete(Schedulers.javafx(), (exception) -> {
if (exception != null) {
Controllers.dialog(i18n("message.failed") + StringUtils.getStackTrace(exception), i18n("message.error"), MessageDialogPane.MessageType.ERROR);
}
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

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

FileUtils.deleteDirectoryQuietly swallows IOExceptions and only returns a boolean, so this task will usually complete with exception == null even when deletion fails (permissions/locked files). If you want the error dialog to trigger, use deleteDirectory (let IOException propagate) or check the boolean result and fail the task explicitly when it returns false.

Copilot uses AI. Check for mistakes.
FileUtils.deleteDirectoryQuietly(getProfile().getRepository().getBaseDirectory().resolve("libraries"));
}).whenComplete(Schedulers.javafx(), (exception) -> {
if (exception != null) {
Controllers.dialog(i18n("message.failed") + StringUtils.getStackTrace(exception), i18n("message.error"), MessageDialogPane.MessageType.ERROR);
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

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

The error dialog concatenates i18n("message.failed") and the stack trace without a separator, which makes the message hard to read. Add a newline (as done elsewhere, e.g. "..." + "\n" + StringUtils.getStackTrace(...)).

Copilot uses AI. Check for mistakes.
Comment on lines +179 to +183
Task.runAsync(Schedulers.io(), () -> {
HMCLGameRepository baseDirectory = getProfile().getRepository();
FileUtils.deleteDirectoryQuietly(baseDirectory.getBaseDirectory().resolve("assets"));
if (version.get() != null) {
FileUtils.deleteDirectoryQuietly(baseDirectory.getRunDirectory(version.get().getVersion()).resolve("resources"));
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

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

Inside the IO task you read version.get() (a JavaFX property) and derived values from it. Capture the version name (or the resources path) on the FX thread before scheduling to avoid cross-thread access to JavaFX properties and to ensure a consistent target if the selection changes while the task is running.

Suggested change
Task.runAsync(Schedulers.io(), () -> {
HMCLGameRepository baseDirectory = getProfile().getRepository();
FileUtils.deleteDirectoryQuietly(baseDirectory.getBaseDirectory().resolve("assets"));
if (version.get() != null) {
FileUtils.deleteDirectoryQuietly(baseDirectory.getRunDirectory(version.get().getVersion()).resolve("resources"));
HMCLGameRepository baseDirectory = getProfile().getRepository();
String versionId = (version.get() != null) ? version.get().getVersion() : null;
Task.runAsync(Schedulers.io(), () -> {
FileUtils.deleteDirectoryQuietly(baseDirectory.getBaseDirectory().resolve("assets"));
if (versionId != null) {
FileUtils.deleteDirectoryQuietly(baseDirectory.getRunDirectory(versionId).resolve("resources"));

Copilot uses AI. Check for mistakes.
Comment on lines +181 to +188
FileUtils.deleteDirectoryQuietly(baseDirectory.getBaseDirectory().resolve("assets"));
if (version.get() != null) {
FileUtils.deleteDirectoryQuietly(baseDirectory.getRunDirectory(version.get().getVersion()).resolve("resources"));
}
}).whenComplete(Schedulers.javafx(), (exception) -> {
if (exception != null) {
Controllers.dialog(i18n("message.failed") + StringUtils.getStackTrace(exception), i18n("message.error"), MessageDialogPane.MessageType.ERROR);
}
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

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

Same as above: deleteDirectoryQuietly will not throw, so exception will remain null on common deletion failures. If you need to report failure to the user, use a throwing delete or convert the boolean return value into a task failure.

Copilot uses AI. Check for mistakes.
}
}).whenComplete(Schedulers.javafx(), (exception) -> {
if (exception != null) {
Controllers.dialog(i18n("message.failed") + StringUtils.getStackTrace(exception), i18n("message.error"), MessageDialogPane.MessageType.ERROR);
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

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

This dialog message also misses a separator between the localized failure text and the stack trace; add a newline for readability.

Copilot uses AI. Check for mistakes.
Comment on lines +169 to +170
Task.runAsync(Schedulers.io(), () -> {
FileUtils.deleteDirectoryQuietly(getProfile().getRepository().getBaseDirectory().resolve("libraries"));
Copy link

Copilot AI Jan 29, 2026

Choose a reason for hiding this comment

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

The background task calls getProfile()/version.get() (JavaFX properties) from the IO executor. JavaFX properties are not thread-safe; snapshot the needed values (e.g., repository base path / selected version string) on the FX thread before starting the task, and only use those captured values inside runAsync.

Copilot uses AI. Check for mistakes.
@CiiLu CiiLu force-pushed the 固江河成帝业立国家终归于乱光阴逝千载过功成者都付笑谈间 branch from 916f23a to 71418ac Compare March 25, 2026 13:54
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/VersionPage.java Outdated
Comment on lines +193 to +199
var libraries = getProfile().getRepository().getBaseDirectory().resolve("libraries");
Task.runAsync(Schedulers.io(), () -> {
FileUtils.deleteDirectoryQuietly(libraries);
}).whenComplete(Schedulers.javafx(), (exception) -> {
if (exception != null) {
Controllers.dialog(i18n("message.failed") + StringUtils.getStackTrace(exception), i18n("message.error"), MessageDialogPane.MessageType.ERROR);
}
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

FileUtils.deleteDirectoryQuietly(...) swallows IOException and returns false, so this Task.runAsync(...) closure will usually complete successfully even when deletion fails. As a result, whenComplete(..., exception -> ...) won’t show an error dialog on common failures. If you want to report failures, use deleteDirectory(...) (let the exception propagate) or check the boolean result and throw/show an error accordingly.

Copilot uses AI. Check for mistakes.
Comment on lines +211 to +219
Task.runAsync(Schedulers.io(), () -> {
FileUtils.deleteDirectoryQuietly(assetsDir);
if (resourcesDir != null) {
FileUtils.deleteDirectoryQuietly(resourcesDir);
}
}).whenComplete(Schedulers.javafx(), (exception) -> {
if (exception != null) {
Controllers.dialog(i18n("message.failed") + "\n" + StringUtils.getStackTrace(exception), i18n("message.error"), MessageDialogPane.MessageType.ERROR);
}
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

Same as above: deleteDirectoryQuietly(...) catches IOException, so this task will not surface typical deletion failures via exception. Consider using deleteDirectory(...) or handling the boolean return to ensure the UI can inform users when cleanup fails.

Copilot uses AI. Check for mistakes.
…java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@Glavo Glavo merged commit a06da53 into HMCL-dev:main Mar 30, 2026
2 checks passed
@CiiLu CiiLu deleted the 固江河成帝业立国家终归于乱光阴逝千载过功成者都付笑谈间 branch March 30, 2026 13:26
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.

4 participants