Apollo 技术文档Apollo 技术文档
指南
  • 架构概述
  • BigWorld 架构深度解析
  • BigWorld 进程架构与玩家生命周期
  • AOI九宫格系统详解
  • AOI广播与消息去重
  • Base 模块
  • Core 模块
  • Runtime 模块
  • Data 模块
  • Network 模块
  • /modules/actor.html
  • Game 模块
  • BigWorld 模块
服务器应用
API 参考
QA
GitHub
指南
  • 架构概述
  • BigWorld 架构深度解析
  • BigWorld 进程架构与玩家生命周期
  • AOI九宫格系统详解
  • AOI广播与消息去重
  • Base 模块
  • Core 模块
  • Runtime 模块
  • Data 模块
  • Network 模块
  • /modules/actor.html
  • Game 模块
  • BigWorld 模块
服务器应用
API 参考
QA
GitHub
  • MMORPG 架构 QA

Q18: 什么是 Ghost / Shadow 机制?

核心结论

Ghost / Shadow 机制的本质,是把一个实体拆成不同职责的副本:

  • Real Entity 负责权威逻辑
  • Ghost Entity 负责跨进程、跨分区的远端镜像
  • Shadow State 负责客户端的显示、预测与平滑

它解决的不是“怎么复制一个对象”,而是分布式世界里三个更关键的问题:

  • 权威状态只能有一份
  • 其他节点又必须能看到它
  • 客户端需要一份适合渲染和补偿的本地表示

一、为什么需要这套机制

在单进程游戏里,一个实体通常只有一份状态;但在大世界或多 Cell 架构里,同一个玩家往往同时牵涉多个节点:

  • 所在分区要运行它的真实逻辑
  • 邻接分区要感知它的存在,处理边界交互和 AOI
  • 登录、社交、任务等服务可能需要读到它的部分状态
  • 客户端还要把它渲染出来,并对本地玩家做预测

如果所有节点都直接读写同一份对象,系统会立刻失控:

  • 状态写入来源不清晰
  • 迁移时很难切主
  • 故障恢复边界模糊
  • 客户端容易把显示状态误当权威状态

所以工程上通常会明确分层。

二、三类副本分别负责什么

1. Real Entity

Real Entity 是权威实体,通常存在于当前负责该实体的 Cell 或逻辑分区。

它负责:

  • 移动与碰撞
  • 技能、战斗、仇恨
  • AOI 计算
  • 状态变更的最终提交

核心约束只有一条:同一时刻只能有一个权威 Real。

2. Ghost Entity

Ghost Entity 是远端镜像,通常存在于:

  • 邻近 Cell
  • 需要观察该实体的其他逻辑节点
  • 某些需要只读状态的服务进程

它的职责不是独立决策,而是:

  • 接收 Real 的增量同步
  • 提供局部可见状态
  • 参与邻区 AOI、边界广播、迁移预热

Ghost 一般不应该自行推进权威逻辑,否则就会出现双写和分叉。

3. Shadow State

Shadow State 是客户端本地表示。这里刻意不用“客户端实体”去强调它不是服务端权威实体的平移版本,而是一层偏渲染和交互的状态包装。

它通常负责:

  • 角色模型、动画、特效
  • 插值和平滑
  • 本地玩家预测
  • 接收服务端校正

对于本地玩家,Shadow 可以先走一小步;对于远端实体,Shadow 更常见的是插值显示,而不是强预测。

三、典型数据流

一条常见的数据链路如下:

Real Entity -> Ghost Entity -> Client Shadow State

但这个链路不是固定只有一跳。实际工程里可能出现:

  • Real 直接向客户端广播
  • Real 同步给多个邻区 Ghost
  • Ghost 再参与局部 AOI 或边界计算

无论链路怎么变化,原则不变:

  • 逻辑最终来源是 Real
  • Ghost 是受控镜像
  • Shadow 是展示层状态

四、它和 AOI、分区迁移是什么关系

1. 和 AOI 的关系

AOI 决定“谁应该看到谁”,Ghost/Shadow 决定“看到了之后,用什么副本承载它”。

