188 lines
8.4 KiB
Markdown
188 lines
8.4 KiB
Markdown
## ADDED Requirements
|
||
|
||
### Requirement: GM 双轨入口
|
||
|
||
系统 MUST 提供两种 GM 命令入口:服务端 GM Console(命令行 + ET Watcher 注入)和客户端 GM Panel(YIUI 简易面板)。两者共用同一套命令注册表,避免重复实现。
|
||
|
||
#### Scenario: 服务端 Console
|
||
- **WHEN** 运维登录服务端 shell 执行 `gm AddItem 10001 100` 或通过 ET Watcher Web 接口
|
||
- **THEN** 命令在服务端直接执行(不需要客户端登录)
|
||
- **AND** 操作日志写入 `Logs/Audit/GM/{date}.jsonl`
|
||
|
||
#### Scenario: 客户端 Panel
|
||
- **WHEN** Debug 包内打开 GM Panel
|
||
- **THEN** 显示所有可用命令列表(按分类分 Tab)
|
||
- **AND** 点击命令 → 弹输入框 → 发送 RPC `C2G_GmCommand` 到服务端执行
|
||
|
||
#### Scenario: 命令注册统一
|
||
- **WHEN** 开发者新增 GM 命令
|
||
- **THEN** 用 `[GmCommand("AddItem", "发放道具")]` 属性标记即可
|
||
- **AND** 服务端 Console 和客户端 Panel 都自动出现
|
||
|
||
### Requirement: GM 命令必覆盖全部业务写操作
|
||
|
||
每个新增的服务端 RPC Handler MUST 同时新增对应的 GM 命令。**这是 P1-P3 阶段唯一的业务验证手段**。MUST 在 CI 中自动校验:每个 RPC Handler 都有至少一条对应 GM 命令的注册。
|
||
|
||
#### Scenario: 新增 Handler 必带 GM
|
||
- **WHEN** 开发者新增 `C2G_HeroSkillUpHandler`
|
||
- **THEN** MUST 同时新增 `gm HeroSkillUp <heroId> <skillIndex>` 命令
|
||
- **AND** CI 检查脚本扫描发现缺少 GM 时构建失败
|
||
|
||
#### Scenario: GM 命令调业务接口
|
||
- **WHEN** `gm HeroSkillUp` 被执行
|
||
- **THEN** MUST 调用真实的业务层 API(如 `HeroComponentSystem.SkillUp(hero, skillIndex)`)
|
||
- **AND** NOT 直接修改 Hero 字段(避免绕过业务校验导致数据不一致)
|
||
|
||
### Requirement: GM 命令分类
|
||
|
||
GM 命令 MUST 按以下分类组织:
|
||
|
||
| 类别 | 命令前缀 | 典型命令 |
|
||
|---|---|---|
|
||
| 资源发放 | `Add*` `Set*` | AddItem / AddCurrency / AddHero / SetItemCount |
|
||
| 英雄操作 | `Hero*` | HeroLevelUp / HeroStarUp / HeroSkillUp / HeroAwake / HeroWearEquip |
|
||
| 背包操作 | `Bag*` | BagClear / BagShow / BagDump |
|
||
| 抽卡操作 | `Gacha*` | Gacha / ResetGachaGuarantee / SetGachaCount |
|
||
| 数据查询 | `Show*` `Dump*` | ShowHeroes / ShowBag / ShowGachaStats / DumpPlayer |
|
||
| 数据快照 | `Snapshot*` `Restore*` | Snapshot / Restore / ListSnapshots |
|
||
| 批量模拟 | `Simulate*` | SimulateGacha / SimulateBatchLogin |
|
||
| 数据校验 | `Verify*` | VerifyConsistency / VerifyConfigRef |
|
||
| 服务管理 | (其他) | ReloadConfig / FlushDirty / Kick / SetLogLevel |
|
||
|
||
#### Scenario: 列出所有命令
|
||
- **WHEN** 执行 `gm help`
|
||
- **THEN** 按分类输出所有可用命令 + 简要描述 + 参数说明
|
||
|
||
#### Scenario: 命令搜索
|
||
- **WHEN** 执行 `gm help Gacha`
|
||
- **THEN** 仅输出 Gacha 类别的命令
|
||
|
||
### Requirement: 数据查询命令输出 jsonl
|
||
|
||
`Show*` 和 `Dump*` 类查询命令 MUST 输出 jsonl 或可解析的结构化数据(默认 jsonl,可选 `--pretty` 输出格式化 JSON),便于后续 `jq` 处理或对比。
|
||
|
||
#### Scenario: ShowBag 输出
|
||
- **WHEN** 执行 `gm ShowBag playerId=1001`
|
||
- **THEN** 控制台输出形如:
|
||
```jsonl
|
||
{"itemId":1,"name":"金币","count":12345}
|
||
{"itemId":2,"name":"钻石","count":50}
|
||
{"itemId":10001,"name":"英雄A碎片","count":3}
|
||
```
|
||
- **AND** 可重定向到文件用 `jq` 处理
|
||
|
||
#### Scenario: DumpPlayer 完整快照
|
||
- **WHEN** 执行 `gm DumpPlayer 1001`
|
||
- **THEN** 输出完整玩家数据 JSON:Hero/Bag/Gacha/Equip/Talent
|
||
- **AND** 输出大小可能很大(数百 KB),自动写文件并提示路径
|
||
|
||
### Requirement: 抽卡概率模拟与审计
|
||
|
||
GM MUST 提供 `gm SimulateGacha <poolId> <times>` 命令:服务端循环 N 次抽卡(不消耗真实玩家资源)输出统计数据。MUST 输出每个品质的实际命中率、保底触发次数、与配置期望误差百分比。
|
||
|
||
#### Scenario: 概率审计
|
||
- **WHEN** 执行 `gm SimulateGacha 1 10000`
|
||
- **THEN** 服务端循环 10000 次抽卡(不修改玩家数据)
|
||
- **AND** 输出形如:
|
||
```
|
||
Pool 1 - SimulateGacha 10000 times
|
||
Rare 5: 145 hits (1.45%), expected 1.50%, diff -3.33%
|
||
Rare 4: 1212 hits (12.12%), expected 12.00%, diff +1.00%
|
||
Rare 3: 8643 hits (86.43%), expected 86.50%, diff -0.08%
|
||
Guarantee triggered: 1 times
|
||
```
|
||
- **AND** 误差超过 ±5% 时标红警告
|
||
|
||
#### Scenario: 不影响真实玩家
|
||
- **WHEN** 模拟抽卡执行
|
||
- **THEN** 真实玩家的 GachaComponent.PoolStats 不变
|
||
- **AND** 不发奖励、不扣消耗
|
||
- **AND** 不写真实审计日志(仅写 GM 模拟日志)
|
||
|
||
### Requirement: 数据快照与回滚
|
||
|
||
GM MUST 提供 `Snapshot` / `Restore` 命令:保存当前玩家完整数据,后续可恢复。用于 A/B 测试不同操作链路、验证数据一致性。
|
||
|
||
#### Scenario: 测试场景前后对比
|
||
- **WHEN** 执行 `gm Snapshot before_test playerId=1001`
|
||
- **AND** 执行一系列业务操作
|
||
- **AND** 执行 `gm Snapshot after_test playerId=1001`
|
||
- **AND** 执行 `gm DiffSnapshot before_test after_test`
|
||
- **THEN** 输出两个快照的差异(jsonl 格式)
|
||
- **AND** 便于验证"某操作的副作用是否符合预期"
|
||
|
||
#### Scenario: 一键回滚
|
||
- **WHEN** 测试发现某操作导致数据错乱
|
||
- **AND** 执行 `gm Restore before_test playerId=1001`
|
||
- **THEN** 玩家数据完全恢复到快照时点
|
||
- **AND** 内存 + DB 都同步更新
|
||
|
||
### Requirement: 数据一致性校验
|
||
|
||
GM MUST 提供 `gm VerifyConsistency <playerId>` 命令:扫描玩家所有数据,校验关键不变式(invariants):
|
||
- 装备的 heroId 是否真实存在
|
||
- 英雄的等级是否符合稀有度上限
|
||
- 背包道具的 itemId 是否在 Config 中存在
|
||
- 抽卡保底计数是否 ≤ 配置阈值
|
||
- DB 数据与内存数据是否一致
|
||
|
||
#### Scenario: 发现脏数据
|
||
- **WHEN** 玩家数据中存在 `Equip { heroId: 99999 }`(不存在的英雄)
|
||
- **AND** 执行 `gm VerifyConsistency 1001`
|
||
- **THEN** 输出 `[ERR] Equip itemIndex=123 references invalid heroId=99999`
|
||
- **AND** 输出修复建议
|
||
|
||
#### Scenario: 全服校验
|
||
- **WHEN** 执行 `gm VerifyConsistency --all`
|
||
- **THEN** 对所有在线玩家执行校验
|
||
- **AND** 汇总输出错误统计
|
||
|
||
### Requirement: GM 操作审计
|
||
|
||
每个 GM 命令执行 MUST 写 L2 审计日志(同 observability-logging 中的定义),含 operator(GM 操作者标识)、action(命令名)、targetPlayer(如有)、args(参数)、success(是否成功)。失败的命令也 MUST 记录。
|
||
|
||
#### Scenario: GM 操作审计日志
|
||
- **WHEN** 执行 `gm AddItem 1 1000000 playerId=1001`
|
||
- **THEN** `Logs/Audit/GM/{date}.jsonl` 追加:
|
||
```json
|
||
{"ts":"...","level":"WARN","module":"GM","operator":"admin","action":"AddItem","targetPlayer":1001,"args":{"itemId":1,"count":1000000},"success":true}
|
||
```
|
||
- **AND** 审计日志同步落盘,不允许丢失
|
||
|
||
#### Scenario: 失败也记录
|
||
- **WHEN** GM 执行不存在的命令或参数错误
|
||
- **THEN** 仍然记录审计日志,success=false,附 errorMessage
|
||
|
||
### Requirement: Release 包剥离 GM 代码
|
||
|
||
GM 命令的注册、命令实现、客户端 Panel MUST 通过编译条件 `#if !RELEASE` 包裹,Release 构建时**完全不编译进包**。MUST 通过 CI 校验:Release IL 中不含 GM 命令字符串。
|
||
|
||
#### Scenario: Release 包不含 GM
|
||
- **WHEN** 用 `Build Settings: Release` 出包
|
||
- **AND** 反编译产物(dnSpy / il2cpp dump)
|
||
- **THEN** 不存在任何 `GmCommand` 属性或注册代码
|
||
- **AND** 不含 GM 命令名字符串(如 "AddItem")
|
||
|
||
#### Scenario: Debug 包正常启用
|
||
- **WHEN** 用 Debug 配置编译
|
||
- **THEN** GM Panel 可打开
|
||
- **AND** 所有命令可用
|
||
|
||
### Requirement: 阶段性使用约束
|
||
|
||
在 OpenSpec 当前 change(port-rok-hero-bag-system)的 P1-P3 阶段,业务验证 MUST 通过 GM 命令 + 业务日志,**禁止为了验证而临时写 YIUI Panel**。所有 P1-P3 落地的 RPC Handler MUST 有对应的 GM 命令验证场景在 tasks.md 中明确记录。
|
||
|
||
#### Scenario: P1 抽卡完成验证
|
||
- **WHEN** P1 阶段 GachaComponentSystem.Draw 实现完成
|
||
- **THEN** MUST 通过以下 GM 命令组合验证:
|
||
- `gm AddCurrency 2 100` → 给自己钻石
|
||
- `gm Gacha 1 1` → 单抽
|
||
- `gm ShowBag` → 检查奖励到账、钻石扣除
|
||
- `gm SimulateGacha 1 10000` → 概率审计
|
||
- **AND** 验证场景必须在 tasks.md 的 2.0.7 节明确列出
|
||
|
||
#### Scenario: 禁止临时 UI
|
||
- **WHEN** 开发者觉得"做个简单 UI 看看"更直观
|
||
- **THEN** **不允许**,必须通过 GM 命令验证
|
||
- **AND** UI 集中到 P4 阶段一次性做
|