17 KiB
开发指南
本指南是本地开发流程的权威入口。贡献协作规则见 ../../CONTRIBUTING.md,AI agent 长期规则见 ../../AGENTS.md。
环境准备
安装依赖
Go
# 安装 Go 1.26.4+
# 参考:https://golang.org/doc/install
go version # 验证安装
Rust
# 安装 Rust 1.75+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustc --version # 验证安装
cargo --version
自托管 Gitea runner
.gitea/workflows/bat.yml 使用 runs-on: linux,并且不依赖 actions/checkout、dtolnay/rust-toolchain 等外部 GitHub Action。runner 需要在执行环境中预装以下命令:
git --version
rustc --version
cargo --version
rustfmt --version
cargo clippy --version
go version
该 workflow 会用 GITHUB_SERVER_URL、GITHUB_REPOSITORY、GITHUB_REF 和 GITHUB_SHA 手动 git fetch 当前提交,再执行 Rust workspace 的只读格式检查、检查、构建、clippy 和测试,以及 Go API 格式、测试、vet、构建、可选 lint 和文档状态门禁。这样可以避免自托管 runner 在准备阶段通过代理克隆第三方 action 仓库。
Docker
# 安装 Docker 和 Docker Compose
# 参考:https://docs.docker.com/get-docker/
docker --version
docker compose version
项目结构
请参考 架构文档 了解完整的项目结构。
开发工作流
1. 创建功能分支
git checkout -b feature/your-feature-name
2. 开发
# 运行只读 required 门禁(不会格式化或修改源码)
make ci-check
# 需要格式化时才修改工作树
make format
开发约束:
- 先阅读相关文档和代码,再判断实现方式。
- 跨模块、架构、数据格式或用户工作流变更必须先说明设计取舍。
- 保持改动聚焦,不做无关重构、批量格式化或元数据 churn。
- 公共接口、状态文件、manifest、catalog、patch 和下载流程变更必须补充测试或 fixture。
- 用户可见命令、运行路径、配置、状态文件、日志或缺口状态变化时,同步更新 README、指南、架构文档或
docs/reports/CURRENT_GAPS.md。
3. 提交
git add .
git commit -m "feat(cli): 添加 doctor 命令骨架"
提交信息遵循 Conventional Commits 规范:
- 必须使用
type(scope): 中文说明格式。 type使用feat、fix、docs、style、refactor、test、chore等 Conventional Commits 类型。scope必须写具体模块或文档域,例如ffi、docs、cli、cas、official-sync。- 提交标题和正文默认使用简体中文;代码标识符、协议字段和命令参数仍保持英文。
示例:
feat(cli): 添加 doctor 命令骨架fix(docs): 修正官方同步运行说明refactor(ffi): 降级 FFI 为可选兼容层
4. 推送和 PR
git push origin feature/your-feature-name
# 然后在 GitHub 创建 Pull Request
代码规范
默认使用简体中文写文档、提交说明、开发日志和面向用户的说明。代码标识符、协议字段、数据库字段、包名和命令参数保持英文命名规范。
禁止使用 demo、临时实现、硬编码路径或只为当前测试通过的伪实现。确实未完成的能力应写入当前缺口文档,而不是用 TODO 或 FIXME 隐藏。
解析模块状态
UnityFS / AssetBundle / Addressables / TypeTree 解析当前按路线图继续推进。新增解析类型、扩大解析覆盖和写入型解析 RPC/CLI 仍需遵守现有接口边界、真实 fixture 和回归验收要求。
Go
- 遵循 Effective Go
- 使用
gofmt格式化 - 使用
golangci-lint进行静态检查
Rust
- 遵循 Rust API Guidelines
- 使用
cargo fmt格式化 - 使用
cargo clippy进行静态检查
TypeScript
- 遵循 TypeScript Style Guide
- 使用 ESLint 和 Prettier
测试
合并前通用门禁
make ci-check
make ci-check 是只读门禁入口;make format / make fmt 才会修改源码。缺少
golangci-lint 时,Go lint 作为 optional gate 明确输出 skipped,不会伪报全部门禁通过。
Go 边界与进度以 docs/reports/GO_STATUS.md 为准:
- 同步/运维命令行 = Rust
bat(近乎全自动) - 资源 bootstrap/分发服务与内嵌 dashboard =
cmd/bat-api(make build-go-api) - 默认 Go 门禁 =
make ci-check中的纯 Go API test/vet/build(无 FFI) - 试验 CLI 产物为
bin/bat-go(make build-go-cli),禁止与 Rustbat重名 - 修改 FFI 时再跑
make test-go-ffi
bat-api 与 Rust bat 的生产拓扑是同一主机、同一容器或同一共享文件系统。开发时优先使用隔离 fixture 和本地 bat.sock live smoke,不连接远程服务器,也不读取现有客户端目录:
make test-go-api
make bat-api-local-live-smoke
BAT_API_SKIP_ENV_FILE=1 go run ./cmd/bat-api \
--listen 127.0.0.1:18080 \
--public-base-url http://127.0.0.1:18080 \
--resource-root internal/api/testdata/release \
--refresh-interval 0
其中 make bat-api-local-live-smoke 会在 /tmp 中启动真实 Rust daemon 和 Go API,覆盖 release 切换、清单不完整、无 release、RPC 断开、server-info、CDN Range/缓存和路径越界;报告保留在该次 smoke 的临时目录。单独的 --resource-root 命令只验证 fixture HTTP 形态,不替代 live socket 联调。浏览器检查内嵌 dashboard 时打开 http://127.0.0.1:18080/admin/dashboard/,再在页面内填入管理 token。
生产默认路径仍是 --socket / BAT_API_SOCKET,资源根由 Rust bat RPC 返回;--resource-root 只用于上述 fixture 或应急只读诊断。
常用聚焦命令
cargo test -p bat-core -- --nocapture
cargo test -p bat-adapters -- --nocapture
cargo test -p bat-ffi -- --nocapture
cargo test -p bat-patch -- --nocapture
cargo test -p bat-infrastructure -- --nocapture
cargo test -p bat-infrastructure --bin bat -- --nocapture
cargo clippy -p bat-core -p bat-adapters -p bat-infrastructure --all-targets -- -D warnings
官方资源同步、下载、daemon、status、verify 或 repair 相关改动必须至少覆盖 bat-infrastructure 和 bat 二进制测试。
bat-ffi 只是可选无状态 C ABI 兼容层。修改 FFI 时必须运行 cargo test -p bat-ffi -- --nocapture。Go 服务层默认经 internal/backendrpc 调 daemon;同步任务由 Rust bat 执行,不由 Go 试验 CLI 承担。
Rust bat 的资源拉取、解析、翻译工作流、重打包、汉化发布和持久化调度命令见 docs/guides/bat-workflows.md。推荐使用 res、parse、i18n 三个一级命令。
集成测试
cargo test --workspace
带真实本地资源的测试默认不应启用。只有在明确需要时,才通过对应 BAT_REAL_* 环境变量读取隔离样本路径。不要默认读取 /home/wanye/D/BlueArchive 或任何已有客户端目录。
官方资源同步手动检查
查看参数:
cargo run -p bat-infrastructure --bin bat -- --help
dry-run:
cargo run -p bat-infrastructure --bin bat -- \
--auto-discover \
--dry-run
开发环境真实官方资源下载默认写入 ./bat-resources;汉化产物默认写入独立的 ./bat-localized。如果要覆盖,官方原版资源使用 --output / BAT_OUTPUT,汉化产物使用 --localized-output / BAT_LOCALIZED_OUTPUT。两者都必须使用 /tmp 或其他隔离目录,不要写入现有资源目录,也不要把汉化输出覆盖到官方原版资源目录。
官方 release 拉取并校验完成后会在当前 release 根目录维护
official-resource-changes.json、crowdin-translation-handoff.json、
official-parse-cache.json 和 official-textunit-index.json,随后从
Added/Modified 资源、parse cache 与 TextUnit 明细索引派生
official-textunit-tasks.json、crowdin-textunit-queue.json、
translation-tasks.sqlite 和 translation-handoff.json。本地已有旧完整
版本时,新版本发布后会先按 manifest destination 对比旧/新 release,只把新增和
内容变更的资源写入解析与 Crowdin handoff;删除资源只记录差异,不进入翻译队列。
up-to-date 轮询发现本地文件、解析缓存、TextUnit 明细索引和 TextUnit 队列未变时不会重复解析。
Crowdin 队列当前只落本地文件,不发网络请求。
官方下载服务默认使用 8 个有界 worker,--download-concurrency /
BAT_DOWNLOAD_CONCURRENCY 只接受 1..=256。worker 完成一个 URL 后立即从共享
队列领取下一个任务;finished 进度按实际完成顺序即时上报,完成计数单调递增,
最终 report 的资源列表仍按 pull plan 顺序。需要验证顺序模式时显式使用
--download-concurrency 1。本文档中的真实资源命令仅是隔离 runbook;本地轻量
验证应使用 fake-curl/fixture,不要在开发机执行真实下载或 smoke run。
新 release 会在网络下载前扫描已发布 release 的下载 manifest,按规范化
destination 查找候选,并重新验证 size、BLAKE3 和 ZIP 结构。命中后优先硬链接,
跨文件系统时回退为 staging 内临时文件复制和原子 rename;历史 release 保持不可变。
历史候选不满足校验时才尝试既有 CAS 对象。CAS 复用会记录
official-cas-reuse-references.json,清理 staging/release 时递减引用;损坏、缺失
或元数据不一致会记录诊断并回退网络。报告和进度分别暴露
release_reused_count、cas_reused_count、reused_bytes、
transferred_bytes 以及 release_reused / cas_reused / downloaded 状态。
需要把已校验官方 release 导入 CAS + ResourceRepository 时,显式启用:
cargo run -p bat-infrastructure --bin bat -- \
--auto-discover \
--import-repository \
--import-cas-root /tmp/bat-test.cas \
--import-resource-db /tmp/bat-test-resources.sqlite
对应 config.toml / 环境变量键为 BAT_IMPORT_REPOSITORY、
BAT_IMPORT_CAS_ROOT 和 BAT_IMPORT_RESOURCE_DB。只读查询命令:
cargo run -p bat-infrastructure --bin bat -- parse-status
cargo run -p bat-infrastructure --bin bat -- parse-text-units --limit 50
cargo run -p bat-infrastructure --bin bat -- parse-errors --limit 50
cargo run -p bat-infrastructure --bin bat -- translation-tasks --task-status skipped_parse_failed --has-reason --limit 50
cargo run -p bat-infrastructure --bin bat -- translation-tasks --worker-status failed --has-failure-reason --limit 50
cargo run -p bat-infrastructure --bin bat -- translation-handoff
cargo run -p bat-infrastructure --bin bat -- i18n worker run --provider mock --worker-concurrency 8 --worker-max-tasks 10
cargo run -p bat-infrastructure --bin bat -- localized-status
cargo run -p bat-infrastructure --bin bat -- resource-index --limit 50
cargo run -p bat-infrastructure --bin bat -- resource-index --release-id <ID> --platform windows --archive-entry <PATH> --format json --limit 50
历史 release/CAS 复用的隔离回归测试:
cargo test -p bat-infrastructure official_download::tests::reuses_verified_historical_release_when_cdn_root_changes -- --nocapture
cargo test -p bat-infrastructure official_download::tests::falls_back_to_cas_after_corrupt_historical_release -- --nocapture
cargo test -p bat-infrastructure official_download::tests::corrupted_cas_falls_back_to_network_with_diagnostic -- --nocapture
parse-status 会额外显示 TextUnit 明细索引和队列摘要;parse-text-units /
parse-errors 可按 destination、archive entry、path id、class id、field path
和 format 分页查询当前官方 release 的 TextUnit 明细与解析错误;
translation-tasks 可按 release、destination、archive entry、队列任务状态、provider
worker 状态、parse status、TextUnit format、队列 reason 和 provider failure reason
查询离线 TextUnit 翻译任务状态与跳过/失败原因;发布后的状态保存在当前 release
根目录的 translation-tasks.sqlite,旧 release 没有状态库时回退到 JSON 队列;
i18n worker run / translation.worker.run 会由 Rust provider worker 独立 claim
下一项任务并落库 lease、失败分类、重试计划和 TextUnit 级译文结果,默认并发为 8,
范围 1..=256;
translation-handoff / translation.handoff 会动态合并版本化
translation-handoff.json 与 SQLite 状态,返回 job、unit、provider run 的完整交接
视图;
resource-index 返回的资源 JSON 包含 release、平台、bundle path、TextAsset 和 TextUnit metadata,
以及 Addressables entry 的 provider ID、bundle name、hash、size、CRC 和依赖关系;
并可按 release、平台、destination、bundle path、archive entry、parse status 和 TextUnit format 做资源级过滤;
localized-status 只有在 localized-version-state.json、current symlink 和
localized-patch-manifest.json 都匹配当前官方 release 时才返回 localized;
当 workflow 被 translation.proofread 标记为人工校对中时,会额外返回
translation_workflow_status=manual_proofreading 与
translation_workflow_status_code=translation.manual_proofreading,但不会遮蔽已发布的汉化 release。
文件级写入命令只处理显式输入/输出文件,不切换官方或汉化 release:
cargo run -p bat-infrastructure --bin bat -- patch-apply \
--patch-kind text \
--source-file /tmp/bat-source.txt \
--patch-file /tmp/bat-source.text-patch.json \
--target-file /tmp/bat-target.txt
cargo run -p bat-infrastructure --bin bat -- unityfs-patch-text-asset \
--bundle-file /tmp/source.bundle \
--serialized-file CAB-Example \
--object-path-id 1 \
--replacement-file /tmp/replacement.bytes \
--target-file /tmp/target.bundle
cargo run -p bat-infrastructure --bin bat -- unityfs-patch-string-field \
--bundle-file /tmp/source.bundle \
--serialized-file CAB-Example \
--object-path-id 1 \
--string-field-path message \
--replacement-text "老师" \
--target-file /tmp/target.bundle
cargo run -p bat-infrastructure --bin bat -- unityfs-patch-field \
--bundle-file /tmp/source.bundle \
--serialized-file CAB-Example \
--object-path-id 1 \
--field-path scores[1] \
--expected-json '{"kind":"signed","value":20}' \
--replacement-json '{"kind":"signed","value":42}' \
--target-file /tmp/target.bundle
cargo run -p bat-infrastructure --bin bat -- unityfs-patch-field \
--bundle-file /tmp/source.bundle \
--serialized-file CAB-Example \
--object-path-id 1 \
--field-path difficulty \
--expected-json '{"kind":"enum","value":{"type_name":"ScenarioDifficulty","storage_type":"int","value":2}}' \
--replacement-json '{"kind":"enum","value":{"type_name":"ScenarioDifficulty","storage_type":"int","value":3}}' \
--target-file /tmp/target.bundle
cargo run -p bat-infrastructure --bin bat -- unityfs-patch-field \
--bundle-file /tmp/source.bundle \
--serialized-file CAB-Example \
--object-path-id 1 \
--field-path target_layers \
--expected-json '{"kind":"bit_field","value":{"type_name":"LayerMask","storage_type":"UInt32","bits":5}}' \
--replacement-json '{"kind":"bit_field","value":{"type_name":"LayerMask","storage_type":"UInt32","bits":9}}' \
--target-file /tmp/target.bundle
cargo run -p bat-infrastructure --bin bat -- unityfs-patch-field \
--bundle-file /tmp/source.bundle \
--serialized-file CAB-Example \
--object-path-id 1 \
--field-path messages \
--replacement-json '{"kind":"array","value":[{"kind":"string","value":"你好"},{"kind":"string","value":"老师"}]}' \
--target-file /tmp/target.bundle
cargo run -p bat-infrastructure --bin bat -- unityfs-patch-field \
--bundle-file /tmp/source.bundle \
--serialized-file CAB-Example \
--object-path-id 1 \
--field-path texts \
--replacement-json '{"kind":"map","value":[{"kind":"object","value":[{"name":"first","value":{"kind":"string","value":"jp"}},{"name":"second","value":{"kind":"string","value":"你好"}}]}]}' \
--target-file /tmp/target.bundle
生产或 CI 环境不得依赖安装官方启动器。需要启动器信息时,只能分析启动器资源、官方 manifest 或公开更新数据,并将解析结果固化为可验证流程。
基准测试
make bench
调试
Go
使用 Delve 调试器:
go install github.com/go-delve/delve/cmd/dlv@latest
dlv debug ./cmd/bat
Rust
使用 rust-lldb 或 rust-gdb:
rust-lldb target/debug/bat-cas-engine
常见问题
1. 编译失败
确保安装了所有依赖:
go mod download
cargo fetch
2. 测试失败
先确认失败是否来自真实网络或本地资源路径。默认测试应使用 fixture/mock,不应依赖官方线上资源或开发机已有资源目录。
3. FFI 兼容层问题
bat-ffi 不是主集成边界,只用于需要 C ABI 的兼容场景。当前 Go 正式产品入口是
bat-api 资源 bootstrap/分发服务,默认通过 internal/backendrpc 调用 daemon RPC;
cmd/bat 仍是试验 CLI。新的 Go 集成优先使用 Rust bat.sock RPC 或稳定 SDK;
bat --json 仅是 Rust CLI 的机器输出形态。
重新构建兼容库:
cd crates/bat-ffi
cargo build