换句话说:

  • AOI 负责筛选同步对象
  • Ghost 负责在服务端跨节点保留远端镜像
  • Shadow 负责在客户端把这些同步结果显示出来

没有 AOI,Ghost 数量会爆炸;没有 Ghost,跨分区感知会变得很重;没有 Shadow,客户端就只能生硬显示最新包。

2. 和迁移的关系

当实体跨分区移动时,系统通常不是“瞬间删除旧 Real,再创建新 Real”,而是先让目标分区准备好镜像,再完成主从切换。

一个更稳妥的迁移流程通常是:

  1. 目标分区提前创建该实体的 Ghost
  2. 持续接收权威状态,完成预热
  3. 到达切换条件后,目标分区提升为新的 Real
  4. 旧分区降级为 Ghost 或进入回收流程
  5. 周边 AOI 和客户端连接关系再更新

这样做的价值是降低切换抖动和状态丢失风险。

五、常见设计边界

1. Ghost 不是“第二个 Real”

Ghost 可以持有很多状态,但不应该独立得出最终战斗结果,也不应该私自改写关键属性。

如果 Ghost 也能执行关键逻辑,就会出现:

  • 同一技能被结算两次
  • 邻区和主区状态不一致
  • 迁移时很难判断谁是真实结果

2. Shadow 不是服务端镜像的简单拷贝

客户端需要的是“适合渲染和交互的一份状态”,而不是把服务端对象逐字段原样照搬。

例如:

  • 服务端位置可能以离散快照形式同步
  • 客户端要做插值和预测
  • 某些服务端内部字段根本不该暴露给客户端

所以 Shadow 通常会有自己的一层表示结构。

3. BaseApp 或其他服务是否一定要持有 Ghost

不一定,取决于架构。

如果某个服务只需要通过 RPC 查询当前权威状态,未必需要长期持有镜像;如果它需要高频读取、低延迟访问或参与复杂协作,镜像副本才更有价值。

关键不在“有没有 Ghost”,而在:

  • 读取频率有多高
  • 是否需要本地快速访问
  • 是否能接受异步查询延迟

六、这套机制真正带来的收益

1. 控制写入来源

只有 Real 负责最终写入,系统更容易维持一致性。

2. 降低跨节点交互成本

邻区或其他进程不必每次都远程查询权威节点,可以先读本地 Ghost。

3. 支撑平滑迁移

分区切换前先预热 Ghost,迁移过程更稳。

4. 让客户端同步更可控

客户端只关心 Shadow 所需数据,不必直接承担服务端实体模型的复杂性。

七、常见问题

1. Ghost 越多越好吗

不是。Ghost 越多,意味着:

  • 更多内存占用
  • 更多同步链路
  • 更多脏数据传播
  • 更复杂的回收和故障处理

Ghost 的数量应该由 AOI、边界预热、服务依赖来约束,而不是无节制复制。

2. 所有实体都需要完整 Ghost 吗

也不需要。很多时候只需要“裁剪后的镜像”。

例如邻区只关心:

  • 位置
  • 朝向
  • 阵营
  • 生命周期

那就没必要同步整份背包、任务、脚本私有状态。

3. Shadow 一定要做预测吗

不一定。

  • 本地玩家通常需要预测
  • 远端玩家多用插值
  • 怪物或低价值对象甚至只做简单平滑

预测是体验优化,不是必须给所有对象都上。

八、工程落地时更重要的点

如果要把 Ghost / Shadow 机制真正做稳,通常要同时回答这些问题:

  • Real 和 Ghost 的字段边界怎么定义
  • 同步是全量、增量还是脏字段
  • Ghost 的生命周期由谁管理
  • 实体迁移时如何做主从切换
  • 客户端 Shadow 的校正阈值是多少
  • AOI 收缩和扩张时如何创建、冻结、回收镜像

这些比单纯背术语更决定系统质量。

参考资料

  • KBEngine 文档中的 Entity / Ghost / Shadow 设计说明
  • BigWorld 架构资料中的 Real / Ghost 实体迁移机制
  • Glenn Fiedler, Snapshot Interpolation
在 GitHub 上编辑此页
最后更新: 3/20/26, 6:06 AM
贡献者: cuihairu