Skip to content

Repository files navigation

红警3战网元数据

本仓库是战网客户端的元数据仓库,承担两件事:数据仓库(作者提交源树,CI 构建后发布静态文件)+ 解析库(Ra3.BattleNet.Metadata,客户端经 URL/本地入口读取发布物)。

发布物

静态文件树,不是 RPC:

  • {BaseUrl}/metadata.xml —— 展平后的业务数据(无 Include)
  • {BaseUrl}/{相对路径} —— 资源(图片/Markdown/叶子 Manifest),路径来自登记节点的 Source
  • 根属性:SchemaVersion(契约版本)、ContentRevision(内容修订,用于缓存判断)

使用方如何解析(Desktop 视角)

  1. 配置 BaseUrl(生产 = CDN;调试 = 本地缓存目录)。
  2. MetadataBuilder.Load(url|path) → 列出实体、接 LINQ 组合(见下表)。
  3. SchemaVersion 不兼容 → 提示升级客户端(MetadataSchema.IsCompatible);ContentRevision 变化 → 整树刷新。
  4. Package.Manifest / Icon / Changelog / Post.Content 等一律是限定 ID(路径前缀:localId),在展平树中找同 ID 的登记节点——别自己走树,用库的导航方法。
  5. 资源 = BaseUrl + 登记节点 Source(MetadataResourceUri.Resolve);图片扩展名以 Source 为准(发布后可能是 .webp)。
  6. 叶子 Manifest(含 File 表)用 Source 再拉一次并解析——File 表不在 metadata.xml 内;挂载顺序交给 ManifestMountPlanner。

开发调试 = 同一套解析:MetadataBuilder.Build(本地源仓, 缓存目录) → Load(cache/metadata.xml)。

// 生产:URL;开发:本地展平文件
var doc = MetadataBuilder.Load("https://metadata.ra3battle.net/metadata.xml");

// 列出实体:延迟序列,直接接 LINQ
foreach (var mod in doc.Mods().Where(m => m.Version is not null).OrderBy(m => m.Id))
{
    Console.WriteLine($"{mod.Id}@{mod.Version}");
}
var app = doc.Catalog().Application("RA3BattleNet");   // 按 ID 查,大小写不敏感

// 导航:条目 → 叶子清单地址 → 叶子清单
var corona = doc.Catalog().Mod("Corona")!;
var leafUrl = MetadataResourceUri.Resolve("https://metadata.ra3battle.net/metadata.xml", corona.ManifestSource());
var manifest = MetadataBuilder.Load(leafUrl.AbsoluteUri).Find("Manifest")!.ToManifestEntry();

// 导航:应用更新日志的相对 Source(找不到该语言返回 null)
var changelog = app!.ChangelogSource("zh-CN");

// 挂载计划:写了 Skudef 按声明顺序与条件,没写按旧 File@Mount 角色
var plan = ManifestMountPlanner.Build(
    manifest.Skudef,
    manifest.Files.Select(f => new ManifestMountFile(f.FileName, f.Mount, f.Language, f.Package)).ToList(),
    language: "en",
    packages: ["hd-shadow"],
    localConfigExists: _ => false);

查询与导航 API

API 作用
root.Mods() / Applications() / Markdowns() / Images() 列出全部实体/资源;延迟求值,可接 Where/OrderBy/Select/First
root.Catalog() 目录入口:Mods/Applications/Markdowns/Images 属性 + Mod(id) / Application(id)(大小写不敏感)
root.GetAllElements(name) 按节点名遍历展平树(延迟)
root.ManifestRegistration(id) 按限定 ID 找 Manifest 登记节点;不存在返回 null
ModEntry.Package(version) / ManifestSource(version) 取版本包(缺省取当前版本)/ 取叶子清单相对 Source;缺版本、缺 Manifest、缺 Source 都抛,消息带原因
ApplicationEntry.Package(version) / ChangelogSource(language) 取版本包 / 取更新日志相对 Source;没配该语言返回 null
node.ToManifestEntry() 叶子清单节点 → ManifestEntry(Files / Dependencies / Skudef)
MetadataSchema.Current / IsCompatible(version) 契约版本常量与兼容判断
MetadataResourceUri.Resolve(baseUrl, source) 相对 Source → 绝对地址
ManifestMountPlanner.Build(skudef, files, language, packages, localConfigExists) Skudef 声明或 File@Mount 角色 → 有序挂载计划 ManifestMountCommand(Big / Config)

核心构建(作者/CI/Desktop 调试):

MetadataBuilder.Build(sourceDir, outputDir, schemaVersion: "1.0", contentRevision: gitSha);

主库无 SkiaSharp;WebP 仅发布期 Ra3.BattleNet.Metadata.Imaging。

校验与失败

核心构建硬失败(非 0、清理半残输出):源树 XSD、发布物 XSD、循环 Include、缺资源、断 ID、残留 ${...}、新格式清单校验。

文件级清单(叶子 Manifest)

叶子 Manifest 的 File 表可声明下载来源与哈希算法(新格式);未声明新格式的旧清单照旧可用。

