优化开发守则文档

This commit is contained in:
2026-07-05 22:29:20 +08:00
parent 2e59742bc9
commit ae43bd528f
15 changed files with 70 additions and 882 deletions
@@ -21,7 +21,7 @@
- `app/route/rpc` 三字段驱动三层路由(见 01)。
- `data` 里**必含平台参数**:`agentid`、`playerid`、`gameid`、`roomcode`、`seat` 等;数值参数收包时要 `parseInt`。
- **一包多信息**:一个响应包应携带本次状态变更的完整信息(出了什么牌、轮到谁、各家剩余、倒计时、比分……),减少往返。
- **一包多信息**:一个响应/推送包应带全「本次状态变更」所需的核心数据(出了什么牌、轮到谁、各家剩余、倒计时、比分……),使前端**仅凭本包 + 已有本地数据**就能把相关界面刷对——既减少往返,也避免"把一个界面状态拆成几个包拼、中途丢一个就停在自相矛盾的中间态"。("发全哪些"的具体要求见 §1.2。)
### 1.1 `route` / `routename` 从哪来、在哪定义、怎么匹配
@@ -44,6 +44,18 @@
> 一句话:**`routename` 在服务端 `cls_mod.new` 第二参定义(自定、应用内唯一),前端发包 `route` 必须与它逐字相同,平台据此把包投到你的模块**;改名要两端同步改。
### 1.2 发包必须自带前端界面所需的全部核心数据
**前端以服务端数据为权威——只做界面展示与交互、不做权威计算**(见 [`client 05`](../../../client/docs/development-guide/05-开发规范与红线.md))。推论落到服务端:**每个下发包都必须携带前端渲染/刷新该界面所需的全部核心数据**,让前端「据包写入数据 → 直接刷新出界面」,而**不需要前端自行推算、补全或兜底**。
- **界面要用的字段都要发全**:凡界面要显示、或前端 set/refresh 要用到的核心字段(出了什么牌、轮到谁、各家剩余张数、手牌/副露、分数/比分、倒计时锚点、庄家/座位、各类状态标志……)都要放进 `data`,不能让前端"猜"或本地推算权威结果。
- **漏发是服务端的缺陷,不许前端补**:前端遵循数据权威原则——权威字段缺失应显式报错/留空、不用 `|| 0`/`|| []` 兜造(见 client 05)。所以服务端漏发 = 前端界面缺数据,**修在服务端发包处,不在前端补洞**。
- **落实「一包多信息」(见 §1)**:本包要让前端**仅凭本包 + 已有本地数据**把相关界面刷对,别把一个界面状态拆成几个包拼(中途丢一个就停在自相矛盾的中间态)。
- **差异化但要发全**:每个座位只发它**该看到**的核心数据(手牌只发本人,见 §3),但"该看到的"必须发全、发准。
- **重连/中途加入发完整快照**:`get_deskinfo` 必须给出该座恢复整个界面所需的**全量**核心数据,让前端 `Reconnect` 据快照一次重画(见 §6.4、02)。
> 一句话:**服务端权威不仅是"服务端算",也是"服务端把界面要用的核心数据发全"——前端只据包渲染,不推算、不兜底。**
---
## 2. 收包处理的固定步骤(在 handler 里)
@@ -254,6 +266,7 @@ if (!data.success) { /* 失败处理 */ return; }
## 8. 小结
- 包结构恒为 `{app, route, rpc, data}`;收包先 `check_player`,失败静默 `return`。
- **下发包必须发全前端界面所需的核心数据**(§1.2):前端以服务端为权威、不自算,漏发修在服务端发包处、前端不补洞。
- 三种发包方式按"谁该看到什么"选;差异化广播逐座位定制,敏感信息只发本人。
- **主动推送是唯一可靠下发通道**,`return` 不算。
- **成败只认 `data.success`**,推送必自带 `success`,禁止 `status`/`code` 判成败与兼容兜底。
@@ -60,6 +60,7 @@ function bar() { var GSM = require('./dataStructures/GameStateManager.js'); }
- **禁止猜测兜底**:不用默认值掩盖缺失数据。**关键权威字段缺失要显式报错或返回 `null`**,由上层决定是否终止,**禁止** `|| 0`、`|| []`、`|| ''`、双源回退等掩盖。
- **下游不修补**:下游不得为"让流程跑起来"而补写/重算上游本该提供的数据;发现下游在修补,应把责任前移到权威来源。
- **边界**:只有展示层、纯 UI 兼容、非关键日志字段,才可在明确边界内用安全默认值。
- **下发面要发全**:因前端以服务端为权威、不自算权威结果,服务端**每个下发包必须携带前端界面所需的全部核心数据**(界面要显示、或前端 set-refresh 要用的字段都发全);漏发 = 前端缺数据,**修在服务端发包处、前端不补洞**(详见 [03 §1.2](./03-数据收发与通信协议.md))。
> 审查信号:看到 `|| []`、`|| 0`、`|| ''`、三元默认、双源字段,先判断它是不是**权威字段**(影响发牌/庄家/手牌/规则/结算/重连/状态流转)。是 → 改成权威读取;只是展示/日志 → 才可保留默认。
@@ -167,7 +168,7 @@ function bar() { var GSM = require('./dataStructures/GameStateManager.js'); }
| 范围 | 只改子游戏目录 `<容器目录>/<游戏>/` 与允许的前端范围 |
| 语言 | 纯 ES5;`require` 仅在顶部守卫块 |
| 成败 | 只认 `data.success`,推送必自带,禁 `status` 兜底 |
| 数据 | 权威唯一、缺失报错/返回 null、禁兜底、下游不修补 |
| 数据 | 权威唯一、缺失报错/返回 null、禁兜底、下游不修补;下发包发全前端界面所需核心数据 |
| 职责 | 一职能一模块,调用不重造 |
| 隔离 | 状态挂 `o_desk.data.*`,禁全局;`房间+seat` 作 key;定时器随房清理 |
| 自动操作 | 复用真人链路,对前端透明 |
+2 -1
View File
@@ -84,7 +84,7 @@ o_room.method.sendpack_toseat 按座位取连接信息(conmode/fromid) → 下
- **模块职责边界**:一个职能只在一个模块实现,其他模块**调用而非重造**。
- **房间隔离**:一切随对局变化的状态都挂 `o_room.o_desk.data.*`;**禁止模块级单例/全局变量存对局态**;缓存/定时器/决策表不得只用 `seat` 作 key,必须 `房间 + seat`;结束/解散/开新局时按房间清理全部定时器与残留状态。
- **服务端自动操作复用真人链路**:服务端代替玩家做的操作(如 AI 托管)必须复用「真人操作」的同一套处理入口与广播链路,对前端透明,不为它单开一套下发分支。
- **服务器权威**:房卡、胜负、积分、状态等关键数据与操作一律以服务器为准,前端数据仅供显示。
- **服务器权威 + 发全下发面**:房卡、胜负、积分、状态等关键数据与操作一律以服务器为准、前端只作显示不自算;因此**服务端每个下发包必须携带前端界面所需的全部核心数据**,漏发修在服务端发包处、前端不补(详见 03 §1.2 / 04 §4)。
- **测试纪律**:测试失败先用证据裁定「业务缺陷 vs 测试脚本缺陷」,禁止 skip/软化断言/吞异常掩盖;**正式代码不得为兼容测试而加逻辑**。
---
@@ -94,5 +94,6 @@ o_room.method.sendpack_toseat 按座位取连接信息(conmode/fromid) → 下
- 平台级、面向「所有子游戏」的总纲在 `docs/important/server/`(友乐框架收发包规范、子游戏开发要求)。
- 本套文档是其**落地版**:结合本仓库真实代码,给出可直接照做的接入步骤与红线,并对其中已被本项目实践修正的部分(如成败标志由 `status` 收敛为 `success`)以本套为准。
- 各子游戏内部的架构细节(模块划分、算法)仍以各自 `<游戏容器目录>/<游戏>/docs/` 为准。
- **平台无关的通用工程与架构规范**(分层、可扩展模式、配置化、数据权威、反模式与审查清单)见 [`docs/engineering/`](../../../docs/engineering/):本套讲"平台怎么接、红线是什么",`engineering/` 讲"该怎么设计、怎么长久演进",互补阅读。
</content>
</invoke>