From 138b35310b3d32e1cc3ade0d5d115947da53ff63 Mon Sep 17 00:00:00 2001 From: 2012hzy <3681596340p@qq.com> Date: Sun, 14 Jun 2026 11:04:10 +0800 Subject: [PATCH 1/9] =?UTF-8?q?=E9=92=88=E5=AF=B9#issues6086=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E6=95=B4=E5=90=88=E5=8C=85=E6=97=B6=E5=86=85=E5=AD=98?= =?UTF-8?q?=E6=BA=A2=E5=87=BA=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mod/mcbbs/McbbsModpackExportTask.java | 241 +++++++++++++----- .../modrinth/ModrinthModpackExportTask.java | 204 +++++++++------ 2 files changed, 311 insertions(+), 134 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java index 2f8eccd2ae7..ea87d3a59eb 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java @@ -17,6 +17,7 @@ */ package org.jackhuang.hmcl.mod.mcbbs; +import com.google.gson.stream.JsonWriter; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.game.Library; @@ -34,6 +35,8 @@ import java.io.File; import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -72,66 +75,185 @@ public void execute() throws Exception { blackList.add(version + ".jar"); blackList.add(version + ".json"); LOG.info("Compressing game files without some files in blacklist, including files or directories: usernamecache.json, asm, logs, backups, versions, assets, usercache.json, libraries, crash-reports, launcher_profiles.json, NVIDIA, TCNodeTracker"); - try (var zip = new Zipper(modpackFile)) { - Path runDirectory = repository.getRunDirectory(version); - List files = new ArrayList<>(); - zip.putDirectory(runDirectory, "overrides", path -> { - if (Modpack.acceptFile(path, blackList, info.getWhitelist())) { - Path file = runDirectory.resolve(path); - if (Files.isRegularFile(file)) { - String relativePath = runDirectory.relativize(file).normalize().toString().replace(File.separatorChar, '/'); - files.add(new McbbsModpackManifest.AddonFile(true, relativePath, DigestUtils.digestToString("SHA-1", file))); + + Path runDirectory = repository.getRunDirectory(version); + String gameVersion = repository.getGameVersion(version) + .orElseThrow(() -> new IOException("Cannot parse the version of " + version)); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(version), gameVersion); + + Path tempManifest = Files.createTempFile("mcbbs_packmeta_", ".json"); + try { + try (JsonWriter writer = new JsonWriter(new OutputStreamWriter(Files.newOutputStream(tempManifest), StandardCharsets.UTF_8))) { + writer.setIndent(" "); + writer.beginObject(); + + writer.name("manifestType").value(McbbsModpackManifest.MANIFEST_TYPE); + writer.name("manifestVersion").value(2); + writer.name("name").value(info.getName()); + writer.name("version").value(info.getVersion()); + writer.name("author").value(info.getAuthor()); + writer.name("description").value(info.getDescription()); + writer.name("fileApi").value(info.getFileApi() == null ? null : StringUtils.removeSuffix(info.getFileApi(), "/")); + writer.name("url").value(info.getUrl()); + writer.name("forceUpdate").value(info.isForceUpdate()); + + writer.name("origins").beginArray(); + writer.endArray(); + + writer.name("addons").beginArray(); + writer.beginObject(); + writer.name("id").value(MINECRAFT.getPatchId()); + writer.name("version").value(gameVersion); + writer.endObject(); + analyzer.getVersion(FORGE).ifPresent(forgeVersion -> { + try { + writer.beginObject(); + writer.name("id").value(FORGE.getPatchId()); + writer.name("version").value(forgeVersion); + writer.endObject(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + analyzer.getVersion(CLEANROOM).ifPresent(cleanroomVersion -> { + try { + writer.beginObject(); + writer.name("id").value(CLEANROOM.getPatchId()); + writer.name("version").value(cleanroomVersion); + writer.endObject(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + analyzer.getVersion(NEO_FORGE).ifPresent(neoForgeVersion -> { + try { + writer.beginObject(); + writer.name("id").value(NEO_FORGE.getPatchId()); + writer.name("version").value(neoForgeVersion); + writer.endObject(); + } catch (IOException e) { + throw new RuntimeException(e); } - return true; - } else { - return false; + }); + analyzer.getVersion(LITELOADER).ifPresent(liteLoaderVersion -> { + try { + writer.beginObject(); + writer.name("id").value(LITELOADER.getPatchId()); + writer.name("version").value(liteLoaderVersion); + writer.endObject(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + analyzer.getVersion(OPTIFINE).ifPresent(optifineVersion -> { + try { + writer.beginObject(); + writer.name("id").value(OPTIFINE.getPatchId()); + writer.name("version").value(optifineVersion); + writer.endObject(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + analyzer.getVersion(FABRIC).ifPresent(fabricVersion -> { + try { + writer.beginObject(); + writer.name("id").value(FABRIC.getPatchId()); + writer.name("version").value(fabricVersion); + writer.endObject(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + analyzer.getVersion(QUILT).ifPresent(quiltVersion -> { + try { + writer.beginObject(); + writer.name("id").value(QUILT.getPatchId()); + writer.name("version").value(quiltVersion); + writer.endObject(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + analyzer.getVersion(LEGACY_FABRIC).ifPresent(legacyfabricVersion -> { + try { + writer.beginObject(); + writer.name("id").value(LEGACY_FABRIC.getPatchId()); + writer.name("version").value(legacyfabricVersion); + writer.endObject(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + writer.endArray(); + + writer.name("libraries").beginArray(); + writer.endArray(); + + writer.name("files").beginArray(); + Files.walk(runDirectory) + .filter(Files::isRegularFile) + .forEach(file -> { + try { + Path relative = runDirectory.relativize(file); + String relativePath = relative.toString().replace(File.separatorChar, '/'); + if (Modpack.acceptFile(relativePath, blackList, info.getWhitelist())) { + String sha1 = DigestUtils.digestToString("SHA-1", file); + writer.beginObject(); + writer.name("type").value(true); + writer.name("path").value(relativePath); + writer.name("hash").value(sha1); + writer.endObject(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + writer.endArray(); + + writer.name("settings").beginObject(); + writer.endObject(); + + writer.name("launchInfo").beginObject(); + writer.name("minMemory").value(info.getMinMemory()); + + writer.name("supportedJavaVersions").beginArray(); + for (int ver : info.getSupportedJavaVersions()) { + writer.value(ver); } - }); - - String gameVersion = repository.getGameVersion(version) - .orElseThrow(() -> new IOException("Cannot parse the version of " + version)); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(version), gameVersion); - - // Mcbbs manifest - List addons = new ArrayList<>(); - addons.add(new McbbsModpackManifest.Addon(MINECRAFT.getPatchId(), gameVersion)); - analyzer.getVersion(FORGE).ifPresent(forgeVersion -> - addons.add(new McbbsModpackManifest.Addon(FORGE.getPatchId(), forgeVersion))); - analyzer.getVersion(CLEANROOM).ifPresent(cleanroomVersion -> - addons.add(new McbbsModpackManifest.Addon(CLEANROOM.getPatchId(), cleanroomVersion))); - analyzer.getVersion(NEO_FORGE).ifPresent(neoForgeVersion -> - addons.add(new McbbsModpackManifest.Addon(NEO_FORGE.getPatchId(), neoForgeVersion))); - analyzer.getVersion(LITELOADER).ifPresent(liteLoaderVersion -> - addons.add(new McbbsModpackManifest.Addon(LITELOADER.getPatchId(), liteLoaderVersion))); - analyzer.getVersion(OPTIFINE).ifPresent(optifineVersion -> - addons.add(new McbbsModpackManifest.Addon(OPTIFINE.getPatchId(), optifineVersion))); - analyzer.getVersion(FABRIC).ifPresent(fabricVersion -> - addons.add(new McbbsModpackManifest.Addon(FABRIC.getPatchId(), fabricVersion))); - analyzer.getVersion(QUILT).ifPresent(quiltVersion -> - addons.add(new McbbsModpackManifest.Addon(QUILT.getPatchId(), quiltVersion))); - analyzer.getVersion(LEGACY_FABRIC).ifPresent(legacyfabricVersion -> - addons.add(new McbbsModpackManifest.Addon(LEGACY_FABRIC.getPatchId(), legacyfabricVersion))); - - List libraries = new ArrayList<>(); - // TODO libraries - - List origins = new ArrayList<>(); - // TODO origins - - McbbsModpackManifest.Settings settings = new McbbsModpackManifest.Settings(); - McbbsModpackManifest.LaunchInfo launchInfo = new McbbsModpackManifest.LaunchInfo(info.getMinMemory(), info.getSupportedJavaVersions(), StringUtils.tokenize(info.getLaunchArguments()), StringUtils.tokenize(info.getJavaArguments())); - - McbbsModpackManifest mcbbsManifest = new McbbsModpackManifest(McbbsModpackManifest.MANIFEST_TYPE, 2, info.getName(), info.getVersion(), info.getAuthor(), info.getDescription(), info.getFileApi() == null ? null : StringUtils.removeSuffix(info.getFileApi(), "/"), info.getUrl(), info.isForceUpdate(), origins, addons, libraries, files, settings, launchInfo); - zip.putTextFile(JsonUtils.GSON.toJson(mcbbsManifest), "mcbbs.packmeta"); - - // CurseForge manifest - List modLoaders = new ArrayList<>(); - analyzer.getVersion(FORGE).ifPresent(forgeVersion -> modLoaders.add(new CurseManifestModLoader("forge-" + forgeVersion, true))); - analyzer.getVersion(NEO_FORGE).ifPresent(forgeVersion -> modLoaders.add(new CurseManifestModLoader("neoforge-" + forgeVersion, true))); - analyzer.getVersion(FABRIC).ifPresent(fabricVersion -> modLoaders.add(new CurseManifestModLoader("fabric-" + fabricVersion, true))); - // OptiFine and LiteLoader are not supported by CurseForge modpack. - CurseManifest curseManifest = new CurseManifest(CurseManifest.MINECRAFT_MODPACK, 1, info.getName(), info.getVersion(), info.getAuthor(), "overrides", new CurseManifestMinecraft(gameVersion, modLoaders), Collections.emptyList()); - zip.putTextFile(JsonUtils.GSON.toJson(curseManifest), "manifest.json"); + writer.endArray(); + + writer.name("launchArguments").beginArray(); + for (String arg : StringUtils.tokenize(info.getLaunchArguments())) { + writer.value(arg); + } + writer.endArray(); + writer.name("javaArguments").beginArray(); + for (String arg : StringUtils.tokenize(info.getJavaArguments())) { + writer.value(arg); + } + writer.endArray(); + writer.endObject(); + + writer.endObject(); + } + + try (var zip = new Zipper(modpackFile)) { + zip.putFile(tempManifest, "mcbbs.packmeta"); + + List modLoaders = new ArrayList<>(); + analyzer.getVersion(FORGE).ifPresent(forgeVersion -> modLoaders.add(new CurseManifestModLoader("forge-" + forgeVersion, true))); + analyzer.getVersion(NEO_FORGE).ifPresent(neoForgeVersion -> modLoaders.add(new CurseManifestModLoader("neoforge-" + neoForgeVersion, true))); + analyzer.getVersion(FABRIC).ifPresent(fabricVersion -> modLoaders.add(new CurseManifestModLoader("fabric-" + fabricVersion, true))); + CurseManifest curseManifest = new CurseManifest(CurseManifest.MINECRAFT_MODPACK, 1, info.getName(), info.getVersion(), info.getAuthor(), "overrides", new CurseManifestMinecraft(gameVersion, modLoaders), Collections.emptyList()); + zip.putTextFile(JsonUtils.GSON.toJson(curseManifest), "manifest.json"); + + zip.putDirectory(runDirectory, "overrides", path -> { + return Modpack.acceptFile(path, blackList, info.getWhitelist()); + }); + } + } finally { + Files.deleteIfExists(tempManifest); } } @@ -145,5 +267,4 @@ public void execute() throws Exception { .requireLaunchArguments() .requireOrigins() .requireAuthor(); - -} +} \ No newline at end of file diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java index b4f7d9fc29b..b2ce651fc1a 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java @@ -17,25 +17,25 @@ */ package org.jackhuang.hmcl.mod.modrinth; -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.*; - +import com.google.gson.stream.JsonWriter; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.game.DefaultGameRepository; import org.jackhuang.hmcl.mod.ModAdviser; import org.jackhuang.hmcl.mod.Modpack; import org.jackhuang.hmcl.mod.ModpackExportInfo; +import org.jackhuang.hmcl.mod.RemoteMod; +import org.jackhuang.hmcl.mod.curse.CurseForgeRemoteModRepository; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.DigestUtils; -import org.jackhuang.hmcl.util.gson.JsonUtils; import org.jackhuang.hmcl.util.io.Zipper; -import org.jackhuang.hmcl.mod.LocalModFile; -import org.jackhuang.hmcl.mod.RemoteMod; -import org.jackhuang.hmcl.mod.curse.CurseForgeRemoteModRepository; +import java.io.File; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*; import static org.jackhuang.hmcl.util.logging.Logger.LOG; @@ -73,7 +73,6 @@ private ModrinthManifest.File tryGetRemoteFile(Path file, String relativePath) t relativePath = repository.getModManager(version).enableMod(Paths.get(relativePath)).toString(); } - LocalModFile localModFile = null; Optional modrinthVersion = Optional.empty(); Optional curseForgeVersion = Optional.empty(); @@ -130,76 +129,133 @@ public void execute() throws Exception { blackList.add(version + ".jar"); blackList.add(version + ".json"); LOG.info("Compressing game files without some files in blacklist, including files or directories: usernamecache.json, asm, logs, backups, versions, assets, usercache.json, libraries, crash-reports, launcher_profiles.json, NVIDIA, TCNodeTracker"); - try (var zip = new Zipper(modpackFile)) { - Path runDirectory = repository.getRunDirectory(version); - List files = new ArrayList<>(); - Set filesInManifest = new HashSet<>(); - - String[] resourceDirs = {"resourcepacks", "shaderpacks", "mods"}; - for (String dir : resourceDirs) { - Path dirPath = runDirectory.resolve(dir); - if (Files.exists(dirPath)) { - Files.walk(dirPath) - .filter(Files::isRegularFile) - .forEach(file -> { - try { - String relativePath = runDirectory.relativize(file).normalize().toString().replace(File.separatorChar, '/'); - - if (!info.getWhitelist().contains(relativePath)) { - return; - } - ModrinthManifest.File fileEntry = tryGetRemoteFile(file, relativePath); - if (fileEntry != null) { - files.add(fileEntry); - filesInManifest.add(relativePath); + Path runDirectory = repository.getRunDirectory(version); + String gameVersion = repository.getGameVersion(version) + .orElseThrow(() -> new IOException("Cannot parse the version of " + version)); + LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(version), gameVersion); + + String[] resourceDirs = {"resourcepacks", "shaderpacks", "mods"}; + Set remoteFilePaths = new HashSet<>(); + + Path tempIndex = Files.createTempFile("modrinth_index_", ".json"); + try { + try (JsonWriter writer = new JsonWriter(new OutputStreamWriter(Files.newOutputStream(tempIndex), StandardCharsets.UTF_8))) { + writer.setIndent(" "); + writer.beginObject(); + + writer.name("formatVersion").value(1); + writer.name("game").value("minecraft"); + writer.name("versionId").value(info.getVersion()); + writer.name("name").value(info.getName()); + writer.name("summary").value(info.getDescription()); + + writer.name("files").beginArray(); + + Set processedPaths = new HashSet<>(); + + for (String dir : resourceDirs) { + Path dirPath = runDirectory.resolve(dir); + if (Files.exists(dirPath)) { + Files.walk(dirPath) + .filter(Files::isRegularFile) + .forEach(file -> { + try { + String relativePath = runDirectory.relativize(file).normalize().toString().replace(File.separatorChar, '/'); + if (!info.getWhitelist().contains(relativePath)) { + return; + } + if (processedPaths.contains(relativePath)) { + return; + } + processedPaths.add(relativePath); + + ModrinthManifest.File fileEntry = tryGetRemoteFile(file, relativePath); + if (fileEntry != null) { + remoteFilePaths.add(relativePath); + writer.beginObject(); + writer.name("path").value(fileEntry.getPath()); + writer.name("hashes").beginObject(); + for (Map.Entry hash : fileEntry.getHashes().entrySet()) { + writer.name(hash.getKey()).value(hash.getValue()); + } + writer.endObject(); + if (fileEntry.getEnv() != null) { + writer.name("env").beginObject(); + for (Map.Entry env : fileEntry.getEnv().entrySet()) { + writer.name(env.getKey()).value(env.getValue()); + } + writer.endObject(); + } + writer.name("downloads").beginArray(); + for (String url : fileEntry.getDownloads()) { + writer.value(url); + } + writer.endArray(); + writer.name("fileSize").value(fileEntry.getFileSize()); + writer.endObject(); + } + } catch (IOException e) { + throw new RuntimeException(e); } - } catch (IOException e) { - LOG.warning("Failed to process file: " + file, e); - } - }); + }); + } } + + writer.endArray(); + + writer.name("dependencies").beginObject(); + writer.name("minecraft").value(gameVersion); + analyzer.getVersion(FORGE).ifPresent(forgeVersion -> { + try { + writer.name("forge").value(forgeVersion); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + analyzer.getVersion(NEO_FORGE).ifPresent(neoForgeVersion -> { + try { + writer.name("neoforge").value(neoForgeVersion); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + analyzer.getVersion(FABRIC).ifPresent(fabricVersion -> { + try { + writer.name("fabric-loader").value(fabricVersion); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + analyzer.getVersion(QUILT).ifPresent(quiltVersion -> { + try { + writer.name("quilt-loader").value(quiltVersion); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + writer.endObject(); + + writer.endObject(); } - zip.putDirectory(runDirectory, "client-overrides", path -> { - String relativePath = path.replace(File.separatorChar, '/'); - if (filesInManifest.contains(relativePath)) { - return false; - } - return Modpack.acceptFile(path, blackList, info.getWhitelist()); - }); - - String gameVersion = repository.getGameVersion(version) - .orElseThrow(() -> new IOException("Cannot parse the version of " + version)); - LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(version), gameVersion); - - Map dependencies = new HashMap<>(); - dependencies.put("minecraft", gameVersion); - - analyzer.getVersion(FORGE).ifPresent(forgeVersion -> - dependencies.put("forge", forgeVersion)); - analyzer.getVersion(NEO_FORGE).ifPresent(neoForgeVersion -> - dependencies.put("neoforge", neoForgeVersion)); - analyzer.getVersion(FABRIC).ifPresent(fabricVersion -> - dependencies.put("fabric-loader", fabricVersion)); - analyzer.getVersion(QUILT).ifPresent(quiltVersion -> - dependencies.put("quilt-loader", quiltVersion)); - - ModrinthManifest manifest = new ModrinthManifest( - "minecraft", - 1, - info.getVersion(), - info.getName(), - info.getDescription(), - files, - dependencies - ); - - zip.putTextFile(JsonUtils.GSON.toJson(manifest), "modrinth.index.json"); + try (var zip = new Zipper(modpackFile)) { + zip.putFile(tempIndex, "modrinth.index.json"); + + zip.putDirectory(runDirectory, "client-overrides", path -> { + String relativePath = path.replace(File.separatorChar, '/'); + if (remoteFilePaths.contains(relativePath)) { + return false; + } + return Modpack.acceptFile(path, blackList, info.getWhitelist()); + }); + } + } finally { + Files.deleteIfExists(tempIndex); } } public static final ModpackExportInfo.Options OPTION = new ModpackExportInfo.Options() .requireNoCreateRemoteFiles() .requireSkipCurseForgeRemoteFiles(); -} +} \ No newline at end of file From f6a1a1ab8d8628381d6fcbb130a9122316df0b74 Mon Sep 17 00:00:00 2001 From: 2012hzy <3681596340p@qq.com> Date: Sun, 14 Jun 2026 11:26:27 +0800 Subject: [PATCH 2/9] =?UTF-8?q?=E9=92=88=E5=AF=B9#issues6086=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E6=95=B4=E5=90=88=E5=8C=85=E6=97=B6=E5=86=85=E5=AD=98?= =?UTF-8?q?=E6=BA=A2=E5=87=BA=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mod/mcbbs/McbbsModpackExportTask.java | 44 +++++------ .../modrinth/ModrinthModpackExportTask.java | 76 ++++++++++--------- 2 files changed, 61 insertions(+), 59 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java index ea87d3a59eb..1cd35f7b2aa 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java @@ -20,7 +20,6 @@ import com.google.gson.stream.JsonWriter; import org.jackhuang.hmcl.download.LibraryAnalyzer; import org.jackhuang.hmcl.game.DefaultGameRepository; -import org.jackhuang.hmcl.game.Library; import org.jackhuang.hmcl.mod.ModAdviser; import org.jackhuang.hmcl.mod.Modpack; import org.jackhuang.hmcl.mod.ModpackExportInfo; @@ -191,24 +190,25 @@ public void execute() throws Exception { writer.endArray(); writer.name("files").beginArray(); - Files.walk(runDirectory) - .filter(Files::isRegularFile) - .forEach(file -> { - try { - Path relative = runDirectory.relativize(file); - String relativePath = relative.toString().replace(File.separatorChar, '/'); - if (Modpack.acceptFile(relativePath, blackList, info.getWhitelist())) { - String sha1 = DigestUtils.digestToString("SHA-1", file); - writer.beginObject(); - writer.name("type").value(true); - writer.name("path").value(relativePath); - writer.name("hash").value(sha1); - writer.endObject(); + try (var stream = Files.walk(runDirectory)) { + stream.filter(Files::isRegularFile) + .forEach(file -> { + try { + Path relative = runDirectory.relativize(file); + String relativePath = relative.toString().replace(File.separatorChar, '/'); + if (Modpack.acceptFile(relativePath, blackList, info.getWhitelist())) { + String sha1 = DigestUtils.digestToString("SHA-1", file); + writer.beginObject(); + writer.name("type").value(true); + writer.name("path").value(relativePath); + writer.name("hash").value(sha1); + writer.endObject(); + } + } catch (IOException e) { + throw new RuntimeException(e); } - } catch (IOException e) { - throw new RuntimeException(e); - } - }); + }); + } writer.endArray(); writer.name("settings").beginObject(); @@ -216,13 +216,13 @@ public void execute() throws Exception { writer.name("launchInfo").beginObject(); writer.name("minMemory").value(info.getMinMemory()); - writer.name("supportedJavaVersions").beginArray(); - for (int ver : info.getSupportedJavaVersions()) { - writer.value(ver); + if (info.getSupportedJavaVersions() != null) { + for (int ver : info.getSupportedJavaVersions()) { + writer.value(ver); + } } writer.endArray(); - writer.name("launchArguments").beginArray(); for (String arg : StringUtils.tokenize(info.getLaunchArguments())) { writer.value(arg); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java index b2ce651fc1a..fb5bca8cfe0 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java @@ -28,6 +28,7 @@ import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.DigestUtils; import org.jackhuang.hmcl.util.io.Zipper; + import java.io.File; import java.io.IOException; import java.io.OutputStreamWriter; @@ -157,48 +158,49 @@ public void execute() throws Exception { for (String dir : resourceDirs) { Path dirPath = runDirectory.resolve(dir); if (Files.exists(dirPath)) { - Files.walk(dirPath) - .filter(Files::isRegularFile) - .forEach(file -> { - try { - String relativePath = runDirectory.relativize(file).normalize().toString().replace(File.separatorChar, '/'); - if (!info.getWhitelist().contains(relativePath)) { - return; - } - if (processedPaths.contains(relativePath)) { - return; - } - processedPaths.add(relativePath); - - ModrinthManifest.File fileEntry = tryGetRemoteFile(file, relativePath); - if (fileEntry != null) { - remoteFilePaths.add(relativePath); - writer.beginObject(); - writer.name("path").value(fileEntry.getPath()); - writer.name("hashes").beginObject(); - for (Map.Entry hash : fileEntry.getHashes().entrySet()) { - writer.name(hash.getKey()).value(hash.getValue()); + try (var stream = Files.walk(dirPath)) { + stream.filter(Files::isRegularFile) + .forEach(file -> { + try { + String relativePath = runDirectory.relativize(file).normalize().toString().replace(File.separatorChar, '/'); + if (!info.getWhitelist().contains(relativePath)) { + return; + } + if (processedPaths.contains(relativePath)) { + return; } - writer.endObject(); - if (fileEntry.getEnv() != null) { - writer.name("env").beginObject(); - for (Map.Entry env : fileEntry.getEnv().entrySet()) { - writer.name(env.getKey()).value(env.getValue()); + processedPaths.add(relativePath); + + ModrinthManifest.File fileEntry = tryGetRemoteFile(file, relativePath); + if (fileEntry != null) { + remoteFilePaths.add(relativePath); + writer.beginObject(); + writer.name("path").value(fileEntry.getPath()); + writer.name("hashes").beginObject(); + for (Map.Entry hash : fileEntry.getHashes().entrySet()) { + writer.name(hash.getKey()).value(hash.getValue()); } writer.endObject(); + if (fileEntry.getEnv() != null) { + writer.name("env").beginObject(); + for (Map.Entry env : fileEntry.getEnv().entrySet()) { + writer.name(env.getKey()).value(env.getValue()); + } + writer.endObject(); + } + writer.name("downloads").beginArray(); + for (String url : fileEntry.getDownloads()) { + writer.value(url); + } + writer.endArray(); + writer.name("fileSize").value(fileEntry.getFileSize()); + writer.endObject(); } - writer.name("downloads").beginArray(); - for (String url : fileEntry.getDownloads()) { - writer.value(url); - } - writer.endArray(); - writer.name("fileSize").value(fileEntry.getFileSize()); - writer.endObject(); + } catch (IOException e) { + throw new RuntimeException(e); } - } catch (IOException e) { - throw new RuntimeException(e); - } - }); + }); + } } } From 49ee0c003107b57bd74da8af4690c2ab583fb1d1 Mon Sep 17 00:00:00 2001 From: 2012hzy <3681596340p@qq.com> Date: Sun, 14 Jun 2026 11:29:48 +0800 Subject: [PATCH 3/9] =?UTF-8?q?=E9=92=88=E5=AF=B9#issues6086=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E6=95=B4=E5=90=88=E5=8C=85=E6=97=B6=E5=86=85=E5=AD=98?= =?UTF-8?q?=E6=BA=A2=E5=87=BA=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java | 2 +- .../jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java index 1cd35f7b2aa..910aca17e5c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java @@ -267,4 +267,4 @@ public void execute() throws Exception { .requireLaunchArguments() .requireOrigins() .requireAuthor(); -} \ No newline at end of file +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java index fb5bca8cfe0..673ebb0098e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java @@ -260,4 +260,4 @@ public void execute() throws Exception { public static final ModpackExportInfo.Options OPTION = new ModpackExportInfo.Options() .requireNoCreateRemoteFiles() .requireSkipCurseForgeRemoteFiles(); -} \ No newline at end of file +} From b7d815e731c245832f8ef889a4cad5115047665f Mon Sep 17 00:00:00 2001 From: 2012hzy <3681596340p@qq.com> Date: Sun, 14 Jun 2026 11:40:15 +0800 Subject: [PATCH 4/9] =?UTF-8?q?=E9=92=88=E5=AF=B9#issues6086=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E6=95=B4=E5=90=88=E5=8C=85=E6=97=B6=E5=86=85=E5=AD=98?= =?UTF-8?q?=E6=BA=A2=E5=87=BA=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mod/mcbbs/McbbsModpackExportTask.java | 103 ++++-------------- .../modrinth/ModrinthModpackExportTask.java | 48 ++++---- 2 files changed, 39 insertions(+), 112 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java index 910aca17e5c..7576990fad3 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java @@ -41,6 +41,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Optional; import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*; import static org.jackhuang.hmcl.util.logging.Logger.LOG; @@ -96,7 +97,7 @@ public void execute() throws Exception { writer.name("url").value(info.getUrl()); writer.name("forceUpdate").value(info.isForceUpdate()); - writer.name("origins").beginArray(); + writer.name("origin").beginArray(); writer.endArray(); writer.name("addons").beginArray(); @@ -104,86 +105,19 @@ public void execute() throws Exception { writer.name("id").value(MINECRAFT.getPatchId()); writer.name("version").value(gameVersion); writer.endObject(); - analyzer.getVersion(FORGE).ifPresent(forgeVersion -> { - try { - writer.beginObject(); - writer.name("id").value(FORGE.getPatchId()); - writer.name("version").value(forgeVersion); - writer.endObject(); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - analyzer.getVersion(CLEANROOM).ifPresent(cleanroomVersion -> { - try { - writer.beginObject(); - writer.name("id").value(CLEANROOM.getPatchId()); - writer.name("version").value(cleanroomVersion); - writer.endObject(); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - analyzer.getVersion(NEO_FORGE).ifPresent(neoForgeVersion -> { - try { - writer.beginObject(); - writer.name("id").value(NEO_FORGE.getPatchId()); - writer.name("version").value(neoForgeVersion); - writer.endObject(); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - analyzer.getVersion(LITELOADER).ifPresent(liteLoaderVersion -> { - try { - writer.beginObject(); - writer.name("id").value(LITELOADER.getPatchId()); - writer.name("version").value(liteLoaderVersion); - writer.endObject(); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - analyzer.getVersion(OPTIFINE).ifPresent(optifineVersion -> { - try { - writer.beginObject(); - writer.name("id").value(OPTIFINE.getPatchId()); - writer.name("version").value(optifineVersion); - writer.endObject(); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - analyzer.getVersion(FABRIC).ifPresent(fabricVersion -> { - try { - writer.beginObject(); - writer.name("id").value(FABRIC.getPatchId()); - writer.name("version").value(fabricVersion); - writer.endObject(); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - analyzer.getVersion(QUILT).ifPresent(quiltVersion -> { - try { - writer.beginObject(); - writer.name("id").value(QUILT.getPatchId()); - writer.name("version").value(quiltVersion); - writer.endObject(); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - analyzer.getVersion(LEGACY_FABRIC).ifPresent(legacyfabricVersion -> { - try { + + LibraryAnalyzer.LibraryType[] addonTypes = { + FORGE, CLEANROOM, NEO_FORGE, LITELOADER, OPTIFINE, FABRIC, QUILT, LEGACY_FABRIC + }; + for (LibraryAnalyzer.LibraryType type : addonTypes) { + Optional addonVersion = analyzer.getVersion(type); + if (addonVersion.isPresent()) { writer.beginObject(); - writer.name("id").value(LEGACY_FABRIC.getPatchId()); - writer.name("version").value(legacyfabricVersion); + writer.name("id").value(type.getPatchId()); + writer.name("version").value(addonVersion.get()); writer.endObject(); - } catch (IOException e) { - throw new RuntimeException(e); } - }); + } writer.endArray(); writer.name("libraries").beginArray(); @@ -194,12 +128,13 @@ public void execute() throws Exception { stream.filter(Files::isRegularFile) .forEach(file -> { try { - Path relative = runDirectory.relativize(file); + Path relative = runDirectory.relativize(file).normalize(); String relativePath = relative.toString().replace(File.separatorChar, '/'); if (Modpack.acceptFile(relativePath, blackList, info.getWhitelist())) { String sha1 = DigestUtils.digestToString("SHA-1", file); writer.beginObject(); - writer.name("type").value(true); + writer.name("type").value("addon"); + writer.name("force").value(true); writer.name("path").value(relativePath); writer.name("hash").value(sha1); writer.endObject(); @@ -212,23 +147,25 @@ public void execute() throws Exception { writer.endArray(); writer.name("settings").beginObject(); + writer.name("install_mods").value(true); + writer.name("install_resourcepack").value(true); writer.endObject(); writer.name("launchInfo").beginObject(); writer.name("minMemory").value(info.getMinMemory()); - writer.name("supportedJavaVersions").beginArray(); + writer.name("supportJava").beginArray(); if (info.getSupportedJavaVersions() != null) { for (int ver : info.getSupportedJavaVersions()) { writer.value(ver); } } writer.endArray(); - writer.name("launchArguments").beginArray(); + writer.name("launchArgument").beginArray(); for (String arg : StringUtils.tokenize(info.getLaunchArguments())) { writer.value(arg); } writer.endArray(); - writer.name("javaArguments").beginArray(); + writer.name("javaArgument").beginArray(); for (String arg : StringUtils.tokenize(info.getJavaArguments())) { writer.value(arg); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java index 673ebb0098e..36169a66fef 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java @@ -197,9 +197,11 @@ public void execute() throws Exception { writer.endObject(); } } catch (IOException e) { - throw new RuntimeException(e); + LOG.warning("Failed to process file: " + file, e); } }); + } catch (IOException e) { + LOG.warning("Failed to walk directory: " + dirPath, e); } } } @@ -208,34 +210,22 @@ public void execute() throws Exception { writer.name("dependencies").beginObject(); writer.name("minecraft").value(gameVersion); - analyzer.getVersion(FORGE).ifPresent(forgeVersion -> { - try { - writer.name("forge").value(forgeVersion); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - analyzer.getVersion(NEO_FORGE).ifPresent(neoForgeVersion -> { - try { - writer.name("neoforge").value(neoForgeVersion); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - analyzer.getVersion(FABRIC).ifPresent(fabricVersion -> { - try { - writer.name("fabric-loader").value(fabricVersion); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - analyzer.getVersion(QUILT).ifPresent(quiltVersion -> { - try { - writer.name("quilt-loader").value(quiltVersion); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); + Optional forgeVersion = analyzer.getVersion(FORGE); + if (forgeVersion.isPresent()) { + writer.name("forge").value(forgeVersion.get()); + } + Optional neoForgeVersion = analyzer.getVersion(NEO_FORGE); + if (neoForgeVersion.isPresent()) { + writer.name("neoforge").value(neoForgeVersion.get()); + } + Optional fabricVersion = analyzer.getVersion(FABRIC); + if (fabricVersion.isPresent()) { + writer.name("fabric-loader").value(fabricVersion.get()); + } + Optional quiltVersion = analyzer.getVersion(QUILT); + if (quiltVersion.isPresent()) { + writer.name("quilt-loader").value(quiltVersion.get()); + } writer.endObject(); writer.endObject(); From af6c4b946d33943309b7576a5f908d480d16b9de Mon Sep 17 00:00:00 2001 From: 2012hzy <3681596340p@qq.com> Date: Sun, 14 Jun 2026 11:48:32 +0800 Subject: [PATCH 5/9] =?UTF-8?q?=E9=92=88=E5=AF=B9#issues6086=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E6=95=B4=E5=90=88=E5=8C=85=E6=97=B6=E5=86=85=E5=AD=98?= =?UTF-8?q?=E6=BA=A2=E5=87=BA=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mod/mcbbs/McbbsModpackExportTask.java | 60 ++++++++----- .../modrinth/ModrinthModpackExportTask.java | 90 +++++++++---------- 2 files changed, 84 insertions(+), 66 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java index 7576990fad3..b5b91ffb3e5 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java @@ -36,8 +36,11 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; +import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -93,11 +96,21 @@ public void execute() throws Exception { writer.name("version").value(info.getVersion()); writer.name("author").value(info.getAuthor()); writer.name("description").value(info.getDescription()); - writer.name("fileApi").value(info.getFileApi() == null ? null : StringUtils.removeSuffix(info.getFileApi(), "/")); + if (info.getFileApi() != null) { + writer.name("fileApi").value(StringUtils.removeSuffix(info.getFileApi(), "/")); + } writer.name("url").value(info.getUrl()); writer.name("forceUpdate").value(info.isForceUpdate()); writer.name("origin").beginArray(); + if (info.getOrigins() != null) { + for (McbbsModpackManifest.Origin origin : info.getOrigins()) { + writer.beginObject(); + writer.name("type").value(origin.getType()); + writer.name("id").value(origin.getId()); + writer.endObject(); + } + } writer.endArray(); writer.name("addons").beginArray(); @@ -124,26 +137,31 @@ public void execute() throws Exception { writer.endArray(); writer.name("files").beginArray(); - try (var stream = Files.walk(runDirectory)) { - stream.filter(Files::isRegularFile) - .forEach(file -> { - try { - Path relative = runDirectory.relativize(file).normalize(); - String relativePath = relative.toString().replace(File.separatorChar, '/'); - if (Modpack.acceptFile(relativePath, blackList, info.getWhitelist())) { - String sha1 = DigestUtils.digestToString("SHA-1", file); - writer.beginObject(); - writer.name("type").value("addon"); - writer.name("force").value(true); - writer.name("path").value(relativePath); - writer.name("hash").value(sha1); - writer.endObject(); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - } + Files.walkFileTree(runDirectory, new SimpleFileVisitor() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + String relativePath = runDirectory.relativize(dir).normalize().toString().replace(File.separatorChar, '/'); + if (!Modpack.acceptFile(relativePath, blackList, info.getWhitelist())) { + return FileVisitResult.SKIP_SUBTREE; + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + String relativePath = runDirectory.relativize(file).normalize().toString().replace(File.separatorChar, '/'); + if (Modpack.acceptFile(relativePath, blackList, info.getWhitelist())) { + String sha1 = DigestUtils.digestToString("SHA-1", file); + writer.beginObject(); + writer.name("type").value("addon"); + writer.name("force").value(true); + writer.name("path").value(relativePath); + writer.name("hash").value(sha1); + writer.endObject(); + } + return FileVisitResult.CONTINUE; + } + }); writer.endArray(); writer.name("settings").beginObject(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java index 36169a66fef..156a6874194 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java @@ -33,9 +33,12 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; +import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; import java.util.*; import static org.jackhuang.hmcl.download.LibraryAnalyzer.LibraryType.*; @@ -149,7 +152,9 @@ public void execute() throws Exception { writer.name("game").value("minecraft"); writer.name("versionId").value(info.getVersion()); writer.name("name").value(info.getName()); - writer.name("summary").value(info.getDescription()); + if (info.getDescription() != null) { + writer.name("summary").value(info.getDescription()); + } writer.name("files").beginArray(); @@ -158,51 +163,46 @@ public void execute() throws Exception { for (String dir : resourceDirs) { Path dirPath = runDirectory.resolve(dir); if (Files.exists(dirPath)) { - try (var stream = Files.walk(dirPath)) { - stream.filter(Files::isRegularFile) - .forEach(file -> { - try { - String relativePath = runDirectory.relativize(file).normalize().toString().replace(File.separatorChar, '/'); - if (!info.getWhitelist().contains(relativePath)) { - return; - } - if (processedPaths.contains(relativePath)) { - return; - } - processedPaths.add(relativePath); - - ModrinthManifest.File fileEntry = tryGetRemoteFile(file, relativePath); - if (fileEntry != null) { - remoteFilePaths.add(relativePath); - writer.beginObject(); - writer.name("path").value(fileEntry.getPath()); - writer.name("hashes").beginObject(); - for (Map.Entry hash : fileEntry.getHashes().entrySet()) { - writer.name(hash.getKey()).value(hash.getValue()); - } - writer.endObject(); - if (fileEntry.getEnv() != null) { - writer.name("env").beginObject(); - for (Map.Entry env : fileEntry.getEnv().entrySet()) { - writer.name(env.getKey()).value(env.getValue()); - } - writer.endObject(); - } - writer.name("downloads").beginArray(); - for (String url : fileEntry.getDownloads()) { - writer.value(url); - } - writer.endArray(); - writer.name("fileSize").value(fileEntry.getFileSize()); - writer.endObject(); - } - } catch (IOException e) { - LOG.warning("Failed to process file: " + file, e); + Files.walkFileTree(dirPath, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + String relativePath = runDirectory.relativize(file).normalize().toString().replace(File.separatorChar, '/'); + if (!info.getWhitelist().contains(relativePath)) { + return FileVisitResult.CONTINUE; + } + if (processedPaths.contains(relativePath)) { + return FileVisitResult.CONTINUE; + } + processedPaths.add(relativePath); + + ModrinthManifest.File fileEntry = tryGetRemoteFile(file, relativePath); + if (fileEntry != null) { + remoteFilePaths.add(relativePath); + writer.beginObject(); + writer.name("path").value(fileEntry.getPath()); + writer.name("hashes").beginObject(); + for (Map.Entry hash : fileEntry.getHashes().entrySet()) { + writer.name(hash.getKey()).value(hash.getValue()); + } + writer.endObject(); + if (fileEntry.getEnv() != null) { + writer.name("env").beginObject(); + for (Map.Entry env : fileEntry.getEnv().entrySet()) { + writer.name(env.getKey()).value(env.getValue()); } - }); - } catch (IOException e) { - LOG.warning("Failed to walk directory: " + dirPath, e); - } + writer.endObject(); + } + writer.name("downloads").beginArray(); + for (String url : fileEntry.getDownloads()) { + writer.value(url); + } + writer.endArray(); + writer.name("fileSize").value(fileEntry.getFileSize()); + writer.endObject(); + } + return FileVisitResult.CONTINUE; + } + }); } } From b823a745a23809881b8d08eb2a5e86c8cf2d46ef Mon Sep 17 00:00:00 2001 From: 2012hzy <3681596340p@qq.com> Date: Sun, 14 Jun 2026 12:02:20 +0800 Subject: [PATCH 6/9] =?UTF-8?q?=E9=92=88=E5=AF=B9#issues6086=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E6=95=B4=E5=90=88=E5=8C=85=E6=97=B6=E5=86=85=E5=AD=98?= =?UTF-8?q?=E6=BA=A2=E5=87=BA=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hmcl/mod/modrinth/ModrinthModpackExportTask.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java index 156a6874194..4c14cab1984 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java @@ -175,7 +175,12 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO } processedPaths.add(relativePath); - ModrinthManifest.File fileEntry = tryGetRemoteFile(file, relativePath); + ModrinthManifest.File fileEntry = null; + try { + fileEntry = tryGetRemoteFile(file, relativePath); + } catch (IOException e) { + LOG.warning("Failed to process file: " + file, e); + } if (fileEntry != null) { remoteFilePaths.add(relativePath); writer.beginObject(); From 46fc5aecf19c6a4d3fac5c49f9d007c659d25b9b Mon Sep 17 00:00:00 2001 From: 2012hzy <3681596340p@qq.com> Date: Sun, 14 Jun 2026 12:09:19 +0800 Subject: [PATCH 7/9] =?UTF-8?q?=E9=92=88=E5=AF=B9#issues6086=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E6=95=B4=E5=90=88=E5=8C=85=E6=97=B6=E5=86=85=E5=AD=98?= =?UTF-8?q?=E6=BA=A2=E5=87=BA=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hmcl/mod/mcbbs/McbbsModpackExportTask.java | 16 ++++++++++++---- .../mod/modrinth/ModrinthModpackExportTask.java | 5 ++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java index b5b91ffb3e5..2de23747a86 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java @@ -178,14 +178,22 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO } } writer.endArray(); + writer.name("launchArgument").beginArray(); - for (String arg : StringUtils.tokenize(info.getLaunchArguments())) { - writer.value(arg); + List launchArgs = StringUtils.tokenize(info.getLaunchArguments()); + if (launchArgs != null) { + for (String arg : launchArgs) { + writer.value(arg); + } } writer.endArray(); + writer.name("javaArgument").beginArray(); - for (String arg : StringUtils.tokenize(info.getJavaArguments())) { - writer.value(arg); + List javaArgs = StringUtils.tokenize(info.getJavaArguments()); + if (javaArgs != null) { + for (String arg : javaArgs) { + writer.value(arg); + } } writer.endArray(); writer.endObject(); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java index 4c14cab1984..f498cdba29f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java @@ -139,6 +139,9 @@ public void execute() throws Exception { .orElseThrow(() -> new IOException("Cannot parse the version of " + version)); LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(version), gameVersion); + // Convert whitelist List to HashSet for O(1) lookups + Set whitelistSet = new HashSet<>(info.getWhitelist()); + String[] resourceDirs = {"resourcepacks", "shaderpacks", "mods"}; Set remoteFilePaths = new HashSet<>(); @@ -167,7 +170,7 @@ public void execute() throws Exception { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { String relativePath = runDirectory.relativize(file).normalize().toString().replace(File.separatorChar, '/'); - if (!info.getWhitelist().contains(relativePath)) { + if (!whitelistSet.contains(relativePath)) { return FileVisitResult.CONTINUE; } if (processedPaths.contains(relativePath)) { From a37feb7a45e020cf3fd6038d009e5c1d3accd776 Mon Sep 17 00:00:00 2001 From: 2012hzy <3681596340p@qq.com> Date: Sun, 14 Jun 2026 12:24:10 +0800 Subject: [PATCH 8/9] =?UTF-8?q?=E9=92=88=E5=AF=B9#issues6086=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E6=95=B4=E5=90=88=E5=8C=85=E6=97=B6=E5=86=85=E5=AD=98?= =?UTF-8?q?=E6=BA=A2=E5=87=BA=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hmcl/mod/mcbbs/McbbsModpackExportTask.java | 14 ++++++++++++-- .../mod/modrinth/ModrinthModpackExportTask.java | 8 ++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java index 2de23747a86..4b895b8edd8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java @@ -141,7 +141,11 @@ public void execute() throws Exception { @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { String relativePath = runDirectory.relativize(dir).normalize().toString().replace(File.separatorChar, '/'); - if (!Modpack.acceptFile(relativePath, blackList, info.getWhitelist())) { + if (relativePath.isEmpty()) { + return FileVisitResult.CONTINUE; + } + // Only skip directories that match blacklist (e.g., assets, libraries) + if (ModAdviser.match(blackList, relativePath, false)) { return FileVisitResult.SKIP_SUBTREE; } return FileVisitResult.CONTINUE; @@ -212,7 +216,13 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO zip.putTextFile(JsonUtils.GSON.toJson(curseManifest), "manifest.json"); zip.putDirectory(runDirectory, "overrides", path -> { - return Modpack.acceptFile(path, blackList, info.getWhitelist()); + Path resolved = runDirectory.resolve(path); + if (Files.isDirectory(resolved)) { + // For directories, only check blacklist, never skip because of whitelist + return !ModAdviser.match(blackList, path, false); + } else { + return Modpack.acceptFile(path, blackList, info.getWhitelist()); + } }); } } finally { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java index f498cdba29f..f90a1dad003 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java @@ -139,7 +139,6 @@ public void execute() throws Exception { .orElseThrow(() -> new IOException("Cannot parse the version of " + version)); LibraryAnalyzer analyzer = LibraryAnalyzer.analyze(repository.getResolvedPreservingPatchesVersion(version), gameVersion); - // Convert whitelist List to HashSet for O(1) lookups Set whitelistSet = new HashSet<>(info.getWhitelist()); String[] resourceDirs = {"resourcepacks", "shaderpacks", "mods"}; @@ -244,10 +243,15 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO zip.putDirectory(runDirectory, "client-overrides", path -> { String relativePath = path.replace(File.separatorChar, '/'); + Path resolved = runDirectory.resolve(relativePath); + if (Files.isDirectory(resolved)) { + // For directories, only check blacklist, never skip due to whitelist + return !ModAdviser.match(blackList, relativePath, false); + } if (remoteFilePaths.contains(relativePath)) { return false; } - return Modpack.acceptFile(path, blackList, info.getWhitelist()); + return Modpack.acceptFile(relativePath, blackList, info.getWhitelist()); }); } } finally { From dd16443640a3ad24549b8d51e1bf532bba2b48c6 Mon Sep 17 00:00:00 2001 From: 2012hzy <3681596340p@qq.com> Date: Sun, 14 Jun 2026 16:17:13 +0800 Subject: [PATCH 9/9] =?UTF-8?q?=E9=92=88=E5=AF=B9#issues6086=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E6=95=B4=E5=90=88=E5=8C=85=E6=97=B6=E5=86=85=E5=AD=98?= =?UTF-8?q?=E6=BA=A2=E5=87=BA=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java | 2 -- .../jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java index 4b895b8edd8..dd4d0418db0 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/mcbbs/McbbsModpackExportTask.java @@ -144,7 +144,6 @@ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) th if (relativePath.isEmpty()) { return FileVisitResult.CONTINUE; } - // Only skip directories that match blacklist (e.g., assets, libraries) if (ModAdviser.match(blackList, relativePath, false)) { return FileVisitResult.SKIP_SUBTREE; } @@ -218,7 +217,6 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO zip.putDirectory(runDirectory, "overrides", path -> { Path resolved = runDirectory.resolve(path); if (Files.isDirectory(resolved)) { - // For directories, only check blacklist, never skip because of whitelist return !ModAdviser.match(blackList, path, false); } else { return Modpack.acceptFile(path, blackList, info.getWhitelist()); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java index f90a1dad003..bb69a3803d8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modrinth/ModrinthModpackExportTask.java @@ -75,6 +75,7 @@ private ModrinthManifest.File tryGetRemoteFile(Path file, String relativePath) t boolean isDisabled = repository.getModManager(version).isDisabled(file); if (isDisabled) { relativePath = repository.getModManager(version).enableMod(Paths.get(relativePath)).toString(); + file = repository.getRunDirectory(version).resolve(relativePath); } Optional modrinthVersion = Optional.empty(); @@ -245,7 +246,6 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO String relativePath = path.replace(File.separatorChar, '/'); Path resolved = runDirectory.resolve(relativePath); if (Files.isDirectory(resolved)) { - // For directories, only check blacklist, never skip due to whitelist return !ModAdviser.match(blackList, relativePath, false); } if (remoteFilePaths.contains(relativePath)) {