实时多人对战服务端:把规则层和网络层彻底拆开

1262 字
6 分钟
实时多人对战服务端:把规则层和网络层彻底拆开

这是我自己写完并部署上线的一个四人实时对局平台:真人房、AI 补位、房间邀请,H5 和 Android 包都出了,跑在自己的服务器上。

选这个题目练手是有原因的——它同时具备强规则、多人实时、状态必须绝对一致三个特征。任何一处判断写歪,四个客户端立刻就会吵起来。做完一遍,实时服务端该踩的坑基本踩了一轮。

一条核心设计决定#

整个项目只有一个决定是真正重要的:规则引擎和网络层彻底分开。

server/
src/
core/ # 纯函数式的规则与对局状态机,不认识 socket
server/ # HTTP + Socket.IO,只负责收发和广播
test/ # 直接对 core 做单元测试,不起服务

core 里没有任何网络代码,它就是一个 (状态, 动作) => 新状态 的状态机。带来三个直接好处:

测规则不用起服务器。 单元测试几秒跑完,不需要开四个浏览器手动打一局。

AI 和真人走完全同一条代码路径。 AI 就是一个「读状态、返回动作」的函数,不是特殊分支。这意味着 AI 能触发的 bug 真人也能触发,反之亦然——不会出现”只有 AI 在的时候才出问题”这种最难查的情况。

换传输层不用动业务。 以后要换成原生 WebSocket 或者 KCP,core 一行都不用改。

服务端权威,客户端只负责画#

客户端不重写任何规则。玩家操作发上去,所有判定以服务端为准,客户端只做渲染和输入。

这条在多人对战里没有商量余地。一旦让客户端参与判定,就要面对两个问题:客户端之间算出不同结果,以及有人改客户端。

代价是每个动作都要一个来回。所以协议设计上把「立刻能确定的反馈」(比如按钮按下的视觉响应)和「需要服务端确认的结果」分开处理,不让网络延迟表现成卡顿。

动作优先级仲裁#

实时对局里最容易写错的地方,是同一个事件多个玩家同时有权响应。

同一个动作,可能 A 玩家要执行操作甲、B 玩家要执行操作乙,而乙的优先级更高。这时不能谁的包先到就听谁的——那样结果取决于网络抖动,同一局重放两次会不一样。

正确做法是开一个短暂的响应窗口,收齐(或超时)之后按固定优先级仲裁,再广播结果。还要处理同一优先级多人命中的情况。

这类逻辑全部在 core 里,所以能用单元测试穷举各种组合,而不是靠线上撞。

玩家中途离场#

这个平台的规则里,有玩家达成目标后不是整局结束,而是该玩家离场、其余人继续。

看起来是个小规则,实际上它否决了一个很自然的建模方式:不能假定「一局 = 固定四个人从头打到尾」。对局状态里必须显式维护「还在场的玩家」这个集合,所有轮转、判定、结算都基于它。

一开始没意识到这点,是后来重构掉的。能不能在建模阶段就识别出这种”看起来是规则、实际是结构”的需求,很影响后面返工的量。

一次系统性缺陷排查#

上线跑了一段时间后我做过一轮完整的缺陷审计,整理出 20 条问题,修掉 17 条,剩下 3 条需要改数据结构,排进了后续迭代。

最大的收获不是修了多少 bug,而是印证了一件事:

凡是能在 core 层用单元测试复现的问题,修起来都很快;凡是只能靠”开四个浏览器手动打一局”复现的,都很慢。

后来我把所有能下沉的判断全部往 core 挪了。这条经验在别的项目上一样成立——可测试性不是测试的事,是架构的事。

客户端与打包#

客户端用 Cocos Creator + TypeScript,一套工程同时出 H5 和 Android。协议完全复用服务端已有的定义。

Android 出包这一步坑最集中,主要是 Gradle 与 NDK 的版本匹配,以及资源压缩后的加载路径问题。

部署#

服务端直接推到自己的服务器,不走 CI。原因很实际:部署频率不高,而且有几个目录(玩家数据、房间快照)绝对不能被覆盖,走自动化脚本反而容易手滑。涉及数据的变更必须先停服。

这算是本职工作带过来的习惯——部署方案要匹配变更频率和出错代价,不是越自动化越好。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

实时多人对战服务端:把规则层和网络层彻底拆开
https://dr.cctv15.net/posts/realtime-game-server/
作者
董锐
发布于
2026-08-20
许可协议
CC BY-NC-SA 4.0
相关文章智能推荐
1
海外园区宿舍管理系统:给上万人混住的园区做一套住得下、查得清的平台
个人上线的系统印尼工业园区 8 栋楼、近 3,000 床位、上万名中方与印尼籍员工混住。市面上的开源宿舍系统没有一个能用,因为约束根本不同。这是我从建模到上线做完的一套。
2
用 Rust 写一套直播平台:推流、鉴权、回放,也能当在线教育用
个人上线的系统面向个人、私域和小团队的自建直播平台。RTMP 推流、HLS/FLV 拉流、房间权限、录制回放、实时弹幕——把这几样凑齐,它就不只是直播,也是一套能用的在线教育平台。
3
订阅制服务的用量计量和三通道支付
个人上线的系统一套跑在 GCP 上的订阅计费:支付宝 / PayPal / USDT 三条通道,重构了原实现里用量统计对不上账的部分。只写计量和通道边界,不写业务名和域名。
4
云资源自助交付平台:把「申请一台机器」做成不用找人的事
个人上线的系统对接 GCP / AWS API 做计算实例的自动开通、回收与生命周期管理,用户在自助控制台就能开关机、重装、重置凭据。做这套的动机来自运维本职——重复劳动应该被消灭。
5
给 Jeepay 加一条东南亚支付通道
个人上线的系统Jeepay 是国内比较成熟的开源聚合支付系统,但它内置的通道全是国内的。这篇记录我怎么在不动核心的前提下,给它接一条 ABA Pay。
随机文章随机推荐
Profile Image of the Author
董锐
网络 / 系统工程师。身份底座、跨境网络、信息化交付。
公告
这是项目履历站,配合简历看。中英文 PDF 在「关于我」页可下载。
分类
标签
站点统计
文章
16
分类
3
标签
76
总字数
20,278
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Local
博客版本
Firefly v6.16.6
文章许可
CC BY-NC-SA 4.0