From 8fc013d315c8cb4dee6dd4ca2b279a7c32bdcd24 Mon Sep 17 00:00:00 2001 From: YELANDAOKONG <116365019+YELANDAOKONG@users.noreply.github.com> Date: Fri, 26 Jun 2026 12:32:38 +0800 Subject: [PATCH 1/9] =?UTF-8?q?fix(#6153):=20=E5=AF=BC=E5=85=A5=E5=8C=85?= =?UTF-8?q?=E5=90=AB=E5=90=AF=E5=8A=A8=E5=99=A8=E7=9A=84=E6=95=B4=E5=90=88?= =?UTF-8?q?=E5=8C=85=E6=97=B6=E6=8F=90=E7=A4=BA"=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E8=AF=86=E5=88=AB"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jackhuang/hmcl/game/ModpackHelper.java | 25 +++++++++++++++++++ .../hmcl/ui/download/LocalModpackPage.java | 8 +++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java index 669b3ba3903..91dcab1c700 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java @@ -124,6 +124,31 @@ public static Modpack readModpackManifest(Path file, Charset charset) throws Uns throw new UnsupportedModpackException(file.toString()); } + /// Checks if the given ZIP file is an HMCL "include launcher" wrapper + /// that contains an inner modpack file ([modpack.zip] or [modpack.mrpack]). + /// If so, returns the [Path] of the inner entry within the wrapper filesystem + /// so the inner modpack can be read without extracting to disk. + /// The caller must keep the returned [Path] reachable, which keeps the + /// wrapper filesystem alive. Returns [null] if the file is not a wrapper. + @Nullable + public static Path unwrapIfLauncherWrapper(Path file, Charset charset) { + try { + FileSystem outerFs = CompressingUtils.createReadOnlyZipFileSystem(file, charset); + for (String innerName : new String[]{"modpack.zip", "modpack.mrpack"}) { + Path entryPath = outerFs.getPath("/" + innerName); + if (Files.isRegularFile(entryPath)) { + return entryPath; + } + } + try { + outerFs.close(); + } catch (IOException ignored) { + } + } catch (IOException ignored) { + } + return null; + } + public static Path findMinecraftDirectoryInManuallyCreatedModpack(String modpackName, FileSystem fs) throws IOException, UnsupportedModpackException { Path root = fs.getPath("/"); if (isMinecraftDirectory(root)) return root; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java index c731a684d6d..71bd90e710b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java @@ -104,7 +104,13 @@ public LocalModpackPage(WizardController controller) { Task.supplyAsync(() -> CompressingUtils.findSuitableEncoding(selectedFile)) .thenApplyAsync(encoding -> { charset = encoding; - manifest = ModpackHelper.readModpackManifest(selectedFile, encoding); + Path actualFile = selectedFile; + Path inner = ModpackHelper.unwrapIfLauncherWrapper(selectedFile, encoding); + if (inner != null) { + actualFile = inner; + controller.getSettings().put(MODPACK_FILE, inner); + } + manifest = ModpackHelper.readModpackManifest(actualFile, encoding); return manifest; }) .whenComplete(Schedulers.javafx(), (manifest, exception) -> { From 15903716f083e493267a69199b6a573061655450 Mon Sep 17 00:00:00 2001 From: YELANDAOKONG <116365019+YELANDAOKONG@users.noreply.github.com> Date: Fri, 26 Jun 2026 12:45:45 +0800 Subject: [PATCH 2/9] =?UTF-8?q?fix(#6153):=20=E5=AF=BC=E5=85=A5=E5=8C=85?= =?UTF-8?q?=E5=90=AB=E5=90=AF=E5=8A=A8=E5=99=A8=E7=9A=84=E6=95=B4=E5=90=88?= =?UTF-8?q?=E5=8C=85=E6=97=B6=E6=8F=90=E7=A4=BA"=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E8=AF=86=E5=88=AB"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/jackhuang/hmcl/game/ModpackHelper.java | 16 ++++++++-------- .../hmcl/ui/download/LocalModpackPage.java | 11 +++++++---- .../download/ModpackInstallWizardProvider.java | 13 ++++++++++++- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java index 91dcab1c700..0de5f13240e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java @@ -39,6 +39,7 @@ import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.Lang; +import org.jackhuang.hmcl.util.Pair; import org.jackhuang.hmcl.util.PortablePath; import org.jackhuang.hmcl.util.function.ExceptionalConsumer; import org.jackhuang.hmcl.util.function.ExceptionalRunnable; @@ -124,20 +125,19 @@ public static Modpack readModpackManifest(Path file, Charset charset) throws Uns throw new UnsupportedModpackException(file.toString()); } - /// Checks if the given ZIP file is an HMCL "include launcher" wrapper - /// that contains an inner modpack file ([modpack.zip] or [modpack.mrpack]). - /// If so, returns the [Path] of the inner entry within the wrapper filesystem - /// so the inner modpack can be read without extracting to disk. - /// The caller must keep the returned [Path] reachable, which keeps the - /// wrapper filesystem alive. Returns [null] if the file is not a wrapper. + /// Detects whether [file] is an HMCL launcher wrapper ZIP that embeds + /// the actual modpack as [modpack.zip] or [modpack.mrpack]. + /// Returns a [Pair] of the inner entry [Path] and the wrapper + /// [FileSystem], or [null] if this is not a wrapper. + /// The caller must close the wrapper filesystem when done. @Nullable - public static Path unwrapIfLauncherWrapper(Path file, Charset charset) { + public static Pair unwrapIfLauncherWrapper(Path file, Charset charset) { try { FileSystem outerFs = CompressingUtils.createReadOnlyZipFileSystem(file, charset); for (String innerName : new String[]{"modpack.zip", "modpack.mrpack"}) { Path entryPath = outerFs.getPath("/" + innerName); if (Files.isRegularFile(entryPath)) { - return entryPath; + return pair(entryPath, outerFs); } } try { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java index 71bd90e710b..0b1b2d2a532 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java @@ -42,6 +42,7 @@ import org.jackhuang.hmcl.util.io.FileUtils; import java.nio.charset.Charset; +import java.nio.file.FileSystem; import java.nio.file.Path; import static org.jackhuang.hmcl.util.logging.Logger.LOG; @@ -105,10 +106,11 @@ public LocalModpackPage(WizardController controller) { .thenApplyAsync(encoding -> { charset = encoding; Path actualFile = selectedFile; - Path inner = ModpackHelper.unwrapIfLauncherWrapper(selectedFile, encoding); - if (inner != null) { - actualFile = inner; - controller.getSettings().put(MODPACK_FILE, inner); + var wrapper = ModpackHelper.unwrapIfLauncherWrapper(selectedFile, encoding); + if (wrapper != null) { + actualFile = wrapper.getKey(); + controller.getSettings().put(MODPACK_FILE, wrapper.getKey()); + controller.getSettings().put(MODPACK_WRAPPER_FS, wrapper.getValue()); } manifest = ModpackHelper.readModpackManifest(actualFile, encoding); return manifest; @@ -185,6 +187,7 @@ protected void onDescribe() { } public static final SettingsMap.Key MODPACK_FILE = new SettingsMap.Key<>("MODPACK_FILE"); + public static final SettingsMap.Key MODPACK_WRAPPER_FS = new SettingsMap.Key<>("MODPACK_WRAPPER_FS"); public static final SettingsMap.Key MODPACK_NAME = new SettingsMap.Key<>("MODPACK_NAME"); public static final SettingsMap.Key MODPACK_MANIFEST = new SettingsMap.Key<>("MODPACK_MANIFEST"); public static final SettingsMap.Key MODPACK_CHARSET = new SettingsMap.Key<>("MODPACK_CHARSET"); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java index cc8fae2ba95..ffcc0dcc2ff 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java @@ -39,6 +39,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.nio.charset.Charset; +import java.nio.file.FileSystem; import java.nio.file.Path; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; @@ -145,7 +146,17 @@ public Object finish(SettingsMap settings) { } }); - return finishModpackInstallingAsync(settings); + FileSystem wrapperFs = settings.get(LocalModpackPage.MODPACK_WRAPPER_FS); + Task task = finishModpackInstallingAsync(settings); + if (wrapperFs != null && task != null) { + task = task.whenComplete(Schedulers.defaultScheduler(), ignored -> { + try { + wrapperFs.close(); + } catch (IOException e) { + } + }); + } + return task; } private static Node createModpackInstallPage(WizardController controller) { From cddbec8257396992265436809edd94299102f2bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=9C=E5=96=B5=E7=A8=BB=E7=A9=BA?= <116365019+YELANDAOKONG@users.noreply.github.com> Date: Fri, 26 Jun 2026 12:51:37 +0800 Subject: [PATCH 3/9] Update HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- .../download/ModpackInstallWizardProvider.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java index ffcc0dcc2ff..89d3489dc81 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java @@ -148,13 +148,21 @@ public Object finish(SettingsMap settings) { FileSystem wrapperFs = settings.get(LocalModpackPage.MODPACK_WRAPPER_FS); Task task = finishModpackInstallingAsync(settings); - if (wrapperFs != null && task != null) { - task = task.whenComplete(Schedulers.defaultScheduler(), ignored -> { + if (wrapperFs != null) { + if (task != null) { + settings.remove(LocalModpackPage.MODPACK_WRAPPER_FS); + task = task.whenComplete(Schedulers.defaultScheduler(), ignored -> { + try { + wrapperFs.close(); + } catch (IOException e) { + } + }); + } else { try { wrapperFs.close(); - } catch (IOException e) { + } catch (IOException ignored) { } - }); + } } return task; } From 449cab2e5ad4e8dd555f5a742e0fd6e00987db9a Mon Sep 17 00:00:00 2001 From: YELANDAOKONG <116365019+YELANDAOKONG@users.noreply.github.com> Date: Fri, 26 Jun 2026 12:52:46 +0800 Subject: [PATCH 4/9] =?UTF-8?q?fix(#6153):=20=E5=AF=BC=E5=85=A5=E5=8C=85?= =?UTF-8?q?=E5=90=AB=E5=90=AF=E5=8A=A8=E5=99=A8=E7=9A=84=E6=95=B4=E5=90=88?= =?UTF-8?q?=E5=8C=85=E6=97=B6=E6=8F=90=E7=A4=BA"=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E8=AF=86=E5=88=AB"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/jackhuang/hmcl/game/ModpackHelper.java | 18 ++++++++++++------ .../hmcl/ui/download/LocalModpackPage.java | 8 ++++++++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java index 0de5f13240e..84201e4fe2b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java @@ -132,19 +132,25 @@ public static Modpack readModpackManifest(Path file, Charset charset) throws Uns /// The caller must close the wrapper filesystem when done. @Nullable public static Pair unwrapIfLauncherWrapper(Path file, Charset charset) { + FileSystem outerFs = null; try { - FileSystem outerFs = CompressingUtils.createReadOnlyZipFileSystem(file, charset); + outerFs = CompressingUtils.createReadOnlyZipFileSystem(file, charset); for (String innerName : new String[]{"modpack.zip", "modpack.mrpack"}) { Path entryPath = outerFs.getPath("/" + innerName); if (Files.isRegularFile(entryPath)) { - return pair(entryPath, outerFs); + FileSystem fs = outerFs; + outerFs = null; + return pair(entryPath, fs); } } - try { - outerFs.close(); - } catch (IOException ignored) { - } } catch (IOException ignored) { + } finally { + if (outerFs != null) { + try { + outerFs.close(); + } catch (IOException ignored) { + } + } } return null; } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java index 0b1b2d2a532..d42bf2ba18b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java @@ -41,6 +41,7 @@ import org.jackhuang.hmcl.util.io.CompressingUtils; import org.jackhuang.hmcl.util.io.FileUtils; +import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.FileSystem; import java.nio.file.Path; @@ -155,6 +156,13 @@ public LocalModpackPage(WizardController controller) { @Override public void cleanup(SettingsMap settings) { settings.remove(MODPACK_FILE); + FileSystem wrapperFs = settings.remove(MODPACK_WRAPPER_FS); + if (wrapperFs != null) { + try { + wrapperFs.close(); + } catch (IOException ignored) { + } + } } protected void onInstall() { From e90914e7cd663c9fb0f2c88bb30c0cd77c7ad29f Mon Sep 17 00:00:00 2001 From: YELANDAOKONG <116365019+YELANDAOKONG@users.noreply.github.com> Date: Fri, 26 Jun 2026 12:57:30 +0800 Subject: [PATCH 5/9] =?UTF-8?q?fix(#6153):=20=E5=AF=BC=E5=85=A5=E5=8C=85?= =?UTF-8?q?=E5=90=AB=E5=90=AF=E5=8A=A8=E5=99=A8=E7=9A=84=E6=95=B4=E5=90=88?= =?UTF-8?q?=E5=8C=85=E6=97=B6=E6=8F=90=E7=A4=BA"=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E8=AF=86=E5=88=AB"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java | 1 + .../java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java | 1 + .../hmcl/ui/download/ModpackInstallWizardProvider.java | 2 ++ 3 files changed, 4 insertions(+) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java index 84201e4fe2b..9098d660f45 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java @@ -149,6 +149,7 @@ public static Pair unwrapIfLauncherWrapper(Path file, Charset try { outerFs.close(); } catch (IOException ignored) { + // Ignore close errors for wrapper filesystem } } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java index d42bf2ba18b..a1ceca0d711 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java @@ -161,6 +161,7 @@ public void cleanup(SettingsMap settings) { try { wrapperFs.close(); } catch (IOException ignored) { + // Ignore close errors for wrapper filesystem } } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java index 89d3489dc81..55a1209433b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java @@ -155,12 +155,14 @@ public Object finish(SettingsMap settings) { try { wrapperFs.close(); } catch (IOException e) { + // Ignore close errors for wrapper filesystem } }); } else { try { wrapperFs.close(); } catch (IOException ignored) { + // Ignore close errors for wrapper filesystem } } } From 158036fde7b08dcc74fe0134a0e5bceeede5fcc4 Mon Sep 17 00:00:00 2001 From: YELANDAOKONG <116365019+YELANDAOKONG@users.noreply.github.com> Date: Fri, 26 Jun 2026 12:58:54 +0800 Subject: [PATCH 6/9] =?UTF-8?q?fix(#6153):=20=E5=AF=BC=E5=85=A5=E5=8C=85?= =?UTF-8?q?=E5=90=AB=E5=90=AF=E5=8A=A8=E5=99=A8=E7=9A=84=E6=95=B4=E5=90=88?= =?UTF-8?q?=E5=8C=85=E6=97=B6=E6=8F=90=E7=A4=BA"=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E8=AF=86=E5=88=AB"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hmcl/ui/download/LocalModpackPage.java | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java index a1ceca0d711..e749a1990c5 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java @@ -44,6 +44,7 @@ import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.FileSystem; +import java.nio.file.FileSystems; import java.nio.file.Path; import static org.jackhuang.hmcl.util.logging.Logger.LOG; @@ -107,11 +108,29 @@ public LocalModpackPage(WizardController controller) { .thenApplyAsync(encoding -> { charset = encoding; Path actualFile = selectedFile; - var wrapper = ModpackHelper.unwrapIfLauncherWrapper(selectedFile, encoding); - if (wrapper != null) { - actualFile = wrapper.getKey(); - controller.getSettings().put(MODPACK_FILE, wrapper.getKey()); - controller.getSettings().put(MODPACK_WRAPPER_FS, wrapper.getValue()); + if (selectedFile.getFileSystem() == FileSystems.getDefault()) { + var wrapper = ModpackHelper.unwrapIfLauncherWrapper(selectedFile, encoding); + if (wrapper != null) { + actualFile = wrapper.getKey(); + controller.getSettings().put(MODPACK_FILE, wrapper.getKey()); + FileSystem oldFs = controller.getSettings().put(MODPACK_WRAPPER_FS, wrapper.getValue()); + if (oldFs != null) { + try { + oldFs.close(); + } catch (IOException ignored) { + // Ignore close errors for wrapper filesystem + } + } + } else { + FileSystem oldFs = controller.getSettings().remove(MODPACK_WRAPPER_FS); + if (oldFs != null) { + try { + oldFs.close(); + } catch (IOException ignored) { + // Ignore close errors for wrapper filesystem + } + } + } } manifest = ModpackHelper.readModpackManifest(actualFile, encoding); return manifest; From 38937c632dc50cac6343c34408cb67723a43badd Mon Sep 17 00:00:00 2001 From: YELANDAOKONG <116365019+YELANDAOKONG@users.noreply.github.com> Date: Fri, 26 Jun 2026 13:04:08 +0800 Subject: [PATCH 7/9] =?UTF-8?q?fix(#6153):=20=E5=AF=BC=E5=85=A5=E5=8C=85?= =?UTF-8?q?=E5=90=AB=E5=90=AF=E5=8A=A8=E5=99=A8=E7=9A=84=E6=95=B4=E5=90=88?= =?UTF-8?q?=E5=8C=85=E6=97=B6=E6=8F=90=E7=A4=BA"=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E8=AF=86=E5=88=AB"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/download/ModpackInstallWizardProvider.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java index 55a1209433b..3c61c45132a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java @@ -147,7 +147,19 @@ public Object finish(SettingsMap settings) { }); FileSystem wrapperFs = settings.get(LocalModpackPage.MODPACK_WRAPPER_FS); - Task task = finishModpackInstallingAsync(settings); + Task task; + try { + task = finishModpackInstallingAsync(settings); + } catch (Throwable t) { + if (wrapperFs != null) { + try { + wrapperFs.close(); + } catch (IOException ignored) { + // Ignore close errors for wrapper filesystem + } + } + throw t; + } if (wrapperFs != null) { if (task != null) { settings.remove(LocalModpackPage.MODPACK_WRAPPER_FS); From 9b0748ffbba89df3e5d6d957474f4f621e747da3 Mon Sep 17 00:00:00 2001 From: YELANDAOKONG <116365019+YELANDAOKONG@users.noreply.github.com> Date: Fri, 3 Jul 2026 17:01:34 +0800 Subject: [PATCH 8/9] =?UTF-8?q?fix(#6153):=20=E5=AF=BC=E5=85=A5=E5=8C=85?= =?UTF-8?q?=E5=90=AB=E5=90=AF=E5=8A=A8=E5=99=A8=E7=9A=84=E6=95=B4=E5=90=88?= =?UTF-8?q?=E5=8C=85=E6=97=B6=E6=8F=90=E7=A4=BA"=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E8=AF=86=E5=88=AB"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jackhuang/hmcl/game/ModpackHelper.java | 37 ++++++------ .../hmcl/ui/download/LocalModpackPage.java | 56 ++++++++++--------- .../ModpackInstallWizardProvider.java | 42 ++++---------- 3 files changed, 62 insertions(+), 73 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java index 9098d660f45..809513881d9 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java @@ -39,7 +39,7 @@ import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.Lang; -import org.jackhuang.hmcl.util.Pair; + import org.jackhuang.hmcl.util.PortablePath; import org.jackhuang.hmcl.util.function.ExceptionalConsumer; import org.jackhuang.hmcl.util.function.ExceptionalRunnable; @@ -47,9 +47,11 @@ import org.jackhuang.hmcl.util.i18n.LocalizedText; import org.jackhuang.hmcl.util.io.CompressingUtils; import org.jackhuang.hmcl.util.io.FileUtils; +import org.jackhuang.hmcl.util.io.IOUtils; import org.jetbrains.annotations.NotNullByDefault; import org.jetbrains.annotations.Nullable; +import java.io.Closeable; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.charset.Charset; @@ -125,33 +127,36 @@ public static Modpack readModpackManifest(Path file, Charset charset) throws Uns throw new UnsupportedModpackException(file.toString()); } - /// Detects whether [file] is an HMCL launcher wrapper ZIP that embeds - /// the actual modpack as [modpack.zip] or [modpack.mrpack]. - /// Returns a [Pair] of the inner entry [Path] and the wrapper - /// [FileSystem], or [null] if this is not a wrapper. - /// The caller must close the wrapper filesystem when done. + /// 存储解析启动器包装 ZIP 后的结果 + /// @param innerPath 包装文件系统内的整合包条目路径 + /// @param wrapperFs 包装文件系统;当不再需要 [innerPath] 时必须被关闭 + public record LauncherWrapper(Path innerPath, FileSystem wrapperFs) implements Closeable { + /// 关闭包装的 [FileSystem]。 + @Override + public void close() throws IOException { + wrapperFs.close(); + } + } + + /// 检测 [file] 是否为 HMCL 启动器包装 ZIP(其内部嵌入了实际的整合包 `modpack.zip` 或 `modpack.mrpack`) + /// 返回一个包含内部条目路径和包装文件系统的 [LauncherWrapper], + /// 如果 [file] 不是包装 ZIP,则返回 `null` @Nullable - public static Pair unwrapIfLauncherWrapper(Path file, Charset charset) { + public static LauncherWrapper unwrapIfLauncherWrapper(Path file, Charset charset) { FileSystem outerFs = null; try { outerFs = CompressingUtils.createReadOnlyZipFileSystem(file, charset); for (String innerName : new String[]{"modpack.zip", "modpack.mrpack"}) { Path entryPath = outerFs.getPath("/" + innerName); if (Files.isRegularFile(entryPath)) { - FileSystem fs = outerFs; + LauncherWrapper result = new LauncherWrapper(entryPath, outerFs); outerFs = null; - return pair(entryPath, fs); + return result; } } } catch (IOException ignored) { } finally { - if (outerFs != null) { - try { - outerFs.close(); - } catch (IOException ignored) { - // Ignore close errors for wrapper filesystem - } - } + IOUtils.closeQuietly(outerFs); } return null; } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java index e749a1990c5..724e28c52f5 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java @@ -40,12 +40,15 @@ import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.io.CompressingUtils; import org.jackhuang.hmcl.util.io.FileUtils; +import org.jackhuang.hmcl.util.io.IOUtils; +import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReference; import static org.jackhuang.hmcl.util.logging.Logger.LOG; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; @@ -56,6 +59,11 @@ public final class LocalModpackPage extends ModpackPage { private Modpack manifest = null; private Charset charset; + private final AtomicReference wrapperFsRef = new AtomicReference<>(); + @Nullable + private volatile Path resolvedModpackFile; + private volatile boolean cleanedUp; + public LocalModpackPage(WizardController controller) { super(controller); @@ -111,31 +119,28 @@ public LocalModpackPage(WizardController controller) { if (selectedFile.getFileSystem() == FileSystems.getDefault()) { var wrapper = ModpackHelper.unwrapIfLauncherWrapper(selectedFile, encoding); if (wrapper != null) { - actualFile = wrapper.getKey(); - controller.getSettings().put(MODPACK_FILE, wrapper.getKey()); - FileSystem oldFs = controller.getSettings().put(MODPACK_WRAPPER_FS, wrapper.getValue()); - if (oldFs != null) { - try { - oldFs.close(); - } catch (IOException ignored) { - // Ignore close errors for wrapper filesystem - } - } - } else { - FileSystem oldFs = controller.getSettings().remove(MODPACK_WRAPPER_FS); - if (oldFs != null) { - try { - oldFs.close(); - } catch (IOException ignored) { - // Ignore close errors for wrapper filesystem - } - } + actualFile = wrapper.innerPath(); + resolvedModpackFile = actualFile; + wrapperFsRef.set(wrapper.wrapperFs()); } } manifest = ModpackHelper.readModpackManifest(actualFile, encoding); return manifest; }) .whenComplete(Schedulers.javafx(), (manifest, exception) -> { + FileSystem fs = wrapperFsRef.getAndSet(null); + if (fs != null) { + if (exception != null || cleanedUp) { + IOUtils.closeQuietly(fs); + } else { + Path innerPath = resolvedModpackFile; + if (innerPath != null) { + controller.getSettings().put(MODPACK_FILE, innerPath); + } + controller.getSettings().put(MODPACK_WRAPPER_FS, fs); + } + } + if (exception instanceof ManuallyCreatedModpackException) { hideSpinner(); nameProperty.set(FileUtils.getName(selectedFile)); @@ -174,15 +179,12 @@ public LocalModpackPage(WizardController controller) { @Override public void cleanup(SettingsMap settings) { + cleanedUp = true; settings.remove(MODPACK_FILE); - FileSystem wrapperFs = settings.remove(MODPACK_WRAPPER_FS); - if (wrapperFs != null) { - try { - wrapperFs.close(); - } catch (IOException ignored) { - // Ignore close errors for wrapper filesystem - } - } + // 同时从 AtomicReference(后台任务可能尚未转移) + // 和 settings(可能已被 whenComplete 转移)中关闭 FS。 + IOUtils.closeQuietly(wrapperFsRef.getAndSet(null)); + IOUtils.closeQuietly(settings.remove(MODPACK_WRAPPER_FS)); } protected void onInstall() { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java index 3c61c45132a..1b5b344ea69 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/ModpackInstallWizardProvider.java @@ -35,6 +35,8 @@ import org.jackhuang.hmcl.ui.wizard.WizardProvider; import org.jackhuang.hmcl.util.SettingsMap; import org.jackhuang.hmcl.util.StringUtils; +import org.jackhuang.hmcl.util.io.IOUtils; +import org.jetbrains.annotations.Nullable; import java.io.FileNotFoundException; import java.io.IOException; @@ -146,39 +148,19 @@ public Object finish(SettingsMap settings) { } }); - FileSystem wrapperFs = settings.get(LocalModpackPage.MODPACK_WRAPPER_FS); - Task task; + @Nullable FileSystem wrapperFs = settings.remove(LocalModpackPage.MODPACK_WRAPPER_FS); try { - task = finishModpackInstallingAsync(settings); - } catch (Throwable t) { - if (wrapperFs != null) { - try { - wrapperFs.close(); - } catch (IOException ignored) { - // Ignore close errors for wrapper filesystem - } - } - throw t; - } - if (wrapperFs != null) { - if (task != null) { - settings.remove(LocalModpackPage.MODPACK_WRAPPER_FS); - task = task.whenComplete(Schedulers.defaultScheduler(), ignored -> { - try { - wrapperFs.close(); - } catch (IOException e) { - // Ignore close errors for wrapper filesystem - } - }); - } else { - try { - wrapperFs.close(); - } catch (IOException ignored) { - // Ignore close errors for wrapper filesystem - } + Task task = finishModpackInstallingAsync(settings); + if (task != null && wrapperFs != null) { + FileSystem fs = wrapperFs; + wrapperFs = null; + task = task.whenComplete(Schedulers.defaultScheduler(), + ignored -> IOUtils.closeQuietly(fs)); } + return task; + } finally { + IOUtils.closeQuietly(wrapperFs); } - return task; } private static Node createModpackInstallPage(WizardController controller) { From ac753c94225be1eb99a6eb7a1359f1b1bfa804f4 Mon Sep 17 00:00:00 2001 From: YELANDAOKONG <116365019+YELANDAOKONG@users.noreply.github.com> Date: Fri, 3 Jul 2026 17:22:25 +0800 Subject: [PATCH 9/9] =?UTF-8?q?fix(#6153):=20=E5=AF=BC=E5=85=A5=E5=8C=85?= =?UTF-8?q?=E5=90=AB=E5=90=AF=E5=8A=A8=E5=99=A8=E7=9A=84=E6=95=B4=E5=90=88?= =?UTF-8?q?=E5=8C=85=E6=97=B6=E6=8F=90=E7=A4=BA"=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E8=AF=86=E5=88=AB"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java | 1 - 1 file changed, 1 deletion(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java index 724e28c52f5..ee78f3a81e6 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/LocalModpackPage.java @@ -43,7 +43,6 @@ import org.jackhuang.hmcl.util.io.IOUtils; import org.jetbrains.annotations.Nullable; -import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.FileSystem; import java.nio.file.FileSystems;