前端的阶段/状态/显示一律以服务器为准,前端不得自建状态机、不得本地推导或推进流程; 由此推出请求包只带意图、服务端按可见性下发两条防作弊约束。 - client 05 §6 改为「数据驱动架构:服务端状态的投影」,新增 6.1 前端无对局状态机、 6.2 视图=f(服务端快照)(丢弃 this.data 仅凭最近快照重画须一致)、6.3 本地 UI 态白名单 (并澄清与 04 §5.6 乐观清除例外的关系);原条目归入 6.4 - client 04 §1 新增「请求包只带意图,不带结论」;§5.3 补「不得据本地推断补齐未下发状态」 - server 03 新增 §1.3 按可见性下发(下发即泄露)、§1.4 状态机唯一在服务端并随包下发 - server 04 §4 补「前端不是数据源」;§8 新增「只接受意图入参,客户端回传结论一律忽略」 - 两份 README 红线速查与审查速查表同步补条目 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
前端 · 子游戏开发指导文档
本套文档是 gameabc 平台前端模板框架(
gameabc-framework) 下「子游戏前端」开发的通用指导与规范。 它讲清楚四件事:前端怎么分层运行、界面怎么用精灵与组件搭、事件/动画/音频/Spine 怎么用、怎么和服务端收发包并启动一局。文中以「麻将」一类房卡棋牌作举例,但
gameabc-framework是游戏中立的通用框架,所有结论不绑定具体玩法。 新开发者按本套文档即可理解前端运作、从模板派生出一个子游戏并写出符合规范的代码。
这套文档写给谁
- 新接手子游戏前端的开发者:先读 01、02 建立全局认知,再按 03、04 动手,05 随时回查。
- 正在开发/维护某子游戏前端的开发者:02–05 是日常手册与红线。
- 做代码审查的人:05 是审查清单来源。
阅读顺序
| 篇 | 文档 | 解决什么问题 |
|---|---|---|
| 00 | 本文 README | 这套文档是什么、怎么读、红线速查 |
| 01 | 01-前端架构与运行环境.md | 双运行时/ES5、平台/框架/子游戏三层、新旧架构并存、目录与加载顺序 |
| 02 | 02-渲染与UI组件体系.md | 精灵 ID 体系、SpriteManager 分层、资源常量组织、BaseComponent 组件化、组件数据/set-refresh 范式、UIManager 场景、动态列表 |
| 03 | 03-事件·动画·音频·Spine.md | EventBus、AnimationManager+配置、AudioManager+音效资源、SpineMgr 全链路 |
| 04 | 04-网络对接与启动编排.md | 发包链路(RpcHelper 注入平台字段)、收包统一分发、新旧架构对接边界、启动编排、处理器/管理器职责 |
| 05 | 05-开发规范与红线.md | 可编辑范围、ES5、框架中立、常量集中、组件生命周期、数据驱动架构(前端无状态机/视图=服务端快照投影/本地 UI 态白名单)、组件数据与表现延后、data.success、模块职责、测试 |
| 06 | 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
- 框架(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/的同步副本,不在前端改,改服务端权威源后跑同步脚本。