删除hook,框架改动
This commit is contained in:
@@ -33,6 +33,21 @@ RpcHelper.sendGameRpc(rpc, gameData, options); // route = "room"/子游戏路
|
||||
|
||||
> 平台字段缺失(如 `playerid` 为 0/空)应 fail-fast 暴露,不静默发出残缺包。
|
||||
|
||||
### 请求包只带「意图」,不带结论
|
||||
|
||||
**请求包表达的是「我想做什么」,不是「结果是什么」。** 这是前端数据驱动架构([05 §6](./05-开发规范与红线.md))在发包侧的必然推论:前端不是数据源,凡由前端算出并回传的"结论",都等于把裁定权交给了不可信的客户端。
|
||||
|
||||
| 可以带(意图) | 禁止带(结论) |
|
||||
|---|---|
|
||||
| 操作类型(出牌/碰/杠/过/胡/叫分…) | 算好的得分、番数、结算金额 |
|
||||
| 目标标识(牌的 `uniqueId`、`targetCard`、`choiceIndex`) | "我胡了 / 我听了 / 这步合法" 之类的判定结果 |
|
||||
| 座位号(仅供服务端做一致性校验,**不作身份依据**) | 下一阶段是什么、下一个该谁、剩余时间 |
|
||||
| 纯客户端偏好(音量、语言等非对局字段) | 手牌全量、他人信息等本应由服务端持有的状态 |
|
||||
|
||||
- **服务端按自己的权威数据重算**,对请求里出现的结论字段一律**忽略**(服务端侧见 [04 §8](../../server/development-guide/04-开发规范与红线.md#8-操作请求合法性验证不可信客户端));协议设计阶段就**不应该定义**这类入参——定义了它,就是留了一个可被伪造的洞。
|
||||
- **前端 `shared/` 算出的结果不回传**:它只用于本地提示与预校验(见 05 §6.1、§8),发包时只发意图。
|
||||
- **违例信号**:发包方法里出现 `score`、`isWin`、`nextSeat`、`phase`、`result`、`handCards` 之类"由前端填的结论字段"。
|
||||
|
||||
---
|
||||
|
||||
## 2. 收包:统一分发
|
||||
@@ -156,6 +171,7 @@ AI 托管:服务端 AI 决策 →┘
|
||||
|
||||
- **不得假设「本座刚点过」**:所需数据一律从**包内权威字段**读取,**禁止**依赖「点击时暂存的本地变量」。
|
||||
- **先判 `data.success`**(§4),再据包字段渲染;无对应点击也能正确渲染。
|
||||
- **不得据本地推断补齐服务端未下发的状态**:包里没有的阶段/控制权/可用操作,不由前端算出来顶上——那是服务端漏发,修在服务端发包处(见 [05 §6.1](./05-开发规范与红线.md)、服务端 [03 §1.2](../../server/development-guide/03-数据收发与通信协议.md))。
|
||||
|
||||
### 5.4 适用范围
|
||||
|
||||
@@ -235,6 +251,8 @@ AI 托管:服务端 AI 决策 →┘
|
||||
| DO ✅ | DON'T ❌ |
|
||||
|------|---------|
|
||||
| 发包走语义化发包封装/`RpcHelper` | 业务里直接 `Utl.sendData` 手拼包 |
|
||||
| 请求包只带意图(操作类型+目标标识) | 包里回传前端算出的分数/判定/阶段等结论字段 |
|
||||
| 阶段/控制权/可用操作/倒计时只读包内权威字段 | 前端自建对局状态机、本地推导或用定时器自行推进阶段 |
|
||||
| 收包统一进收包分发器,一 rpc 一处理器 | 在 `Game_Modify.*` 或散点里直接处理推送 |
|
||||
| 受限入口只解包转交,业务在新架构 handler | 在受限文件堆业务逻辑 |
|
||||
| 成败只认 `data.success` | 用 `status`/`code` 判成败、写 status 兜底 |
|
||||
|
||||
@@ -70,9 +70,43 @@
|
||||
|
||||
---
|
||||
|
||||
## 6. 数据权威、组件数据与表现延后
|
||||
## 6. 数据驱动架构:服务端状态的投影
|
||||
|
||||
**前端是服务端对局状态的一个投影(view),不是状态的第二个来源。** 阶段、轮次、控制权、可用操作、倒计时、分数、按钮可用性……一切对局态都由服务端唯一维护并随包下发,前端只做「**读包字段 → 写 `this.data` → 据 `this.data` 画界面**」这一条链路。这既是正确性要求(两端各推一套必然分叉),也是**反作弊**要求:**前端能自己推出来的东西,就是玩家能改的东西**。
|
||||
|
||||
- **服务端权威**:所有核心运算与胜负裁定以服务端为准、数据以服务端为权威;前端只做界面展示与玩家交互,本地数据仅为「渲染副本」,不做权威计算。
|
||||
|
||||
### 6.1 前端无对局状态机
|
||||
|
||||
- **状态机唯一在服务端**:阶段(`phase`)、当前控制权(轮到谁)、该座位可用操作(`availableActions`)、倒计时剩余、比分/结算——一律**读服务端下发的权威字段**渲染。
|
||||
- **禁止本地推导**:不得由「上一个包 + 本地规则」推出「现在该轮到谁 / 现在进入哪个阶段 / 现在该显示哪几个按钮」。缺字段是**服务端漏发**,修在服务端发包处(见服务端 [03 §1.2](../../server/development-guide/03-数据收发与通信协议.md)),**前端不推、不补、不猜**。
|
||||
- **禁止前端推进流程**:不得用本地定时器自动推进阶段、自行结算、超时后自判胜负或自动补发包。**超时/托管一律由服务端驱动**,前端只显示服务端下发的结果。
|
||||
- **倒计时的正确形态**:服务端下发锚点/剩余时长,前端可本地 tick 做插值显示;但**归零不代表状态改变**——超时的裁定与后续推进仍等服务端推送。
|
||||
- **`shared/` 预判不是状态**:前端跑 `shared/` 算出的胡牌/听牌/合法性只用于**提示与预校验**(如置灰不可点的牌),**不得**据其改写对局态、也**不得**用来替代服务端下发的 `availableActions`(见 §8)。
|
||||
|
||||
### 6.2 视图 = f(服务端快照)
|
||||
|
||||
- `this.data` 是**服务端状态的镜像**,不是第二份真相;**不得存在「只活在前端、服务端不知道」的对局态**。
|
||||
- **判据(可直接用于自检与代码审查)**:任意时刻丢弃全部 `this.data`,仅用**最近一次服务端快照**(重连 `get_deskinfo` 或最近一次推送)重画,界面与交互状态必须**完全一致**。做不到 → 要么前端私存了对局态、要么服务端漏发了字段,二者必居其一,**都要修**。
|
||||
- 这与「重连即重画」是同一条路径(见 [04 §3.3](./04-网络对接与启动编排.md)):**重连之所以能只靠服务端快照还原,正因为前端从来没有过独占状态。**
|
||||
|
||||
### 6.3 允许的本地 UI 态(白名单)
|
||||
|
||||
只有**不影响对局裁定、服务端根本不关心**的纯表现态,才可以只存在于前端:
|
||||
|
||||
| 允许只在前端 | 不允许(属对局态,必须来自服务端) |
|
||||
|---|---|
|
||||
| 选中/待出牌的高亮、拖拽位置 | 这张牌能不能出、出了之后轮到谁 |
|
||||
| 按钮按下高亮/缩放、点击音效 | 按钮**该不该出现**、**能不能点** |
|
||||
| 列表滚动位置、面板展开、设置开关 | 阶段、控制权、倒计时基准、分数、结算 |
|
||||
| 动画进度、特效播放中标记 | 手牌/牌河/副露内容、亮牌信息、托管状态 |
|
||||
|
||||
判别:**这个值若被玩家改成任意值,会不会影响对局结果、或让他看到/做到本不该的事?** 会 → 它是对局态,必须服务端权威。
|
||||
|
||||
> **与 [04 §5.6](./04-网络对接与启动编排.md#56-受控例外响应交互按钮乐观清除--服务端合法性验证) 受控例外的关系**:本玩家点击响应/掷骰交互按钮后的「乐观隐藏」仍然允许——它是抢先一步做了服务端稍后会确认的事,**不是前端私有状态**:按钮**该不该出现**依旧由服务端下发的 `availableActions` 决定,收包侧必须能独立得出同一结果(AI 托管无点击时也正确)。判据仍成立:丢弃 `this.data` 后按最近快照重画,该按钮的显隐与服务端一致。
|
||||
|
||||
### 6.4 组件数据与表现延后
|
||||
|
||||
- **组件数据自持 + set-refresh**:组件(及其下每个「零件 UI」)的自有数据集中在 `this.data`,**不散落**各处;每个 UI 都有 `setXxx`(只写数据)/`refreshXxx`(只据数据画界面)成对方法,组件另有总 `refresh()` 据 `this.data` 重建整块界面(见 02)。
|
||||
- **更新时机——发包只请求、收包才表现(悲观 UI / 输入-渲染解耦)**:用户点击**只发请求包**,**绝不**在点击时改动任何对局状态界面(提示显隐、按钮增删、当前控制权、倒计时启停、落牌、阶段/托管图标);这些表现一律在**收到服务端结果/推送包后**更新。点击回调只做「发包 +(可选)纯本地物理反馈(按下高亮/音效、防连点 `disable`)」。收包处理器**触发源无关**——数据从包字段读、不依赖「点击时暂存的本地变量」,故真人操作与 **AI 托管/他人广播共用同一更新路径、对前端透明**(无点击时也正确更新);把更新挂在「点击」而非「收包」会导致 AI 托管时界面卡死。详见 [04 §5](./04-网络对接与启动编排.md#5-输入渲染解耦发包只请求收包才表现)。**受控例外**:本玩家点击的**响应/掷骰交互按钮**(碰/杠/过/胡按钮、手动掷骰按钮)的**隐藏**允许乐观清除(兼作防连点+即时反馈),前提是**服务端合法性验证 + 收包侧兜底(AI 托管一致)**,见 [04 §5.6](./04-网络对接与启动编排.md#56-受控例外响应交互按钮乐观清除--服务端合法性验证) 与服务端 [04 §8](../../server/development-guide/04-开发规范与红线.md#8-操作请求合法性验证不可信客户端);其余提示/控制权/倒计时/落牌仍严格收包驱动。
|
||||
- **收包节奏**:**先 `setXxx` 写数据 →(必要时 `refresh` 刷静态界面)→ 再播动画 → 动画回调里只刷新界面**;动画的开始/结束/出错等生命周期回调里**绝不设置核心数据**,动画期间**不改数据**。
|
||||
@@ -91,7 +125,7 @@
|
||||
|
||||
## 8. shared 同步
|
||||
|
||||
- **`shared/` 只放"前后端都真正用到"的那部分逻辑,务必慎重**:进 `shared/` 的门槛不是"它是玩法逻辑",而是"**前端确实会用到、且两端必须算出完全一致的结果**"。因为**前端只做展示、不做核心运算**(服务端权威,见 §5)叠加**悲观 UI**(可操作项/提示/结果由服务端推送、前端不预测,见 §5),**前端实际会重算的逻辑很少**——发牌/胡牌裁定/计分/AI 等计算类逻辑前端根本不算(服务端算好推来展示即可),它们**不进 `shared/`**。只有本地即时预判/高亮所需的校验、或纯展示所需的规则常量/牌型映射等,才把**那一部分**提升进 `shared/`。**默认留服务端**,拿不准就别放。它不是平台代码,平台既不提供也不感知。
|
||||
- **`shared/` 是子游戏自己的游戏逻辑,与平台无关**:存放本玩法**前后端必须算出完全一致**的纯逻辑(胡牌/听牌/比精/牌型/计分/规则常量等),前端用于即时表现与预校验,服务端做权威裁定。它不是平台代码,平台既不提供也不感知。
|
||||
- `01_SubGame/codes/shared/` 是服务端 `server/<游戏容器目录>/<游戏>/shared/` 的**同步副本**,前端**只读**(脚本生成)。
|
||||
- 改共享算法只改服务端权威源,再运行同步脚本覆盖前端副本;**禁止直接编辑前端 `codes/shared/`**。
|
||||
- 逻辑同源不改变数据权威:前端 `shared/` 算的是表现/预判,最终以服务端为准(数据优先、表现延后,见 §6)。详见服务端 [04 §9 shared 文件同步流程](../../server/development-guide/04-开发规范与红线.md#9-shared-文件同步流程)。
|
||||
@@ -125,6 +159,8 @@
|
||||
| 渲染 | 只走 `SpriteManager`;ID 守范围、不编造;查返回值 |
|
||||
| 常量 | 精灵/群组/图层/图片/声音/Spine/坐标/动画/事件全进常量;UI 代码禁硬编码 id 与裸值 |
|
||||
| 组件 | 继承 `BaseComponent`;事件 `addEventListener`;`destroy` 清动态精灵/定时器 |
|
||||
| 数据驱动 | 前端**无对局状态机**:阶段/控制权/可用操作/倒计时/分数只读包内权威字段,禁本地推导与本地推进流程;`this.data` 只是服务端快照的镜像,丢弃后仅凭最近快照重画须完全一致;只有白名单纯表现态可只存在于前端(§6.1–6.3) |
|
||||
| 发包内容 | 请求包只带「意图」(操作类型+目标标识),**不带结论**(分数/判定结果/阶段指令);服务端按自己的权威数据重算(04 §1「请求包只带意图」) |
|
||||
| 数据 | 服务端权威(核心运算/裁定在服务端);组件自有数据集中 `this.data`;每个 UI/零件有 set/refresh 对、组件有总 `refresh` |
|
||||
| 更新时机 | 点击只发请求包,对局状态表现等**收包后**更新(悲观 UI);收包处理器触发源无关、数据从包字段读;AI 托管/他人广播共用同一更新路径。**受控例外**:响应/掷骰交互按钮隐藏可乐观清除(防连点+即时反馈),前提是服务端合法性验证+收包兜底(04 §5.6、服务端 04 §8) |
|
||||
| 节奏 | 先写数据后表现;动画回调只刷界面、**不写核心数据**;重画可随时据数据还原界面 |
|
||||
|
||||
@@ -18,60 +18,24 @@
|
||||
|
||||
| 篇 | 文档 | 解决什么问题 |
|
||||
|----|------|--------------|
|
||||
| 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 全链路 |
|
||||
| 04 | [04-网络对接与启动编排.md](./04-网络对接与启动编排.md) | 发包链路(RpcHelper 注入平台字段)、收包统一分发、新旧架构对接边界、启动编排、处理器/管理器职责 |
|
||||
| 05 | [05-开发规范与红线.md](./05-开发规范与红线.md) | 可编辑范围、ES5、框架中立、常量集中、组件生命周期、服务端权威/组件数据/表现延后、data.success、模块职责、测试 |
|
||||
| 05 | [05-开发规范与红线.md](./05-开发规范与红线.md) | 可编辑范围、ES5、框架中立、常量集中、组件生命周期、**数据驱动架构(前端无状态机/视图=服务端快照投影/本地 UI 态白名单)**、组件数据与表现延后、data.success、模块职责、测试 |
|
||||
| 06 | [06-子游戏接入模式与Hooks外置.md](./06-子游戏接入模式与Hooks外置.md) | 内联模式 vs Hooks 外置模式、三契约文件可改性、退化为纯转发壳 + SubGameHooks 委托、subgame-entry 模板 |
|
||||
|
||||
建议第一次**从 01 顺序读到 05**;之后把 02–05 当手册随用随查。06 在接入新游戏或迁移到 Hooks 外置模式时选读。
|
||||
|
||||
---
|
||||
|
||||
## 一页纸:前端分层模型
|
||||
## 一页纸与红线
|
||||
|
||||
```
|
||||
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 资源 / 坐标尺寸 / 动画时长 / 事件名 一律定义在对应常量文件、只引用常量。
|
||||
- **服务端权威、前端只展示**:核心运算与胜负裁定以服务端为准、数据以服务端为权威;前端只做界面展示与玩家交互,本地数据仅为渲染副本,不做权威计算。
|
||||
- **组件走 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)。
|
||||
|
||||
---
|
||||
|
||||
@@ -80,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/` 的同步副本,**不在前端改**,改服务端权威源后跑同步脚本。
|
||||
Reference in New Issue
Block a user