Files
BlueArchiveToolkit/docs/architecture/resource-release-layout.md
nyaKazuha 99355effe4
bat-rust / Build and test Rust (push) Canceled after 0s
bat-rust / Build and test Go API (push) Canceled after 0s
fix(release): 完成分发证明代际绑定与质量门禁收口
2026-09-15 21:13:52 +08:00

24 KiB
Raw Permalink Blame History

官方资源 Release 布局与资源侧契约

  • 更新时间2026-09-12
  • 用途:冻结日服官方资源在本地发布根上的布局、URL 映射、seed 规则、bat/bat-api 关系,以及 bat-api 分发 path 的 1:1 对应关系。
  • 范围:资源发现 / 清单 / 落盘 / 只读分发(不是完整游戏业务 API)。
  • 权威代码
    • URL / 平台 / seedadapters/src/official/yostar_jp.rs
    • inventory 抽取:adapters/src/official/inventory.rs
    • 落盘与 manifestinfrastructure/src/official_download.rsdestination_for_url
    • 发布布局:infrastructure/src/official_update.rs
    • 双 release 视图、分发选择与清理:infrastructure/src/release_ops.rs
    • 分发:cmd/bat-api + internal/api(见 docs/reports/GO_STATUS.md

1. 产品边界(资源侧)

角色 组件 职责
同步 / 运维(近乎全自动) Rust bat auto-discover、拉取、校验、发布、watch/daemon、RPC 后端
资源 bootstrap / 只读分发 Go bat-api 同环境经 bat.sock 读取 Rust 选择的已验证版本和 resource_root,提供 /v1/bootstrap、server-info 改写、官方/localized CDN path 字节和 release 管理转发
试验 CLI Go cmd/batbin/bat-go 非产品;禁止与 Rust bat 重名

禁止:把已安装客户端目录或 /home/wanye/D/BlueArchive 当作生产输入;真实全量样本优先服务器 release 或 /tmp 隔离目录。


2. 发布根布局(L1

<output>/                          # 官方原版资源发布根(--output / BAT_OUTPUT
  current -> versions/<id>         # 原子 symlink,生产读侧
  versions/<id>/                   # 已发布 versioned release= resource_root
    official-download-manifest.json
    official-distribution-publication.json # 独立发布事实:release、mapping、manifest identity、entry count
    official-parse-cache.json       # 校验后派生解析缓存,不是汉化产物
    official-textunit-index.json    # TextUnit 明细与解析错误索引,不是汉化产物
    official-textunit-tasks.json    # 翻译任务候选派生队列,不发 Crowdin 网络请求
    crowdin-textunit-queue.json     # Crowdin worker 离线输入队列
    official-sync-snapshot.json     # 常在 active root / current 下
    official-launcher-bootstrap.json # 官方 launcher 引导链版本化产物
    official-cas-reuse-references.json # 当前 release 获取的 CAS 引用和 ownership_id
    prod-clientpatch.bluearchiveyostar.com/
      <root_token>/
        TableBundles/
          TableCatalog.bytes
          TableCatalog.hash
          <table files...>           # e.g. ExcelDB.db, Excel.zip
        Windows_PatchPack/
          BundlePackingInfo.bytes
          BundlePackingInfo.hash
          catalog_StandaloneWindows64.zip
          catalog_StandaloneWindows64.hash
          FullPatch_NNN.zip
        Android_PatchPack/
          BundlePackingInfo.bytes
          BundlePackingInfo.hash
          catalog_Android.zip
          catalog_Android.hash
          FullPatch_NNN.zip
        MediaResources-Windows/
          Catalog/MediaCatalog.bytes
          Catalog/MediaCatalog.hash
          GameData/...
          Prologue/...
        MediaResources/              # Android
          Catalog/MediaCatalog.bytes
          Catalog/MediaCatalog.hash
          ...
    yostar-serverinfo.bluearchiveyostar.com/   # 若曾下载 server-info
      <name>.json
  .staging/<id>/                   # 未发布写侧(失败可复用)
  official-version-state.json      # 发布根级版本状态
  official-bootstrap-cache.json    # auto-discover 缓存
  official-launcher-bootstrap.pending.json # 维护期 launcher 已前进但资源未开放时的待处理证据

<localized-output>/                # 汉化产物发布根(--localized-output / BAT_LOCALIZED_OUTPUT
  current -> versions/<id>         # 已汉化后才切换;未汉化状态不发布
  versions/<id>/                   # 与官方相对路径一致的汉化资源
    localized-patch-manifest.json  # localized wrapper + generic PatchManifest 审计输入/结果
    localized-distribution-manifest.json # 实际 localized bytes/hash 的轻量分发索引
  .staging/<id>/                   # generic/translation patch 未发布写侧
  localized-version-state.json     # localized current、官方 source release 和 workflow 状态
  .localized-release.lock          # 跨进程单写者锁
  .localized-transaction.json      # publish/rollback 崩溃恢复日志

官方资源发布和汉化发布是两个独立状态:

  • not_localized:官方原版资源已经完成下载、校验和发布,汉化资源尚未发布;这是官方同步完成后的默认状态。
  • localized:同一官方版本的原版资源和汉化资源都已发布,生产侧可以同时提供两套资源。

2.1 双 release 读写边界

Rust batrelease.status 是 official/localized 的统一重型只读视图,基于既有 version state、current symlink、release manifest、文件系统和必要的 CAS/reference 元数据计算,不建立第二个 release 数据库。release.list 返回两个 namespace 的当前 与历史 release,包含稳定 ID、created/published、source official relation、生命周期、 rollback_available、manifest contract、artifact/distribution integrity、 stale/damaged/referenced/unknown、rollback previous 和诊断;缺少 generic manifest 的 旧 localized release 保留为 legacy/unknown,不自动改写。

release.distribution 的默认 channel 是 official。只有当前或显式历史、路径归属安全、 source relation 正确且 manifest/artifact integrity 通过的 release 才能被选择;staging、 损坏、缺失、symlink/path escape 或未验证历史项不会回退到另一 channel。Rust 使用已发布 manifest 做轻量选择,HTTP 热路径不重新执行完整 release auditlocalized 必须额外满足 localized-distribution-manifest.json 与 source official manifest 的 destination/URL 集合及 deterministic source mapping identity 一致,并返回实际 localized bytes/hash。 manifest 同时保存 localized mapping identity 和 destination index;返回的 resource_root 和 manifest entry 由 Rust 决定,Go 只做 typed forwarding;传入 destination 时 Rust 会重新校验该文件的 实际 bytes/BLAKE3。单条请求只比较发布时持久化的 identity、通过 destination index 定位 entry,不重新遍历全量映射或资源文件。

localized publish/rollback 先取得 .localized-release.lock,并在 output root 下记录 .localized-transaction.json。current、version-state 和 version 目录的切换按日志阶段 推进;publish 只有最终 verified phase 才能 roll-forward,下一次写操作会先恢复或完成 未决事务,避免跨进程并发写入和中断后留下半发布状态。release.cleanup execute 先取得 同一 official .official-sync.lock,再按固定顺序取得 localized 锁并在持锁状态下重建 计划;dry-run 不占用 official mutation lock。

release.cleanup 先生成 dry-run 计划和 plan_id,执行时重新计算并比对计划。current、 rollback previous、active/in-progress、localized source official、state/manifest/CAS reference、无法确认 ownership 的对象均保留;只删除重新验证后仍为普通目录且确定无引用的 历史 release。它不改变 current,不执行 rollback,也不负责自动 repairstaging 默认保留 以避免删除未持久化任务。

2.2 读侧 vs 写侧

阶段 根目录
下载写入 <output>/.staging/<id>
发布完成 rename 到 versions/<id>,再切换 current
生产读取 / bat-api RPC 给出的已验证 resource_root;默认等价于 official current 解析后的 versioned 目录,localized 必须显式选择

每个 release 的 official-download-manifest.json 是历史复用的索引。新 staging 按规范化 destination 查找候选,并重新验证 manifest 中的 size、BLAKE3 和 ZIP 结构;URL、CDN 根或 release ID 变化本身不构成失效条件。复用文件先尝试硬链接, 跨文件系统时复制到 staging 内的临时文件并原子 rename,旧 versions/<id> 目录 保持不可变。

新 official release 在完整下载、manifest、文件和 ZIP 校验完成后,才会在 versioned 目录中原子写入 official-distribution-publication.json。该文件独立记录 official_release_id、完整 distribution mapping identity、manifest content identity manifest 文件 BLAKE3)和 entry_count。普通 manifest 读写、release status 查询和 release.distribution 不会重建或刷新它;如果 manifest 在发布后变化、publication 文件缺失或两者 identity 不一致,该 release 的 distribution_integrity_status 不是 valid,不能被 distribution 读侧选择。没有该文件的历史 release 仍可被状态/清理逻辑 识别为 legacy,但不会被当作 distribution-ready。

从 CAS 物化资源时,official-cas-reuse-references.json 首次创建时生成并持久化 ownership_id,记录每个获取的对象引用;文件带版本字段且允许重复 object ID。 没有 ownership_id 的旧清单首次 cleanup 按 output-root scope、release ID、稳定 source mapping identity 和 generation counter 建立 persistent legacy generation identity 若已存在 basename ledger,则在该 generation 完成前继续使用 basename compatibility key 完成后同名新 generation 使用新的 ownership。.cas-owner-scope 是 output-root 私有状态, 不会复制到另一个 release 或 staging。 孤儿 staging 或显式 release 清理必须先按 清单减少 CAS 引用,再删除目录;cleanup execute 与官方同步共用 .official-sync.locklocalized cleanup 使用 .localized-release.lock。CAS 对象损坏、缺失或元数据不一致时只产生诊断, 回退网络下载,不发布未经校验的文件。


3. URL → 磁盘映射(核心不变量)

实现:OfficialResourcePullService::destination_for_url

https://{host}/{path...}  →  <resource_root>/{host}/{path...}

规则:

  1. https://
  2. host 必须是官方 JP 资源 host(见下节)
  3. path 分段不得为 . / ..
  4. 禁止 query / fragment(否则直接拒绝,避免同路径覆盖)
  5. 分段经 sanitize 后 join;结果必须在 resource_root

3.1 官方 host

Host 用途
prod-clientpatch.bluearchiveyostar.com Addressables / Table / Media / PatchPack 内容
yostar-serverinfo.bluearchiveyostar.com server-info JSON

launcher 包 CDN 属于启动器链,不是默认资源 release 主体。Rust bat 会把启动器链中与资源发现相关的 launcher metadata、CDN config、remote manifest 文件列表、选中的 resources.assets 来源和 GameMainConfig 摘要写入 official-launcher-bootstrap.json,供后续 bat-api / 自研客户端在 Rust 侧完成前继续以 versioned release 为权威来源。)