成员 位置 说明
Manifest@HashAlgorithm 属性(可选) CRC32C / MD5 / SHA256,缺省 CRC32C
File@Hash 属性(必填) 解压后安装文件(FileName)的哈希,算法由 Manifest@HashAlgorithm 决定
File@Size 属性(可选) 下载物的字节数,正整数
File@DownloadName 属性(可选) 下载物的服务端文件名;缺省与 FileName 相同
File@Compression 属性(可选) 下载物的压缩格式,目前只有 zstd;校验通过后解压成 FileName
Sources/Source 子元素(可选) @Type(HTTP / BT)+ @Url;HTTP 必须 http/https 绝对地址,BT 必须以 .torrent 结尾
Dependencies/Dll 子元素(可选) @Name、@Hash 必填,@Version、@KindOf 可选
Skudef 子元素(可选) 客户端 skudef 的声明,必须写在 File 之前;@GameVersion 缺省 1.12
Skudef/AddBig 子元素(Skudef 内) @File(必填,引用同清单的 FileName)配可选条件 @Language 或 @Package(只能写一个);不写条件就是总是挂
Skudef/AddConfig 子元素(Skudef 内) @LocalFile(必填,模组目录下的纯文件名)、@Optional(缺省 false:客户端找不到该文件就报错)

Skudef 是「写什么、按什么顺序」的唯一来源:客户端按子元素的文档顺序生成 add-big / add-config 行, @Language 与客户端语言设置一致、@Package 在客户端开关里才挂。写了 Skudef 的清单,File 上不许再写 Mount / Language / Package, 且每个 File 必须被恰好一条 AddBig 引用。

File@Hash 描述的是解压后的安装文件(FileName)的 CRC32C;File@Size 描述的是下载物(DownloadName 对应的字节): 服务端只放压缩包(如 corona_3.258.zst),安装名是解压结果(corona_3.258.lyi),两者用 DownloadName + Compression 表达。客户端下载后先解压,再按 @Hash 校验解压结果。

不压缩直发的文件(如启动器资源 Disabler.big)不写 DownloadName 与 Compression,此时 @Size 与 @Hash 描述的就是 FileName 本身。

声明了新格式(带 HashAlgorithm、写了 Skudef,或任一 File 含 Sources)的清单,构建期额外硬校验:哈希长度与算法匹配、禁止占位哈希、每个 File 至少一个 Source、Size 为正整数、FileName + RelativePath 唯一、Dll@Name 唯一、RelativePath 必须是相对路径、DownloadName 必须是不含路径分隔符的文件名、Compression 只能是 zstd 且必须配 DownloadName(且与 FileName 不同名)。旧清单不触发。

写了 Skudef 的清单再多查一层:GameVersion 形如 1.12、至少一条指令、FileName 全表唯一、每条 AddBig@File 指向存在的 FileName 且只指一次、所有 File 都被引用到、Language / Package 只能写一个且只含字母数字下划线连字符、AddConfig@LocalFile 必须是纯文件名、File 上不能再写 Mount / Language / Package。没写 Skudef 的旧清单继续用 File@Mount(base / language / optional)那套角色标记,规则不变。

解析:doc.ManifestRegistration(id) 取登记节点,叶子清单节点 ToManifestEntry() → ManifestEntry(Files / Dependencies / Skudef)。

模块属性清单

成员 位置 说明
SchemaVersion / ContentRevision 根属性 契约版本 / 构建注入修订
Application 子元素 @ID、Version、Packages、Posts
Mod 子元素 @ID、CurrentVersion、Icon(Image ID)、DisplayName(@Language + 文本,可多条)、Style、Packages、Posts
Package 子元素 @Version、ReleaseDate、Changelogs(@Language+Markdown ID)、Manifest(ID)
Post 子元素 @DateTime、Titles/Contents(@Language + Markdown ID)
Image / Markdown / Manifest 登记节点 @ID(限定 ID)、@Source(相对路径);Image 可 @Url 外链;Manifest 为 stub

Style(Logo/Controls/Background)与继承(Base/InheritFrom)细节:见 Metadata/MetadataSchema.xsd 与 AGENTS.md。 源树中登记 ID 一律短名,展平时自动限定为 {路径前缀}:{localId};实体 ID 全局唯一,冲突即构建失败。

构建与发布

npm run build           # 核心构建(展平 + 校验)→ ./Output
npm run build:release   # 核心 + WebP(发布)
npm run deploy          # 构建 + Cloudflare Pages 部署
  • 核心构建(纯 managed):XSD → 展平 → 变量 → 复制被引用资源 → 语义校验;Output/ 只含 metadata.xml + 被引用资源 + _redirects
  • 叶子清单:Manifest 登记节点的 Source 文件与入口一样走展平(去掉 Includes、限定 ID、替换变量)后发布,客户端 MetadataBuilder.Load 可直接解析;图片与 Markdown 原样复制
  • Imaging:只转图并回传 MD5,不进主 NuGet;--webp 时展平后按图调用
  • Schema:Metadata/MetadataSchema.xsd(源树)、Metadata/MetadataPublishSchema.xsd(发布物)
  • 变量:${TIMESTAMP} / ${ENV:NAME} / ${MD5:}

测试

  • MSTest only(禁止 xunit);dotnet test Metadata.sln
  • 覆盖:展平、XSD 硬失败、ID/资源、防冲突、发布面、消费端 Catalog 解析、查询/导航 API、挂载计划(Skudef 与旧 Mount 角色)

目录

Metadata/                         源数据 + XSD
Ra3.BattleNet.Metadata/           纯 managed 库 + 核心 CLI
Ra3.BattleNet.Metadata.Imaging/   发布 WebP
Ra3.BattleNet.Metadata.Tests/     MSTest
AGENTS.md                         Agent/贡献者/数据作者指引

About

红警3战网元数据文件

Resources

Stars

0 stars

Watchers

2 watching

Forks

Used by

Contributors

Languages