Files
BlueArchiveToolkit/docs/architecture/official-resource-backend.md
T
nyaKazuhaandClaude Fable 5 3edbe7ccee feat(daemon): 任务历史文件态持久化与 .env 无参启动(issue #1)
任务持久化:
- 任务历史落 <state-dir>/bat-tasks.json(版本化、0600 原子写、不跟随
  symlink),生命周期转换时 write-through;seq 持久化避免 pid 复用撞 ID
- daemon 重启恢复历史,中断时仍 queued/running 的任务标记 failed,
  新增错误码 TASK_INTERRUPTED(BAT-ERR-700005)
- 损坏文件改名 .corrupt 留证后从空历史开始;无法识别的记录计数跳过
- core 新增 ErrorCode::ALL 公开码表与 from_id 反查(持久化错误码往返)

.env 配置:
- 首次启动在二进制所在目录释放 .env 模板(0600、create_new 防竞态),
  之后每次启动加载为进程环境变量(不覆盖已存在变量)
- 优先级:CLI > 进程环境变量 > .env > 内置默认;BAT_* 键映射到
  CliOptions 默认值,BAT_WATCH/BAT_DAEMON 仅对无子命令 Run 生效且
  CLI 显式模式/dry-run 时让位;Redis 键预留(未接入)
- 工具/代理"非默认"判断改按 env 应用后基线,status/stop/logs 在
  .env 存在时不误判;BAT_SKIP_ENV_FILE=1 整体禁用

验证:新增 10 个单测(env 解析/优先级/守卫回归/持久化往返/中断标记/
损坏恢复/未知类型跳过);真机 e2e:.env 释放加载、daemon 纯 .env 启动、
catalog.refresh 任务带类型化错误码落盘并跨 daemon 重启恢复(含日志);
fmt / clippy --workspace --all-targets -D warnings / test --workspace 全绿

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 21:15:47 -07:00

20 KiB
Raw Permalink Blame History

官方资源后端说明

本文档说明 BlueArchiveToolkit 中“官方资源后端”的职责、数据流和工作原理,供审核使用。

1. 范围

这个后端只处理 日服官方资源,只接受官方 .jp/.com 域名下的资源链路。

明确排除:

  • bluearchive.cafe
  • 任何镜像层、转写层、二次代理层
  • 人工拼接出来的样例 URL
  • Linux 生产环境安装或执行官方启动器
  • 把已安装客户端目录或官方启动器安装目录当作生产输入

本地 HTTP/SOCKS 代理只允许作为 curl 传输层配置使用,不改变资源来源判定。无论是否配置代理,下载器仍只接受官方 URL,并拒绝镜像、转写或非官方资源域名。

当前默认平台集合是:

  • Windows
  • Android

iOSmacOS 已从默认支持中移除。

2. 后端负责什么

任务 结果
发现层 读取官方 server-info,选择 connection group 和版本覆盖 得到官方资源根和种子端点
清单层 解析 BundlePackingInfo.bytesTableCatalog.bytesMediaCatalog.bytes 得到完整文件清单
计划层 合并 discovery + inventory,去重并保序 得到全量 pull plan
下载层 校验官方 URL,调用下载器,落盘并记录字节数 得到本地资源副本
导入层 将 bundle 写入 CAS 和 ResourceRepository 得到可查询的资源索引
同步层 比较当前快照和历史快照 决定下载、校验、发布
更新层 保存上次官方 snapshot,定期执行 discovery + diff + pull 形成自动更新闭环

3. 工作原理

3.0 Linux 生产约束

生产环境目标是 Linux 后端任务,不是 Windows 桌面客户端环境。

因此生产同步入口必须满足:

  1. 不要求安装官方启动器。
  2. 不要求启动官方启动器进程。
  3. 不要求把生产环境当作客户端安装目录。
  4. 可以显式执行 official metadata discovery 自动发现 server-info URL、connection-groupapp-version
  5. 也可以通过配置、调度状态或已审计 metadata snapshot 显式提供这些值。
  6. --auto-discover 只允许通过官方 HTTP metadata 和临时目录解析 GameMainConfiglauncher metadata 未变时必须复用缓存,metadata 变化时才按 manifest 重新下载必要 resources.assets 或旧版官方 game zip。

3.1 发现官方资源根

入口是 YostarJpServerInfo

流程是:

  1. 读取官方 server-info JSON。
  2. connection_group + app_version 规则选择版本覆盖。
  3. AddressablesCatalogUrlRoot 提取 root token
  4. 校验该 root 只能落在官方 prod-clientpatch.bluearchiveyostar.com 之下。
  5. 基于 root token 生成 discovery 端点。

对应实现主要在:

  • adapters/src/official/yostar_jp.rs

3.2 枚举完整资源清单

资源清单不是“猜几个文件”,而是从官方 catalog 字节里提取完整文件名列表。

当前做法:

  1. 读取 BundlePackingInfo.bytes
  2. 提取所有 FullPatch_*.zip 包名。
  3. 读取 TableCatalog.bytes
  4. 提取所有表资源名,例如 ExcelDB.db
  5. 读取 MediaCatalog.bytes
  6. 提取所有媒体资源名,例如 JP_Airi.zip

然后对 verified platforms 生成完整 URL 集:

  • Windows patch pack
  • Android patch pack
  • TableBundles
  • MediaResources-Windows
  • MediaResources

对应实现主要在:

  • adapters/src/official/inventory.rs
  • adapters/src/official/yostar_jp.rs

3.3 组装全量 pull plan

OfficialResourcePullPlan 是这个后端的关键对象。

它做三件事:

  1. 保存 discovery 端点。
  2. 保存完整 inventory。
  3. 用默认平台集或指定平台集生成最终 URL 列表。

all_urls() 是执行层的权威输入:

  • 先放 discovery URLs
  • 再放 content URLs
  • 统一去重
  • 保持顺序

这意味着后端拉取的是 完整资源包集合,不是抽样下载。

对应实现主要在:

  • infrastructure/src/official_pull.rs

3.4 执行下载

OfficialResourcePullService 负责真正下载。

工作方式:

  1. 检查 URL 是否属于官方 host。
  2. 把 URL 映射到当前工作根目录下的本地输出路径;在自动更新服务中,读侧根目录是 current 指向的 versioned 目录,写侧根目录是 staging。
  3. 读取工作根目录下的 official-download-manifest.json
  4. 已存在目标文件只有在本地清单中的相对路径、size、BLAKE3 都匹配时才跳过;.zip 文件还必须通过 ZIP central directory / local header 结构校验。
  5. 缺少清单、清单不匹配、ZIP 结构无效或文件损坏时重新下载。
  6. TableCatalog.bytesBundlePackingInfo.bytesMediaCatalog.bytes 总是刷新并用官方 .hash 强校验;该 .hashxxHash32(seed=0) 的十进制文本。
  7. catalog_*.hash 当前只作为 Addressables catalog 变更标记,不作为 zip/JSON 内容校验算法;Unity Addressables/SBP builder 对 JSON/bin catalog 使用 HashingMethods.Calculate 生成 Hash128 文本,运行时用它判断 remote catalog cache 是否过期,它不能套用 seed catalog 的 xxHash32 规则。
  8. 官方 seed .hash 校验失败会让当前下载失败,并移除对应 data/hash URL 的本地 manifest 条目,避免失败产物在下一轮被本地 BLAKE3 audit 误判为健康缓存。
  9. 存在 .part 临时文件时通过 curl --continue-at - 尝试断点续传。
  10. 新下载写入 .part,成功并通过必要校验后原子 rename 到 staging 内最终路径;断点续传后的 .zip 如果结构无效,会删除 .part 并重新全量下载。
  11. 成功下载后更新本地下载清单。
  12. 上一轮失败或中断留下的 staging 只有在 official-version-state.json 中存在同一 app version、bundle version 和 Addressables root 的失败记录,且 <output>/.staging/<id> 仍安全存在、versions/<id> 尚未发布时才会复用;复用后仍按 manifest、BLAKE3、ZIP 结构和官方 .hash 逐 URL 校验,不信任散落文件。
  13. curl 默认自动检测 HTTPS_PROXY / ALL_PROXY / HTTP_PROXY 及小写环境变量,保留 NO_PROXY;带凭据的代理推荐用这些环境变量配置。CLI 也可用 --proxy <URL> 显式指定代理,或用 --no-proxy 强制直连。代理决策会进入 progress log、daemon log 和 doctor 诊断输出。代理凭据全程不落世界可读位置:日志与 status 输出脱敏;传给 curl 子进程时经 ALL_PROXY 环境变量而非 --proxy 参数,不进 curl 的 /proc/<pid>/cmdline--daemon 模式下经环境变量下传后台子进程,不进子进程 argv 或 bat-status.json,复用凭据单独存于 bat-proxy.secret0600),clean-stable 会在后台停止后清除。
  14. curl 失败按 HTTP/网络类型分类:403/404/普通 4xx 不重试,5xx、429、DNS、连接、超时、中断和网络类错误按尝试次数重试。
  15. 单个 URL 最终失败时写入 official-download-quarantine.json,发出 Failed progress,并阻止发布不完整资源。
  16. 旧 launcher 包或 resources.assets 下载使用官方 launcher CDN 配置,primary CDN 失败后切换 official backup CDN;资源 patch host 不猜测非官方镜像。
  17. 记录最终文件大小、本次传输字节数、官方 hash 校验数和执行状态。
  18. 非官方 URL 直接拒绝。

