Files
spellforge/docs_dev/certification_checklist.md
T
joywayerandClaude Opus 4.8 ad2f4c0bc6 docs: 整理文档目录并对齐代码现状
- 将开发过程/归档文档迁至 docs_dev/(development_plan、certification_checklist、
  已废弃的 Cocos 架构草案 archived_cocos_architecture_draft),并修正全部跨引用
- 新增根 README.md(项目介绍,暂定名 Spellforge)与 docs_dev/README.md 索引
- 新增 docs_dev/doc_code_audit_2026-07-20.md:文档 vs 代码交叉审计报告(经 6
  路对抗性复核,零证伪),含「代码更优 / 文档更优 / 中性」判定汇总
- 在 docs/ 各设计·技术·机制文档就地加「实现现状 (2026-07-20)」callout:
  追认代码更优实现(纯 JSON 数据驱动、SpatialGrid-only 碰撞、MultiMesh 单档、
  存档选最新槽等),订正陈旧/矛盾内容(.tres→JSON、Boss HP/阈值/波次、EventID、
  StatusManager.apply 签名等),标记未实现功能(C# 热路径、Mana、元进展、
  Boss 阶段/抗性、Tutorial、轨迹/连锁/催化等)与 latent bug(CoreFeatureTag 位运算、
  pierce 空操作、MAX_OPS 不读 cpu_limit)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 14:35:55 +08:00

