《MMO 服务器开发》7.2 确定性随机与随机种子
本节定稿全卷的随机口径:用 math/rand/v2 的 PCG,为每个实体单独构造 PRNG,种子 = hash(玩家ID, 掉落批次序号),禁止全局 rand。把为什么全局随机源会让结果不可复算讲透,并给出可测的种子推导与重放实现,这是后续所有随机玩法的地基。
tag
本节定稿全卷的随机口径:用 math/rand/v2 的 PCG,为每个实体单独构造 PRNG,种子 = hash(玩家ID, 掉落批次序号),禁止全局 rand。把为什么全局随机源会让结果不可复算讲透,并给出可测的种子推导与重放实现,这是后续所有随机玩法的地基。
掉落是玩法系统的第一个随机点:怪物死了掉什么、掉几个。本节从零写 internal/loot 包,用权重表描述候选物品、在 [Min,Max] 区间取数量,把多次抽取聚合成一次可复算的掉落结算,并附一份能直接 go test 跑过的 L1 测试。
一次公会战结算要同时做对三件事:命中扣血、判定死亡、发放奖励,而且重复到达的结算请求只能发一次奖。本节给 aegis 从零写战斗结算与防重复发奖:为什么结算和发奖必须分开、迟到的伤害包怎么被死亡状态挡掉、为什么发奖的去重键是业务指纹而不是传输序号,并链回卷一的幂等窗口。
战斗里最容易写崩的两块是技能冷却和 Buff 叠加。本节给 aegis 从零建技能与 Buff 模型:施法为什么拆成请求、前摇、命中三段,技能冷却与公共冷却怎么记,Buff 的刷新与叠层两种策略怎么选、周期伤害怎么按 tick 精确结算,并用真跑的 L1 测试把每条边界钉死。
客户端在多个连接线程里收包,战斗却必须在一个场景线程里按固定顺序结算。本节从零写 aegis 的按帧指令队列:每实体单调序号怎么挡住重试与乱序、排空顺序为什么必须确定、每帧预算怎么把突发流量摊平,并用真跑的 L1 测试与本机实测的投递成本把这几条边界钉死。
权威移动的前两节假设落点可信,这一节把不可信也纳入设计。本节从零写 internal/move 的三道闸:序号单调拦重放、速度上限拦加速外挂、DDA 走格视线检测拦穿墙;被拒的移动绝不更新基准位置,并把原因码随 0x0202 回给客户端让它自行回拉,配 go test 逐条验收。
上一节让角色会走,这一节让它知道往哪走。本节用 container/heap 从零实现八邻域 A*:对角代价根号二、octile 启发、禁止切角穿墙;再把「格子」换成凸多边形导航网格,用多边形邻接图 A* 求通道,最后用拉绳算法把锯齿路径拉成直线,并实测 200x200 网格的单次寻路开销。
服务器权威移动是防作弊的地基:位置只能由服务器算,客户端只能提交「我要去哪」。本节从零写 internal/move 包的 Vec2 逻辑坐标与 Mover 状态机,用 20Hz 固定步长推进、到达即吸附不过冲,并只广播真正产生位移的实体,配 go test 验收终点精确与超距请求被拒。
把暴力扫描、九宫格、十字链表三种 AOI 放在同一批 5000 个玩家、同一张地图上实跑基准:九宫格查询约 890ns,十字链表约 4350ns 且移动慢得多,暴力扫描约 37500ns。再把平均兴趣集合 6.21 人换算成每帧出站消息,量化兴趣管理到底省下了多少带宽。
十字链表 AOI 给每个实体同时挂两条有序链表——一条按 X 升序、一条按 Y 升序,查询时两条「带」取交,不需要格边长这个参数。本节从零实现四指针节点、哨兵环链、O(1) 索引、带内取交查询,并与九宫格逐点比对证明语义一致,同时讲清它移动 O(n) 的代价。
九宫格 AOI 把连续地图切成 GridSize=8 的方格,玩家只关心自己所在格与相邻八格。本节用 aegis 的共享坐标类型从零实现格点哈希、O(1) 跨格移动、圆形半径过滤,以及把「谁进入/离开视野」翻译成 CmdAoiAdd 与 CmdAoiRemove 的 Watcher,并附一份 Java 21 对照实现。
《MMO 服务器开发》卷二《单服架构与玩法系统》全书导航页:说明本卷在四卷本中的位置与承接关系,交代读者画像、前置知识与三条阅读路径,讲清分层与状态归属这一条贯穿全卷的主线,并按六个部分列出 18 章 54 节的完整章节索引,另含《晨曦大陆》单服可玩的验收清单与常见问题速查表,方便读者按需定位与随时查阅。
《MMO 服务器开发》卷二《单服架构与玩法系统》全 55 篇,把玩法装进卷一造好的 aegis 底座:从 gateway / logic / scene / data 分层与状态归属起步,经实体运行时与对象池、地图实例生命周期、九宫格与十字链表 AOI 从零实现及基准对照、服务器权威移动与 A* 寻路、战斗指令管线与结算防重、掉落表与确定性随机、背包并发修改、属性成长与离线推进、任务依赖图与每日重置、聊天频道与好友关系、组队与队长迁移、公会仓库与公会战报名、排行榜分段与赛季结算、邮件投递与红点聚合、拍卖行托管与竞价回滚、匹配算法与大厅生命周期,最后把《晨曦大陆》装配成一个单服可玩的版本并跑出承载数字。
同一张地图模板,落地方式却要分三六九等:野外和主城该共享分线,副本要一人一实例。本节从零写 internal/scene 的 LineManager 与 Router:分线按容量塞满一条开下一条,副本每次进入开独立实例,并用统一入口把两种落点收敛到一个 EnterWorld。全部 L1,go test 可直接跑。
地图模板是只读的,真正装玩家的是地图实例。本节从零写 internal/scene 的 InstanceManager:单调递增的 InstanceID、高 16 位实例号加低 48 位序号拼出的 EntityID、Active→Draining→Closed 状态机,以及把空置超时实例回收进对象池复用。L1 全绿,另附池化对照基准。
地图是场景逻辑的地基。本节从零写 internal/scene 的地图数据结构:一张 Width×Height 格网加一张阻挡位图描述静态地图,float64 逻辑坐标 Vec2 与客户端像素解耦,格边长固定 GridSize=8.0,并实现世界坐标与格号互转、可走判定与边界夹取。全部是 L1,go test 可直接跑。
实体散落在多个逻辑线程与场景里,核心问题是「谁有权写它」。本节实现单写者模型下的归属表(正向 + 反向双索引),再把跨运行时搬迁拆成冻结—传输—接管三段,用缓冲重放解决冻结期间在飞的消息,最后用一个 Router 固化分流规则,并用 go test 验证迁移幂等与消息不丢不重。
2.1 的立即 Despawn 不够用:创建要先登记再可见,销毁要先结算再回收。本节把实体生命周期拆成 None/Pending/Active/Dying/Dead 五态,用一张迁移表管住所有合法跳转,再把载荷接进对象池,最后用 go test 验证回收与复用真的发生。
实体、句柄、运行时是三个层次。本节从零定下 EntityID 的高 16 位场景号 + 低 48 位序号位布局,用代际计数解决销毁后序号复用带来的 ABA 问题,再实现一张 O(1) 校验的运行时表,并用 16 个 go test 用例把三种过期引用逐一跑出来。
站内已经有一大批游戏服务器架构文章,卷一又写完了网络内核,那这一卷到底写什么、不写什么?本节把三份内容的分工摊开讲清楚:站内 practice 架构文负责「应该怎么设计」,卷一负责「网络底座怎么写」,本卷负责「玩法系统从零实现并跑出数字」,并把这条边界编码成一张可查询的表。