路径映射时会做分段清理,并在写入前做输出目录安全校验、相对路径归属校验和现有路径组件 symlink 检查,避免把不安全路径写进输出目录或通过 symlink 跳出输出目录。

对应实现主要在:

  • infrastructure/src/official_download.rs

3.5 导入到 CAS 和资源仓储

资源下载后,导入层会:

  1. 把 bundle 原始字节写入 CAS。
  2. 解析 UnityFS 基础摘要。
  3. 把资源条目写入 ResourceRepository
  4. 记录资源路径、hash、大小和解析摘要。

这层的意义是把“下载到磁盘的文件”变成“可查询、可复用、可去重”的资源对象。

对应实现主要在:

  • infrastructure/src/import.rs
  • infrastructure/src/resources.rs

3.6 同步决策

同步层只做判断,不做重业务。

它比较当前快照和历史快照,关注:

  • root token 是否变化
  • addressables root 是否变化
  • endpoint 是否变化
  • bundle version 是否变化

判断结果分三类:

  • UpToDate
  • DownloadAndVerify
  • DownloadVerifyAndPublish

对应实现主要在:

  • infrastructure/src/official_sync.rs

3.7 自动更新闭环

OfficialUpdateService 是当前 Rust 侧的自动更新核心,正式命令入口是 bat

