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 01/10] =?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 02/10] =?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 03/10] 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 04/10] =?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 05/10] =?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 06/10] =?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 07/10] =?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 08/10] =?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 09/10] =?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; From 3029455aae43256bceca32fdf1900c79ad177254 Mon Sep 17 00:00:00 2001 From: YELANDAOKONG <116365019+yelandaokong@users.noreply.github.com> Date: Thu, 23 Jul 2026 09:13:38 +0800 Subject: [PATCH 10/10] =?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 | 40 +++++++++-- .../hmcl/ui/download/LocalModpackPage.java | 67 ++++++++++--------- .../ModpackInstallWizardProvider.java | 18 ++--- .../hmcl/ui/wizard/WizardController.java | 29 +++++--- 4 files changed, 99 insertions(+), 55 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 809513881d9..c6718d11b42 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java @@ -63,6 +63,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Stream; import static org.jackhuang.hmcl.util.Lang.mapOf; @@ -127,14 +128,39 @@ public static Modpack readModpackManifest(Path file, Charset charset) throws Uns throw new UnsupportedModpackException(file.toString()); } - /// 存储解析启动器包装 ZIP 后的结果 - /// @param innerPath 包装文件系统内的整合包条目路径 - /// @param wrapperFs 包装文件系统;当不再需要 [innerPath] 时必须被关闭 - public record LauncherWrapper(Path innerPath, FileSystem wrapperFs) implements Closeable { - /// 关闭包装的 [FileSystem]。 - @Override + /// Owns the launcher wrapper file system and the embedded modpack path within it. + /// + /// The owner is safe to close repeatedly and from competing terminal paths. + public static final class LauncherWrapper implements Closeable { + /// Embedded modpack path backed by the wrapper file system. + private final Path innerPath; + + /// Wrapper file system, atomically cleared by the first close operation. + private final AtomicReference<@Nullable FileSystem> wrapperFsRef; + + /// Creates an owner for an embedded modpack path and its backing file system. + /// + /// @param innerPath embedded modpack path + /// @param wrapperFs backing wrapper file system + private LauncherWrapper(Path innerPath, FileSystem wrapperFs) { + this.innerPath = innerPath; + this.wrapperFsRef = new AtomicReference<>(wrapperFs); + } + + /// Returns the embedded modpack path. + /// + /// The path is valid only until this owner is closed. + public Path innerPath() { + return innerPath; + } + + /// Closes the backing wrapper file system if it is still open. + public void close() throws IOException { - wrapperFs.close(); + FileSystem wrapperFs = wrapperFsRef.getAndSet(null); + if (wrapperFs != null) { + wrapperFs.close(); + } } } 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 ee78f3a81e6..47c76fe5082 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 @@ -24,6 +24,7 @@ import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.game.ManuallyCreatedModpackException; import org.jackhuang.hmcl.game.ModpackHelper; +import org.jackhuang.hmcl.game.ModpackHelper.LauncherWrapper; import org.jackhuang.hmcl.modpack.Modpack; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Profiles; @@ -44,9 +45,9 @@ import org.jetbrains.annotations.Nullable; import java.nio.charset.Charset; -import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Path; +import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; import static org.jackhuang.hmcl.util.logging.Logger.LOG; @@ -58,9 +59,8 @@ 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 final AtomicReference wrapperRef = new AtomicReference<>(); + private volatile boolean cleanedUp; public LocalModpackPage(WizardController controller) { @@ -101,11 +101,12 @@ public LocalModpackPage(WizardController controller) { FileChooser chooser = new FileChooser(); chooser.setTitle(i18n("modpack.choose")); chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(i18n("modpack"), "*.zip")); - selectedFile = FileUtils.toPath(chooser.showOpenDialog(Controllers.getStage())); - if (selectedFile == null) { + Path chosenFile = FileUtils.toPath(chooser.showOpenDialog(Controllers.getStage())); + if (chosenFile == null) { controller.onEnd(); return; } + selectedFile = chosenFile; controller.getSettings().put(MODPACK_FILE, selectedFile); } @@ -116,30 +117,30 @@ public LocalModpackPage(WizardController controller) { charset = encoding; Path actualFile = selectedFile; if (selectedFile.getFileSystem() == FileSystems.getDefault()) { - var wrapper = ModpackHelper.unwrapIfLauncherWrapper(selectedFile, encoding); + LauncherWrapper wrapper = ModpackHelper.unwrapIfLauncherWrapper(selectedFile, encoding); if (wrapper != null) { actualFile = wrapper.innerPath(); - resolvedModpackFile = actualFile; - wrapperFsRef.set(wrapper.wrapperFs()); + wrapperRef.set(wrapper); } } manifest = ModpackHelper.readModpackManifest(actualFile, encoding); return manifest; }) .whenComplete(Schedulers.javafx(), (manifest, exception) -> { - FileSystem fs = wrapperFsRef.getAndSet(null); - if (fs != null) { + LauncherWrapper wrapper = wrapperRef.getAndSet(null); + if (wrapper != null) { if (exception != null || cleanedUp) { - IOUtils.closeQuietly(fs); + IOUtils.closeQuietly(wrapper); } else { - Path innerPath = resolvedModpackFile; - if (innerPath != null) { - controller.getSettings().put(MODPACK_FILE, innerPath); - } - controller.getSettings().put(MODPACK_WRAPPER_FS, fs); + controller.getSettings().put(MODPACK_FILE, wrapper.innerPath()); + controller.getSettings().put(MODPACK_WRAPPER, wrapper); } } + if (cleanedUp) { + return; + } + if (exception instanceof ManuallyCreatedModpackException) { hideSpinner(); nameProperty.set(FileUtils.getName(selectedFile)); @@ -161,17 +162,18 @@ public LocalModpackPage(WizardController controller) { Platform.runLater(controller::onEnd); } else { hideSpinner(); - controller.getSettings().put(MODPACK_MANIFEST, manifest); - nameProperty.set(manifest.getName()); - versionProperty.set(manifest.getVersion()); - authorProperty.set(manifest.getAuthor()); + Modpack parsedManifest = Objects.requireNonNull(manifest); + controller.getSettings().put(MODPACK_MANIFEST, parsedManifest); + nameProperty.set(parsedManifest.getName()); + versionProperty.set(parsedManifest.getVersion()); + authorProperty.set(parsedManifest.getAuthor()); if (name == null) { // trim: https://github.com/HMCL-dev/HMCL/issues/962 - txtModpackName.setText(manifest.getName().trim()); + txtModpackName.setText(parsedManifest.getName().trim()); } - btnDescription.setVisible(StringUtils.isNotBlank(manifest.getDescription())); + btnDescription.setVisible(StringUtils.isNotBlank(parsedManifest.getDescription())); } }).start(); } @@ -180,13 +182,16 @@ public LocalModpackPage(WizardController controller) { public void cleanup(SettingsMap settings) { cleanedUp = true; settings.remove(MODPACK_FILE); - // 同时从 AtomicReference(后台任务可能尚未转移) - // 和 settings(可能已被 whenComplete 转移)中关闭 FS。 - IOUtils.closeQuietly(wrapperFsRef.getAndSet(null)); - IOUtils.closeQuietly(settings.remove(MODPACK_WRAPPER_FS)); + IOUtils.closeQuietly(wrapperRef.getAndSet(null)); + IOUtils.closeQuietly(settings.remove(MODPACK_WRAPPER)); } protected void onInstall() { + Charset detectedCharset = charset; + if (detectedCharset == null) { + return; + } + String name = txtModpackName.getText(); // Check for non-ASCII characters. @@ -197,7 +202,7 @@ protected void onInstall() { MessageDialogPane.MessageType.QUESTION) .yesOrNo(() -> { controller.getSettings().put(MODPACK_NAME, name); - controller.getSettings().put(MODPACK_CHARSET, charset); + controller.getSettings().put(MODPACK_CHARSET, detectedCharset); controller.onFinish(); }, () -> { // The user selects Cancel and does nothing. @@ -205,7 +210,7 @@ protected void onInstall() { .build()); } else { controller.getSettings().put(MODPACK_NAME, name); - controller.getSettings().put(MODPACK_CHARSET, charset); + controller.getSettings().put(MODPACK_CHARSET, detectedCharset); controller.onFinish(); } } @@ -216,7 +221,9 @@ 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_WRAPPER = + new SettingsMap.Key<>("MODPACK_WRAPPER"); + 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 1b5b344ea69..c997da35624 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 @@ -36,12 +36,10 @@ 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; import java.nio.charset.Charset; -import java.nio.file.FileSystem; import java.nio.file.Path; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; @@ -97,6 +95,9 @@ private Task finishModpackInstallingAsync(SettingsMap settings) { boolean isManuallyCreated = settings.getOrDefault(LocalModpackPage.MODPACK_MANUALLY_CREATED, false); if (isManuallyCreated) { + if (selected == null || name == null || charset == null) { + return null; + } return ModpackHelper.getInstallManuallyCreatedModpackTask(profile, selected, name, charset); } @@ -148,18 +149,17 @@ public Object finish(SettingsMap settings) { } }); - @Nullable FileSystem wrapperFs = settings.remove(LocalModpackPage.MODPACK_WRAPPER_FS); + ModpackHelper.LauncherWrapper wrapper = settings.get(LocalModpackPage.MODPACK_WRAPPER); try { Task task = finishModpackInstallingAsync(settings); - if (task != null && wrapperFs != null) { - FileSystem fs = wrapperFs; - wrapperFs = null; - task = task.whenComplete(Schedulers.defaultScheduler(), - ignored -> IOUtils.closeQuietly(fs)); + if (task != null && wrapper != null) { + ModpackHelper.LauncherWrapper ownedWrapper = wrapper; + wrapper = null; + task = task.whenComplete(Schedulers.defaultScheduler(), ignored -> IOUtils.closeQuietly(ownedWrapper)); } return task; } finally { - IOUtils.closeQuietly(wrapperFs); + IOUtils.closeQuietly(wrapper); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/WizardController.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/WizardController.java index c1a0b0bf28d..5790f3c9391 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/WizardController.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/wizard/WizardController.java @@ -20,6 +20,7 @@ import javafx.scene.Node; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.SettingsMap; +import org.jetbrains.annotations.Nullable; import java.util.*; @@ -55,19 +56,20 @@ public List getPages() { @Override public void onStart() { - Objects.requireNonNull(provider); + WizardProvider activeProvider = Objects.requireNonNull(provider); + stopped = false; settings.clear(); - provider.start(settings); + activeProvider.start(settings); pages.clear(); Node page = navigatingTo(0); - pages.push(page); if (stopped) { // navigatingTo may stop this wizard. return; } + pages.push(page); if (page instanceof WizardPage) ((WizardPage) page).onNavigate(settings); @@ -87,12 +89,11 @@ public void onNext(Node page) { } public void onNext(Node page, NavigationDirection direction) { - pages.push(page); - if (stopped) { // navigatingTo may stop this wizard. return; } + pages.push(page); if (page instanceof WizardPage) ((WizardPage) page).onNavigate(settings); @@ -107,7 +108,7 @@ public void onPrev(boolean cleanUp) { public void onPrev(boolean cleanUp, NavigationDirection direction) { if (!canPrev()) { - if (provider.cancelIfCannotGoBack()) { + if (Objects.requireNonNull(provider).cancelIfCannotGoBack()) { onCancel(); return; } else { @@ -134,7 +135,8 @@ public boolean canPrev() { @Override public void onFinish() { - Object result = provider.finish(settings); + WizardProvider activeProvider = Objects.requireNonNull(provider); + @Nullable Object result = activeProvider.finish(settings); if (result instanceof Summary) displayer.navigateTo(((Summary) result).getComponent(), NavigationDirection.NEXT); else if (result instanceof Task) displayer.handleTask(settings, ((Task) result)); else if (result != null) throw new IllegalStateException("Unrecognized wizard result: " + result); @@ -143,8 +145,17 @@ public void onFinish() { @Override public void onEnd() { stopped = true; + while (!pages.isEmpty()) { + Node page = pages.pop(); + if (page instanceof WizardPage wizardPage) { + try { + wizardPage.cleanup(settings); + } catch (RuntimeException e) { + LOG.warning("Failed to clean up wizard page " + page, e); + } + } + } settings.clear(); - pages.clear(); displayer.onEnd(); } @@ -155,6 +166,6 @@ public void onCancel() { } protected Node navigatingTo(int step) { - return provider.createPage(this, step, settings); + return Objects.requireNonNull(provider).createPage(this, step, settings); } }