claude doctor以及规则修改,修复

This commit is contained in:
2026-08-19 08:01:47 +08:00
parent b255730c1b
commit 685b629854
7 changed files with 173 additions and 139 deletions
+7 -14
View File
@@ -1,12 +1,12 @@
# CLAUDE.md
本文件为 Claude Code(claude.ai/code)在本仓库中工作时提供指导。**它是入口与强制指引**:三套文档的 README(含各自「红线速查 / 一页纸总则」)已通过 `@import` 常驻上下文(见下「文档」一节),**红线以它们为权威、始终在上下文里,本文件不再另抄一份**;规范的完整细节在 `docs/` 编号文档里,改动前先读对应文档。
本文件为 Claude Code(claude.ai/code)在本仓库中工作时提供指导。**它是入口与强制指引**:三套文档的「红线速查 / 一页纸总则」单页已通过 `@import` 常驻上下文(见下「文档」一节),**红线以它们为权威、始终在上下文里,本文件不再另抄一份**;规范的完整细节在 `docs/` 编号文档里,改动前先读对应文档。
## 这是一个什么仓库
一个友乐/gameabc 房卡类小游戏平台:浏览器端由私有的 `gameabc.min.js` 2D Canvas 引擎驱动,服务端是一个 Node.js 游戏服务器平台(`youle` 应用),双方通过 WebSocket/HTTP 收发 JSON 包通信。项目根目录**没有 `package.json`、没有 npm 构建/lint/测试流水线**——这是纯静态 JS:客户端靠 `<script>` 标签加载,服务端靠平台自带的 `min_loadJsFile`/`require` 加载。
`client/js/01_SubGame/codes/` 和 `server/games` 目前都是**空的**——尚未接入任何具体子游戏。本仓库是平台层 + 可复用框架的脚手架,未来第一个子游戏会在此基础上搭建。
本仓库是平台层 + 可复用框架的脚手架,尚未接入任何具体子游戏,未来第一个子游戏会在此基础上搭建。
本仓库是**模板项目**——后续会被克隆出去开新项目。因此所有需要长期保留的约定与知识**一律进仓库**(`docs/` 权威文档、本 CLAUDE.md、`.claude/` 钩子与 `settings.json`、`.githooks/`),**不依赖 Claude memory**(memory 按机器/会话存放、不随 `git clone` 迁移,对模板克隆无效)。
@@ -56,20 +56,13 @@ git config core.hooksPath .githooks
三者互补不冲突:engineering 讲「该怎么设计、怎么长久演进」,dev-guide 讲「平台怎么接、红线是什么」;硬红线冲突时以 dev-guide 为准。
以下三份 README(含各自「红线速查 / 一页纸总则」)通过 `@import` **自动加载进每次会话的上下文**——红线以它们为权威,无需在本文件另抄一份;改动具体区域时再深入读对应编号文档:
以下三份「红线速查 / 一页纸总则」通过 `@import` **自动加载进每次会话的上下文**——红线以它们为权威,无需在本文件另抄一份;改动具体区域时再深入读对应编号文档:
@docs/client/development-guide/README.md
@docs/server/development-guide/README.md
@docs/games/engineering/README.md
各篇「编号 ↔ 主题」见上面 `@import` 的三份 README 的「阅读顺序 / 阅读导航」表,此处不再复述。
具体子游戏「二七王」(server/games/erqiwang/)另有两份权威文档,改动该子游戏前必读:
- server/games/erqiwang/docs/design/design.md —— *二七王玩法规则的唯一权威说明,必须严格遵守*。牌局构成、主牌顺序、叫分坐庄、出牌/跟牌/甩牌、捡分扣底、算子升级、算奖、房间选项等一切玩法规则以此为准;代码实现与该文档冲突时,属于代码缺陷,应改代码去符合规则(除非该规则项标注为「待确认」),*不得反过来改规则去迁就代码*。
- server/games/erqiwang/docs/protocol/packet_protocol.md —— 二七王前后端收发包协议与包数据定义,*必须完全符合子游戏服务器代码实现(server/games/erqiwang/\*.js**)***。方向与 design.md 相反:协议文档是对既有代码行为的如实记录,两者不一致时以*代码为准*——应修改本协议文档去适配代码;代码新增/删除包或字段时,也要同步补全/删除本文档对应条目。
@docs/client/development-guide/红线速查.md
@docs/server/development-guide/红线速查.md
@docs/games/engineering/一页纸总则.md
各套文档的「编号 ↔ 主题」导航表、面向人的阅读顺序与定位说明留在各自 README(`docs/client/development-guide/README.md`、`docs/server/development-guide/README.md`、`docs/games/engineering/README.md`)——那些是查阅时才需要的内容,不常驻上下文,需要时直接读。
## 测试与 Git
+5 -45
View File
@@ -18,7 +18,8 @@
| 篇 | 文档 | 解决什么问题 |
|----|------|--------------|
| 00 | 本文 README | 这套文档是什么、怎么读、红线速查 |
| 00 | 本文 README | 这套文档是什么、怎么读 |
| — | [红线速查.md](./红线速查.md) | **一页纸分层模型 + 红线清单**(由 `CLAUDE.md` 常驻加载,改代码前先对照) |
| 01 | [01-前端架构与运行环境.md](./01-前端架构与运行环境.md) | 双运行时/ES5、平台/框架/子游戏三层、新旧架构并存、目录与加载顺序 |
| 02 | [02-渲染与UI组件体系.md](./02-渲染与UI组件体系.md) | 精灵 ID 体系、SpriteManager 分层、资源常量组织、BaseComponent 组件化、组件数据/set-refresh 范式、UIManager 场景、动态列表 |
| 03 | [03-事件·动画·音频·Spine.md](./03-事件·动画·音频·Spine.md) | EventBus、AnimationManager+配置、AudioManager+音效资源、SpineMgr 全链路 |
@@ -30,51 +31,11 @@
---
## 一页纸:前端分层模型
## 一页纸与红线
```
gameabc.min.js(引擎,js/vendor,第三方)
▲
GameABCUtils(core,唯一直接调引擎原生 API 的模块)
▲
SpriteManager(core,业务级精灵 API:ID 校验 + 单位换算) EventBus / AnimationManager / AudioManager / SpineMgr(system)
▲ ▲
BaseComponent / UIManager(ui,组件化与场景) ← gameabc-framework(游戏中立,可复用)
══════════════════════════════════════════════════════════════════════════
01_SubGame/codes(子游戏实例,单向依赖框架;内部结构由子游戏自行组织)
shared 前后端共享算法(与服务端同源,脚本同步,只读)
══════════════════════════════════════════════════════════════════════════
旧受限对接层(平台入口,尽量不改)
00/01/02_SubGame_*.js → Game_Modify.StartWar / Reconnect / _ReceiveData / appStart
```
**分层模型与红线清单已独立成篇:[红线速查.md](./红线速查.md)。**
- **框架(gameabc-framework)**:游戏中立,提供精灵/组件/事件/动画/音频/Spine 通用能力,可被任何子游戏复用。
- **子游戏(01_SubGame/codes)**:单向依赖框架,在其内部自由组织实现(目录/文件命名由子游戏自定,仅 `shared/` 为只读同步副本)。
- **旧受限对接层**:平台框架的固定入口(`Game_Modify.*`),把平台事件转交给新架构,**尽量不改**(详见 01/04)。
---
## 红线速查(详见 05)
- **可编辑范围**:前端平台代码 `js/00_Surface/` 禁改;受限接口文件 `01_SubGame/00_/01_/02_SubGame_*.js` 不新增接口、尽量不改;其余在 `gameabc-framework/`、`01_SubGame/codes/` 内开发。
- **严格 ES5**:用 `var`/`function`/`Object.create`,禁 `let`/`const`/箭头/模板串/`class`。
- **框架游戏中立**:`gameabc-framework/` 内**不得**出现任何具体玩法逻辑或专属常量;玩法专属的事件/资源/配置一律定义在子游戏侧(如玩法事件在子游戏的事件常量文件里追加到 `EventBus.Events`)。
- **精灵只走 SpriteManager**:UI 代码**禁止**直接调 `GameABCUtils` 或引擎原生 API;ID 必须落在规定范围(精灵 1001–3000、群组 ≥201、图层 101–200/弹窗 301–400,另有 501–600、701+ 备用段,框架保留 1–1000 及 3001+ 等交替段)。
- **常量集中、禁硬编码**:UI 代码**禁止任何硬编码 id / 裸值**——精灵 ID / 群组 ID / 图层 ID / 图片资源 ID / 声音 ID / Spine 资源 / 坐标尺寸 / 动画时长 / 事件名 一律定义在对应常量文件、只引用常量。
- **服务端权威、前端只展示**:核心运算与胜负裁定以服务端为准、数据以服务端为权威;前端只做界面展示与玩家交互,本地数据仅为渲染副本,不做权威计算。
- **数据驱动、前端无对局状态机**:阶段、轮次/控制权、可用操作、倒计时、分数、按钮该不该出现,一律**读服务端下发的权威字段**渲染;**禁止**前端自建状态机、由本地规则推导「现在轮到谁/进入哪个阶段/显示哪些按钮」,**禁止**用本地定时器自行推进阶段或自判超时(超时与托管由服务端驱动,倒计时前端只做插值显示)。包里没有的状态是**服务端漏发**,修在服务端发包处,前端不推、不补、不猜(详见 05 §6.1)。
- **视图 = f(服务端快照)**:`this.data` 只是服务端状态的镜像,**不得存在只活在前端、服务端不知道的对局态**。判据:任意时刻丢弃 `this.data`、仅凭最近一次服务端快照重画,界面必须完全一致——做不到即"前端私存了状态"或"服务端漏发了字段",都要修。只有不影响裁定的纯表现态(选中高亮、按下反馈、滚动位置、动画进度)可只存在于前端(详见 05 §6.2–6.3)。
- **请求包只带「意图」**:发包只带「做什么 + 目标标识」(操作类型、牌 `uniqueId`、`choiceIndex`),**禁止**回传前端算出的结论(分数/番数、"我胡了"之类判定、结算结果、阶段推进指令);前端 `shared/` 的计算只用于本地提示与预校验,结果不回传(详见 04 §1)。**前端能自己推出来的东西,就是玩家能改的东西**——这是防作弊的根本。
- **组件走 BaseComponent**:UI 组件继承 `BaseComponent`,事件用 `this.addEventListener` 注册(`destroy()` 自动清理,防泄漏);动态精灵/定时器在 `onDestroy` 清理。
- **组件数据自持 + set-refresh**:组件自有数据集中 `this.data`;每个 UI/零件有 `setXxx`(只写数据)/`refreshXxx`(只据数据画)对,组件有总 `refresh()` 可据数据重建界面。
- **UI 组件专职自己的界面**:每个界面的数据与渲染只由其对应 UI 组件实现;别的模块要改/刷该界面一律**调该组件的公开接口**(`setXxx`/`refreshXxx`/语义方法),**禁止**在别处重复实现重叠或类似的界面数据/渲染逻辑。
- **显隐走 `showXxx`/`hideXxx`**:UI 组件/零件的精灵、群组显隐必须由组件暴露的 `showXxx`/`hideXxx` 接口控制;**禁止**别处直接用其精灵 ID/群组 ID 去 `SpriteManager.show/hide` 控显隐。
- **发包只请求、收包才表现(悲观 UI / 输入-渲染解耦)**:用户点击**只发请求包**,对局状态界面(提示/按钮/控制权/倒计时/落牌/阶段)一律**收到服务端结果或推送包后**才更新,点击时不做乐观预测;收包处理器**触发源无关**(数据从包字段读,不依赖点击时的本地变量),故真人操作与 **AI 托管/他人广播共用同一更新路径、对前端透明**——挂在「点击」而非「收包」会导致 AI 托管时界面卡死(详见 04 §5)。**受控例外**:本玩家点击的**响应/掷骰交互按钮**(碰/杠/过/胡、手动掷骰按钮)的隐藏允许乐观清除(兼作防连点+即时反馈),前提是**服务端合法性验证 + 收包侧兜底(AI 托管一致)**,见 04 §5.6 与服务端 04 §8;其余提示/控制权/倒计时/落牌仍严格收包驱动。
- **数据优先、表现延后**:收到推送先 `setXxx` 写数据、再播动画;动画的开始/结束/出错回调里**只刷界面、不写核心数据**。即使动画缺失/卡住/出错,数据、逻辑、界面仍正确、互不影响。
- **重连即重画(断线重连 + 硬刷新都要处理)**:重连/页面重载的本质是**恢复数据 → 调各 UI 组件 set-refresh 恢复数据与界面状态**,复用同一条重画路径,不为重连单写一套渲染(详见 04 §3.3、05 §6)。
- **成败只认 `data.success`**:前端一律 `if (!data.success)` 判成败,**不**用 `status`/`code`,**不**写 `status` 兼容兜底。
- **收发包走统一通道**:发包经统一发送封装(底层 `RpcHelper` 自动注入平台字段),收包统一分发(一 rpc 一处理器);业务逻辑放处理器,**不**写进 `Game_Modify.*`。
- **shared 只读**:`01_SubGame/codes/shared/` 是服务端 `shared/` 的同步副本,**不在前端改**,改服务端权威源后跑同步脚本。
该文件由 `CLAUDE.md` 通过 `@import` 常驻每次会话上下文,是红线的权威源;本 README 只负责导航,不重复抄写红线(避免两处不同步)。红线的完整细节见 [05-开发规范与红线.md](./05-开发规范与红线.md)。
---
@@ -83,4 +44,3 @@ BaseComponent / UIManager(ui,组件化与场景) ← gameabc-framework(
- 服务端的对应文档见 [服务端开发指导文档](../../server/development-guide/);前端「成败标志 `data.success`」「收发包链路」与之同源,互为对照。
- 子游戏前端各层可能另有局部说明文档;本套是总纲,与之不冲突时以本套的通用原则为准。
- **平台无关的通用工程与架构规范**(分层、可扩展模式、配置化、数据权威、反模式与审查清单)见 [工程与架构通则](../../games/engineering/):本套讲前端接入与红线,工程通则讲前后端通用的设计方法论,互补阅读。
</content>
@@ -0,0 +1,53 @@
# 前端 · 一页纸分层模型与红线速查
> 本文是前端开发**必须常驻在手边**的那一页:分层模型 + 红线清单。
> 它由 `CLAUDE.md` 通过 `@import` 常驻每次会话上下文;**红线以本文为权威**。
> 文档导航、阅读顺序、各篇主题见 [README](./README.md);红线的完整细节见 [05-开发规范与红线.md](./05-开发规范与红线.md)。
---
## 一页纸:前端分层模型
```
gameabc.min.js(引擎,js/vendor,第三方)
▲
GameABCUtils(core,唯一直接调引擎原生 API 的模块)
▲
SpriteManager(core,业务级精灵 API:ID 校验 + 单位换算) EventBus / AnimationManager / AudioManager / SpineMgr(system)
▲ ▲
BaseComponent / UIManager(ui,组件化与场景) ← gameabc-framework(游戏中立,可复用)
══════════════════════════════════════════════════════════════════════════
01_SubGame/codes(子游戏实例,单向依赖框架;内部结构由子游戏自行组织)
shared 前后端共享算法(与服务端同源,脚本同步,只读)
══════════════════════════════════════════════════════════════════════════
旧受限对接层(平台入口,尽量不改)
00/01/02_SubGame_*.js → Game_Modify.StartWar / Reconnect / _ReceiveData / appStart
```
- **框架(gameabc-framework)**:游戏中立,提供精灵/组件/事件/动画/音频/Spine 通用能力,可被任何子游戏复用。
- **子游戏(01_SubGame/codes)**:单向依赖框架,在其内部自由组织实现(目录/文件命名由子游戏自定,仅 `shared/` 为只读同步副本)。
- **旧受限对接层**:平台框架的固定入口(`Game_Modify.*`),把平台事件转交给新架构,**尽量不改**(详见 01/04)。
---
## 红线速查(详见 05)
- **可编辑范围**:前端平台代码 `js/00_Surface/` 禁改;受限接口文件 `01_SubGame/00_/01_/02_SubGame_*.js` 不新增接口、尽量不改;其余在 `gameabc-framework/`、`01_SubGame/codes/` 内开发。
- **严格 ES5**:用 `var`/`function`/`Object.create`,禁 `let`/`const`/箭头/模板串/`class`。
- **框架游戏中立**:`gameabc-framework/` 内**不得**出现任何具体玩法逻辑或专属常量;玩法专属的事件/资源/配置一律定义在子游戏侧(如玩法事件在子游戏的事件常量文件里追加到 `EventBus.Events`)。
- **精灵只走 SpriteManager**:UI 代码**禁止**直接调 `GameABCUtils` 或引擎原生 API;ID 必须落在规定范围(精灵 1001–3000、群组 ≥201、图层 101–200/弹窗 301–400,另有 501–600、701+ 备用段,框架保留 1–1000 及 3001+ 等交替段)。
- **常量集中、禁硬编码**:UI 代码**禁止任何硬编码 id / 裸值**——精灵 ID / 群组 ID / 图层 ID / 图片资源 ID / 声音 ID / Spine 资源 / 坐标尺寸 / 动画时长 / 事件名 一律定义在对应常量文件、只引用常量。
- **服务端权威、前端只展示**:核心运算与胜负裁定以服务端为准、数据以服务端为权威;前端只做界面展示与玩家交互,本地数据仅为渲染副本,不做权威计算。
- **数据驱动、前端无对局状态机**:阶段、轮次/控制权、可用操作、倒计时、分数、按钮该不该出现,一律**读服务端下发的权威字段**渲染;**禁止**前端自建状态机、由本地规则推导「现在轮到谁/进入哪个阶段/显示哪些按钮」,**禁止**用本地定时器自行推进阶段或自判超时(超时与托管由服务端驱动,倒计时前端只做插值显示)。包里没有的状态是**服务端漏发**,修在服务端发包处,前端不推、不补、不猜(详见 05 §6.1)。
- **视图 = f(服务端快照)**:`this.data` 只是服务端状态的镜像,**不得存在只活在前端、服务端不知道的对局态**。判据:任意时刻丢弃 `this.data`、仅凭最近一次服务端快照重画,界面必须完全一致——做不到即"前端私存了状态"或"服务端漏发了字段",都要修。只有不影响裁定的纯表现态(选中高亮、按下反馈、滚动位置、动画进度)可只存在于前端(详见 05 §6.2–6.3)。
- **请求包只带「意图」**:发包只带「做什么 + 目标标识」(操作类型、牌 `uniqueId`、`choiceIndex`),**禁止**回传前端算出的结论(分数/番数、"我胡了"之类判定、结算结果、阶段推进指令);前端 `shared/` 的计算只用于本地提示与预校验,结果不回传(详见 04 §1)。**前端能自己推出来的东西,就是玩家能改的东西**——这是防作弊的根本。
- **组件走 BaseComponent**:UI 组件继承 `BaseComponent`,事件用 `this.addEventListener` 注册(`destroy()` 自动清理,防泄漏);动态精灵/定时器在 `onDestroy` 清理。
- **组件数据自持 + set-refresh**:组件自有数据集中 `this.data`;每个 UI/零件有 `setXxx`(只写数据)/`refreshXxx`(只据数据画)对,组件有总 `refresh()` 可据数据重建界面。
- **UI 组件专职自己的界面**:每个界面的数据与渲染只由其对应 UI 组件实现;别的模块要改/刷该界面一律**调该组件的公开接口**(`setXxx`/`refreshXxx`/语义方法),**禁止**在别处重复实现重叠或类似的界面数据/渲染逻辑。
- **显隐走 `showXxx`/`hideXxx`**:UI 组件/零件的精灵、群组显隐必须由组件暴露的 `showXxx`/`hideXxx` 接口控制;**禁止**别处直接用其精灵 ID/群组 ID 去 `SpriteManager.show/hide` 控显隐。
- **发包只请求、收包才表现(悲观 UI / 输入-渲染解耦)**:用户点击**只发请求包**,对局状态界面(提示/按钮/控制权/倒计时/落牌/阶段)一律**收到服务端结果或推送包后**才更新,点击时不做乐观预测;收包处理器**触发源无关**(数据从包字段读,不依赖点击时的本地变量),故真人操作与 **AI 托管/他人广播共用同一更新路径、对前端透明**——挂在「点击」而非「收包」会导致 AI 托管时界面卡死(详见 04 §5)。**受控例外**:本玩家点击的**响应/掷骰交互按钮**(碰/杠/过/胡、手动掷骰按钮)的隐藏允许乐观清除(兼作防连点+即时反馈),前提是**服务端合法性验证 + 收包侧兜底(AI 托管一致)**,见 04 §5.6 与服务端 04 §8;其余提示/控制权/倒计时/落牌仍严格收包驱动。
- **数据优先、表现延后**:收到推送先 `setXxx` 写数据、再播动画;动画的开始/结束/出错回调里**只刷界面、不写核心数据**。即使动画缺失/卡住/出错,数据、逻辑、界面仍正确、互不影响。
- **重连即重画(断线重连 + 硬刷新都要处理)**:重连/页面重载的本质是**恢复数据 → 调各 UI 组件 set-refresh 恢复数据与界面状态**,复用同一条重画路径,不为重连单写一套渲染(详见 04 §3.3、05 §6)。
- **成败只认 `data.success`**:前端一律 `if (!data.success)` 判成败,**不**用 `status`/`code`,**不**写 `status` 兼容兜底。
- **收发包走统一通道**:发包经统一发送封装(底层 `RpcHelper` 自动注入平台字段),收包统一分发(一 rpc 一处理器);业务逻辑放处理器,**不**写进 `Game_Modify.*`。
- **shared 只读**:`01_SubGame/codes/shared/` 是服务端 `shared/` 的同步副本,**不在前端改**,改服务端权威源后跑同步脚本。
+5 -18
View File
@@ -25,35 +25,22 @@
| 篇 | 文档 | 解决什么 |
|----|------|----------|
| 00 | 本文 README | 定位、适用范围、一页纸总则、与既有文档的关系 |
| 00 | 本文 README | 定位、怎么读、与既有文档的关系 |
| — | [一页纸总则.md](./一页纸总则.md) | **七大总则 + 适用边界**(由 `CLAUDE.md` 常驻加载,设计取舍时对照) |
| 01 | [01-架构总则与分层.md](./01-架构总则与分层.md) | 七大架构总则;前后端参考分层;依赖方向与稳定依赖 |
| 02 | [02-可扩展性与配置化.md](./02-可扩展性与配置化.md) | 扩展模式(注册表/策略/管线/工厂/事件)何时用;配置化与去硬编码;避免过度设计 |
| 03 | [03-数据权威·错误处理·演进.md](./03-数据权威·错误处理·演进.md) | 数据权威(前后端);错误处理与可观测;演进与重构;反模式与审查清单 |
---
## 一页纸:七大总则
## 一页纸总则
1. **单一权威数据源(SSOT)**:同一业务数据只有一个计算/写入处,其他只读;缺失即显式失败,不兜底掩盖。
2. **单向依赖**:分层自上而下依赖,稳定的被依赖、易变的作依赖方;**禁止环形依赖**。
3. **职责单一、边界清晰**:一个职能只在一个模块实现,别处**调用而非重造**。
4. **关注点分离**:决策与机制分离、数据与表现分离、编排与算法分离。
5. **对扩展开放、对修改封闭(OCP)**:用注册/策略/管线**加**能力,不改动已稳定的核心。
6. **配置优先于硬编码**:会变、复用、无语义的值一律外提为常量/配置,用数据驱动行为。
7. **显式失败优于隐式兜底**:关键路径缺数据就报错/返回 `null`,把问题暴露在最近处。
**七大总则与适用边界已独立成篇:[一页纸总则.md](./一页纸总则.md)。**
> 这七条互相支撑:**SSOT + 显式失败**保正确,**单向依赖 + 职责单一 + 关注点分离**保清晰,
> **OCP + 配置化**保可演进。任何设计取舍,回到这七条对照。
该文件由 `CLAUDE.md` 通过 `@import` 常驻每次会话上下文,是总则的权威源;本 README 只负责导航,不重复抄写总则(避免两处不同步)。总则的展开见 [01-架构总则与分层.md](./01-架构总则与分层.md)。
---
## 适用范围与边界
- **适用**:子游戏自身的前后端业务代码(玩法逻辑、对局编排、收发包处理、表现层、共享算法)。
- **不覆盖**:平台框架代码(不可改)、平台接入契约(见各端 `development-guide/`)。
- **与硬约束的关系**:ES5、`require` 守卫、可编辑范围、成败标志 `data.success` 等**硬红线**仍以
`development-guide/` 为准;本套是**方法论层**,与之互补不冲突。
## 与既有文档的关系
| 文档 | 定位 |
+29
View File
@@ -0,0 +1,29 @@
# 工程与架构通则 · 一页纸
> 本文是与平台无关的**工程方法论那一页**:七大总则 + 适用边界。
> 它由 `CLAUDE.md` 通过 `@import` 常驻每次会话上下文;**总则以本文为权威**。
> 文档导航、各篇主题、与既有文档的关系见 [README](./README.md)。
---
## 一页纸:七大总则
1. **单一权威数据源(SSOT)**:同一业务数据只有一个计算/写入处,其他只读;缺失即显式失败,不兜底掩盖。
2. **单向依赖**:分层自上而下依赖,稳定的被依赖、易变的作依赖方;**禁止环形依赖**。
3. **职责单一、边界清晰**:一个职能只在一个模块实现,别处**调用而非重造**。
4. **关注点分离**:决策与机制分离、数据与表现分离、编排与算法分离。
5. **对扩展开放、对修改封闭(OCP)**:用注册/策略/管线**加**能力,不改动已稳定的核心。
6. **配置优先于硬编码**:会变、复用、无语义的值一律外提为常量/配置,用数据驱动行为。
7. **显式失败优于隐式兜底**:关键路径缺数据就报错/返回 `null`,把问题暴露在最近处。
> 这七条互相支撑:**SSOT + 显式失败**保正确,**单向依赖 + 职责单一 + 关注点分离**保清晰,
> **OCP + 配置化**保可演进。任何设计取舍,回到这七条对照。
---
## 适用范围与边界
- **适用**:子游戏自身的前后端业务代码(玩法逻辑、对局编排、收发包处理、表现层、共享算法)。
- **不覆盖**:平台框架代码(不可改)、平台接入契约(见各端 `development-guide/`)。
- **与硬约束的关系**:ES5、`require` 守卫、可编辑范围、成败标志 `data.success` 等**硬红线**仍以
`development-guide/` 为准;本套是**方法论层**,与之互补不冲突。
+5 -62
View File
@@ -18,7 +18,8 @@
| 篇 | 文档 | 解决什么问题 |
|----|------|--------------|
| 00 | 本文 README | 这套文档是什么、怎么读、红线速查 |
| 00 | 本文 README | 这套文档是什么、怎么读 |
| — | [红线速查.md](./红线速查.md) | **一页纸运作模型 + 命名约定 + 红线清单**(由 `CLAUDE.md` 常驻加载,改代码前先对照) |
| 01 | [01-服务端环境与框架基础.md](./01-服务端环境与框架基础.md) | 平台与子游戏的关系、核心对象模型、三层路由、房间生命周期 |
| 02 | [02-子游戏接入与开发流程.md](./02-子游戏接入与开发流程.md) | 三文件架构、export/import 接口、makewar/重连、一次操作的完整数据流 |
| 03 | [03-数据收发与通信协议.md](./03-数据收发与通信协议.md) | 包结构、收发包方式、**下发面发全 / 按可见性裁剪 / 状态机唯一在服务端**、主动推送、成败标志协议、前后端对接点 |
@@ -28,67 +29,11 @@
---
## 一页纸:核心运作模型
## 一页纸与红线
```
客户端数据包 { app, route, rpc, data }
│
▼
packet_face.ReceivePack 按 pack.app 找到「应用」
│
▼
app.ReceivePack 按 pack.route 找到「模块」(子游戏)
│
▼
mod.DoPack 按 pack.rpc 调到「方法」 mod[rpc](pack)
│
▼
子游戏 RPC handler 校验玩家 → 取对局状态 → 执行业务 → 主动推送
│
▼
o_room.method.sendpack_toseat 按座位取连接信息(conmode/fromid) → 下发客户端
```
**运作模型、命名约定与红线清单已独立成篇:[红线速查.md](./红线速查.md)。**
- **平台框架**负责:网络收发、应用/模块/房间/玩家对象、路由、房卡、战绩、房间生命周期。
- **子游戏**负责:玩法规则、对局状态、每个操作的处理与广播。两者通过 **export / import** 两组接口对接。
> 上图是**入站半程**(前端 → 服务端 handler)。完整的往返链路(含服务端 `sendpack_toseat` 推回 → 前端 `Game_Modify._ReceiveData` 按 `rpc` 分发)见 [03 §6「端到端收发全链路」](./03-数据收发与通信协议.md#6-端到端收发全链路前后端对照),前端侧细节见 [前端 04 网络对接与启动编排](../../client/development-guide/04-网络对接与启动编排.md)。
---
## 命名约定:哪些是框架契约,哪些只是示例
本套文档的代码示例里出现的**具体名字,绝大多数是举例,并非强制标准**。请区分两类:
- **框架契约(必须照用)**——由平台代码决定,改了就对接不上:
- 数据包四字段 `{ app, route, rpc, data }`,其中 `app` 固定为 `"youle"`;
- 平台真实回调的 export 钩子名、你调用的 import 接口名(如 `makewar`、`get_deskinfo`、`check_player`、`deduct_roomcard`、`save_grade`);
- 成败字段 `data.success`;
- 平台 API 名(`cls_mod.new`、`min_loadJsFile`、`o_room.method.sendpack_toseat` / `sendpack_toother` 等)。
- **示例命名(可自定)**——本项目的举例,你的子游戏可按自己的风格命名:
- 业务 `rpc` 名(如 `playCard`、`declareHu`,只需**前后端约定一致**即可);
- handler / 类 / 文件 / 变量 / 分层目录名(如 `RpcHandler.handlePlayCard`、`OperationManager`、`ResponseBuilder`、`BroadcastManager`、`RoomAdapter`、`GameController`、`game/`、`rpc/`、`rules/` 等)。
> 一句话:**平台接缝上的名字是契约、要照用;接缝之内你自己代码里的名字都是示例、可自定。** 后续 01–04 的代码块同样遵循这条约定,不再逐处重复声明。
---
## 红线速查(详见 04)
> 以下每一条都有过真实事故或返工,**改代码前先对照**。
- **可编辑范围**:服务端只能改子游戏目录 `server/<游戏容器目录>/<你的游戏>/`(容器目录名由接入方自定,**非框架强制**,换任何不与框架冲突的目录皆可),`server/` 其余皆平台代码,禁改。接入新游戏还需在 `server/youle/app.js` 里加一行 `min_loadJsFile` 加载其 `mod.js`(唯一必须触碰的平台文件,属游戏注册接入点)。
- **双运行时**:代码同时跑在 Node 与浏览器/友乐平台。必须严格 **ES5**;`require` 只能写在文件开头 `if (typeof require !== 'undefined') {}` 守卫块内,**禁止函数体内/中途 require**。
- **成败标志唯一是 `data.success`**:前端只认 `if (!data.success)`;**主动推送的 `data` 必须自带 `success`**(mod 的 `return` 值不可靠下发前端);禁止用 `status`/`code` 判成败、禁止 `status` 兼容兜底。
- **数据权威**:同一业务数据只有一个权威来源,下游只读不重算;权威字段缺失要**显式报错/返回 null**,禁止 `|| 0`、`|| []`、`|| ''` 兜底掩盖。
- **模块职责边界**:一个职能只在一个模块实现,其他模块**调用而非重造**。
- **房间隔离**:一切随对局变化的状态都挂 `o_room.o_desk.data.*`;**禁止模块级单例/全局变量存对局态**;缓存/定时器/决策表不得只用 `seat` 作 key,必须 `房间 + seat`;结束/解散/开新局时按房间清理全部定时器与残留状态。
- **服务端自动操作复用真人链路**:服务端代替玩家做的操作(如 AI 托管)必须复用「真人操作」的同一套处理入口与广播链路,对前端透明,不为它单开一套下发分支。
- **服务器权威 + 发全下发面**:房卡、胜负、积分、状态等关键数据与操作一律以服务器为准、前端只作显示不自算;因此**服务端每个下发包必须携带前端界面所需的全部核心数据**,漏发修在服务端发包处、前端不补(详见 03 §1.2 / 04 §4)。
- **状态机唯一在服务端**:前端是**数据驱动**的、不持有对局状态机(见前端 05 §6),所以阶段、控制权(轮到谁)、该座位 `availableActions`、倒计时锚点**必须由服务端唯一维护并在相关下发包里显式给出**;**任何状态变更都要有包承载**——没有包的状态变化等于逼前端去猜。超时裁定在服务端,不接受前端上报"我超时了"(详见 03 §1.4)。
- **前端不是数据源,只收「意图」**:请求包只接受「操作类型 + 目标标识」;协议**不定义** `score`/`isWin`/`phase`/`nextSeat`/`handCards` 之类**结论入参**,即便客户端硬塞也一律**不读、不落地**,全部按服务端权威数据重算;包内 `seat` 只作一致性校验、身份由连接反查(详见 04 §8)。
- **发全 ≠ 发多,按可见性下发**:他人手牌、牌堆剩余序列、未公开判定、尚未揭晓的结果**不发给不该看到的座位**(差异化下发逐座位裁剪)。**下发即泄露**——包到了客户端就能被抓包看到,"前端拿到但不渲染"不算防护(详见 03 §1.3)。
- **测试纪律**:测试失败先用证据裁定「业务缺陷 vs 测试脚本缺陷」,禁止 skip/软化断言/吞异常掩盖;**正式代码不得为兼容测试而加逻辑**。
该文件由 `CLAUDE.md` 通过 `@import` 常驻每次会话上下文,是红线的权威源;本 README 只负责导航,不重复抄写红线(避免两处不同步)。红线的完整细节见 [04-开发规范与红线.md](./04-开发规范与红线.md)。
---
@@ -97,5 +42,3 @@ o_room.method.sendpack_toseat 按座位取连接信息(conmode/fromid) → 下
- 本套文档是平台级收发包/子游戏开发规范的**落地版**:结合本仓库真实代码,给出可直接照做的接入步骤与红线,并对其中已被本项目实践修正的部分(如成败标志由 `status` 收敛为 `success`)以本套为准。
- 各子游戏内部的架构细节(模块划分、算法)仍以各自 `<游戏容器目录>/<游戏>/docs/` 为准。
- **平台无关的通用工程与架构规范**(分层、可扩展模式、配置化、数据权威、反模式与审查清单)见 [工程与架构通则](../../games/engineering/):本套讲"平台怎么接、红线是什么",工程通则讲"该怎么设计、怎么长久演进",互补阅读。
</content>
</invoke>
@@ -0,0 +1,69 @@
# 服务端 · 一页纸运作模型与红线速查
> 本文是服务端开发**必须常驻在手边**的那一页:运作模型 + 命名约定 + 红线清单。
> 它由 `CLAUDE.md` 通过 `@import` 常驻每次会话上下文;**红线以本文为权威**。
> 文档导航、阅读顺序、各篇主题见 [README](./README.md);红线的完整细节见 [04-开发规范与红线.md](./04-开发规范与红线.md)。
---
## 一页纸:核心运作模型
```
客户端数据包 { app, route, rpc, data }
│
▼
packet_face.ReceivePack 按 pack.app 找到「应用」
│
▼
app.ReceivePack 按 pack.route 找到「模块」(子游戏)
│
▼
mod.DoPack 按 pack.rpc 调到「方法」 mod[rpc](pack)
│
▼
子游戏 RPC handler 校验玩家 → 取对局状态 → 执行业务 → 主动推送
│
▼
o_room.method.sendpack_toseat 按座位取连接信息(conmode/fromid) → 下发客户端
```
- **平台框架**负责:网络收发、应用/模块/房间/玩家对象、路由、房卡、战绩、房间生命周期。
- **子游戏**负责:玩法规则、对局状态、每个操作的处理与广播。两者通过 **export / import** 两组接口对接。
> 上图是**入站半程**(前端 → 服务端 handler)。完整的往返链路(含服务端 `sendpack_toseat` 推回 → 前端 `Game_Modify._ReceiveData` 按 `rpc` 分发)见 [03 §6「端到端收发全链路」](./03-数据收发与通信协议.md#6-端到端收发全链路前后端对照),前端侧细节见 [前端 04 网络对接与启动编排](../../client/development-guide/04-网络对接与启动编排.md)。
---
## 命名约定:哪些是框架契约,哪些只是示例
本套文档的代码示例里出现的**具体名字,绝大多数是举例,并非强制标准**。请区分两类:
- **框架契约(必须照用)**——由平台代码决定,改了就对接不上:
- 数据包四字段 `{ app, route, rpc, data }`,其中 `app` 固定为 `"youle"`;
- 平台真实回调的 export 钩子名、你调用的 import 接口名(如 `makewar`、`get_deskinfo`、`check_player`、`deduct_roomcard`、`save_grade`);
- 成败字段 `data.success`;
- 平台 API 名(`cls_mod.new`、`min_loadJsFile`、`o_room.method.sendpack_toseat` / `sendpack_toother` 等)。
- **示例命名(可自定)**——本项目的举例,你的子游戏可按自己的风格命名:
- 业务 `rpc` 名(如 `playCard`、`declareHu`,只需**前后端约定一致**即可);
- handler / 类 / 文件 / 变量 / 分层目录名(如 `RpcHandler.handlePlayCard`、`OperationManager`、`ResponseBuilder`、`BroadcastManager`、`RoomAdapter`、`GameController`、`game/`、`rpc/`、`rules/` 等)。
> 一句话:**平台接缝上的名字是契约、要照用;接缝之内你自己代码里的名字都是示例、可自定。** 01–04 的代码块同样遵循这条约定,不再逐处重复声明。
---
## 红线速查(详见 04)
> 以下每一条都有过真实事故或返工,**改代码前先对照**。
- **可编辑范围**:服务端只能改子游戏目录 `server/<游戏容器目录>/<你的游戏>/`(容器目录名由接入方自定,**非框架强制**,换任何不与框架冲突的目录皆可),`server/` 其余皆平台代码,禁改。接入新游戏还需在 `server/youle/app.js` 里加一行 `min_loadJsFile` 加载其 `mod.js`(唯一必须触碰的平台文件,属游戏注册接入点)。
- **双运行时**:代码同时跑在 Node 与浏览器/友乐平台。必须严格 **ES5**;`require` 只能写在文件开头 `if (typeof require !== 'undefined') {}` 守卫块内,**禁止函数体内/中途 require**。
- **成败标志唯一是 `data.success`**:前端只认 `if (!data.success)`;**主动推送的 `data` 必须自带 `success`**(mod 的 `return` 值不可靠下发前端);禁止用 `status`/`code` 判成败、禁止 `status` 兼容兜底。
- **数据权威**:同一业务数据只有一个权威来源,下游只读不重算;权威字段缺失要**显式报错/返回 null**,禁止 `|| 0`、`|| []`、`|| ''` 兜底掩盖。
- **模块职责边界**:一个职能只在一个模块实现,其他模块**调用而非重造**。
- **房间隔离**:一切随对局变化的状态都挂 `o_room.o_desk.data.*`;**禁止模块级单例/全局变量存对局态**;缓存/定时器/决策表不得只用 `seat` 作 key,必须 `房间 + seat`;结束/解散/开新局时按房间清理全部定时器与残留状态。
- **服务端自动操作复用真人链路**:服务端代替玩家做的操作(如 AI 托管)必须复用「真人操作」的同一套处理入口与广播链路,对前端透明,不为它单开一套下发分支。
- **服务器权威 + 发全下发面**:房卡、胜负、积分、状态等关键数据与操作一律以服务器为准、前端只作显示不自算;因此**服务端每个下发包必须携带前端界面所需的全部核心数据**,漏发修在服务端发包处、前端不补(详见 03 §1.2 / 04 §4)。
- **状态机唯一在服务端**:前端是**数据驱动**的、不持有对局状态机(见前端 05 §6),所以阶段、控制权(轮到谁)、该座位 `availableActions`、倒计时锚点**必须由服务端唯一维护并在相关下发包里显式给出**;**任何状态变更都要有包承载**——没有包的状态变化等于逼前端去猜。超时裁定在服务端,不接受前端上报"我超时了"(详见 03 §1.4)。
- **前端不是数据源,只收「意图」**:请求包只接受「操作类型 + 目标标识」;协议**不定义** `score`/`isWin`/`phase`/`nextSeat`/`handCards` 之类**结论入参**,即便客户端硬塞也一律**不读、不落地**,全部按服务端权威数据重算;包内 `seat` 只作一致性校验、身份由连接反查(详见 04 §8)。
- **发全 ≠ 发多,按可见性下发**:他人手牌、牌堆剩余序列、未公开判定、尚未揭晓的结果**不发给不该看到的座位**(差异化下发逐座位裁剪)。**下发即泄露**——包到了客户端就能被抓包看到,"前端拿到但不渲染"不算防护(详见 03 §1.3)。
- **测试纪律**:测试失败先用证据裁定「业务缺陷 vs 测试脚本缺陷」,禁止 skip/软化断言/吞异常掩盖;**正式代码不得为兼容测试而加逻辑**。