集成边界:

  1. 当前生产和 Go CLI 默认集成路径是运行 bat --json 并消费结构化 report。
  2. systemd、容器或上层 Go 进程只负责守护 bat --watch / bat --daemon,不直接接管下载器内部状态。
  3. bat-ffi 只允许作为可选无状态 C ABI 兼容层,用于 Manifest inspect 和 sync plan 这类一次性 JSON helper;它不是官方同步 daemon、下载器、资源锁、CAS handle 或主控制面的承载位置。

流程是:

  1. 显式执行 --auto-discover 或读取已审计 server-info 输入。
  2. --auto-discover 先抓官方 launcher metadatametadata 未变时复用 official-bootstrap-cache.json 中的 GameMainConfig 摘要,metadata 变化时按 manifest 临时下载 resources.assets 或旧版官方 game zip 并重新解析。
  3. 生成当前 v2 snapshot,记录 app_versionconnection_groupbundle_versionaddressables_root、endpoint URL、seed .hash 内容、catalog_*.hash marker、launcher metadata 摘要和 GameMainConfig 摘要。
  4. 读取上一次成功同步写出的 snapshot。
  5. 使用 OfficialSyncPlan 和扩展 snapshot diff 判断是否需要下载;URL 未变但 .hash / marker 内容变化也会触发更新。
  6. 每轮都会基于最新 seed catalog 构建当前 pull plan,并检查输出目录是否已有当前 plan 的 manifest 条目或目标文件。
  7. 如果远端 snapshot 未变化但输出目录没有任何当前 plan 的本地资源,仍按首次运行处理并执行全量拉取。
  8. 远端无变化且本地已有资源时执行 download manifest audit,检查路径、size、BLAKE3 和 ZIP 结构。
  9. 远端变化、本地 audit 发现 repair_needed,首次空目录运行,或缺少 current 原子发布指针时,进入下载/发布流程。
  10. 下载先写入 <output>/.staging/<id>;若已有 active release,会先 seed staging 以复用已验证文件;若 version-state 中存在同一版本的失败 staging,则优先复用该 staging 并跳过 active seed,避免旧 active 覆盖已下载的新文件。
  11. 下载、manifest、本地 BLAKE3、ZIP 和官方 .hash 校验完成后写入新的 snapshot。
  12. 将 staging rename 为 <output>/versions/<id>,再原子替换 <output>/current symlink 指向该 versioned 目录。

