mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-09-18 07:24:55 +08:00
372 lines
17 KiB
Markdown
372 lines
17 KiB
Markdown
# 官方资源 Release 布局与资源侧契约
|
||
|
||
- **更新时间**:2026-07-27
|
||
- **用途**:冻结日服官方资源在本地发布根上的布局、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`
|
||
- 分发:`cmd/bat-api` + `internal/api`(见 `docs/reports/GO_STATUS.md`)
|
||
|
||
---
|
||
|
||
## 1. 产品边界(资源侧)
|
||
|
||
| 角色 | 组件 | 职责 |
|
||
|---|---|---|
|
||
| 同步 / 运维(近乎全自动) | Rust `bat` | auto-discover、拉取、校验、发布、watch/daemon、RPC 后端 |
|
||
| 资源 bootstrap / 只读分发 | Go `bat-api` | 同环境经 `bat.sock` 发现已发布版本和 `resource_root`,提供 `/v1/bootstrap`、server-info 改写和官方 CDN path 字节 |
|
||
| 试验 CLI | Go `cmd/bat` → `bin/bat-go` | 非产品;禁止与 Rust `bat` 重名 |
|
||
|
||
**禁止**:把已安装客户端目录或 `/home/wanye/D/BlueArchive` 当作生产输入;真实全量样本优先服务器 release 或 `/tmp` 隔离目录。
|
||
|
||
---
|
||
|
||
## 2. 发布根布局(L1)
|
||
|
||
```text
|
||
<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 引用
|
||
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-version-state.json # 预留:后续 Patch 发布阶段维护,官方同步阶段不写入
|
||
```
|
||
|
||
官方资源发布和汉化发布是两个独立状态:
|
||
|
||
- `not_localized`:官方原版资源已经完成下载、校验和发布,汉化资源尚未发布;这是官方同步完成后的默认状态。
|
||
- `localized`:同一官方版本的原版资源和汉化资源都已发布,生产侧可以同时提供两套资源。
|
||
|
||
### 2.1 读侧 vs 写侧
|
||
|
||
| 阶段 | 根目录 |
|
||
|---|---|
|
||
| 下载写入 | `<output>/.staging/<id>` |
|
||
| 发布完成 | rename 到 `versions/<id>`,再切换 `current` |
|
||
| 生产读取 / bat-api | RPC 给出的 `version.resource_root`;通常等价于 `current` 解析后的 versioned 目录 |
|
||
|
||
每个 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` 记录每个获取的对象引用,
|
||
文件带版本字段且允许重复 object ID。孤儿 staging 或显式 release 清理必须先按
|
||
清单减少 CAS 引用,再删除目录;CAS 对象损坏、缺失或元数据不一致时只产生诊断,
|
||
回退网络下载,不发布未经校验的文件。
|
||
|
||
---
|
||
|
||
## 3. URL → 磁盘映射(核心不变量)
|
||
|
||
实现:`OfficialResourcePullService::destination_for_url`。
|
||
|
||
```text
|
||
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 对外 path(1:1)
|
||
|
||
```text
|
||
GET {public-base-url}/prod-clientpatch.bluearchiveyostar.com/<root_token>/...
|
||
≡ 磁盘 <resource_root>/prod-clientpatch.bluearchiveyostar.com/<root_token>/...
|
||
```
|
||
|
||
默认仅服务 **download manifest 索引内且 Present + size 匹配** 的文件。
|
||
|
||
### 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:
|
||
|
||
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;`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` 形如:
|
||
|
||
```text
|
||
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 端点模板
|
||
|
||
共享(非平台):
|
||
|
||
```text
|
||
{CLIENT_PATCH}/{token}/TableBundles/TableCatalog.bytes
|
||
{CLIENT_PATCH}/{token}/TableBundles/TableCatalog.hash
|
||
```
|
||
|
||
每平台:
|
||
|
||
```text
|
||
{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 改写后客户端拼接:
|
||
|
||
```text
|
||
{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. `catalog.status`(`version.resource_root`、`addressables_root`、app/bundle)
|
||
4. `resource.manifest` 分页(url / destination / bytes / blake3)
|
||
5. 在 `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. issue #2 / #3 样本索引(R4/R5 预置)
|
||
|
||
在服务器 release 上优先采集到 `/tmp` 隔离目录(**不入库大文件**):
|
||
|
||
| 用途 | 建议路径模式 |
|
||
|---|---|
|
||
| Addressables(#2) | `{PatchDir}/catalog_*.zip` 解压后的 JSON/bin + 旁路 `.hash` |
|
||
| UnityFS(#3) | `FullPatch_*.zip` 内抽样 `.bundle`,或已解包 bundle |
|
||
| seed 加固(R2) | 各平台 `TableCatalog` / `BundlePackingInfo` / `MediaCatalog` 的 `.bytes`+`.hash` |
|
||
|
||
字段目标(#2,已有 `m_Crc` 部分):继续扩大 hash/size/CRC/依赖等可校验字段覆盖。
|
||
结构目标(#3):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`(无凭据)
|
||
|
||
所需:
|
||
|
||
```text
|
||
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-009 / #2 / #3
|
||
|
||
---
|
||
|
||
## 12. 变更纪律
|
||
|
||
1. 改 URL 模板或落盘规则 → **必须**同步本文 + 相关单测。
|
||
2. 改 inventory 启发式 → 说明覆盖的真实风险并补 fixture。
|
||
3. 真机实勘若发现与本文冲突 → **以真机为准** 修代码与本文,禁止静默分叉。
|