Files
BlueArchiveToolkit/docs/guides/development.md
T
nyaKazuha 8fc93b8f39
bat-rust / Build and test Rust (push) Canceled after 0s
bat-rust / Build and test Go API (push) Canceled after 0s
docs: document Translation Memory and config contracts
2026-09-06 22:52:10 +08:00

431 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 开发指南
本指南是本地开发流程的权威入口。贡献协作规则见 `../../CONTRIBUTING.md`AI agent 长期规则见 `../../AGENTS.md`
## 环境准备
### 安装依赖
#### Go
```bash
# 安装 Go 1.26.4+
# 参考:https://golang.org/doc/install
go version # 验证安装
```
#### Rust
```bash
# 安装 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 需要在执行环境中预装以下命令:
```bash
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 门禁和文档状态门禁。这样可以避免自托管 runner 在准备阶段通过代理克隆第三方 action 仓库。
#### Docker
```bash
# 安装 Docker 和 Docker Compose
# 参考:https://docs.docker.com/get-docker/
docker --version
docker compose version
```
---
## 项目结构
请参考 [架构文档](../architecture/README.md) 了解完整的项目结构。
---
## 开发工作流
### 1. 创建功能分支
```bash
git checkout -b feature/your-feature-name
```
### 2. 开发
```bash
# 实时编译检查
make check
# 运行测试
make test
# 格式化代码
make fmt
```
开发约束:
1. 先阅读相关文档和代码,再判断实现方式。
2. 跨模块、架构、数据格式或用户工作流变更必须先说明设计取舍。
3. 保持改动聚焦,不做无关重构、批量格式化或元数据 churn。
4. 公共接口、状态文件、manifest、catalog、patch 和下载流程变更必须补充测试或 fixture。
5. 用户可见命令、运行路径、配置、状态文件、日志或缺口状态变化时,同步更新 README、指南、架构文档或 `docs/reports/CURRENT_GAPS.md`
### 3. 提交
```bash
git add .
git commit -m "feat(cli): 添加 doctor 命令骨架"
```
提交信息遵循 [Conventional Commits](https://www.conventionalcommits.org/) 规范:
- 必须使用 `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
```bash
git push origin feature/your-feature-name
# 然后在 GitHub 创建 Pull Request
```
---
## 代码规范
默认使用简体中文写文档、提交说明、开发日志和面向用户的说明。代码标识符、协议字段、数据库字段、包名和命令参数保持英文命名规范。
禁止使用 demo、临时实现、硬编码路径或只为当前测试通过的伪实现。确实未完成的能力应写入当前缺口文档,而不是用 `TODO``FIXME` 隐藏。
### 解析模块状态
UnityFS / AssetBundle / Addressables / TypeTree 解析当前按路线图继续推进。新增解析类型、扩大解析覆盖和写入型解析 RPC/CLI 仍需遵守现有接口边界、真实 fixture 和回归验收要求。
### Go
- 遵循 [Effective Go](https://golang.org/doc/effective_go)
- 使用 `gofmt` 格式化
- 使用 `golangci-lint` 进行静态检查
### Rust
- 遵循 [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/)
- 使用 `cargo fmt` 格式化
- 使用 `cargo clippy` 进行静态检查
### TypeScript
- 遵循 [TypeScript Style Guide](https://google.github.io/styleguide/tsguide.html)
- 使用 ESLint 和 Prettier
---
## 测试
### 合并前通用门禁
```bash
cargo fmt --all -- --check
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings
make test-go-api
make build-go-api
go vet ./internal/api/... ./internal/backendrpc/... ./cmd/bat-api/...
make check-docs
```
Go 边界与进度以 `docs/reports/GO_STATUS.md` 为准:
- **同步/运维命令行** = Rust `bat`(近乎全自动)
- **资源 bootstrap/分发服务与内嵌 dashboard** = `cmd/bat-api``make build-go-api`
- **默认 Go 门禁** = `make test-go-api`(无 FFI
- 试验 CLI 产物为 `bin/bat-go``make build-go-cli`),**禁止**与 Rust `bat` 重名
- 修改 FFI 时再跑 `make test-go-ffi`
`bat-api` 与 Rust `bat` 的生产拓扑是同一主机、同一容器或同一共享文件系统。开发时优先使用隔离 fixture 和本地 `bat.sock` live smoke,不连接远程服务器,也不读取现有客户端目录:
```bash
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 或应急只读诊断。
### 常用聚焦命令
```bash
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`](bat-workflows.md)。推荐使用 `res``parse``i18n` 三个一级命令。
### 集成测试
```bash
cargo test --workspace
```
带真实本地资源的测试默认不应启用。只有在明确需要时,才通过对应 `BAT_REAL_*` 环境变量读取隔离样本路径。不要默认读取 `/home/wanye/D/BlueArchive` 或任何已有客户端目录。
### 官方资源同步手动检查
查看参数:
```bash
cargo run -p bat-infrastructure --bin bat -- --help
```
dry-run
```bash
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` 时,显式启用:
```bash
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`。只读查询命令:
```bash
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 复用的隔离回归测试:
```bash
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:
```bash
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 或公开更新数据,并将解析结果固化为可验证流程。
### 基准测试
```bash
make bench
```
---
## 调试
### Go
使用 Delve 调试器:
```bash
go install github.com/go-delve/delve/cmd/dlv@latest
dlv debug ./cmd/bat
```
### Rust
使用 rust-lldb 或 rust-gdb
```bash
rust-lldb target/debug/bat-cas-engine
```
---
## 常见问题
### 1. 编译失败
确保安装了所有依赖:
```bash
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 的机器输出形态。
重新构建兼容库:
```bash
cd crates/bat-ffi
cargo build
```
---
更多当前状态请查看 [当前状态](../../CURRENT_STATUS.md) 和 [当前缺口清单](../reports/CURRENT_GAPS.md)。