这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
《MMO 服务器开发》5.3 移动校验与防穿墙
权威移动的前两节假设落点可信,这一节把不可信也纳入设计。本节从零写 internal/move 的三道闸:序号单调拦重放、速度上限拦加速外挂、DDA 走格视线检测拦穿墙;被拒的移动绝不更新基准位置,并把原因码随 0x0202 回给客户端让它自行回拉,配 go test 逐条验收。
posts
这里是全站文章的归档入口,下面是按时间倒序排列的全部文章。若想按主题浏览,请从首页的专题分类进入。
权威移动的前两节假设落点可信,这一节把不可信也纳入设计。本节从零写 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 对照实现。
把 3.x 到 4.x 的迁移排成有依赖关系的可执行步骤:Java 与构建插件基线、依赖坐标与版本对照、被移除与改名的类与注解、配置属性重命名与移除、spring.factories 到 AutoConfiguration.imports 的沿革,并给出用 unzip 读属性元数据核实的方法。
《MMO 服务器开发》卷二《单服架构与玩法系统》全书导航页:说明本卷在四卷本中的位置与承接关系,交代读者画像、前置知识与三条阅读路径,讲清分层与状态归属这一条贯穿全卷的主线,并按六个部分列出 18 章 54 节的完整章节索引,另含《晨曦大陆》单服可玩的验收清单与常见问题速查表,方便读者按需定位与随时查阅。
同一张地图模板,落地方式却要分三六九等:野外和主城该共享分线,副本要一人一实例。本节从零写 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 架构文负责「应该怎么设计」,卷一负责「网络底座怎么写」,本卷负责「玩法系统从零实现并跑出数字」,并把这条边界编码成一张可查询的表。
上一节立了「一个状态只能有一个写者」,但现实里这条原则会被两种方式破坏:写者其实不止一个,以及写者写的是旧数据。它们对应两个最经典的事故——重复发奖与旧数据覆盖。本节先把两个事故的根因拆开,再推出版本单调与幂等键两条守卫,最后用一份能跑的 Go 代码把这两条守卫钉成断言。
卷一讲完网络内核的四层,这一卷换一个视角重新切一遍:从玩法系统的状态归属出发,回答 gateway / logic / scene / data 各自该持有什么状态。本节先把「谁写状态,谁就是那一层」立成第一原则,再把每条命令归到唯一的处理层、把每份状态归到唯一的写入者,最后用一份能跑的 Go 代码把这两条归属规则钉成断言。
16.2 把服务编排起来了,但容器编排还有三件「看不见却致命」的事:配置怎么注入、健康检查怎么区分 liveness 与 readiness、收到 SIGTERM 后怎么优雅退出。本节讲清用环境变量注入配置、两条健康线各自的取舍、以及「停止接新请求→等在途 run 收尾→关连接池→退出」四步。用 uvicorn 0.54.0 + lifespan 真发 SIGTERM,贴出完整日志序列与参数表。
上一节做出了 ResearchCopilot 的镜像,但一个镜像撑不起一个服务——它还要数据库、缓存和后台 worker。本节写 docker-compose.yml 编排这四个服务,讲清 depends_on 不等于「依赖就绪」(须配 healthcheck)、同一镜像靠 command 扮演 api/worker 两种角色。真跑 config 与 ps 起栈验证。
本节是 TS 落点三的上半场:把 ResearchCopilot 打成 Python + Node 双栈镜像。用多阶段构建分别产出 TS 前端产物与 Python 依赖,只把产物拷进运行时镜像;再叠加 .dockerignore、--no-cache-dir、slim 基础镜像与 digest 固定四个瘦身杠杆。Dockerfile 在本机真构建,贴出镜像体积、分层与容器内验证的真实输出。