该入口不安装、不执行官方启动器,也不读取生产外的本地客户端目录。Rust 正式 binary bat 支持单次运行、--watch 常驻模式、--daemon 后台模式,以及 statusstoprestartreloadlogsrefreshverifyrepairdoctorclean-stable 管理命令。--daemon 会在后台状态目录下创建 bat.sock,使用 Unix socket JSON-RPC 作为 live control planebat.pidbat-status.jsonbat-daemon.log 是快照、诊断和兼容 fallback;bat-events.jsonl 是带轮转的结构化 JSONL 事件日志;bat-control.lock 串行化控制命令,并在 stale/corrupt 时由下一次控制命令或 clean-stable 恢复。bat-status.jsonstatus 子命令包含最后成功时间、下次检查时间、最后错误摘要、当前阶段和当前下载 URL 进度。PID、status、log 和控制锁文件创建时使用私有权限,读取和写入时不跟随 symlink。statusstoplogsreload 和默认形态的 refresh 优先走 RPCreload 会唤醒或排队 watch 循环重新自动发现并强制刷新,restart 才负责重启进程或替换启动参数;显式 --proxy / --no-proxy 会作为启动参数保存并在后台重启时复用。后台 daemon 管理某个资源目录时,前台 run/watch/refresh/repair 不允许直接写入同一目录;默认形态 refresh 会通过 RPC 触发后台刷新。正常情况下默认每 1 小时执行一次检查;每天北京时间(UTC+8)03:0016:0018:00 会中断普通 sleep 并强制执行一次自动刷新,该轮注入 force=true。远端和本地一致时静默等待下次检查,不一致时自动下载或 repair。下载、发现或校验失败时不等待完整正常周期,默认 60 秒后重试;如果固定时间强制刷新失败,会保留 pending force 并按失败重试周期继续重试,可用 --error-retry--error-retry-seconds 调整。默认资源输出目录是 ./bat-resources,默认后台状态目录是 /tmp/bat-pid,二者通过 --output--state-dir 分别配置。单次运行仍保留为核心幂等路径,systemd service、容器或 Go 进程可以只负责守护该常驻进程;cron/systemd timer 调单次模式只是可选集成方式。项目是否热更新、热重载或重启进程,由上层业务集成决定。

对应实现主要在:

  • infrastructure/src/official_update.rs
  • infrastructure/src/bin/bat_official_sync.rs
  • infrastructure/examples/official_update_check.rs(历史/开发入口)

4. 官方 bootstrap 与用户流程

当前已经提供的官方用户流程分两类。

Linux 生产路径:

  1. 显式执行 --auto-discover,或提供已审计的官方 metadata snapshot。
  2. official_pull_plan 依据官方 server-info、catalog 和 verified platforms 生成全量拉取计划。
  3. bat 对比上次 snapshot 和远端 .hash marker;远端无变化时 audit 本地清单,有变化或本地损坏时下载、校验并落盘官方资源。

开发/审计辅助路径:

  1. official_launcher_bootstrap 获取官方 launcher 信息。
  2. official_game_main_config 按官方 manifest 获取 resources.assets(旧 ZIP manifest 则先解出)并解密 GameMainConfig
  3. 手动确认最新 PC metadata、server-info URL 和默认 connection group。

自动发现路径和开发/审计辅助路径都只使用官方 HTTP 和临时目录,不安装或启动官方 launcher。

这些入口都只接受官方域名,不走 bluearchive.cafe 镜像链。

5. 已验证行为