185 lines
9.4 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.
# 平台发行认证清单 (Platform Certification Checklist)
> **用途**:列出 Steam / Nintendo Switch 两个目标平台的强制通过项,并映射到对应开发切片。
> 每项完成后在 `[ ]` 处打勾,并记录验证者与日期。
> **规则**:所有 P0 项必须在 S6 发布前全部完成,P1 项视平台要求决定是否阻断发布。
---
## 一、Steam (PC) 必须项
### 1.1 隐私与数据合规(EU GDPR / Steam 要求)
| 编号 | 检查项 | 切片 | 状态 |
| :--- | :--- | :--- | :--- |
| ST-01 | 游戏不收集任何可识别玩家身份的数据(无账号系统、无上报遥测) | S2 | ⬜ |
| ST-02 | 崩溃日志(`user://crash_log.txt`)不包含 Steam ID / 机器码等 PII | S2 | ⬜ |
| ST-03 | 设置菜单提供「清除所有本地数据」选项(删除 `user://` 下所有文件) | S6 | ✅ 2026-06-05`SettingsManager.clear_all_local_data`,二次确认;实测删除 save_data/endless_records/run_a·b/runs|
| ST-04 | Steam 商店页包含隐私政策链接(即使只写"本游戏不收集任何数据" | S6 前置 | ⬜ |
| ST-05 | 如启用 opt-in 崩溃上报,玩家首次启动须明确同意 | S6(可选)| ⬜ |
> **ST-03 实现指引**
> `SettingsManager.clear_all_local_data()` 遍历删除:
> `user://crash_log.txt`、`user://crash_log_prev.txt`、`user://run_a.json`、
> `user://run_b.json`、`user://endless_records.json`、`user://save_data.json`、
> `user://runs/`(截图目录)。调用后重置 `ProfileManager` 单例状态并返回主菜单。
### 1.2 Steam 成就 (Achievements)
| 编号 | 检查项 | 切片 | 状态 |
| :--- | :--- | :--- | :--- |
| ST-10 | Steam API 初始化成功(`Steam.init()` 返回 OK,或使用 GodotSteam 插件) | S6 | ⬜ |
| ST-11 | 所有成就在 Steamworks 后台已定义并与 EventBus 钩子对应 | S6 | ⬜ |
| ST-12 | 离线模式下成就解锁不崩溃(本地缓存后联网再同步) | S6 | ⬜ |
| ST-13 | 成就解锁调用幂等(重复触发不报错、不重复计数) | S6 | ⬜ |
> **EventBus 成就钩子**`EventID.ACHIEVEMENT_UNLOCKED` **固定为 ID `18`**(负载:`{ "achievement_id": String }`),权威定义见 **`technical/implementation_plan.md` §2.1** 事件目录与 `event_ids.gd` 常量。
> `AchievementManager` 订阅此事件,调用 `Steam.set_achievement(achievement_id)` + `Steam.store_stats()`(须幂等,见 ST-13)。
### 1.3 Steam 输入与手柄
| 编号 | 检查项 | 切片 | 状态 |
| :--- | :--- | :--- | :--- |
| ST-20 | 手柄(Xbox / PS / Switch Pro)基本功能可用 | S1 | ⬜ |
| ST-21 | 手柄连接/断开时游戏不崩溃,自动切换输入方案 | S4 | ⬜ |
| ST-22 | 按键提示图标根据当前设备自动切换(Xbox A / PS × / 键盘 Space | S6 | ⬜ |
### 1.4 Steam Deck 兼容性
| 编号 | 检查项 | 切片 | 状态 |
| :--- | :--- | :--- | :--- |
| ST-30 | 游戏在 Steam Deck 默认分辨率(1280×800)下可正常显示 | S6 | ⬜ |
| ST-31 | 所有 UI 文字在 Steam Deck 屏幕上可读(最小字号 ≥ 16px)| S6 | ⬜ |
| ST-32 | Steam Deck 触摸屏操作不引起误触崩溃 | S6 | ⬜ |
| ST-33 | Proton 兼容性验证(在 SteamOS 下通过 Proton 层运行测试) | S6 | ⬜ |
### 1.5 排行榜数据完整性
| 编号 | 检查项 | 切片 | 状态 |
| :--- | :--- | :--- | :--- |
| ST-40 | 本地 `endless_records.json` 写入时附加 HMAC-SHA256 签名 | S5 | ✅ 2026-06-05`endless_records.gd`,对精确 JSON 字符串签名)|
| ST-41 | 读取时验证签名,签名失败则标记分数为「未验证」并拒绝提交 Steam 排行榜 | S5 | ✅ 2026-06-05(验签失败 `push_warning`+返回空,实测篡改拒绝)|
| ST-42 | Steam 排行榜提交时服务端异常(网络断开)不崩溃,本地缓存待下次提交 | S6 | ⬜ |
> **ST-40 HMAC 实现指引(C-4 修复)**
> ```gdscript
> # endless_records_manager.gd
> # HMAC Key 存于 GDScript 编译常量(不含在存档内),对抗普通玩家手动编辑
> # 注意:本地 HMAC 无法对抗逆向工程,主要目的是防止随意手改
> const _HMAC_KEY: PackedByteArray = [0xA3, 0x7F, 0x2C, ...] # S5 前生成 32 字节随机密钥
>
> func _sign(data: String) -> String:
> var crypto := Crypto.new()
> var hmac := crypto.hmac_digest(HashingContext.HASH_SHA256, _HMAC_KEY,
> data.to_utf8_buffer())
> return hmac.hex_encode()
>
> func save_records(records: Array) -> void:
> var payload := JSON.stringify(records)
> var signed := { "data": records, "sig": _sign(payload) }
> FileAccess.open("user://endless_records.json", FileAccess.WRITE
> ).store_string(JSON.stringify(signed))
>
> func load_records() -> Array:
> if not FileAccess.file_exists("user://endless_records.json"): return []
> var raw := JSON.parse_string(
> FileAccess.open("user://endless_records.json",
> FileAccess.READ).get_as_text())
> if not raw is Dictionary: return []
> var expected := _sign(JSON.stringify(raw.get("data", [])))
> if raw.get("sig", "") != expected:
> push_warning("EndlessRecords: 签名验证失败,分数不可信")
> return [] # 拒绝载入被篡改的数据
> return raw.get("data", [])
> ```
---
### 1.6 Steam 云存档 (Steam Cloud Save)
| 编号 | 检查项 | 切片 | 状态 |
| :--- | :--- | :--- | :--- |
| ST-50 | `ProfileManager.save_run()` / `save_settings()` 通过 `Steam.beginFileWriteBatch()` / `Steam.endFileWriteBatch()` 写入,确保 Steam Cloud 同步 | S6 | ⬜ |
| ST-51 | `user://run_a.json``user://run_b.json``user://save_data.json` 均在 Steamworks 后台 Remote Storage 中启用 | S6 前置 | ⬜ |
| ST-52 | 跨设备(PC → Steam Deck)存档同步测试:PC 端存档可在 Steam Deck 读取并继续游戏 | S6 | ⬜ |
| ST-53 | Steam Cloud 配额不超标(Steam 默认 100MB/游戏;所有存档文件估算总量 < 1MB) | S6 | ⬜ |
> **ST-50 实现指引**
> ```gdscript
> # profile_manager.gd
> func save_run(data: Dictionary) -> void:
> if Steam.is_steam_running():
> Steam.beginFileWriteBatch() # GodotSteam: 开始批量写入,触发 Steam Cloud 同步
> var which := get_int("run_write_slot", 0)
> var path := _SLOT_A if which == 0 else _SLOT_B
> var f := FileAccess.open(path, FileAccess.WRITE)
> if f:
> f.store_string(JSON.stringify(data))
> if Steam.is_steam_running():
> Steam.endFileWriteBatch() # 结束批量写入,触发 Steam Cloud 上传
> set_int("run_write_slot", 1 - which)
> ```
> **注意**`beginFileWriteBatch` / `endFileWriteBatch` 仅在 `Steam.is_steam_running()` 时调用;离线模式下正常写本地文件,不崩溃。`Steam.is_steam_running()` 需 GodotSteam 插件支持。
---
## 二、Nintendo Switch(未来扩展)
> 以下为 Nintendo Lotcheck 最常见的阻断项,仅在立项 Switch 移植时启用。
### 2.1 系统级强制项
| 编号 | 检查项 | 优先度 |
| :--- | :--- | :--- |
| SW-01 | 游戏内按钮提示使用 Switch 图标(A/B/X/Y),不得显示 Xbox 图标 | P0 |
| SW-02 | Joy-Con 横持模式不崩溃(若不支持须在商品页说明) | P0 |
| SW-03 | 游戏可从睡眠状态恢复(`NOTIFICATION_APPLICATION_PAUSED` 处理) | P0 |
| SW-04 | 存档容量不超过 Switch 游戏卡限制(存档 < 32MB | P0 |
| SW-05 | 不在联机功能中收集 Nintendo Account 信息 | P0 |
| SW-06 | Logo & RatingCERO / PEGI / ESRB)在正确位置显示 | P0 |
| SW-07 | 游戏内截图功能不包含版权保护内容 | P1 |
### 2.2 性能要求
| 编号 | 检查项 | 优先度 |
| :--- | :--- | :--- |
| SW-10 | 掌机模式(720p)稳定 30fpsSwitch 性能下限) | P0 |
| SW-11 | 内存使用峰值 < 2.5GBSwitch 总 RAM 4GB 共享系统/GPU | P0 |
| SW-12 | 存档读写不阻塞主线程超过 100ms | P0 |
---
## 三、无障碍合规(Accessibility — 跨平台必须项)
> 参见 `architecture_design.md ADR-C1` 获取完整技术实现规范。
| 编号 | 检查项 | 切片 | 状态 |
| :--- | :--- | :--- | :--- |
| AC-01 | 元素标签同时用颜色 + 形状图标标识(色盲玩家可区分) | S3 | ⬜ |
| AC-02 | 所有 UI 字体支持缩放(50%~150%,设置项) | S4 | ⬜ |
| AC-03 | 高对比度模式(黑底白字选项)| S6 | ⬜ |
| AC-04 | 所有 VFX 提供「减少闪光」选项(防光敏性癫痫)| S6 | ⬜ |
| AC-05 | 游戏内所有音效有对应视觉提示(不依赖纯音频传达关键信息) | S6 | ⬜ |
---
## 四、评级申报
| 平台 | 机构 | 提交时机 | 状态 |
| :--- | :--- | :--- | :--- |
| Steam | IARC(自评系统,Steam 自动申报) | S6 发布前 | ⬜ |
| Steam(欧洲) | PEGI 自评或正式申请 | S6 发布前 | ⬜ |
| Switch | CERO(日本)/ PEGI / ESRB(需提前约 8 周提交) | 若立项 Switch | ⬜ |
---
## 五、发布前最终清单
| 编号 | 检查项 | 完成标志 |
| :--- | :--- | :--- |
| PRE-01 | Steam 商店页(截图 × 5、宣传片、描述文字)审核通过 | Steam 后台审核 OK |
| PRE-02 | 版本号 `1.0.0` 打 git tag,构建哈希记录在 `CHANGELOG.md` | git tag v1.0.0 |
| PRE-03 | 所有 P0 认证项状态为 ✅ | 本文档所有 P0 行 ✅ |
| PRE-04 | 发布分支通过完整 Wave 1~20 通关测试(无崩溃) | CI 通关测试日志 |
| PRE-05 | `crash_reporter.gd` 已注册为首位 Autoload,日志路径可写 | project.godot 验证 |