3.2 bat-api 对外 path1:1

GET {public-base-url}/prod-clientpatch.bluearchiveyostar.com/<root_token>/...
    ≡ 磁盘 <resource_root>/prod-clientpatch.bluearchiveyostar.com/<root_token>/...

默认仅服务 official download manifest 索引内且 Present + size 匹配 的文件。需要 localized 或历史 release 时,调用 release.distribution 选择 Rust 已验证的 resource_root,再由 /v1/distribution 或带 channel/release_id 的 CDN path 转发;localized CDN 使用 Rust 返回的实际 bytes/hash 生成 ETag,并在显式请求时校验 实际文件长度;Go 不在本地判断健康度,也不回退到 official。

3.3 launcher 资源引导兼容

只读分析本机样本时可见两类启动器形态:

目录形态 说明
AllResources/YostarGames/BlueArchive_JP_Gamelauncher 官方 Electron 启动器目录
Localized / Localized_Official 汉化或改造启动器目录
AllResources/YostarGames/BlueArchive_JP 官方安装后的游戏客户端目录,含 game-launcher-config.jsonmanifest.jsonBlueArchive_Data/StreamingAssets/catalog_Remote.*

这些目录只作为开发期样本;生产链路不得依赖 /home/wanye/D/BlueArchive 或任何已安装客户端目录。

