From 30320788639e3442928a52d1c606fdd994a5c149 Mon Sep 17 00:00:00 2001 From: dovisutu <40313014+dovisutu@users.noreply.github.com> Date: Sat, 6 Jun 2026 20:43:09 +0800 Subject: [PATCH 1/3] Add fallback and namespace subpacks --- src/Packer/Extensions/ArchiveExtension.cs | 62 +++++++++++++--- src/Packer/Extensions/ContentExtension.cs | 8 ++- src/Packer/Helpers/ConfigHelpers.cs | 7 +- src/Packer/Helpers/EnumerationHelper.cs | 71 +++++++++++++++++++ src/Packer/Models/Config.cs | 5 ++ src/Packer/Models/IResourceFileProvider.cs | 7 +- src/Packer/Models/Providers/McMetaProvider.cs | 56 --------------- src/Packer/Models/Providers/RawFile.cs | 12 +--- .../Models/Providers/TermMappingProvider.cs | 13 ++-- src/Packer/Models/Providers/TextFile.cs | 13 ++-- src/Packer/Packer.csproj | 1 + src/Packer/Program.cs | 49 +++---------- 12 files changed, 166 insertions(+), 138 deletions(-) create mode 100644 src/Packer/Helpers/EnumerationHelper.cs delete mode 100644 src/Packer/Models/Providers/McMetaProvider.cs diff --git a/src/Packer/Extensions/ArchiveExtension.cs b/src/Packer/Extensions/ArchiveExtension.cs index 62121c98727b..ae57765142a2 100644 --- a/src/Packer/Extensions/ArchiveExtension.cs +++ b/src/Packer/Extensions/ArchiveExtension.cs @@ -1,5 +1,14 @@ -using System; +using Packer.Models; +using Serilog; +using SharpCompress.Writers; +using SharpCompress.Writers.Tar; +using System; +using System.Collections.Generic; +using System.IO; using System.IO.Compression; +using System.Linq; +using System.Text; +using System.Threading.Tasks; namespace Packer.Extensions { @@ -8,18 +17,55 @@ namespace Packer.Extensions /// static public class ArchiveExtension { - /// - /// 校验将要传入压缩包的的文件是否存在重名
- ///
- /// 所查询的压缩包 - /// 所查询的路径 - /// 传入文件存在重名。 - public static void ValidateEntryDistinctness(this ZipArchive archive, string entryName) + + public static ZipArchiveEntry CreateEntryValidated(this ZipArchive archive, string entryName) { + var normalized = entryName.NormalizePath(); if (archive.GetEntry(entryName) != null) { throw new InvalidOperationException($"An entry named {entryName} already exists."); } + Log.Debug("写入路径 {0}", normalized); + return archive.CreateEntry(normalized); + } + + public static async Task WriteDirect(this ZipArchive archive, IEnumerable providers) + { + foreach (var provider in providers) + { + var entry = archive.CreateEntryValidated(provider.Destination); + using var stream = entry.Open(); + await provider.WriteToStream(stream); + } + } + + public static async Task WriteGrouped(this ZipArchive archive, IEnumerable providers) + { + var grouped = + from provider in providers + group provider by provider.Destination.GetNamespace(); + foreach (var group in grouped) + { + var targetPath = $"assets/{group.Key}.tar.lzma"; + var entry = archive.CreateEntryValidated(targetPath); + using var stream = await entry.OpenAsync(); + using (var entryWriter = new TarWriter(stream, new TarWriterOptions(SharpCompress.Common.CompressionType.LZip))) + { + foreach (var provider in group) + { + using var dataStream = new MemoryStream(); + await provider.WriteToStream(dataStream); + dataStream.Seek(0, SeekOrigin.Begin); + await entryWriter.WriteAsync(provider.Destination.Split('/', 3)[2], dataStream); + } + } + + var md5 = stream.ComputeMD5(); + var md5Entry = archive.CreateEntryValidated($"assets/{group.Key}.md5"); + using var md5Writer = new StreamWriter(md5Entry.Open(), + new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); + md5Writer.Write(md5); + } } } } diff --git a/src/Packer/Extensions/ContentExtension.cs b/src/Packer/Extensions/ContentExtension.cs index 936e0cfab327..7b07838e938c 100644 --- a/src/Packer/Extensions/ContentExtension.cs +++ b/src/Packer/Extensions/ContentExtension.cs @@ -20,6 +20,12 @@ public static partial class ContentExtension public static string NormalizePath(this string path) => path.Replace('\\', '/'); // 修正正反斜杠导致的压缩文件读取问题 + public static string GetNamespace(this string path) + { + // assets//... + return path.Split('/')[1]; + } + [GeneratedRegex(@"^[a-z0-9_.-]+$", RegexOptions.Singleline)] internal static partial Regex ValidNamespaceRegex(); @@ -107,7 +113,7 @@ public static string LogToDebug(this string message) /// public static string ComputeMD5(this Stream stream) { - stream.Seek(0, SeekOrigin.Begin); // 确保文件流的位置被重置 + stream.Seek(0, SeekOrigin.Begin); return Convert.ToHexString(MD5.Create().ComputeHash(stream)); } } diff --git a/src/Packer/Helpers/ConfigHelpers.cs b/src/Packer/Helpers/ConfigHelpers.cs index 7ca78ba37375..08b15468bc18 100644 --- a/src/Packer/Helpers/ConfigHelpers.cs +++ b/src/Packer/Helpers/ConfigHelpers.cs @@ -40,7 +40,7 @@ public static class ConfigHelpers /// /// 配置路径模板 /// 打包版本,用于定位全局配置 - public static async Task RetrieveConfig(string configTemplate, string version) + public static Config RetrieveConfig(string configTemplate, string version) { Log.Information("正在获取配置。目标版本:{0}", version); @@ -48,9 +48,10 @@ public static async Task RetrieveConfig(string configTemplate, string ve Log.Information("配置位置:{0}", configPath); - var content = await File.ReadAllBytesAsync(configPath); + using var stream = File.OpenRead(configPath); + return JsonSerializer.Deserialize( - content, + stream, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase })!; } diff --git a/src/Packer/Helpers/EnumerationHelper.cs b/src/Packer/Helpers/EnumerationHelper.cs new file mode 100644 index 000000000000..c655063cb9a8 --- /dev/null +++ b/src/Packer/Helpers/EnumerationHelper.cs @@ -0,0 +1,71 @@ +using Packer.Extensions; +using Packer.Models; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Packer.Helpers +{ + internal static class EnumerationHelper + { + public static IEnumerable EnumerateUnmerged(IEnumerable targetModIdentifiers, Config config, string version) + { + return + // ./projects/assets/... + from modDirectory in new DirectoryInfo("./projects/assets").EnumerateDirectories() + let modIdentifier = modDirectory.Name + where targetModIdentifiers.Count() == 0 // 未提供列表,全部打包 + || targetModIdentifiers.Contains(modIdentifier) // 有列表,仅打包列表中的项 + where !config.Base.ExclusionMods.Contains(modIdentifier) // 没有被明确排除 + // .../ + let versionedDirectory = modDirectory.GetDirectories(version).FirstOrDefault(defaultValue: null) + where versionedDirectory is not null + // .../-CFPA- + from namespaceDirectory in versionedDirectory.EnumerateDirectories() + let namespaceName = namespaceDirectory.Name + where !config.Base.ExclusionNamespaces.Contains(namespaceName) // 没有被明确排除 + where namespaceName.ValidateNamespace() // 不是非法名称 + // .../* + from provider in namespaceDirectory.EnumerateProviders(config) + select provider; + } + + public static IEnumerable PostProcess(this IEnumerable providers, Config config) + { + return + from provider in providers + select config.Floating.CharacterReplacement // 内容的字符替换 + .Aggregate(seed: provider, + (accumulate, replacement) + => accumulate.ReplaceContent( + replacement.Key, + replacement.Value)) into provider + select config.Floating.DestinationReplacement // 全局路径替换:预留 + .Aggregate(seed: provider, + (accumulate, replacement) + => accumulate.ReplaceDestination( + replacement.Key, + replacement.Value)); + } + + public static IEnumerable MergeDeep(this IEnumerable providers) + { + return + from provider in providers + group provider by provider.Destination into destinationGroup + select destinationGroup + .Aggregate(seed: null as IResourceFileProvider, // 合并文件 + (accumulate, next) + => next.ApplyTo( + accumulate)); + } + + public static IEnumerable MergeShallow(this IEnumerable providers) + { + return from provider in providers + group provider by provider.Destination into destinationGroup + select destinationGroup.First(); + } + } +} diff --git a/src/Packer/Models/Config.cs b/src/Packer/Models/Config.cs index 85e7262a901f..5438c5d3e1f9 100644 --- a/src/Packer/Models/Config.cs +++ b/src/Packer/Models/Config.cs @@ -78,6 +78,11 @@ public class BaseConfig /// 不进行打包的namespace /// public IEnumerable ExclusionNamespaces { get; set; } + + /// + /// 回退版本,当该版本不存在相应语言文件时从这些版本获取文件 + /// + public IEnumerable FallbackVersions { get; set; } } /// diff --git a/src/Packer/Models/IResourceFileProvider.cs b/src/Packer/Models/IResourceFileProvider.cs index 521c0470c939..6f82b7736c45 100644 --- a/src/Packer/Models/IResourceFileProvider.cs +++ b/src/Packer/Models/IResourceFileProvider.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.IO.Compression; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -43,11 +44,11 @@ public IResourceFileProvider ReplaceContent(string searchPattern, string replace public IResourceFileProvider ReplaceDestination(string searchPattern, string replacement); /// - /// 将该提供器的内容写入到资源包的正确位置 + /// 将该提供器的内容写入给定的中 /// - /// + /// /// 资源包中已有同名文件 - public Task WriteToArchive(ZipArchive archive); + public Task WriteToStream(Stream stream); /// /// 目标在资源包中的相对位置,从根目录算起 diff --git a/src/Packer/Models/Providers/McMetaProvider.cs b/src/Packer/Models/Providers/McMetaProvider.cs deleted file mode 100644 index 0ad3431f2f4a..000000000000 --- a/src/Packer/Models/Providers/McMetaProvider.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Packer.Extensions; -using Serilog; -using System; -using System.IO; -using System.IO.Compression; -using System.Text; -using System.Threading.Tasks; - -namespace Packer.Models.Providers -{ - /// - /// 用于表示pack.mcmeta的提供器。写入时将会附加打包时间 - /// - public class McMetaProvider : TextFile - { - internal McMetaProvider(string content, string destination) : base(content, destination) { } - - /// - /// 从给定的构造提供器。 - /// - /// 读取源 - /// 目标地址 - public static new McMetaProvider Create(FileInfo file, string destination) - { - using var stream = file.OpenRead(); - using var reader = new StreamReader(stream, Encoding.UTF8); - var content = reader.ReadToEnd(); - return new McMetaProvider(content, destination); - } - - /// - public override string Destination => "pack.mcmeta"; - /// - public override IResourceFileProvider ReplaceContent(string searchPattern, string replacement) - => this; - /// - public override IResourceFileProvider ReplaceDestination(string searchPattern, string replacement) - => this; - /// - public override async Task WriteToArchive(ZipArchive archive) - { - var destination = Destination.NormalizePath(); - Log.Debug("[McMetaProvider]写入路径 {0}", destination); - - var content = string.Format(Content, DateTime.UtcNow.AddHours(8) /* UTC +8:00 */); - - archive.ValidateEntryDistinctness(destination); - - using var writer = new StreamWriter( - archive.CreateEntry(destination) - .Open(), - new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); - await writer.WriteAsync(content); - } - } -} diff --git a/src/Packer/Models/Providers/RawFile.cs b/src/Packer/Models/Providers/RawFile.cs index 1c282d7242d2..4025f61acdee 100644 --- a/src/Packer/Models/Providers/RawFile.cs +++ b/src/Packer/Models/Providers/RawFile.cs @@ -44,18 +44,10 @@ public IResourceFileProvider ReplaceDestination(string searchPattern, string rep RegexOptions.Singleline)); /// - public async Task WriteToArchive(ZipArchive archive) + public async Task WriteToStream(Stream stream) { - var destination = Destination.NormalizePath(); - Log.Debug("[RawFile]写入路径 {0}", destination); - - archive.ValidateEntryDistinctness(destination); - - // 为什么这ZipArchive.CreateEntryFromFile没有Async变种...只有手动实现了 using var source = SourceFile.OpenRead(); - using var entry = archive.CreateEntry(destination) - .Open(); - await source.CopyToAsync(entry); + await source.CopyToAsync(stream); } } } diff --git a/src/Packer/Models/Providers/TermMappingProvider.cs b/src/Packer/Models/Providers/TermMappingProvider.cs index 3566b0470e7c..31bf2889267d 100644 --- a/src/Packer/Models/Providers/TermMappingProvider.cs +++ b/src/Packer/Models/Providers/TermMappingProvider.cs @@ -190,17 +190,12 @@ public IResourceFileProvider ReplaceDestination(string searchPattern, string rep RegexOptions.Singleline)); /// - public async Task WriteToArchive(ZipArchive archive) + public async Task WriteToStream(Stream stream) { - var destination = Destination.NormalizePath(); - Log.Debug("[TermMappingProvider`1]写入路径 {0}", destination); - - archive.ValidateEntryDistinctness(destination); - using var writer = new StreamWriter( - archive.CreateEntry(destination) - .Open(), - new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); + stream, + new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), + leaveOpen: true); await writer.WriteAsync(Map.ProvideStringContent()); } } diff --git a/src/Packer/Models/Providers/TextFile.cs b/src/Packer/Models/Providers/TextFile.cs index 4745375c2da7..a1eae37858ba 100644 --- a/src/Packer/Models/Providers/TextFile.cs +++ b/src/Packer/Models/Providers/TextFile.cs @@ -94,17 +94,12 @@ public virtual IResourceFileProvider ReplaceDestination(string searchPattern, st replacement, RegexOptions.Singleline)); /// - public virtual async Task WriteToArchive(ZipArchive archive) + public virtual async Task WriteToStream(Stream stream) { - var destination = Destination.NormalizePath(); - Log.Debug("[TextFile]写入路径 {0}", destination); - - archive.ValidateEntryDistinctness(destination); - using var writer = new StreamWriter( - archive.CreateEntry(destination) - .Open(), - new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); + stream, + new UTF8Encoding(encoderShouldEmitUTF8Identifier: false), + leaveOpen: true); await writer.WriteAsync(Content); } } diff --git a/src/Packer/Packer.csproj b/src/Packer/Packer.csproj index 86108fe4c5b7..b65c20c5714d 100644 --- a/src/Packer/Packer.csproj +++ b/src/Packer/Packer.csproj @@ -12,6 +12,7 @@ + diff --git a/src/Packer/Program.cs b/src/Packer/Program.cs index ef7bf379a524..751678250a14 100644 --- a/src/Packer/Program.cs +++ b/src/Packer/Program.cs @@ -23,48 +23,18 @@ public static async Task Main(string version, bool increment = false) .MinimumLevel.Debug() .CreateLogger(); - var config = await ConfigHelpers.RetrieveConfig(configTemplate: "./config/packer/{0}.json", - version: version); + var config = ConfigHelpers.RetrieveConfig(configTemplate: "./config/packer/{0}.json", + version: version); Log.Information("开始对版本 {0} 的打包", config.Base.Version); var targetModIdentifiers = increment ? GitHelpers.EnumerateChangedMods(config.Base.Version) : Enumerable.Empty(); - var query = - // ./projects/assets/... - from modDirectory in new DirectoryInfo("./projects/assets").EnumerateDirectories() - let modIdentifier = modDirectory.Name - where targetModIdentifiers.Count() == 0 // 未提供列表,全部打包 - || targetModIdentifiers.Contains(modIdentifier) // 有列表,仅打包列表中的项 - where !config.Base.ExclusionMods.Contains(modIdentifier) // 没有被明确排除 - // .../ - let versionedDirectory = modDirectory.GetDirectories(config.Base.Version).FirstOrDefault(defaultValue: null) - where versionedDirectory is not null - // .../-CFPA- - from namespaceDirectory in versionedDirectory.EnumerateDirectories() - let namespaceName = namespaceDirectory.Name - where !config.Base.ExclusionNamespaces.Contains(namespaceName) // 没有被明确排除 - where namespaceName.ValidateNamespace() // 不是非法名称 - // .../* - from provider in namespaceDirectory.EnumerateProviders(config) - group provider by provider.Destination into destinationGroup - select destinationGroup - .Aggregate(seed: null as IResourceFileProvider, // 合并文件 - (accumulate, next) - => next.ApplyTo( - accumulate)) into provider - select config.Floating.CharacterReplacement // 内容的字符替换 - .Aggregate(seed: provider, - (accumulate, replacement) - => accumulate.ReplaceContent( - replacement.Key, - replacement.Value)) into provider - select config.Floating.DestinationReplacement // 全局路径替换:预留 - .Aggregate(seed: provider, - (accumulate, replacement) - => accumulate.ReplaceDestination( - replacement.Key, - replacement.Value)); + var query = config.Base.FallbackVersions.Prepend(config.Base.Version) + .SelectMany(_ => EnumerationHelper.EnumerateUnmerged(targetModIdentifiers, config, _).MergeDeep()) + .MergeShallow() + .PostProcess(config); + IEnumerable initialFiles = [ new RawFile(new FileInfo("./projects/templates/pack.png"), "pack.png"), @@ -82,8 +52,9 @@ select config.Floating.DestinationReplacement // 全局路 using (var archive = new ZipArchive(stream, ZipArchiveMode.Update, leaveOpen: true)) { - await Task.WhenAll(from provider in query.Concat(initialFiles) - select provider.WriteToArchive(archive)); + await archive.WriteDirect(initialFiles); + // await archive.WriteDirect(query); + await archive.WriteGrouped(query); } Log.Information("对版本 {0} 的打包结束。", version); From 09debd38a6a2664a29c118a33bbdb3b41104f494 Mon Sep 17 00:00:00 2001 From: dovisutu <40313014+dovisutu@users.noreply.github.com> Date: Sun, 7 Jun 2026 10:22:33 +0800 Subject: [PATCH 2/3] Cleanup --- .github/workflows/packer.yml | 13 ++++++- .github/workflows/pr-packer.yml | 2 +- .gitignore | 1 + Packer-Doc.md | 4 ++ config/packer/1.12.2.json | 3 +- config/packer/1.16-fabric.json | 3 +- config/packer/1.16.json | 3 +- config/packer/1.18-fabric.json | 3 +- config/packer/1.18.json | 3 +- config/packer/1.19.json | 3 ++ config/packer/1.20-fabric.json | 4 ++ config/packer/1.20.json | 4 ++ config/packer/1.21-fabric.json | 7 +++- config/packer/1.21.json | 5 +++ config/packer/26.1-fabric.json | 7 +++- config/packer/26.1.json | 7 +++- src/Packer/Helpers/EnumerationHelper.cs | 8 +++- src/Packer/Program.cs | 52 +++++++++++++++++-------- 18 files changed, 104 insertions(+), 28 deletions(-) diff --git a/.github/workflows/packer.yml b/.github/workflows/packer.yml index f76a26091024..c7d015987116 100644 --- a/.github/workflows/packer.yml +++ b/.github/workflows/packer.yml @@ -109,7 +109,7 @@ jobs: - name: Run Packer for ${{ matrix.version }} # 分发包中应当包含全部内容 - run: ./Packer --version="${{ matrix.version }}" + run: ./Packer --version="${{ matrix.version }}" --grouped --flattened # 运行逻辑:内容有更改 或 手动运行 if: steps.check-changes.outputs.changed == 'true' || github.event_name == 'workflow_dispatch' @@ -122,6 +122,15 @@ jobs: Minecraft-Mod-Language-Modpack-${{ matrix.version }}.zip ${{ matrix.version }}.md5 if: steps.check-changes.outputs.changed == 'true' || github.event_name == 'workflow_dispatch' + + # feat:按命名空间打小包 + - name: Upload Artifact for ${{ matrix.version }} grouped + uses: actions/upload-artifact@v4 + with: + name: Minecraft-Mod-Language-Modpack-${{ matrix.version }}-grouped + path: | + Minecraft-Mod-Language-Modpack-${{ matrix.version }}-namespaced.zip + if: steps.check-changes.outputs.changed == 'true' || github.event_name == 'workflow_dispatch' upload: concurrency: @@ -154,6 +163,8 @@ jobs: uses: actions/download-artifact@v4 with: path: artifacts/ + + # Pending: 把按命名空间传的包也传上去 # feat: UTC 20:00~21:00 取消上传(避开远程服务器的4:00-4:10) - name: Fail at inappropriate time diff --git a/.github/workflows/pr-packer.yml b/.github/workflows/pr-packer.yml index dcec1c92cead..726ce69e2e03 100644 --- a/.github/workflows/pr-packer.yml +++ b/.github/workflows/pr-packer.yml @@ -100,7 +100,7 @@ jobs: - name: Run Packer for ${{ matrix.version }} # 部分包原则:Packer和配置均没有改动 - run: ./Packer --version="${{ matrix.version }}" --increment=${{ steps.check-critical-changes.outputs.changed == 'false' }} + run: ./Packer --version="${{ matrix.version }}" --increment=${{ steps.check-critical-changes.outputs.changed == 'false' }} --flattened # 运行逻辑:内容有更改 或 手动运行 if: steps.check-changes.outputs.changed == 'true' || github.event_name == 'workflow_dispatch' diff --git a/.gitignore b/.gitignore index 6f6f0e95e2f9..d7cea7269b30 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # CFPA-specifics Minecraft-Mod-Language-Package-*.zip +Minecraft-Mod-Language-Modpack-*.zip *.md5 build/ Packer.exe diff --git a/Packer-Doc.md b/Packer-Doc.md index f3f5ff11df0d..aac691682baf 100644 --- a/Packer-Doc.md +++ b/Packer-Doc.md @@ -52,6 +52,10 @@ 暂时闲置,以待后续需求。 - string 排除的命名空间。 + - `fallbackVersions` list + 该配置所允许的回退版本。对每个模组文件夹,打包器会先寻找 `version` 字段指向的版本,再在这里按顺序寻找回退版本;打包器会选择找到的**第一个**版本,而不会加载其他版本的内容。 + - string + 回退版本,以 `projects/` 中的文件夹名称为准。 - `floating` object 打包流程中的*可变配置*,可能被文件结构中的**局域配置文件**改写。包含的内容都是**低于**命名空间层级的,因为局域配置文件就是放在命名空间一级中的。 - `inclusionDomains` list diff --git a/config/packer/1.12.2.json b/config/packer/1.12.2.json index 23d1807418b6..b084153c9137 100644 --- a/config/packer/1.12.2.json +++ b/config/packer/1.12.2.json @@ -16,7 +16,8 @@ ], "exclusionNamespaces": [ "srparasites" - ] + ], + "fallbackVersions": [] }, "floating": { "inclusionDomains": [ diff --git a/config/packer/1.16-fabric.json b/config/packer/1.16-fabric.json index a3d490405549..37f42bb4f89a 100644 --- a/config/packer/1.16-fabric.json +++ b/config/packer/1.16-fabric.json @@ -13,7 +13,8 @@ "1.16.5 Fabric" ], "exclusionMods": [], - "exclusionNamespaces": [] + "exclusionNamespaces": [], + "fallbackVersions": [] }, "floating": { "inclusionDomains": [ diff --git a/config/packer/1.16.json b/config/packer/1.16.json index 757f2d21aae0..57c371b259c1 100644 --- a/config/packer/1.16.json +++ b/config/packer/1.16.json @@ -13,7 +13,8 @@ "1.16.5 Forge" ], "exclusionMods": [], - "exclusionNamespaces": [] + "exclusionNamespaces": [], + "fallbackVersions": [] }, "floating": { "inclusionDomains": [ diff --git a/config/packer/1.18-fabric.json b/config/packer/1.18-fabric.json index 9be63326d86b..706ebbe01d08 100644 --- a/config/packer/1.18-fabric.json +++ b/config/packer/1.18-fabric.json @@ -15,7 +15,8 @@ "exclusionMods": [], "exclusionNamespaces": [ "litematica" - ] + ], + "fallbackVersions": [] }, "floating": { "inclusionDomains": [ diff --git a/config/packer/1.18.json b/config/packer/1.18.json index 9b0f2a48d5f9..460a61dad52c 100644 --- a/config/packer/1.18.json +++ b/config/packer/1.18.json @@ -20,7 +20,8 @@ "thermal", "tinkers_things", "createaddition" - ] + ], + "fallbackVersions": [] }, "floating": { "inclusionDomains": [ diff --git a/config/packer/1.19.json b/config/packer/1.19.json index f635d966cf84..0ff8f16cc372 100644 --- a/config/packer/1.19.json +++ b/config/packer/1.19.json @@ -16,6 +16,9 @@ "exclusionNamespaces": [ "nochatreports", "illager_additions" + ], + "fallbackVersions": [ + "1.18" ] }, "floating": { diff --git a/config/packer/1.20-fabric.json b/config/packer/1.20-fabric.json index 89baf3ee1b9c..853e79aed46f 100644 --- a/config/packer/1.20-fabric.json +++ b/config/packer/1.20-fabric.json @@ -15,6 +15,10 @@ "exclusionMods": [], "exclusionNamespaces": [ "hexcasting" + ], + "fallbackVersions": [ + "1.19-fabric", + "1.18-fabric" ] }, "floating": { diff --git a/config/packer/1.20.json b/config/packer/1.20.json index 24bc9c260b7b..104241d3356a 100644 --- a/config/packer/1.20.json +++ b/config/packer/1.20.json @@ -20,6 +20,10 @@ "biomancy", "create-sound-of-steam", "occultism" + ], + "fallbackVersions": [ + "1.19", + "1.18" ] }, "floating": { diff --git a/config/packer/1.21-fabric.json b/config/packer/1.21-fabric.json index 05ebd581b1c0..1070516e38aa 100644 --- a/config/packer/1.21-fabric.json +++ b/config/packer/1.21-fabric.json @@ -13,7 +13,12 @@ "1.21 Fabric" ], "exclusionMods": [], - "exclusionNamespaces": [] + "exclusionNamespaces": [], + "fallbackVersions": [ + "1.20-fabric", + "1.19", + "1.18-fabric" + ] }, "floating": { "inclusionDomains": [ diff --git a/config/packer/1.21.json b/config/packer/1.21.json index 803946075a20..32e498512791 100644 --- a/config/packer/1.21.json +++ b/config/packer/1.21.json @@ -16,6 +16,11 @@ "exclusionNamespaces": [ "create-sound-of-steam", "replication" + ], + "fallbackVersions": [ + "1.20", + "1.19", + "1.18" ] }, "floating": { diff --git a/config/packer/26.1-fabric.json b/config/packer/26.1-fabric.json index 6e1f761f87bc..d53dcf8af84b 100644 --- a/config/packer/26.1-fabric.json +++ b/config/packer/26.1-fabric.json @@ -13,7 +13,12 @@ "26.1 Fabric" ], "exclusionMods": [], - "exclusionNamespaces": [] + "exclusionNamespaces": [], + "fallbackVersions": [ + "1.21-fabric", + "1.20-fabric", + "1.19" + ] }, "floating": { "inclusionDomains": [ diff --git a/config/packer/26.1.json b/config/packer/26.1.json index db587d944b86..2d7aa88eeae4 100644 --- a/config/packer/26.1.json +++ b/config/packer/26.1.json @@ -13,7 +13,12 @@ "26.1" ], "exclusionMods": [], - "exclusionNamespaces": [] + "exclusionNamespaces": [], + "fallbackVersions": [ + "1.21", + "1.20", + "1.19" + ] }, "floating": { "inclusionDomains": [ diff --git a/src/Packer/Helpers/EnumerationHelper.cs b/src/Packer/Helpers/EnumerationHelper.cs index c655063cb9a8..1736b21a19f6 100644 --- a/src/Packer/Helpers/EnumerationHelper.cs +++ b/src/Packer/Helpers/EnumerationHelper.cs @@ -9,7 +9,9 @@ namespace Packer.Helpers { internal static class EnumerationHelper { - public static IEnumerable EnumerateUnmerged(IEnumerable targetModIdentifiers, Config config, string version) + public static IEnumerable EnumerateUnmerged(IEnumerable targetModIdentifiers, + Config config, + IEnumerable acceptableVersions) { return // ./projects/assets/... @@ -19,7 +21,9 @@ where targetModIdentifiers.Count() == 0 // 未提供 || targetModIdentifiers.Contains(modIdentifier) // 有列表,仅打包列表中的项 where !config.Base.ExclusionMods.Contains(modIdentifier) // 没有被明确排除 // .../ - let versionedDirectory = modDirectory.GetDirectories(version).FirstOrDefault(defaultValue: null) + // 在此只选择可选版本中最新的一个,其他的不参与打包 + let versionedDirectory = acceptableVersions.Select(version => modDirectory.GetDirectories(version).FirstOrDefault()) + .FirstOrDefault(_ => _ is not null) where versionedDirectory is not null // .../-CFPA- from namespaceDirectory in versionedDirectory.EnumerateDirectories() diff --git a/src/Packer/Program.cs b/src/Packer/Program.cs index 751678250a14..5873afe58c59 100644 --- a/src/Packer/Program.cs +++ b/src/Packer/Program.cs @@ -15,7 +15,7 @@ namespace Packer class Program { // System.CommandLine.DragonFruit支持 - public static async Task Main(string version, bool increment = false) + public static async Task Main(string version, bool increment = false, bool grouped = false, bool flattened = false) { Log.Logger = new LoggerConfiguration() .Enrich.FromLogContext() @@ -30,10 +30,11 @@ public static async Task Main(string version, bool increment = false) var targetModIdentifiers = increment ? GitHelpers.EnumerateChangedMods(config.Base.Version) : Enumerable.Empty(); - var query = config.Base.FallbackVersions.Prepend(config.Base.Version) - .SelectMany(_ => EnumerationHelper.EnumerateUnmerged(targetModIdentifiers, config, _).MergeDeep()) - .MergeShallow() - .PostProcess(config); + var query = + EnumerationHelper.EnumerateUnmerged(targetModIdentifiers, config, config.Base.FallbackVersions.Prepend(config.Base.Version)) + .MergeDeep() + .PostProcess(config) + .ToArray(); // 好吧要打两种包导致的 IEnumerable initialFiles = [ @@ -47,22 +48,41 @@ public static async Task Main(string version, bool increment = false) [DateTime.UtcNow.AddHours(8), .. config.Base.McMetaParameters]) ]; - string packName = $"./Minecraft-Mod-Language-Modpack-{config.Base.Version}.zip"; - await using var stream = File.Create(packName); - - using (var archive = new ZipArchive(stream, ZipArchiveMode.Update, leaveOpen: true)) + if (flattened) { - await archive.WriteDirect(initialFiles); - // await archive.WriteDirect(query); - await archive.WriteGrouped(query); + string packName = $"./Minecraft-Mod-Language-Modpack-{version}.zip"; + Log.Information("直出资源包:{0}", packName); + await using var stream = File.Create(packName); + + using (var archive = new ZipArchive(stream, ZipArchiveMode.Update, leaveOpen: true)) + { + await archive.WriteDirect(initialFiles); + await archive.WriteDirect(query); + } + var md5 = stream.ComputeMD5(); + + Log.Information("打包文件的 MD5 值:{0}", md5); + File.WriteAllText($"./{version}.md5", md5); } - Log.Information("对版本 {0} 的打包结束。", version); + if (grouped) + { + string packName = $"./Minecraft-Mod-Language-Modpack-{config.Base.Version}-namespaced.zip"; + Log.Information("命名空间组合包:{0}", packName); + await using var stream = File.Create(packName); + + using (var archive = new ZipArchive(stream, ZipArchiveMode.Update, leaveOpen: true)) + { + await archive.WriteDirect(initialFiles); + await archive.WriteGrouped(query); + } + //var md5 = stream.ComputeMD5(); - var md5 = stream.ComputeMD5(); + //Log.Information("打包文件的 MD5 值:{0}", md5); + //File.WriteAllText($"./{config.Base.Version}.md5", md5); + } - Log.Information("打包文件的 MD5 值:{0}", md5); - File.WriteAllText($"./{config.Base.Version}.md5", md5); + Log.Information("对版本 {0} 的打包结束。", version); } } } From 2c1d6ab5a03845bd41a1e58240002f6eaecec533 Mon Sep 17 00:00:00 2001 From: dovisutu <40313014+dovisutu@users.noreply.github.com> Date: Sun, 7 Jun 2026 22:34:28 +0800 Subject: [PATCH 3/3] Revert fallback for old format --- src/Packer/Program.cs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Packer/Program.cs b/src/Packer/Program.cs index 5873afe58c59..1d9e99c2555b 100644 --- a/src/Packer/Program.cs +++ b/src/Packer/Program.cs @@ -30,11 +30,9 @@ public static async Task Main(string version, bool increment = false, bool group var targetModIdentifiers = increment ? GitHelpers.EnumerateChangedMods(config.Base.Version) : Enumerable.Empty(); - var query = - EnumerationHelper.EnumerateUnmerged(targetModIdentifiers, config, config.Base.FallbackVersions.Prepend(config.Base.Version)) - .MergeDeep() - .PostProcess(config) - .ToArray(); // 好吧要打两种包导致的 + + + IEnumerable initialFiles = [ @@ -52,6 +50,11 @@ public static async Task Main(string version, bool increment = false, bool group { string packName = $"./Minecraft-Mod-Language-Modpack-{version}.zip"; Log.Information("直出资源包:{0}", packName); + var query = + EnumerationHelper.EnumerateUnmerged(targetModIdentifiers, config, [config.Base.Version]) + .MergeDeep() + .PostProcess(config); + await using var stream = File.Create(packName); using (var archive = new ZipArchive(stream, ZipArchiveMode.Update, leaveOpen: true)) @@ -68,7 +71,12 @@ public static async Task Main(string version, bool increment = false, bool group if (grouped) { string packName = $"./Minecraft-Mod-Language-Modpack-{config.Base.Version}-namespaced.zip"; - Log.Information("命名空间组合包:{0}", packName); + Log.Information("组合包:{0}", packName); + var query = + EnumerationHelper.EnumerateUnmerged(targetModIdentifiers, config, config.Base.FallbackVersions.Prepend(config.Base.Version)) + .MergeDeep() + .PostProcess(config); + await using var stream = File.Create(packName); using (var archive = new ZipArchive(stream, ZipArchiveMode.Update, leaveOpen: true))