当前代码已经验证:

  • 默认平台是 Windows + Android
  • iOSmacOS 不再进入默认官方流程
  • verified inventory 会产出完整内容 URL 集
  • pull plan 会同时包含 discovery URLs 和 content URLs
  • 全量样本下是 2 个 discovery URL + 5 个内容 URL = 7 个 URL
  • OfficialUpdateService 能持久化 v2 snapshot,并在远端 marker 内容变化时触发下载决策
  • bat 默认向 stderr 输出 BlueArchiveToolkit ASCII banner 和 progress logstdout 默认输出人类可读摘要;progress log 覆盖代理决策、总体下载进度、单文件开始/完成状态、下载中断失败分类和校验结果摘要;支持 --proxy / --no-proxy 控制 curl 传输代理,支持 --json 输出稳定 JSON,支持 --no-progress 关闭进度日志,支持 --no-banner 只关闭横幅,支持 --watch --interval 1h --error-retry 60s 常驻运行,支持 --daemon Unix socket JSON-RPC 控制、statusstoprestartreloadlogsrefresh --forceverifyrepairdoctorclean-stable,非 dry-run 使用 .official-sync.lock 防止并发写资源目录,控制命令使用 bat-control.lock 防止并发状态修改,资源发布使用 .stagingversionscurrent 原子切换,daemon 写 bat-events.jsonl 结构化日志并在 status 中暴露下载进度、失败类型、HTTP 状态和调度状态
  • curl 失败分类和重试策略已覆盖 404 不重试、5xx 重试耗尽后 quarantine、launcher primary CDN 失败后切换 official backup CDN
  • official-version-state.json 已覆盖当前完成版本、正在拉取版本、上一个可用版本和失败版本;同一 app version、bundle version 和 Addressables root 的失败只保留最新一条,重新拉取或成功发布后清理同版本失败记录,同版本失败 staging 会在路径安全且未发布时复用,bat status 会暴露版本状态摘要和最近历史失败原因
  • 资源导入链路已覆盖 CAS 写入、ResourceRepository 索引、AssetBundle UnityFS 摘要,以及 TextAsset/Table/Media 分类
  • 离线回归样本已覆盖当前 catalog、上一个版本 catalog、catalog 结构变化、403、404 和 seed hash mismatch
  • OfficialUpdateService 能读写 official-bootstrap-cache.json,并支持默认开启的 audit_local / repair CLI 行为
  • 下载层能在本地文件 size/BLAKE3/path、ZIP 结构或 manifest 不匹配时重新下载
  • 官方 seed .hash mismatch 会导致下载失败,而不是降级为本地 BLAKE3 猜测
  • 非官方 URL 会在 fetch/download 入口被拒绝
  • 真实官方网络全量下载 smoke 已固化为 scripts/official-full-pull-smoke.shmake official-smokedocs/guides/official-full-pull-smoke.md;大型资源文件和运行报告默认保留在 /tmp 隔离目录,不纳入 Git

相关验证主要来自:

  • cargo test -p bat-adapters -- --nocapture
  • cargo test -p bat-ffi -- --nocapture
  • cargo test -p bat-infrastructure -- --nocapture
  • cargo test -p bat-infrastructure --bin bat -- --nocapture
  • cargo run -p bat-infrastructure --bin bat -- --help

6. 审核重点

请重点检查这几件事:

  1. 是否只接受官方 host。
  2. 是否默认只走 Windows + Android。
  3. 是否把完整 content URL 集都纳入 all_urls()
  4. 是否拒绝镜像域名和手工拼接样例。
  5. 是否在下载前做了 URL 和路径安全校验。
  6. 是否能在官方客户端变动时只改适配层。

7. RPC Backend API 与 Go 调用边界

daemonbat --daemon)在 <state-dir>/bat.sock 上提供 Unix socket JSON-RPC 2.0 服务,是面向上层服务(Go 层)的主要跨语言边界

7.1 协议契约

  • 应用层统一 envelope(装入 JSON-RPC result):ok / status / data / error / request_id;传输层解析失败走 JSON-RPC 顶层 error-32700)。
  • error 为统一 ApiErrorcodeBAT-ERR-<6 位>)、kinddomainlocationmessageretryable。码表以 core/src/error_code.rs 为准。
  • 长任务(resource.sync / resource.verify / catalog.refresh 入队即返回 task_id,经 task.status / task.list / task.logs 轮询,task.cancel 协作式取消。任务执行器是单 worker FIFO,与 watch 循环经进程内锁互斥。任务历史持久化于 <state-dir>/bat-tasks.json (版本化、0600 原子写,生命周期转换时落盘),daemon 重启后历史任务 仍可经 task.* 查询,中断任务标记 task_interrupted700005)。
  • 方法命名空间与实现状态、请求/响应示例见 USERGUIDE.md §6 daemon.* / resource.* / catalog.* / task.* 已实现; patch.* / unityfs.* 待引擎;task.create / resource.repair 按设计暂缓。

7.2 Go 层职责边界

  • Go 层负责:BlueArchive 客户端请求处理、HTTP API、鉴权、内容分发, 以及作为 RPC client 调用本机 daemon(连接 bat.sock,每行一个 JSON-RPC 请求/响应)。
  • Rust daemon 负责:官方资源自动拉取与校验、catalog 更新检查、 版本状态与发布、任务队列/日志/错误/进度管理等长期状态型工作。
  • Go 层直接嵌入 Rust FFI,不直接读写 daemon 的状态文件与资源 目录内部结构;跨语言交互只经 RPC 契约。

7.3 FFI 的定位(降级说明)

bat-ffi crate(cgo 头文件 + 静态/动态库链接,见 Makefilebuild-ffi / build-go降级为可选的历史兼容边界:保留用于既有 cgo 试验路径与本地工具,不再作为 Go 服务层的主要集成方式,也不会按 RPC 契约的节奏扩展。新能力一律先落 RPC 方法。