官方启动器样本中与资源发现相关的 HTTP path:

Host / path 资源侧意义
https://api-launcher-jp.yo-star.com/api/launcher/game/config 返回 launcher 观察到的最新客户端版本和包路径
https://api-launcher-jp.yo-star.com/api/launcher/game/config/json?version=...&file_path=... 返回远端 package manifest URL
https://api-launcher-jp.yo-star.com/api/launcher/advanced/game/download/cdn 返回 launcher package CDN primary / backup

bat-api 的兼容范围是资源引导,不是完整启动器更新服务:

bat-api path 行为
/v1/launcher/bootstrap 返回资源引导聚合视图:已发布 release、launcher metadata、GameMainConfig 摘要、server-info URL、client-patch base、改写后的 Addressables root
/api/launcher/game/config 返回 {code,message,data} envelope,字段来自 Rust bat snapshot/RPC 中的 launcher_metadata,并附带 resource_bootstrap_url
/api/launcher/game/config/json 返回指向 /api-launcher-jp.yo-star.com/api/launcher/resource/bootstrap.json 的资源引导 JSON URL,显式标记 package_update_manifest=false
/api/launcher/advanced/game/download/cdn 返回 public-base-url 作为资源引导 CDN 根,显式标记 package_update_manifest=false
/api-launcher-jp.yo-star.com/... 与上面裸 path 等价,便于反向代理或 hosts 映射保持官方 host 形状

