From a8b40c4ba05102a5f198514a19baace2b6a62729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=9E=E5=BA=90?= <109708109+Ciilu@users.noreply.github.com> Date: Fri, 20 Mar 2026 23:28:59 +0800 Subject: [PATCH 01/20] update --- .../main/java/org/jackhuang/hmcl/ui/SVG.java | 1 + .../hmcl/ui/construct/MessageDialogPane.java | 15 +- .../hmcl/ui/versions/GameListPage.java | 11 +- .../jackhuang/hmcl/ui/versions/Versions.java | 151 ++++++++++++++++-- .../org/jackhuang/hmcl/util/i18n/I18n.java | 4 + .../hmcl/util/i18n/translator/Translator.java | 10 ++ .../resources/assets/lang/I18N.properties | 3 + .../resources/assets/lang/I18N_zh.properties | 3 + .../assets/lang/I18N_zh_CN.properties | 5 + 9 files changed, 182 insertions(+), 21 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java index c7a99881db..0f3ce1915a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java @@ -44,6 +44,7 @@ public enum SVG { CHECK("M9.55 18 3.85 12.3 5.275 10.875 9.55 15.15 18.725 5.975 20.15 7.4 9.55 18Z"), CHECKROOM("M3 20Q2.575 20 2.2875 19.7125T2 19Q2 18.75 2.1 18.5375T2.4 18.2L11 11.75V10Q11 9.575 11.3 9.2875T12.025 9Q12.65 9 13.075 8.55T13.5 7.475Q13.5 6.85 13.0625 6.425T12 6Q11.375 6 10.9375 6.4375T10.5 7.5H8.5Q8.5 6.05 9.525 5.025T12 4Q13.45 4 14.475 5.0125T15.5 7.475Q15.5 8.65 14.8125 9.575T13 10.85V11.75L21.6 18.2Q21.8 18.325 21.9 18.5375T22 19Q22 19.425 21.7125 19.7125T21 20H3ZM6 18H18L12 13.5 6 18Z"), CHECK_CIRCLE("M10.6 16.6 17.65 9.55 16.25 8.15 10.6 13.8 7.75 10.95 6.35 12.35 10.6 16.6ZM12 22Q9.925 22 8.1 21.2125T4.925 19.075Q3.575 17.725 2.7875 15.9T2 12Q2 9.925 2.7875 8.1T4.925 4.925Q6.275 3.575 8.1 2.7875T12 2Q14.075 2 15.9 2.7875T19.075 4.925Q20.425 6.275 21.2125 8.1T22 12Q22 14.075 21.2125 15.9T19.075 19.075Q17.725 20.425 15.9 21.2125T12 22ZM12 20Q15.35 20 17.675 17.675T20 12Q20 8.65 17.675 6.325T12 4Q8.65 4 6.325 6.325T4 12Q4 15.35 6.325 17.675T12 20ZM12 12Z"), + CLEAN("M3 23v-7q0-2.075 1.463-3.537T8 11h1V3q0-.825.588-1.412T11 1h2q.825 0 1.413.588T15 3v8h1q2.075 0 3.538 1.463T21 16v7zm2-2h2v-3q0-.425.288-.712T8 17t.713.288T9 18v3h2v-3q0-.425.288-.712T12 17t.713.288T13 18v3h2v-3q0-.425.288-.712T16 17t.713.288T17 18v3h2v-5q0-1.25-.875-2.125T16 13H8q-1.25 0-2.125.875T5 16zm8-10V3h-2v8zm0 0h-2z"), CLOSE("M6.4 19 5 17.6 10.6 12 5 6.4 6.4 5 12 10.6 17.6 5 19 6.4 13.4 12 19 17.6 17.6 19 12 13.4 6.4 19Z"), CONTENT_CUT("M19 21l-7-7-2.35 2.35q.2.375.275.8T10 18q0 1.65-1.175 2.825T6 22q-1.65 0-2.825-1.175T2 18t1.175-2.825T6 14q.425 0 .85.075t.8.275L10 12 7.65 9.65q-.375.2-.8.275T6 10q-1.65 0-2.825-1.175T2 6q0-1.65 1.175-2.825T6 2q1.65 0 2.825 1.175T10 6q0 .425-.075.85t-.275.8L22 20v1H19Zm-4-10-2-2 6-6h3v1l-7 7ZM7.4125 7.4125Q8 6.825 8 6t-.5875-1.4125Q6.825 4 6 4t-1.4125.5875Q4 5.175 4 6t.5875 1.4125T6 8t1.4125-.5875ZM12.35 12.35q.15-.15.15-.35t-.15-.35-.35-.15-.35.15-.15.35.15.35.35.15.35-.15ZM7.4125 19.4125Q8 18.825 8 18t-.5875-1.4125Q6.825 16 6 16t-1.4125.5875Q4 17.175 4 18t.5875 1.4125Q5.175 20 6 20t1.4125-.5875Z"), CONTENT_COPY("M9 18Q8.175 18 7.5875 17.4125T7 16V4Q7 3.175 7.5875 2.5875T9 2H18Q18.825 2 19.4125 2.5875T20 4V16Q20 16.825 19.4125 17.4125T18 18H9ZM9 16H18V4H9V16ZM5 22Q4.175 22 3.5875 21.4125T3 20V6H5V20H16V22H5ZM9 16V4 16Z"), diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MessageDialogPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MessageDialogPane.java index 2bed442a5f..7cd2c51692 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MessageDialogPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MessageDialogPane.java @@ -64,6 +64,7 @@ public String getDisplayName() { } private final HBox actions; + private final EnhancedTextFlow textFlow; private @Nullable ButtonBase cancelButton; @@ -87,7 +88,7 @@ public MessageDialogPane(@NotNull String text, @Nullable String title, @NotNull StackPane content = new StackPane(); content.getStyleClass().add("jfx-layout-body"); - EnhancedTextFlow textFlow = new EnhancedTextFlow(text); + textFlow = new EnhancedTextFlow(text); textFlow.setStyle("-fx-font-size: 14px;"); if (textFlow.computePrefHeight(400.0) <= 350.0) content.getChildren().setAll(textFlow); @@ -115,6 +116,10 @@ public MessageDialogPane(@NotNull String text, @Nullable String title, @NotNull }); } + public void setText(String text) { + textFlow.setText(text); + } + public void addButton(Node btn) { btn.addEventHandler(ActionEvent.ACTION, e -> fireEvent(new DialogCloseEvent())); actions.getChildren().add(btn); @@ -130,7 +135,13 @@ public ButtonBase getCancelButton() { private static final class EnhancedTextFlow extends TextFlow { EnhancedTextFlow(String text) { - this.getChildren().setAll(FXUtils.parseSegment(text, Controllers::onHyperlinkAction)); + setText(text); + } + + public void setText(String newText) { + this.getChildren().setAll( + FXUtils.parseSegment(newText, Controllers::onHyperlinkAction) + ); } @Override diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPage.java index cd8aede005..5eab5b0128 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/GameListPage.java @@ -34,7 +34,10 @@ import javafx.scene.control.SkinBase; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; -import javafx.scene.layout.*; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; import javafx.util.Duration; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profiles; @@ -187,6 +190,10 @@ public void refreshList() { Profiles.getSelectedProfile().getRepository().refreshVersionsAsync().start(); } + public void clean() { + Versions.cleanGameFiles(Profiles.getSelectedProfile()); + } + @Override protected Skin createDefaultSkin() { return new GameListSkin(this); @@ -237,7 +244,7 @@ public GameListSkin(GameList skinnable) { searchBar.getChildren().setAll(searchField, closeSearchBar); - toolbarNormal.getChildren().setAll(createToolbarButton2(i18n("button.refresh"), SVG.REFRESH, skinnable::refreshList), createToolbarButton2(i18n("search"), SVG.SEARCH, () -> changeToolbar(searchBar))); + toolbarNormal.getChildren().setAll(createToolbarButton2(i18n("button.refresh"), SVG.REFRESH, skinnable::refreshList), createToolbarButton2(i18n("search"), SVG.SEARCH, () -> changeToolbar(searchBar)), createToolbarButton2(i18n("game.clean"), SVG.CLEAN, skinnable::clean)); toolbarPane.setContent(toolbarNormal, ContainerAnimations.FADE); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index ae72ab4854..b3614a41c8 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -18,7 +18,9 @@ package org.jackhuang.hmcl.ui.versions; import com.jfoenix.controls.JFXButton; +import com.jfoenix.controls.JFXSpinner; import javafx.application.Platform; +import javafx.scene.layout.StackPane; import javafx.stage.FileChooser; import org.jackhuang.hmcl.auth.Account; import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccount; @@ -42,6 +44,7 @@ import org.jackhuang.hmcl.ui.export.ExportWizardProvider; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.TaskCancellationAction; +import org.jackhuang.hmcl.util.i18n.I18n; import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.platform.OperatingSystem; @@ -49,10 +52,14 @@ import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.logging.Logger.LOG; @@ -131,23 +138,23 @@ public static void deleteVersion(Profile profile, String version) { public static CompletableFuture renameVersion(Profile profile, String version) { return Controllers.prompt(i18n("version.manage.rename.message"), (newName, handler) -> { - if (newName.equals(version)) { - handler.resolve(); - return; - } - if (profile.getRepository().renameVersion(version, newName)) { - handler.resolve(); - profile.getRepository().refreshVersionsAsync() - .thenRunAsync(Schedulers.javafx(), () -> { - if (profile.getRepository().hasVersion(newName)) { - profile.setSelectedVersion(newName); - } - }).start(); - } else { - handler.reject(i18n("version.manage.rename.fail")); - } - }, version, new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId), - new Validator(i18n("install.new_game.already_exists"), newVersionName -> !profile.getRepository().versionIdConflicts(newVersionName) || newVersionName.equals(version))); + if (newName.equals(version)) { + handler.resolve(); + return; + } + if (profile.getRepository().renameVersion(version, newName)) { + handler.resolve(); + profile.getRepository().refreshVersionsAsync() + .thenRunAsync(Schedulers.javafx(), () -> { + if (profile.getRepository().hasVersion(newName)) { + profile.setSelectedVersion(newName); + } + }).start(); + } else { + handler.reject(i18n("version.manage.rename.fail")); + } + }, version, new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId), + new Validator(i18n("install.new_game.already_exists"), newVersionName -> !profile.getRepository().versionIdConflicts(newVersionName) || newVersionName.equals(version))); } public static void exportVersion(Profile profile, String version) { @@ -194,6 +201,116 @@ public static void updateGameAssets(Profile profile, String version) { executor.start(); } + public static void cleanGameFiles(Profile profile) { + var dialogBuilder = new MessageDialogPane.Builder(i18n("game.clean.content", "..."), i18n("message.question"), MessageDialogPane.MessageType.QUESTION); + var spinner = new JFXSpinner(); + spinner.getStyleClass().add("small-spinner"); + + StackPane buttonPane = new StackPane(); + + JFXButton okButton = new JFXButton(i18n("button.yes")); + okButton.getStyleClass().add("dialog-accept"); + + dialogBuilder.addAction(buttonPane); + dialogBuilder.addCancel(null); + + var dialog = dialogBuilder.build(); + + Task.supplyAsync(() -> { + var repository = profile.getRepository(); + var versions = repository.getVersions(); + + Set activeAssets = versions.parallelStream() + .flatMap(version -> { + try { + var index = repository.getAssetIndex(version.getId(), version.getAssetIndex().getId()); + return index.getObjects().values().stream().map(AssetObject::getLocation); + } catch (IOException ignored) { + return Stream.empty(); + } + }) + .collect(Collectors.toSet()); + + Set activeLibraries = versions.parallelStream() + .flatMap(version -> version.getLibraries().stream()) + .map(Library::getPath) + .collect(Collectors.toSet()); + List junkFiles = new ArrayList<>(); + + Path assetsDir = repository.getBaseDirectory().resolve("assets").resolve("objects"); + + junkFiles.addAll(findUnlistedFiles(assetsDir, activeAssets)); + + Path libsDir = repository.getBaseDirectory().resolve("libraries"); + junkFiles.addAll(findUnlistedFiles(libsDir, activeLibraries)); + + List logsAndCrashes = new ArrayList<>(); + logsAndCrashes.add(repository.getBaseDirectory().resolve("logs")); + logsAndCrashes.add(repository.getBaseDirectory().resolve("crash-reports")); + versions.forEach(v -> { + logsAndCrashes.add(repository.getVersionRoot(v.getId()).resolve("logs")); + logsAndCrashes.add(repository.getVersionRoot(v.getId()).resolve("crash-reports")); + }); + + for (Path dir : logsAndCrashes) { + if (Files.exists(dir)) { + try (var s = Files.walk(dir)) { + s.filter(Files::isRegularFile).forEach(junkFiles::add); + } catch (IOException ignored) { + } + } + } + + return junkFiles; + }).thenApplyAsync((list) -> { + long totalSize = list.stream() + .mapToLong(path -> { + try { + return Files.size(path); + } catch (IOException e) { + return 0L; + } + }) + .sum(); + + FXUtils.runInFX(() -> { + dialog.setText(i18n("game.clean.content", I18n.formatSize(totalSize))); + buttonPane.getChildren().setAll(okButton); + okButton.setOnAction(event -> { + buttonPane.getChildren().setAll(spinner); + Task.runAsync(() -> list.forEach(path -> { + try { + Files.deleteIfExists(path); + } catch (IOException ignored) { + } + })).thenRunAsync(Schedulers.javafx(), () -> { + dialog.fireEvent(new DialogCloseEvent()); + }).start(); + }); + }); + return null; + }).start(); + + buttonPane.getChildren().setAll(spinner); + + Controllers.dialog(dialog); + } + + private static List findUnlistedFiles(Path root, Set activePaths) { + if (!Files.exists(root)) return List.of(); + try (var stream = Files.walk(root)) { + return stream + .filter(Files::isRegularFile) + .filter(path -> { + String relative = root.relativize(path).toString(); + return !activePaths.contains(relative); + }) + .toList(); + } catch (IOException e) { + return List.of(); + } + } + public static void cleanVersion(Profile profile, String id) { try { profile.getRepository().clean(id); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/I18n.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/I18n.java index 286910be07..5c4db97ffc 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/I18n.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/I18n.java @@ -77,6 +77,10 @@ public static String formatSpeed(long bytes) { return getTranslator().formatSpeed(bytes); } + public static String formatSize(long bytes) { + return getTranslator().formatSize(bytes); + } + public static String getDisplayVersion(RemoteVersion version) { return getTranslator().getDisplayVersion(version); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/translator/Translator.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/translator/Translator.java index 7bdcfc8038..a55a5fc24a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/translator/Translator.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/translator/Translator.java @@ -73,4 +73,14 @@ public String formatSpeed(long bytes) { return supportedLocale.i18n("download.speed.megabyte_per_second", (double) bytes / (1024 * 1024)); } } + + public String formatSize(long bytes) { + if (bytes < 1024) { + return supportedLocale.i18n("download.size.byte", bytes); + } else if (bytes < 1024 * 1024) { + return supportedLocale.i18n("download.size.kibibyte", (double) bytes / 1024); + } else { + return supportedLocale.i18n("download.size.megabyte", (double) bytes / (1024 * 1024)); + } + } } diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 174b7a1f41..49274d1921 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -379,6 +379,9 @@ download.javafx.notes=We are currently downloading dependencies for HMCL from th Note: If your download speed is too slow, you can try switching to another mirror. download.javafx.component=Downloading module "%s" download.javafx.prepare=Preparing to download +download.size.byte=%d B +download.speed.kibibyte=%.1f KiB +download.speed.megabyte=%.1f MiB download.speed.byte_per_second=%d B/s download.speed.kibibyte_per_second=%.1f KiB/s download.speed.megabyte_per_second=%.1f MiB/s diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 96f7df20b1..0d9bf3793d 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -372,6 +372,9 @@ download.javafx=正在下載必要的執行時元件 download.javafx.notes=正在透過網路下載 HMCL 必要的執行時元件。\n點擊「切換下載源」按鈕查看詳情以及選取下載源。點擊「取消」按鈕停止並退出。\n注意:如果下載速度過慢,請嘗試切換下載源。 download.javafx.component=正在下載元件「%s」 download.javafx.prepare=準備開始下載 +download.size.byte=%d B +download.speed.kibibyte=%.1f KiB +download.speed.megabyte=%.1f MiB download.speed.byte_per_second=%d B/s download.speed.kibibyte_per_second=%.1f KiB/s download.speed.megabyte_per_second=%.1f MiB/s 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 2d306511af..98e89d2706 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -374,6 +374,9 @@ download.javafx=正在下载必要的运行时组件…… download.javafx.notes=正在通过网络下载 HMCL 必要的运行时组件。\n点击“切换下载源”按钮查看详情以及选择下载源。点击“取消”按钮停止并退出。\n注意:若下载速度过慢,请尝试切换下载源。 download.javafx.component=正在下载模块“%s” download.javafx.prepare=准备开始下载 +download.size.byte=%d B +download.size.kibibyte=%.1f KiB +download.size.megabyte=%.1f MiB download.speed.byte_per_second=%d B/s download.speed.kibibyte_per_second=%.1f KiB/s download.speed.megabyte_per_second=%.1f MiB/s @@ -437,6 +440,8 @@ folder.screenshots=截图文件夹 folder.world=世界文件夹 game=游戏 +game.clean=清理游戏文件 +game.clean.content=本操作将会清理游戏的日志、冗余库、冗余资源等文件,预计释放 %s 空间。\n你的模组、存档等游戏数据不会受到影响。是否继续操作? game.crash.feedback=请不要将本界面截图或拍照给他人!如果你要向他人求助,请你点击左下角“导出游戏崩溃信息”后将导出的文件发送给他人以供分析。\n你可以点击下方的“帮助”前往交流群寻求帮助。 game.crash.info=游戏信息 game.crash.reason=崩溃原因 From d60bb2efc3d3609948f8d0a3995e127e9d5aec09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=9E=E5=BA=90?= <109708109+Ciilu@users.noreply.github.com> Date: Fri, 20 Mar 2026 23:34:34 +0800 Subject: [PATCH 02/20] update --- .../jackhuang/hmcl/ui/versions/Versions.java | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index b3614a41c8..af339fcb92 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -138,23 +138,23 @@ public static void deleteVersion(Profile profile, String version) { public static CompletableFuture renameVersion(Profile profile, String version) { return Controllers.prompt(i18n("version.manage.rename.message"), (newName, handler) -> { - if (newName.equals(version)) { - handler.resolve(); - return; - } - if (profile.getRepository().renameVersion(version, newName)) { - handler.resolve(); - profile.getRepository().refreshVersionsAsync() - .thenRunAsync(Schedulers.javafx(), () -> { - if (profile.getRepository().hasVersion(newName)) { - profile.setSelectedVersion(newName); - } - }).start(); - } else { - handler.reject(i18n("version.manage.rename.fail")); - } - }, version, new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId), - new Validator(i18n("install.new_game.already_exists"), newVersionName -> !profile.getRepository().versionIdConflicts(newVersionName) || newVersionName.equals(version))); + if (newName.equals(version)) { + handler.resolve(); + return; + } + if (profile.getRepository().renameVersion(version, newName)) { + handler.resolve(); + profile.getRepository().refreshVersionsAsync() + .thenRunAsync(Schedulers.javafx(), () -> { + if (profile.getRepository().hasVersion(newName)) { + profile.setSelectedVersion(newName); + } + }).start(); + } else { + handler.reject(i18n("version.manage.rename.fail")); + } + }, version, new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId), + new Validator(i18n("install.new_game.already_exists"), newVersionName -> !profile.getRepository().versionIdConflicts(newVersionName) || newVersionName.equals(version))); } public static void exportVersion(Profile profile, String version) { From a29d193f63dbf485d01a9919ce0ea709d1970e3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=9E=E5=BA=90?= <109708109+Ciilu@users.noreply.github.com> Date: Fri, 20 Mar 2026 23:37:05 +0800 Subject: [PATCH 03/20] update --- HMCL/src/main/resources/assets/lang/I18N.properties | 4 ++-- HMCL/src/main/resources/assets/lang/I18N_zh.properties | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 49274d1921..d0dc585bf0 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -380,8 +380,8 @@ download.javafx.notes=We are currently downloading dependencies for HMCL from th download.javafx.component=Downloading module "%s" download.javafx.prepare=Preparing to download download.size.byte=%d B -download.speed.kibibyte=%.1f KiB -download.speed.megabyte=%.1f MiB +download.size.kibibyte=%.1f KiB +download.size.megabyte=%.1f MiB download.speed.byte_per_second=%d B/s download.speed.kibibyte_per_second=%.1f KiB/s download.speed.megabyte_per_second=%.1f MiB/s diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 0d9bf3793d..df540ed798 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -373,8 +373,8 @@ download.javafx.notes=正在透過網路下載 HMCL 必要的執行時元件。\ download.javafx.component=正在下載元件「%s」 download.javafx.prepare=準備開始下載 download.size.byte=%d B -download.speed.kibibyte=%.1f KiB -download.speed.megabyte=%.1f MiB +download.size.kibibyte=%.1f KiB +download.size.megabyte=%.1f MiB download.speed.byte_per_second=%d B/s download.speed.kibibyte_per_second=%.1f KiB/s download.speed.megabyte_per_second=%.1f MiB/s From dbdd6ff82f20c9512867a0316d0a196695d240aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=9E=E5=BA=90?= <109708109+Ciilu@users.noreply.github.com> Date: Fri, 20 Mar 2026 23:40:56 +0800 Subject: [PATCH 04/20] update --- HMCL/src/main/resources/assets/lang/I18N.properties | 2 ++ HMCL/src/main/resources/assets/lang/I18N_zh.properties | 2 ++ 2 files changed, 4 insertions(+) diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index d0dc585bf0..70cfa44932 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -473,6 +473,8 @@ folder.screenshots=Screenshots folder.world=World Directory game=Games +game.clean=Clean Game Files +game.clean.content=This operation will clean up game logs, redundant libraries, redundant resource files, etc., freeing up approximately %s of space.\nYour mods, save files, and other game data will not be affected. Do you want to continue? game.crash.feedback=Please do not share screenshots or photos of this interface with others! If you ask for help from others, please click "Export Crash Logs" and send the exported file to others for analysis. game.crash.info=Crash Info game.crash.reason=Crash Cause diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index df540ed798..b2b190fd1e 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -436,6 +436,8 @@ folder.screenshots=截圖目錄 folder.world=世界目錄 game=遊戲 +game.clean=清理遊戲文件 +game.clean.content=本操作將會清理遊戲的日誌、冗餘庫、冗餘資源等文件,預計釋放 %s 空間。\n你的模組、存檔等遊戲數據不會受到影響。是否繼續操作? game.crash.feedback=請不要將本介面截圖或拍照給他人!如果你要求助他人,請你點擊左下角「匯出遊戲崩潰資訊」後將匯出的檔案發送給他人以供分析。\n你可以點擊下方的「幫助」前往社群尋求幫助。 game.crash.info=遊戲訊息 game.crash.reason=崩潰原因 From bca62ef8e84fe86c094db3fc6934447643179a9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=9E=E5=BA=90?= <109708109+Ciilu@users.noreply.github.com> Date: Sat, 21 Mar 2026 10:50:04 +0800 Subject: [PATCH 05/20] update --- .../jackhuang/hmcl/ui/versions/Versions.java | 27 +++++++++---------- .../hmcl/util/i18n/translator/Translator.java | 4 ++- .../resources/assets/lang/I18N.properties | 2 ++ .../resources/assets/lang/I18N_zh.properties | 2 ++ .../assets/lang/I18N_zh_CN.properties | 2 ++ 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index af339fcb92..abcb923e92 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -202,7 +202,7 @@ public static void updateGameAssets(Profile profile, String version) { } public static void cleanGameFiles(Profile profile) { - var dialogBuilder = new MessageDialogPane.Builder(i18n("game.clean.content", "..."), i18n("message.question"), MessageDialogPane.MessageType.QUESTION); + var dialogBuilder = new MessageDialogPane.Builder(i18n("game.clean.content", i18n("game.clean.loading")), i18n("message.question"), MessageDialogPane.MessageType.QUESTION); var spinner = new JFXSpinner(); spinner.getStyleClass().add("small-spinner"); @@ -235,33 +235,30 @@ public static void cleanGameFiles(Profile profile) { .flatMap(version -> version.getLibraries().stream()) .map(Library::getPath) .collect(Collectors.toSet()); - List junkFiles = new ArrayList<>(); - Path assetsDir = repository.getBaseDirectory().resolve("assets").resolve("objects"); + List unusedFiles = new ArrayList<>(); - junkFiles.addAll(findUnlistedFiles(assetsDir, activeAssets)); + unusedFiles.addAll(findUnlistedFiles(repository.getBaseDirectory().resolve("assets").resolve("objects"), activeAssets)); + unusedFiles.addAll(findUnlistedFiles(repository.getBaseDirectory().resolve("libraries"), activeLibraries)); - Path libsDir = repository.getBaseDirectory().resolve("libraries"); - junkFiles.addAll(findUnlistedFiles(libsDir, activeLibraries)); - - List logsAndCrashes = new ArrayList<>(); - logsAndCrashes.add(repository.getBaseDirectory().resolve("logs")); - logsAndCrashes.add(repository.getBaseDirectory().resolve("crash-reports")); + List unusedLogs = new ArrayList<>(); + unusedLogs.add(repository.getBaseDirectory().resolve("logs")); + unusedLogs.add(repository.getBaseDirectory().resolve("crash-reports")); versions.forEach(v -> { - logsAndCrashes.add(repository.getVersionRoot(v.getId()).resolve("logs")); - logsAndCrashes.add(repository.getVersionRoot(v.getId()).resolve("crash-reports")); + unusedLogs.add(repository.getVersionRoot(v.getId()).resolve("logs")); + unusedLogs.add(repository.getVersionRoot(v.getId()).resolve("crash-reports")); }); - for (Path dir : logsAndCrashes) { + for (Path dir : unusedLogs) { if (Files.exists(dir)) { try (var s = Files.walk(dir)) { - s.filter(Files::isRegularFile).forEach(junkFiles::add); + s.filter(Files::isRegularFile).forEach(unusedFiles::add); } catch (IOException ignored) { } } } - return junkFiles; + return unusedFiles; }).thenApplyAsync((list) -> { long totalSize = list.stream() .mapToLong(path -> { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/translator/Translator.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/translator/Translator.java index a55a5fc24a..4299c9fb89 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/translator/Translator.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/translator/Translator.java @@ -79,8 +79,10 @@ public String formatSize(long bytes) { return supportedLocale.i18n("download.size.byte", bytes); } else if (bytes < 1024 * 1024) { return supportedLocale.i18n("download.size.kibibyte", (double) bytes / 1024); - } else { + } else if (bytes < 1024 * 1024 * 1024){ return supportedLocale.i18n("download.size.megabyte", (double) bytes / (1024 * 1024)); + } else { + return supportedLocale.i18n("download.size.gibabyte", (double) bytes / (1024 * 1024 * 1024)); } } } diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 70cfa44932..0cbb51a14d 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -382,6 +382,7 @@ download.javafx.prepare=Preparing to download download.size.byte=%d B download.size.kibibyte=%.1f KiB download.size.megabyte=%.1f MiB +download.size.gibabyte=%.1f GiB download.speed.byte_per_second=%d B/s download.speed.kibibyte_per_second=%.1f KiB/s download.speed.megabyte_per_second=%.1f MiB/s @@ -474,6 +475,7 @@ folder.world=World Directory game=Games game.clean=Clean Game Files +game.clean.loading=Loading... game.clean.content=This operation will clean up game logs, redundant libraries, redundant resource files, etc., freeing up approximately %s of space.\nYour mods, save files, and other game data will not be affected. Do you want to continue? game.crash.feedback=Please do not share screenshots or photos of this interface with others! If you ask for help from others, please click "Export Crash Logs" and send the exported file to others for analysis. game.crash.info=Crash Info diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index b2b190fd1e..ccc03792fd 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -375,6 +375,7 @@ download.javafx.prepare=準備開始下載 download.size.byte=%d B download.size.kibibyte=%.1f KiB download.size.megabyte=%.1f MiB +download.size.gibabyte=%.1f GiB download.speed.byte_per_second=%d B/s download.speed.kibibyte_per_second=%.1f KiB/s download.speed.megabyte_per_second=%.1f MiB/s @@ -437,6 +438,7 @@ folder.world=世界目錄 game=遊戲 game.clean=清理遊戲文件 +game.clean.loading=统计中... game.clean.content=本操作將會清理遊戲的日誌、冗餘庫、冗餘資源等文件,預計釋放 %s 空間。\n你的模組、存檔等遊戲數據不會受到影響。是否繼續操作? game.crash.feedback=請不要將本介面截圖或拍照給他人!如果你要求助他人,請你點擊左下角「匯出遊戲崩潰資訊」後將匯出的檔案發送給他人以供分析。\n你可以點擊下方的「幫助」前往社群尋求幫助。 game.crash.info=遊戲訊息 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 98e89d2706..ca2eebdf12 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -377,6 +377,7 @@ download.javafx.prepare=准备开始下载 download.size.byte=%d B download.size.kibibyte=%.1f KiB download.size.megabyte=%.1f MiB +download.size.gibabyte=%.1f GiB download.speed.byte_per_second=%d B/s download.speed.kibibyte_per_second=%.1f KiB/s download.speed.megabyte_per_second=%.1f MiB/s @@ -441,6 +442,7 @@ folder.world=世界文件夹 game=游戏 game.clean=清理游戏文件 +game.clean.loading=统计中... game.clean.content=本操作将会清理游戏的日志、冗余库、冗余资源等文件,预计释放 %s 空间。\n你的模组、存档等游戏数据不会受到影响。是否继续操作? game.crash.feedback=请不要将本界面截图或拍照给他人!如果你要向他人求助,请你点击左下角“导出游戏崩溃信息”后将导出的文件发送给他人以供分析。\n你可以点击下方的“帮助”前往交流群寻求帮助。 game.crash.info=游戏信息 From 007a10325a299ad808684524bd658ee1e90d8649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=9E=E5=BA=90?= <109708109+Ciilu@users.noreply.github.com> Date: Sat, 21 Mar 2026 10:51:34 +0800 Subject: [PATCH 06/20] update --- .../org/jackhuang/hmcl/util/i18n/translator/Translator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/translator/Translator.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/translator/Translator.java index 4299c9fb89..82a7c201eb 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/translator/Translator.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/translator/Translator.java @@ -79,7 +79,7 @@ public String formatSize(long bytes) { return supportedLocale.i18n("download.size.byte", bytes); } else if (bytes < 1024 * 1024) { return supportedLocale.i18n("download.size.kibibyte", (double) bytes / 1024); - } else if (bytes < 1024 * 1024 * 1024){ + } else if (bytes < 1024 * 1024 * 1024) { return supportedLocale.i18n("download.size.megabyte", (double) bytes / (1024 * 1024)); } else { return supportedLocale.i18n("download.size.gibabyte", (double) bytes / (1024 * 1024 * 1024)); From 7ed8f521da509d3a0c4898797c8f17bb38cc5cd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=9E=E5=BA=90?= <109708109+Ciilu@users.noreply.github.com> Date: Sat, 21 Mar 2026 11:17:13 +0800 Subject: [PATCH 07/20] update --- .../jackhuang/hmcl/ui/versions/Versions.java | 62 ++++++++++++------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index abcb923e92..a0e4422250 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -138,23 +138,23 @@ public static void deleteVersion(Profile profile, String version) { public static CompletableFuture renameVersion(Profile profile, String version) { return Controllers.prompt(i18n("version.manage.rename.message"), (newName, handler) -> { - if (newName.equals(version)) { - handler.resolve(); - return; - } - if (profile.getRepository().renameVersion(version, newName)) { - handler.resolve(); - profile.getRepository().refreshVersionsAsync() - .thenRunAsync(Schedulers.javafx(), () -> { - if (profile.getRepository().hasVersion(newName)) { - profile.setSelectedVersion(newName); - } - }).start(); - } else { - handler.reject(i18n("version.manage.rename.fail")); - } - }, version, new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId), - new Validator(i18n("install.new_game.already_exists"), newVersionName -> !profile.getRepository().versionIdConflicts(newVersionName) || newVersionName.equals(version))); + if (newName.equals(version)) { + handler.resolve(); + return; + } + if (profile.getRepository().renameVersion(version, newName)) { + handler.resolve(); + profile.getRepository().refreshVersionsAsync() + .thenRunAsync(Schedulers.javafx(), () -> { + if (profile.getRepository().hasVersion(newName)) { + profile.setSelectedVersion(newName); + } + }).start(); + } else { + handler.reject(i18n("version.manage.rename.fail")); + } + }, version, new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId), + new Validator(i18n("install.new_game.already_exists"), newVersionName -> !profile.getRepository().versionIdConflicts(newVersionName) || newVersionName.equals(version))); } public static void exportVersion(Profile profile, String version) { @@ -241,15 +241,29 @@ public static void cleanGameFiles(Profile profile) { unusedFiles.addAll(findUnlistedFiles(repository.getBaseDirectory().resolve("assets").resolve("objects"), activeAssets)); unusedFiles.addAll(findUnlistedFiles(repository.getBaseDirectory().resolve("libraries"), activeLibraries)); - List unusedLogs = new ArrayList<>(); - unusedLogs.add(repository.getBaseDirectory().resolve("logs")); - unusedLogs.add(repository.getBaseDirectory().resolve("crash-reports")); + List unusedFolders = new ArrayList<>(); + + for (String path : List.of("logs", "crash-reports", "modernfix", "mods/.connector")) { + unusedFolders.add(repository.getBaseDirectory().resolve(path)); + versions.forEach(v -> { + unusedFolders.add(repository.getVersionRoot(v.getId()).resolve(path)); + }); + } + versions.forEach(v -> { - unusedLogs.add(repository.getVersionRoot(v.getId()).resolve("logs")); - unusedLogs.add(repository.getVersionRoot(v.getId()).resolve("crash-reports")); + try (var walker = Files.walk(repository.getVersionRoot(v.getId()), 1)) { + unusedFolders.addAll(walker + .filter(it -> { + var name = it.getFileName().toString(); + System.out.println(name); + System.out.println(Files.isDirectory(it) && (name.startsWith("natives-")) || name.endsWith("-natives")); + return Files.isDirectory(it) && (name.startsWith("natives-")) || name.endsWith("-natives"); + }).toList()); + } catch (IOException ignored) { + } }); - for (Path dir : unusedLogs) { + for (Path dir : unusedFolders) { if (Files.exists(dir)) { try (var s = Files.walk(dir)) { s.filter(Files::isRegularFile).forEach(unusedFiles::add); @@ -299,7 +313,7 @@ private static List findUnlistedFiles(Path root, Set activePaths) return stream .filter(Files::isRegularFile) .filter(path -> { - String relative = root.relativize(path).toString(); + String relative = root.relativize(path).toString().replace("\\", "/"); return !activePaths.contains(relative); }) .toList(); From 06dd28e2b14f0fd11f7a7a5479dc786d7b1211d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=9E=E5=BA=90?= <109708109+Ciilu@users.noreply.github.com> Date: Sat, 21 Mar 2026 11:18:28 +0800 Subject: [PATCH 08/20] update --- HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index a0e4422250..549d032261 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -255,8 +255,6 @@ public static void cleanGameFiles(Profile profile) { unusedFolders.addAll(walker .filter(it -> { var name = it.getFileName().toString(); - System.out.println(name); - System.out.println(Files.isDirectory(it) && (name.startsWith("natives-")) || name.endsWith("-natives")); return Files.isDirectory(it) && (name.startsWith("natives-")) || name.endsWith("-natives"); }).toList()); } catch (IOException ignored) { From 9cc49fb95d31c03d574f0762721d196b9ec6ab34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=9E=E5=BA=90?= <109708109+Ciilu@users.noreply.github.com> Date: Sat, 21 Mar 2026 11:24:15 +0800 Subject: [PATCH 09/20] update --- .../main/java/org/jackhuang/hmcl/ui/versions/Versions.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index 549d032261..95215bb4c5 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -243,7 +243,7 @@ public static void cleanGameFiles(Profile profile) { List unusedFolders = new ArrayList<>(); - for (String path : List.of("logs", "crash-reports", "modernfix", "mods/.connector")) { + for (String path : List.of("logs", "crash-reports", "modernfix", "mods/.connector", "CustomSkinLoader/caches", ".fabric")) { unusedFolders.add(repository.getBaseDirectory().resolve(path)); versions.forEach(v -> { unusedFolders.add(repository.getVersionRoot(v.getId()).resolve(path)); @@ -255,7 +255,7 @@ public static void cleanGameFiles(Profile profile) { unusedFolders.addAll(walker .filter(it -> { var name = it.getFileName().toString(); - return Files.isDirectory(it) && (name.startsWith("natives-")) || name.endsWith("-natives"); + return Files.isDirectory(it) && (name.startsWith("natives-") || name.endsWith("-natives")); }).toList()); } catch (IOException ignored) { } From 4bb530544ebe7bc5641005734af3dce4aea79a65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=9E=E5=BA=90?= <109708109+Ciilu@users.noreply.github.com> Date: Sat, 21 Mar 2026 11:26:44 +0800 Subject: [PATCH 10/20] update --- .../jackhuang/hmcl/ui/versions/Versions.java | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index 95215bb4c5..e19e90616d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -138,23 +138,23 @@ public static void deleteVersion(Profile profile, String version) { public static CompletableFuture renameVersion(Profile profile, String version) { return Controllers.prompt(i18n("version.manage.rename.message"), (newName, handler) -> { - if (newName.equals(version)) { - handler.resolve(); - return; - } - if (profile.getRepository().renameVersion(version, newName)) { - handler.resolve(); - profile.getRepository().refreshVersionsAsync() - .thenRunAsync(Schedulers.javafx(), () -> { - if (profile.getRepository().hasVersion(newName)) { - profile.setSelectedVersion(newName); - } - }).start(); - } else { - handler.reject(i18n("version.manage.rename.fail")); - } - }, version, new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId), - new Validator(i18n("install.new_game.already_exists"), newVersionName -> !profile.getRepository().versionIdConflicts(newVersionName) || newVersionName.equals(version))); + if (newName.equals(version)) { + handler.resolve(); + return; + } + if (profile.getRepository().renameVersion(version, newName)) { + handler.resolve(); + profile.getRepository().refreshVersionsAsync() + .thenRunAsync(Schedulers.javafx(), () -> { + if (profile.getRepository().hasVersion(newName)) { + profile.setSelectedVersion(newName); + } + }).start(); + } else { + handler.reject(i18n("version.manage.rename.fail")); + } + }, version, new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId), + new Validator(i18n("install.new_game.already_exists"), newVersionName -> !profile.getRepository().versionIdConflicts(newVersionName) || newVersionName.equals(version))); } public static void exportVersion(Profile profile, String version) { From 72449904636075ce6aea4a86caa35c62c62c875d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=9E=E5=BA=90?= <109708109+CiiLu@users.noreply.github.com> Date: Sat, 4 Apr 2026 12:33:42 +0800 Subject: [PATCH 11/20] Update I18N.properties --- HMCL/src/main/resources/assets/lang/I18N.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 0cbb51a14d..7153d5f632 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -474,7 +474,7 @@ folder.screenshots=Screenshots folder.world=World Directory game=Games -game.clean=Clean Game Files +game.clean=Cleanup Game Files game.clean.loading=Loading... game.clean.content=This operation will clean up game logs, redundant libraries, redundant resource files, etc., freeing up approximately %s of space.\nYour mods, save files, and other game data will not be affected. Do you want to continue? game.crash.feedback=Please do not share screenshots or photos of this interface with others! If you ask for help from others, please click "Export Crash Logs" and send the exported file to others for analysis. @@ -1677,4 +1677,4 @@ wiki.version.game.snapshot=https://minecraft.wiki/w/Java_Edition_%s wizard.prev=< Prev wizard.failed=Failed wizard.finish=Finish -wizard.next=Next > \ No newline at end of file +wizard.next=Next > From 1470dfa60bbbfed0e91d10580407f3ed158c5838 Mon Sep 17 00:00:00 2001 From: CiiLu <109708109+CiiLu@users.noreply.github.com> Date: Mon, 20 Apr 2026 21:44:39 +0800 Subject: [PATCH 12/20] update --- .../jackhuang/hmcl/ui/versions/Versions.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index e19e90616d..f7effab7ff 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -53,6 +53,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.CancellationException; @@ -221,27 +222,25 @@ public static void cleanGameFiles(Profile profile) { var versions = repository.getVersions(); Set activeAssets = versions.parallelStream() - .flatMap(version -> { + .map(Version::getAssetIndex) + .distinct() + .flatMap(idx -> { try { - var index = repository.getAssetIndex(version.getId(), version.getAssetIndex().getId()); + AssetIndex index = repository.getAssetIndex(null, idx.getId()); return index.getObjects().values().stream().map(AssetObject::getLocation); - } catch (IOException ignored) { + } catch (IOException e) { + LOG.warning("Failed to get asset index", e); return Stream.empty(); } - }) - .collect(Collectors.toSet()); - Set activeLibraries = versions.parallelStream() - .flatMap(version -> version.getLibraries().stream()) - .map(Library::getPath) + }) .collect(Collectors.toSet()); List unusedFiles = new ArrayList<>(); unusedFiles.addAll(findUnlistedFiles(repository.getBaseDirectory().resolve("assets").resolve("objects"), activeAssets)); - unusedFiles.addAll(findUnlistedFiles(repository.getBaseDirectory().resolve("libraries"), activeLibraries)); - List unusedFolders = new ArrayList<>(); + Set unusedFolders = new HashSet<>(); for (String path : List.of("logs", "crash-reports", "modernfix", "mods/.connector", "CustomSkinLoader/caches", ".fabric")) { unusedFolders.add(repository.getBaseDirectory().resolve(path)); From 60d137df8c4f9a51033968b5e55b3d771ae8dd05 Mon Sep 17 00:00:00 2001 From: CiiLu <109708109+CiiLu@users.noreply.github.com> Date: Mon, 20 Apr 2026 21:47:36 +0800 Subject: [PATCH 13/20] update --- 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 7153d5f632..276e5ddfad 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -476,7 +476,7 @@ folder.world=World Directory game=Games game.clean=Cleanup Game Files game.clean.loading=Loading... -game.clean.content=This operation will clean up game logs, redundant libraries, redundant resource files, etc., freeing up approximately %s of space.\nYour mods, save files, and other game data will not be affected. Do you want to continue? +game.clean.content=This operation will clean up game logs, redundant resource files, etc., freeing up approximately %s of space.\nYour mods, save files, and other game data will not be affected. Do you want to continue? game.crash.feedback=Please do not share screenshots or photos of this interface with others! If you ask for help from others, please click "Export Crash Logs" and send the exported file to others for analysis. game.crash.info=Crash Info game.crash.reason=Crash Cause diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index ccc03792fd..b6fde1d8ab 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -439,7 +439,7 @@ folder.world=世界目錄 game=遊戲 game.clean=清理遊戲文件 game.clean.loading=统计中... -game.clean.content=本操作將會清理遊戲的日誌、冗餘庫、冗餘資源等文件,預計釋放 %s 空間。\n你的模組、存檔等遊戲數據不會受到影響。是否繼續操作? +game.clean.content=本操作將會清理遊戲的日誌、冗餘資源等文件,預計釋放 %s 空間。\n你的模組、存檔等遊戲數據不會受到影響。是否繼續操作? game.crash.feedback=請不要將本介面截圖或拍照給他人!如果你要求助他人,請你點擊左下角「匯出遊戲崩潰資訊」後將匯出的檔案發送給他人以供分析。\n你可以點擊下方的「幫助」前往社群尋求幫助。 game.crash.info=遊戲訊息 game.crash.reason=崩潰原因 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 ca2eebdf12..1a245c7e04 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -443,7 +443,7 @@ folder.world=世界文件夹 game=游戏 game.clean=清理游戏文件 game.clean.loading=统计中... -game.clean.content=本操作将会清理游戏的日志、冗余库、冗余资源等文件,预计释放 %s 空间。\n你的模组、存档等游戏数据不会受到影响。是否继续操作? +game.clean.content=本操作将会清理游戏的日志、冗余资源等文件,预计释放 %s 空间。\n你的模组、存档等游戏数据不会受到影响。是否继续操作? game.crash.feedback=请不要将本界面截图或拍照给他人!如果你要向他人求助,请你点击左下角“导出游戏崩溃信息”后将导出的文件发送给他人以供分析。\n你可以点击下方的“帮助”前往交流群寻求帮助。 game.crash.info=游戏信息 game.crash.reason=崩溃原因 From 8588ac603845c2e301a1e33c58d87efca076304a Mon Sep 17 00:00:00 2001 From: CiiLu <109708109+CiiLu@users.noreply.github.com> Date: Sat, 25 Apr 2026 20:48:47 +0800 Subject: [PATCH 14/20] update --- .../hmcl/ui/construct/MessageDialogPane.java | 11 +++++++++++ .../java/org/jackhuang/hmcl/ui/versions/Versions.java | 8 +++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MessageDialogPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MessageDialogPane.java index 7cd2c51692..392698eed3 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MessageDialogPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MessageDialogPane.java @@ -125,6 +125,10 @@ public void addButton(Node btn) { actions.getChildren().add(btn); } + public void addButtonNoClose(Node btn) { + actions.getChildren().add(btn); + } + public void setCancelButton(@Nullable ButtonBase btn) { cancelButton = btn; } @@ -170,6 +174,13 @@ public Builder addAction(Node actionNode) { return this; } + public Builder addActionNoClose(Node actionNode) { + dialog.addButtonNoClose(actionNode); + actionNode.getStyleClass().add("dialog-accept"); + return this; + } + + public Builder addAction(String text, @Nullable Runnable action) { JFXButton btnAction = new JFXButton(text); btnAction.getStyleClass().add("dialog-accept"); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index f7effab7ff..ec80664f2c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -212,7 +212,7 @@ public static void cleanGameFiles(Profile profile) { JFXButton okButton = new JFXButton(i18n("button.yes")); okButton.getStyleClass().add("dialog-accept"); - dialogBuilder.addAction(buttonPane); + dialogBuilder.addActionNoClose(buttonPane); dialogBuilder.addCancel(null); var dialog = dialogBuilder.build(); @@ -244,13 +244,11 @@ public static void cleanGameFiles(Profile profile) { for (String path : List.of("logs", "crash-reports", "modernfix", "mods/.connector", "CustomSkinLoader/caches", ".fabric")) { unusedFolders.add(repository.getBaseDirectory().resolve(path)); - versions.forEach(v -> { - unusedFolders.add(repository.getVersionRoot(v.getId()).resolve(path)); - }); + versions.forEach(v -> unusedFolders.add(repository.getRunDirectory(v.getId()).resolve(path))); } versions.forEach(v -> { - try (var walker = Files.walk(repository.getVersionRoot(v.getId()), 1)) { + try (var walker = Files.walk(repository.getRunDirectory(v.getId()), 1)) { unusedFolders.addAll(walker .filter(it -> { var name = it.getFileName().toString(); From 37b3d986e69d07ce6da80c27b23bec0f04cd83ab Mon Sep 17 00:00:00 2001 From: CiiLu <109708109+CiiLu@users.noreply.github.com> Date: Sat, 25 Apr 2026 20:51:37 +0800 Subject: [PATCH 15/20] update --- HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index ec80664f2c..e1c83493f4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -62,6 +62,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.logging.Logger.LOG; @@ -283,6 +284,7 @@ public static void cleanGameFiles(Profile profile) { dialog.setText(i18n("game.clean.content", I18n.formatSize(totalSize))); buttonPane.getChildren().setAll(okButton); okButton.setOnAction(event -> { + onEscPressed(dialog, null); buttonPane.getChildren().setAll(spinner); Task.runAsync(() -> list.forEach(path -> { try { From 873501b2c6503d32c60a48bd61924af419eb484b Mon Sep 17 00:00:00 2001 From: CiiLu <109708109+CiiLu@users.noreply.github.com> Date: Sat, 25 Apr 2026 20:54:44 +0800 Subject: [PATCH 16/20] update --- .../java/org/jackhuang/hmcl/ui/construct/MessageDialogPane.java | 1 - 1 file changed, 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MessageDialogPane.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MessageDialogPane.java index 392698eed3..51d21fa8d3 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MessageDialogPane.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/MessageDialogPane.java @@ -180,7 +180,6 @@ public Builder addActionNoClose(Node actionNode) { return this; } - public Builder addAction(String text, @Nullable Runnable action) { JFXButton btnAction = new JFXButton(text); btnAction.getStyleClass().add("dialog-accept"); From db5dcf38eecf28377c77f64b880d22e7b9988223 Mon Sep 17 00:00:00 2001 From: CiiLu <109708109+CiiLu@users.noreply.github.com> Date: Sat, 25 Apr 2026 21:05:36 +0800 Subject: [PATCH 17/20] update --- .../jackhuang/hmcl/ui/versions/Versions.java | 48 ++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index e1c83493f4..e61359c826 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -140,23 +140,23 @@ public static void deleteVersion(Profile profile, String version) { public static CompletableFuture renameVersion(Profile profile, String version) { return Controllers.prompt(i18n("version.manage.rename.message"), (newName, handler) -> { - if (newName.equals(version)) { - handler.resolve(); - return; - } - if (profile.getRepository().renameVersion(version, newName)) { - handler.resolve(); - profile.getRepository().refreshVersionsAsync() - .thenRunAsync(Schedulers.javafx(), () -> { - if (profile.getRepository().hasVersion(newName)) { - profile.setSelectedVersion(newName); - } - }).start(); - } else { - handler.reject(i18n("version.manage.rename.fail")); - } - }, version, new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId), - new Validator(i18n("install.new_game.already_exists"), newVersionName -> !profile.getRepository().versionIdConflicts(newVersionName) || newVersionName.equals(version))); + if (newName.equals(version)) { + handler.resolve(); + return; + } + if (profile.getRepository().renameVersion(version, newName)) { + handler.resolve(); + profile.getRepository().refreshVersionsAsync() + .thenRunAsync(Schedulers.javafx(), () -> { + if (profile.getRepository().hasVersion(newName)) { + profile.setSelectedVersion(newName); + } + }).start(); + } else { + handler.reject(i18n("version.manage.rename.fail")); + } + }, version, new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidVersionId), + new Validator(i18n("install.new_game.already_exists"), newVersionName -> !profile.getRepository().versionIdConflicts(newVersionName) || newVersionName.equals(version))); } public static void exportVersion(Profile profile, String version) { @@ -271,6 +271,7 @@ public static void cleanGameFiles(Profile profile) { return unusedFiles; }).thenApplyAsync((list) -> { long totalSize = list.stream() + .parallel() .mapToLong(path -> { try { return Files.size(path); @@ -283,17 +284,20 @@ public static void cleanGameFiles(Profile profile) { FXUtils.runInFX(() -> { dialog.setText(i18n("game.clean.content", I18n.formatSize(totalSize))); buttonPane.getChildren().setAll(okButton); + okButton.setDisable(totalSize == 0); + okButton.setOnAction(event -> { - onEscPressed(dialog, null); + onEscPressed(dialog, () -> { + }); + dialog.getCancelButton().setDisable(true); buttonPane.getChildren().setAll(spinner); Task.runAsync(() -> list.forEach(path -> { try { Files.deleteIfExists(path); - } catch (IOException ignored) { + } catch (IOException e) { + LOG.warning("Failed to delete file " + path, e); } - })).thenRunAsync(Schedulers.javafx(), () -> { - dialog.fireEvent(new DialogCloseEvent()); - }).start(); + })).thenRunAsync(Schedulers.javafx(), () -> dialog.fireEvent(new DialogCloseEvent())).start(); }); }); return null; From d213228b0c85508d4f4580238bc3d18132e69494 Mon Sep 17 00:00:00 2001 From: CiiLu <109708109+CiiLu@users.noreply.github.com> Date: Sat, 25 Apr 2026 21:37:47 +0800 Subject: [PATCH 18/20] update --- .../java/org/jackhuang/hmcl/ui/versions/Versions.java | 10 ++++------ .../hmcl/util/i18n/translator/Translator.java | 2 +- HMCL/src/main/resources/assets/lang/I18N.properties | 2 +- HMCL/src/main/resources/assets/lang/I18N_zh.properties | 2 +- .../main/resources/assets/lang/I18N_zh_CN.properties | 2 +- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index 4d3526b465..ea5bef505f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -55,10 +55,7 @@ import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; @@ -264,6 +261,7 @@ public static void cleanGameFiles(Profile profile) { var versions = repository.getVersions(); Set activeAssets = versions.parallelStream() + .filter(Objects::nonNull) .map(Version::getAssetIndex) .distinct() .flatMap(idx -> { @@ -289,8 +287,8 @@ public static void cleanGameFiles(Profile profile) { versions.forEach(v -> unusedFolders.add(repository.getRunDirectory(v.getId()).resolve(path))); } - versions.forEach(v -> { - try (var walker = Files.walk(repository.getRunDirectory(v.getId()), 1)) { + versions.stream().map(v -> repository.getRunDirectory(v.getId())).distinct().forEach(runDir -> { + try (var walker = Files.walk(runDir, 1)) { unusedFolders.addAll(walker .filter(it -> { var name = it.getFileName().toString(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/translator/Translator.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/translator/Translator.java index 82a7c201eb..713b35813e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/translator/Translator.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/i18n/translator/Translator.java @@ -82,7 +82,7 @@ public String formatSize(long bytes) { } else if (bytes < 1024 * 1024 * 1024) { return supportedLocale.i18n("download.size.megabyte", (double) bytes / (1024 * 1024)); } else { - return supportedLocale.i18n("download.size.gibabyte", (double) bytes / (1024 * 1024 * 1024)); + return supportedLocale.i18n("download.size.gigabyte", (double) bytes / (1024 * 1024 * 1024)); } } } diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 5d1380a45d..3b5665c527 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -385,7 +385,7 @@ download.javafx.prepare=Preparing to download download.size.byte=%d B download.size.kibibyte=%.1f KiB download.size.megabyte=%.1f MiB -download.size.gibabyte=%.1f GiB +download.size.gigabyte=%.1f GiB download.speed.byte_per_second=%d B/s download.speed.kibibyte_per_second=%.1f KiB/s download.speed.megabyte_per_second=%.1f MiB/s diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index aa3f3ee95f..a952b2264b 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -378,7 +378,7 @@ download.javafx.prepare=準備開始下載 download.size.byte=%d B download.size.kibibyte=%.1f KiB download.size.megabyte=%.1f MiB -download.size.gibabyte=%.1f GiB +download.size.gigabyte=%.1f GiB download.speed.byte_per_second=%d B/s download.speed.kibibyte_per_second=%.1f KiB/s download.speed.megabyte_per_second=%.1f MiB/s 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 0dfc1078a7..e2342e3c1a 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -380,7 +380,7 @@ download.javafx.prepare=准备开始下载 download.size.byte=%d B download.size.kibibyte=%.1f KiB download.size.megabyte=%.1f MiB -download.size.gibabyte=%.1f GiB +download.size.gigabyte=%.1f GiB download.speed.byte_per_second=%d B/s download.speed.kibibyte_per_second=%.1f KiB/s download.speed.megabyte_per_second=%.1f MiB/s From 292f7f6f9918639e7f75a98843956b62bedc7d6f Mon Sep 17 00:00:00 2001 From: CiiLu <109708109+CiiLu@users.noreply.github.com> Date: Sat, 25 Apr 2026 21:41:45 +0800 Subject: [PATCH 19/20] update --- .../main/java/org/jackhuang/hmcl/ui/versions/Versions.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index ea5bef505f..91e9fc9458 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -282,9 +282,9 @@ public static void cleanGameFiles(Profile profile) { Set unusedFolders = new HashSet<>(); - for (String path : List.of("logs", "crash-reports", "modernfix", "mods/.connector", "CustomSkinLoader/caches", ".fabric")) { + var uselessFolderNames = Set.of("logs", "crash-reports", "modernfix", "mods/.connector", "CustomSkinLoader/caches", ".fabric"); + for (String path : uselessFolderNames) { unusedFolders.add(repository.getBaseDirectory().resolve(path)); - versions.forEach(v -> unusedFolders.add(repository.getRunDirectory(v.getId()).resolve(path))); } versions.stream().map(v -> repository.getRunDirectory(v.getId())).distinct().forEach(runDir -> { @@ -292,7 +292,7 @@ public static void cleanGameFiles(Profile profile) { unusedFolders.addAll(walker .filter(it -> { var name = it.getFileName().toString(); - return Files.isDirectory(it) && (name.startsWith("natives-") || name.endsWith("-natives")); + return (name.startsWith("natives-") || name.endsWith("-natives") || uselessFolderNames.contains(name)) && Files.isDirectory(it); }).toList()); } catch (IOException ignored) { } From b60b52d25b83e59787e6135b7e9cc56cd10b8b1f Mon Sep 17 00:00:00 2001 From: CiiLu <109708109+CiiLu@users.noreply.github.com> Date: Mon, 27 Apr 2026 21:45:23 +0800 Subject: [PATCH 20/20] update --- .../src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java index 91e9fc9458..f52b3c6cb7 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/Versions.java @@ -260,7 +260,7 @@ public static void cleanGameFiles(Profile profile) { var repository = profile.getRepository(); var versions = repository.getVersions(); - Set activeAssets = versions.parallelStream() + Set activeAssets = versions.stream() .filter(Objects::nonNull) .map(Version::getAssetIndex) .distinct() @@ -310,7 +310,6 @@ public static void cleanGameFiles(Profile profile) { return unusedFiles; }).thenApplyAsync((list) -> { long totalSize = list.stream() - .parallel() .mapToLong(path -> { try { return Files.size(path);