21 KiB
官方资源 Release 布局与资源侧契约
- 更新时间:2026-09-12
- 用途:冻结日服官方资源在本地发布根上的布局、URL 映射、seed 规则、
bat/bat-api关系,以及bat-api分发 path 的 1:1 对应关系。 - 范围:资源发现 / 清单 / 落盘 / 只读分发(不是完整游戏业务 API)。
- 权威代码:
- URL / 平台 / seed:
adapters/src/official/yostar_jp.rs - inventory 抽取:
adapters/src/official/inventory.rs - 落盘与 manifest:
infrastructure/src/official_download.rs(destination_for_url) - 发布布局:
infrastructure/src/official_update.rs - 双 release 视图、分发选择与清理:
infrastructure/src/release_ops.rs - 分发:
cmd/bat-api+internal/api(见docs/reports/GO_STATUS.md)
- URL / 平台 / seed:
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/bat → bin/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-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 bat 的 release.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 audit;localized 必须额外满足
localized-distribution-manifest.json 与 source official manifest 的 destination/URL
集合一致,并返回实际 localized bytes/hash。返回的 resource_root 和 manifest entry
由 Rust 决定,Go 只做 typed forwarding;传入 destination 时 Rust 会重新校验该文件的
实际 bytes/BLAKE3。
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,也不负责自动 repair;staging 默认保留
以避免删除未持久化任务。
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> 目录
保持不可变。
从 CAS 物化资源时,official-cas-reuse-references.json 首次创建时生成并持久化
ownership_id,记录每个获取的对象引用;文件带版本字段且允许重复 object ID。
没有 ownership_id 的旧清单保留 legacy ownership key,不在读取时随机迁移。
孤儿 staging 或显式 release 清理必须先按
清单减少 CAS 引用,再删除目录;cleanup execute 与官方同步共用
.official-sync.lock,localized cleanup 使用 .localized-release.lock。CAS 对象损坏、缺失或元数据不一致时只产生诊断,
回退网络下载,不发布未经校验的文件。
3. URL → 磁盘映射(核心不变量)
实现:OfficialResourcePullService::destination_for_url。
https://{host}/{path...} → <resource_root>/{host}/{path...}
规则:
- 仅
https:// - host 必须是官方 JP 资源 host(见下节)
- path 分段不得为
./.. - 禁止 query / fragment(否则直接拒绝,避免同路径覆盖)
- 分段经 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 对外 path(1: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.json、manifest.json 和 BlueArchive_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:
catalog.status/official-sync-snapshot.json中的launcher_metadata。catalog.status/official-sync-snapshot.json中的game_main_config_bootstrap。official-launcher-bootstrap.json中的官方 launcher bootstrap versioned artifact。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;bat-api / RPC resource.manifest 以此为应有集合,再以磁盘校验 Present。
5. 发现与 seed URL 规则(L2)
常量根:
- server-info:
https://yostar-serverinfo.bluearchiveyostar.com - client-patch:
https://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.zip、Prologue/Scenario/Event/10000_Title_Sound.ogg。
5.4 校验分层
| 对象 | 算法 / 规则 |
|---|---|
seed .bytes + .hash |
官方 .hash 为 xxHash32(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/fixtures、infrastructure/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;字段 PascalCase(ConnectionGroups 等) |
可选加载并只改 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 / 断点 | 官方客户端下载器用 Range;bat 用 curl .part |
ServeContent 支持 Range / 206 / 416 / If-Range |
| 缓存 / 条件请求 | 资源位于 versioned root;manifest 有 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 组织为启动前资源入口:
daemon.statusdaemon.doctorcatalog.status(version.resource_root、addressables_root、app/bundle)resource.manifest分页(url / destination / bytes / blake3)- 在
resource_root上 Lstat 校验 Present / size
不读 bat-status.json / bat-tasks.json 作为常规路径。
生产配置:--socket / BAT_API_SOCKET,bat-api 与 bat 在同服务器、同容器或同共享文件系统环境内运行。--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)
连接信息到位后只读执行:
readlink current→ version id- 顶层是否仅有官方 host 目录 + manifest/snapshot
- manifest 条目数 vs 磁盘抽样 size
- RPC 四步(status → doctor → catalog.status → manifest 首页)
- 将结论写入
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. 变更纪律
- 改 URL 模板或落盘规则 → 必须同步本文 + 相关单测。
- 改 inventory 启发式 → 说明覆盖的真实风险并补 fixture。
- 真机实勘若发现与本文冲突 → 以真机为准 修代码与本文,禁止静默分叉。