模板工程升级修改
This commit is contained in:
@@ -238,7 +238,7 @@ if (!data.success) { /* 失败处理 */ return; }
|
||||
|
||||
## 7. 服务端代替玩家操作时的透明性
|
||||
|
||||
服务端自动替玩家执行的操作(如 AI 托管出牌/碰杠胡),**必须复用真人操作的同一套数据包与广播链路**:产生的包结构、`rpc`、`data` 与真人操作**完全一致**,前端无需也禁止为它单开一套接收/解析分支。唯一区别只在触发源(前端请求 → 服务端决策),其后的数据组织、协议、广播路径不变。(详见 04 的对应红线。)
|
||||
服务端自动替玩家执行的操作(如 AI 托管出牌/碰杠胡),**必须复用真人操作的同一套数据包与广播链路**:产生的包结构、`rpc`、`data` 与真人操作**完全一致**,前端无需也禁止为它单开一套接收/解析分支。唯一区别只在触发源(前端请求 → 服务端决策),其后的数据组织、协议、广播路径不变。(详见 [04 §7 自动操作复用真人链路](./04-开发规范与红线.md#7-服务端自动操作复用真人链路) 与 [§8 操作请求合法性验证](./04-开发规范与红线.md#8-操作请求合法性验证不可信客户端)。)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -102,7 +102,44 @@ function foo() { GameStateManager.doSomething(); }
|
||||
|
||||
---
|
||||
|
||||
## 8. Shared 文件同步流程
|
||||
## 8. 操作请求合法性验证(不可信客户端)
|
||||
|
||||
> 与 §7 配套:§7 保证"自动操作走真人链路",本节保证"到达的每个请求都合法才被链路执行"。
|
||||
|
||||
**客户端完全不可信**:它可能连点、乱序、丢包重发、篡改包体、发送非当前阶段/越权/当前不允许的操作。服务端**必须对每个到达的请求独立判定合法性**,非法即拒绝且**绝不改动任何对局状态**——就当这个包没来过。这是正确性的**根本防线**;前端的乐观清除/防连点(见前端 [04 §5.6](../../client/development-guide/04-网络对接与启动编排.md#56-受控例外响应交互按钮乐观清除--服务端合法性验证))只是体验层,**不承担正确性**。
|
||||
|
||||
### 五层验证栈(进业务前依次过闸,任一不过即 `success:false` + 不改状态)
|
||||
|
||||
1. **座位鉴权(防越权)**:操作者座位**由连接身份反查**(连接绑定的 playerid→座位,如平台 `check_player` 以 `fromid/conmode` 绑定),**绝不信任包体 `seat` 字段**;包内 seat 只做一致性校验,不等即拒。否则 A 玩家发 `{seat: B}` 就能替 B 操作。
|
||||
2. **阶段/状态门(防非当前阶段包)**:校验当前游戏阶段/状态允许该操作——非出牌阶段发出牌、非掷骰窗口发掷骰、无响应窗口发碰/过 → 拒绝。门控读 `gameState.phase` / `pendingResponse.waiting` / 是否轮到该座位(`currentPlayer === seat`)。
|
||||
3. **操作可用性校验(防"当前不允许的操作")——核心闸**:校验该操作**确实在该座位当前权威 `availableActions[seat]` 里**(没可碰的牌却发碰、没有胡机会却发胡 → 拒绝)。
|
||||
> 关键:`availableActions[seat]`(听牌/胡牌检测/操作枚举产出的**权威合法操作集**)**既是自动操作模块的决策选项来源,也是校验真人请求的准入白名单**——同一份权威数据兼任"决策依据"与"准入校验",统一了数据源唯一(§4)、自动操作职责边界(§5)、自动操作复用真人链路(§7)三条线。
|
||||
4. **幂等 / 去重 / 时序(连点的根治)**:不是限流,而是**状态机的单调推进**——一个操作被处理后立即推进状态并更新 `availableActions`,使**重复包因落在新 `availableActions` 之外而天然非法**;响应窗口对同座位重复响应去重(如 `addPlayerResponse` 记录后再收同座位响应即忽略);对已结束窗口/轮次的迟到包丢弃。
|
||||
5. **参数 / 数据合法性**:牌 `uniqueId` 是否真在该玩家手里、`targetCard`/`fromSeat`/`choiceIndex` 是否与权威牌局一致 —— 全按**服务端权威数据**校验,不信客户端传的牌面。
|
||||
|
||||
### 设计形态:统一裁决网关,默认拒绝(fail-closed)
|
||||
|
||||
把五层收敛成**一道所有操作共用的准入网关**(而非每个 handler 各写一遍散点 if)。网关默认 **deny**,仅当请求显式**命中该座位 `availableActions`** 且通过座位/阶段/参数校验才 **allow**。好处:
|
||||
|
||||
- **单点权威**:合法性判定只有一处,各 handler 不会判得不一致(呼应 §5 职责边界);
|
||||
- **自动操作与真人同闸**:自动操作(AI 托管)本就从 `availableActions` 选,必过同一网关,真正做到 §7 "自动操作 ≈ 服务端模拟一次**合法**真人操作"。
|
||||
|
||||
> **反模式警示**:若"是否轮到你/是否允许"的通用时序校验只存在于某个**从未被调用**的方法里(死代码),等于没有验证——尤其**出牌入口最易漏掉回合门**,导致任一玩家可在非自己回合越权出牌。验证网关必须**在统一入口真实生效**,并有测试覆盖。
|
||||
|
||||
### 失败返回:安静且明确
|
||||
|
||||
- 一律 `success:false` +(可选)`reason` 码,**绝不静默改状态**;失败推送前端只做提示/日志、**不改对局界面**(与前端 `if(!data.success)` 闭环)。
|
||||
- 最危险的是**"非法包被误当合法处理"(fail-open)**——默认拒绝就是为杜绝它。
|
||||
|
||||
### 验收(测试构造非法包,硬断言拒绝)
|
||||
|
||||
按 §11 测试纪律补单测:**非自己回合出牌 / 同回合连发两张 / 非当前阶段各操作 / 响应窗口关闭后迟到碰杠 → 断言 `success:false` 且状态不变**;**合法出牌各时机(摸后/碰后/杠后/报定后)→ 断言通过**(防回合门误伤合法出牌)。
|
||||
|
||||
> 一句话:**服务端对每个操作请求默认拒绝,仅当命中该座位当前权威 `availableActions` 且通过座位/阶段/参数校验才执行;操作一经消费即推进状态使重复包天然失效。正确性完全由这道网关保证,前端乐观清除只是体验。**
|
||||
|
||||
---
|
||||
|
||||
## 9. Shared 文件同步流程
|
||||
|
||||
### `shared/` 是什么:子游戏自己的游戏逻辑,与平台无关
|
||||
|
||||
@@ -127,7 +164,7 @@ function foo() { GameStateManager.doSomething(); }
|
||||
|
||||
---
|
||||
|
||||
## 9. 硬编码常量准则
|
||||
## 10. 硬编码常量准则
|
||||
|
||||
**需提取为常量**(满足任一):会随规则变化、跨模块复用、裸值无法自解释。典型:分数数值、状态/类型字符串标识、配置阈值、跨模块共享的 key 名。常量按语义归入 `shared/constants/` 下对应文件(分数类、规则配置类、类型枚举类)。
|
||||
|
||||
@@ -135,7 +172,7 @@ function foo() { GameStateManager.doSomething(); }
|
||||
|
||||
---
|
||||
|
||||
## 10. 测试纪律
|
||||
## 11. 测试纪律
|
||||
|
||||
- **测试唯一目的是验证业务正确性**。失败是有价值的信号,第一反应是**定位根因**,不是"让测试变绿"。
|
||||
- **禁止任何掩盖手段**:skip/条件 return、软化断言、放宽阈值、try-catch 吞异常、把硬断言改成"存在才校验"。
|
||||
@@ -151,7 +188,7 @@ function foo() { GameStateManager.doSomething(); }
|
||||
|
||||
---
|
||||
|
||||
## 11. Git 提交规范
|
||||
## 12. Git 提交规范
|
||||
|
||||
- **及时自动提交**:每完成一个可独立成立的逻辑改动(一个修复/功能/重构/一批测试)就**立即提交**,不堆积工作区。
|
||||
- **无需逐次询问**:完成阶段性改动后主动提交(`push` 按需)。
|
||||
@@ -159,7 +196,7 @@ function foo() { GameStateManager.doSomething(); }
|
||||
|
||||
---
|
||||
|
||||
## 12. 审查速查表
|
||||
## 13. 审查速查表
|
||||
|
||||
| 维度 | 红线 |
|
||||
|------|------|
|
||||
@@ -170,6 +207,7 @@ function foo() { GameStateManager.doSomething(); }
|
||||
| 职责 | 一职能一模块,调用不重造 |
|
||||
| 隔离 | 状态挂 `o_desk.data.*`,禁全局;`房间+seat` 作 key;定时器随房清理 |
|
||||
| 自动操作 | 复用真人链路,对前端透明 |
|
||||
| 请求验证 | 客户端不可信;每个操作请求过座位鉴权/阶段门/操作可用性/幂等去重/参数五层校验,统一网关默认拒绝、失败 `success:false` 不改状态;出牌回合门尤其不可漏(防死代码) |
|
||||
| Shared | 只改服务端 `shared/`,跑同步脚本 |
|
||||
| 测试 | 失败裁定归属、禁掩盖;正式代码不迁就测试 |
|
||||
| Git | 一事一提交、中文信息、及时提交 |
|
||||
|
||||
Reference in New Issue
Block a user