6.4 KiB
6.4 KiB
服务端 · 一页纸运作模型与红线速查
本文是服务端开发必须常驻在手边的那一页:运作模型 + 命名约定 + 红线清单。 它由
CLAUDE.md通过@import常驻每次会话上下文;红线以本文为权威。 文档导航、阅读顺序、各篇主题见 README;红线的完整细节见 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「端到端收发全链路」,前端侧细节见 前端 04 网络对接与启动编排。
命名约定:哪些是框架契约,哪些只是示例
本套文档的代码示例里出现的具体名字,绝大多数是举例,并非强制标准。请区分两类:
- 框架契约(必须照用)——由平台代码决定,改了就对接不上:
- 数据包四字段
{ 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/软化断言/吞异常掩盖;正式代码不得为兼容测试而加逻辑。