数据来源只能是 Rust bat 已发布状态;当前 Go bat-api 仍主要消费 snapshot/RPC 摘要,后续字段统一与联调时应把 versioned launcher artifact 纳入 contract fixture

  1. catalog.status / official-sync-snapshot.json 中的 launcher_metadata
  2. catalog.status / official-sync-snapshot.json 中的 game_main_config_bootstrap
  3. official-launcher-bootstrap.json 中的官方 launcher bootstrap versioned artifact。
  4. resource.manifest 和磁盘 Present/size 检查得到的当前 release 索引。

bat-api 不下载 launcher 包、不生成官方 PC package update manifest、不执行启动器签名/鉴权链、不仿造登录、账号、网关或游戏业务 API。需要真实资源拉取时,仍由 Rust bat --auto-discover 在隔离 staging 中通过官方 HTTP metadata 完成,并把已发布结果通过 RPC 暴露给 bat-api


4. official-download-manifest.json

字段 类型 说明
version u32 当前为 1
entries map URL → entry 按完整官方 URL 为键(有序 BTreeMap

每条 entry

字段 说明
url 官方 https URL
destination 相对 resource_root 的路径(host/path...
bytes 文件大小
blake3 本地 BLAKE3 hex

manifest 还持久化以下 distribution 查询字段:

字段 类型 说明
distribution_mapping_identity string 完整 URL、destination、size、BLAKE3 映射的确定性 identity
destination_index map destination → URL 单 destination 分发的轻量定位索引

权威清单:拉取闭环写入的 manifestbat-api / RPC resource.manifest 以此为应有集合,再以磁盘校验 Present。

official-distribution-publication.json 是发布事实而不是 manifest 派生缓存。单条 distribution 查询只读取该文件、当前 manifest 的内容 identity 和 destination_index,再校验目标文件的 size/BLAKE3;不会为了定位一个 destination 重做完整 mapping canonicalization 或遍历其他资源。

official-distribution-attestation.json 是 Rust full local verification 的结果。发布时 文件先写入 .staging/<id>,但其中的 resource_root 永远记录最终的 versions/<id> canonical root;随后 staging 目录原子重命名并切换 current,不会因为 重命名再次增加 verification generation。周期性 current 验证、显式 verify/repair 和新 release 发布都会写入新的 generation;失败会写入 ready=falseintegrity_status=invalidverified_at=null 的新结果。max_age_seconds 由 Rust watch 的验证周期和失败重试 周期计算,缺失或为 0 的旧结果直接视为不可用,不使用固定兼容 fallback。


5. 发现与 seed URL 规则(L2

常量根:

  • server-infohttps://yostar-serverinfo.bluearchiveyostar.com
  • client-patchhttps://prod-clientpatch.bluearchiveyostar.com

AddressablesCatalogUrlRoot 形如:

https://prod-clientpatch.bluearchiveyostar.com/<root_token>

默认平台:Windows + Android

5.1 平台目录名

平台 Patch 目录 Media 目录 Addressables catalog zip
Windows Windows_PatchPack MediaResources-Windows catalog_StandaloneWindows64.zip
Android Android_PatchPack MediaResources catalog_Android.zip

5.2 Seed 端点模板

共享(非平台):

{CLIENT_PATCH}/{token}/TableBundles/TableCatalog.bytes
{CLIENT_PATCH}/{token}/TableBundles/TableCatalog.hash

每平台:

{CLIENT_PATCH}/{token}/{PatchDir}/BundlePackingInfo.bytes
{CLIENT_PATCH}/{token}/{PatchDir}/BundlePackingInfo.hash
{CLIENT_PATCH}/{token}/{PatchDir}/{catalog_zip}
{CLIENT_PATCH}/{token}/{PatchDir}/{catalog_base}.hash
{CLIENT_PATCH}/{token}/{MediaDir}/Catalog/MediaCatalog.bytes
{CLIENT_PATCH}/{token}/{MediaDir}/Catalog/MediaCatalog.hash

5.3 Content URL 模板

类型 模板
Table 文件 {CLIENT_PATCH}/{token}/TableBundles/{Name}
Patch pack {CLIENT_PATCH}/{token}/{PatchDir}/{FullPatch_NNN.zip}
Media 文件 {CLIENT_PATCH}/{token}/{MediaDir}/{relative_path}

relative_path 示例:GameData/Audio/VOC_JP/JP_Airi.zipPrologue/Scenario/Event/10000_Title_Sound.ogg

5.4 校验分层

对象 算法 / 规则
seed .bytes + .hash 官方 .hashxxHash32(seed=0) 的十进制文本;强校验
一般已下载文件 本地 manifest size + BLAKE3
.zip 另加 ZIP central/local 结构校验
catalog_*.hash Addressables/SBP Hash128 文本标记不是 seed 的 xxHash32 规则

6. Inventory 抽取规则(L3,当前实现)

实现:adapters/src/official/inventory.rs可打印串启发式,非完整 schema 反序列化)。

Catalog 抽取逻辑 风险
BundlePackingInfo.bytes 可打印串中扩展名为 zip 且匹配 FullPatch_NNN.zip(总长 17,中间 3 位数字) 漏抽非 FullPatch 包名(当前有意只 FullPatch
TableCatalog.bytes 可打印串中 .db/.zip 文件名;出现次数 ≥ 2 才收录 依赖「双份列表」启发式;形态变化会漏/多
MediaCatalog.bytes 可打印串中相对路径,扩展名 zip/mp4/png/jpg/jpeg/ogg/wav 路径须 is_plausible_relative_path

R2 待真机核对:用服务器全量 seed 字节跑抽取,与 manifest 中 content URL 集合 diff;有未解释差异再改 inventory + fixture。

仓库内已有:adapters/tests/fixturesinfrastructure/tests/fixtures/official_regression不能替代全量 release 实勘。


7. 客户端资源请求假设(R3,服务 bat-api)

假设(当前工程) bat-api 行为
启动前资源发现 客户端/补丁器需要知道当前资源版本、server-info 和 client-patch 根 GET /v1/bootstrap 返回 bat RPC 健康、release 摘要、server-info URL、client-patch base 和改写后的 Addressables root
服务就绪 运维需要区分进程存活和 release 是否可分发 GET /healthz 返回 liveness + RPC refresh 诊断;GET/HEAD /readyz 无可分发 release 时返回 503
client-patch 内容 GET 官方 path;无业务鉴权头(资源 CDN) GET/HEAD /prod-clientpatch.../... 原样字节,支持 Range
server-info GET JSON;字段 PascalCaseConnectionGroups 等) 可选加载并只改 AddressablesCatalogUrlRoot 指向 {public-base}/prod-clientpatch.../{token}
launcher bootstrap 启动器链会先查 launcher metadata,再找到 server-info / Addressables root /v1/launcher/bootstrap/api/launcher/... 只输出资源引导兼容信息,来源是 Rust snapshot/RPC
seed .hash 纯文本十进制(可含空白) 原样分发
Range / 断点 官方客户端下载器用 Rangebat 用 curl .part ServeContent 支持 Range / 206 / 416 / If-Range
缓存 / 条件请求 资源位于 versioned rootmanifest 有 BLAKE3 ETag 优先使用 manifest BLAKE3;返回 Last-Modified、Accept-Ranges、长期 Cache-Control
业务 ApiUrl/Gateway 游戏协议 不改写、不仿造

Addressables 改写后客户端拼接:

{rewritten_root}/TableBundles/TableCatalog.bytes
≡ {public-base}/prod-clientpatch.../{token}/TableBundles/TableCatalog.bytes

与磁盘映射一致。


8. RPC 与分发发现顺序

bat-api(及任何 Go 服务层)发现当前 release,并由 /v1/bootstrap 组织为启动前资源入口:

  1. daemon.status
  2. daemon.doctor
  3. release.attestation,消费 Rust 当前 official 的 ready、release/publication/ manifest identity、verification generation、freshness 和 integrity 事实
  4. catalog.statusversion.resource_rootaddressables_root、app/bundle
  5. resource.manifest 分页(请求携带 release/publication/manifest identity;响应每页 返回同一组 identity、generation、total、offset、limit
  6. resource_root 上 Lstat 校验 Present / size;该检查只验证 Go 读快照, 不替代 Rust release verifier

不读 bat-status.json / bat-tasks.json 作为常规路径。

生产配置:--socket / BAT_API_SOCKETbat-apibat 在同服务器、同容器或同共享文件系统环境内运行。--resource-root 只用于本地 fixture 或应急只读诊断,不作为生产资源根配置。BAT_API_REFRESH_INTERVAL 控制 bat-api 周期重读 RPC,以跟随 Rust bat 发布新 release。见 cmd/bat-api/.env.example


9. 真实资源样本索引

在服务器 release 上优先采集到 /tmp 隔离目录(不入库大文件):

用途 建议路径模式
Addressables {PatchDir}/catalog_*.zip 解压后的 JSON/bin + 旁路 .hash
UnityFS FullPatch_*.zip 内抽样 .bundle,或已解包 bundle
seed 加固(R2 各平台 TableCatalog / BundlePackingInfo / MediaCatalog.bytes+.hash

字段目标(已有 m_Crc 部分):继续扩大 hash/size/CRC/依赖等可校验字段覆盖。 结构目标:header / block / directory / metadata / object table 引擎级解析。


10. 服务器实勘清单(R1,等 SSH)

连接信息到位后只读执行:

  1. readlink current → version id
  2. 顶层是否仅有官方 host 目录 + manifest/snapshot
  3. manifest 条目数 vs 磁盘抽样 size
  4. RPC 四步(status → doctor → catalog.status → manifest 首页)
  5. 将结论写入 docs/reports/resource-server-survey-YYYYMMDD.md(无凭据)

所需:

SSH: user@host -p PORT
资源目录: .../official
bat.sock 或 state-dir: ...

11. 相关文档

  • docs/reports/GO_STATUS.md — Go 边界与进度
  • docs/architecture/official-resource-backend.md — 拉取后端总览
  • docs/reference/rpc-backend-api.md — RPC 契约
  • docs/guides/official-resource-test-pull.md — 用户向运行说明
  • docs/reports/CURRENT_GAPS.md — G-005 / G-007 / G-009

12. 变更纪律

  1. 改 URL 模板或落盘规则 → 必须同步本文 + 相关单测。
  2. 改 inventory 启发式 → 说明覆盖的真实风险并补 fixture。
  3. 真机实勘若发现与本文冲突 → 以真机为准 修代码与本文,禁止静默分叉。