8.4 KiB
Raw Blame History

ADDED Requirements

Requirement: GM 双轨入口

系统 MUST 提供两种 GM 命令入口:服务端 GM Console命令行 + ET Watcher 注入)和客户端 GM PanelYIUI 简易面板)。两者共用同一套命令注册表,避免重复实现。

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 调用真实的业务层 APIHeroComponentSystem.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 控制台输出形如:
    {"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 输出完整玩家数据 JSONHero/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 中的定义),含 operatorGM 操作者标识、action命令名、targetPlayer如有、args参数、success是否成功。失败的命令也 MUST 记录。

Scenario: GM 操作审计日志

  • WHEN 执行 gm AddItem 1 1000000 playerId=1001
  • THEN Logs/Audit/GM/{date}.jsonl 追加:
    {"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

  • WHENBuild Settings: Release 出包
  • AND 反编译产物dnSpy / il2cpp dump
  • THEN 不存在任何 GmCommand 属性或注册代码
  • AND 不含 GM 命令名字符串(如 "AddItem"

Scenario: Debug 包正常启用

  • WHEN 用 Debug 配置编译
  • THEN GM Panel 可打开
  • AND 所有命令可用

Requirement: 阶段性使用约束

在 OpenSpec 当前 changeport-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 阶段一次性做