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
  • 架构设计

    • 架构概述
    • BigWorld 架构深度解析
    • BigWorld 进程架构与玩家生命周期
  • AOI 系统

    • AOI九宫格系统详解
    • AOI广播与消息去重

KBEngine 源码分析

这篇文档基于本地源码目录 C:\Users\cui\Workspaces\kbengine 整理,目标不是做概念介绍,而是回答两个更实际的问题:

  1. KBEngine 这套架构在源码里到底是怎么落下来的。
  2. Apollo 现在还不完善时,哪些设计最值得直接吸收,哪些只适合借鉴思想而不适合照搬。

如果只看 BigWorld/KBEngine 的架构图,很容易把它理解成“多进程 MMO 服务器”。但看完源码之后会发现,它真正的价值不在于进程多,而在于它把以下几件事收成了一套统一运行时:

  • 进程宿主与生命周期
  • 网络消息与跨进程调用
  • 实体定义、脚本绑定与同步元数据
  • 空间权威、AOI、Witness、Ghost
  • 集群调度、观测和运维

这几件事是一体的。

一、建议的阅读顺序

第一次系统读 KBEngine,建议按下面顺序走:

  1. 启动宿主
  2. 公共服务端骨架
  3. 网络与消息
  4. 实体定义系统
  5. BaseApp / CellApp
  6. Space / Witness / Ghost
  7. BaseAppMgr / CellAppMgr / Machine / Watcher

不要一上来就扎进 cellapp/entity.cpp。那样很快会淹没在细节里。

二、源码分层

KBEngine 的源码分层很清楚,目录本身就在表达架构。

1. 进程层

目录:kbe/src/server/

关键子目录:

  • baseapp
  • baseappmgr
  • cellapp
  • cellappmgr
  • dbmgr
  • loginapp
  • machine
  • tools/logger
  • tools/interfaces

这一层表达的是“集群中的进程角色”。

2. 公共运行时层

目录:kbe/src/lib/

最关键的几个库:

  • server
  • network
  • entitydef
  • helper
  • pyscript
  • resmgr
  • thread
  • db_interface

这一层表达的是“所有进程共享的基础运行时”。

3. 资源与脚本层

目录:kbe/res/

核心子目录:

  • server
  • scripts
  • client

KBEngine 不是纯 C++ 引擎,脚本资源是运行时的一等输入。

三、启动入口是统一模板,不是每个进程各写一套

先看各进程的 main.cpp:

  • kbe/src/server/baseapp/main.cpp
  • kbe/src/server/cellapp/main.cpp
  • kbe/src/server/baseappmgr/main.cpp
  • kbe/src/server/cellappmgr/main.cpp
  • kbe/src/server/dbmgr/main.cpp
  • kbe/src/server/loginapp/main.cpp
  • kbe/src/server/machine/main.cpp

这些入口都很薄,核心都是:

  1. 包含本进程会用到的接口定义头。
  2. 从 g_kbeSrvConfig 读取本进程的监听参数。
  3. 调用 kbeMainT<T>() 启动具体服务。

真正的总入口在:

  • kbe/src/lib/server/kbemain.h

这里定义了 kbeMainT<SERVER_APP>()。

kbeMainT 做了什么

从源码看,它至少统一处理了下面这些事:

  • 校验和生成 componentID
  • 设置环境变量,例如 KBE_COMPONENTID
  • 初始化日志与内存检测
  • 加载密钥文件
  • 初始化 Resmgr
  • 创建 EventDispatcher
  • 创建 NetworkInterface
  • 初始化 Components
  • 构造具体 SERVER_APP
  • 调用 app.initialize()
  • 最后调用 app.run()

这说明 KBEngine 的第一个关键设计是:

进程差异在组件类,不在宿主框架

这点对 Apollo 非常重要。Apollo 现在如果每个 app 都自己拼配置、网络、生命周期、信号、服务发现,后面一定会散。

对 Apollo 的直接启发

Apollo 应该尽快形成统一的进程启动骨架,至少收敛以下能力:

  • 配置装载
  • 日志初始化
  • 网络监听
  • Service Discovery 注册
  • 健康检查与退出流程
  • 脚本/模块初始化
  • 统一 AppHost<T> 或 ProcessMain<T>

否则 gateway-app、login-app、未来的 baseapp/cellapp 很容易各自长出一套宿主。

四、公共服务端骨架:ServerApp 和 EntityApp

真正把各个进程统一起来的,不是 main.cpp,而是这两个类:

  • kbe/src/lib/server/serverapp.h
  • kbe/src/lib/server/entity_app.h

1. ServerApp

ServerApp 是所有服务进程的公共父类。

从 serverapp.h 可以直接看出它的职责:

  • SignalHandler
  • TimerHandler
  • ShutdownHandler
  • ChannelTimeOutHandler
  • ChannelDeregisterHandler
  • ComponentsNotificationHandler

这不是普通的业务基类,它是整个进程运行时的框架类。

ServerApp::initialize()

在 serverapp.cpp 里,这个初始化流程基本串起了整个宿主生命周期:

  1. 安装信号处理
  2. 初始化线程池
  3. 加载配置
  4. initializeBegin()
  5. inInitialize()
  6. initializeEnd()
  7. 初始化网络与 watcher

也就是说,具体组件只需要填三个阶段钩子:

  • initializeBegin
  • inInitialize
  • initializeEnd

这是一种很稳定的分层方式。

ServerApp::run()

run() 最核心的一句是:

dispatcher_.processUntilBreak();

因此从运行模型上看,KBEngine 本质上是:

事件循环 + 定时器 + 网络消息 + 组件回调

而不是“每个服务自己控制主循环”。

2. EntityApp<E>

EntityApp 在 ServerApp 上再加了一层实体运行时。

从 entity_app.h 能看到它增加了这些关键能力:

  • 脚本初始化
  • EntityDef 安装
  • EntityCall 绑定
  • Entities<E> 容器
  • EntityIDClient
  • PY_CALLBACKMGR
  • game tick 驱动

EntityApp 最关键的两件事

第一件:

它在构造时把 EntityDef::setGetEntityFunc(...) 和 EntityCallAbstract::setFindChannelFunc(...) 接到当前 app 上。

这意味着:

  • entitydef 不直接依赖具体进程
  • entitycall 也不直接依赖具体实体容器
  • 具体 app 通过函数绑定把这两者接起来

这是一个很成熟的解耦点。

第二件:

它在 initialize() 里注册了固定频率的 game tick:

  • dispatcher().addTimer(...)
  • 频率来自 gameUpdateHertz

所以 KBEngine 不是所有逻辑都靠消息驱动,它有明确的 tick 驱动核心。

对 Apollo 的直接启发

Apollo 最应该补的一层不是具体业务模块,而是:

  • ServerApp 对应的统一进程基类
  • EntityApp 对应的统一实体宿主

建议 Apollo 至少形成下面两层抽象:

  • ProcessHost
  • WorldHost<EntityT>

其中 WorldHost 要承接:

  • 实体容器
  • 游戏 tick
  • 跨进程实体定位
  • 脚本或配置驱动的实体定义装载

五、网络运行时:KBE 的消息模型为什么长期可维护

网络核心入口:

  • kbe/src/lib/network/network_interface.h

相关基础类:

  • channel.h
  • endpoint.h
  • bundle.h
  • packet.h
  • message_handler.h

1. NetworkInterface

NetworkInterface 是每个进程的网络中枢。

从头文件能直接看出它负责:

  • 维护外网 TCP/UDP 与内网 TCP 端点
  • 管理 ChannelMap
  • 绑定 EventDispatcher
  • 处理 Channel 注册/注销
  • 处理 channel timeout
  • 统一 processChannels

KBEngine 的一个关键点是区分:

  • 外部连接
  • 内部连接

这不只是多开一个端口,而是明确区分两种语义:

  • 外部连接要处理 client 协议与安全边界
  • 内部连接要处理组件间通信和服务编排

2. Channel

Channel 是逻辑连接抽象,不只是 socket。

它承接了:

  • 地址绑定
  • 发送队列
  • 超时管理
  • 和消息处理器的协作

3. Bundle

Bundle 很值得 Apollo 认真参考。

它的价值是:

  • 将多条逻辑消息聚合为一次发送
  • 支持按协议写入 message header 和 payload
  • 避免业务层直接操作裸 buffer

这让“远程实体方法调用”“属性同步”“广播”最终都能收敛到一个统一的发送载体里。

4. MessageHandler

KBEngine 并不是用一套通用字符串路由做 RPC,而是有显式的消息声明与处理器。

优点是:

  • 调度稳定
  • 序列化边界明确
  • 性能和内存布局可控

代价是:

  • 可读性一般
  • 扩展时需要改接口定义和宏

对 Apollo 的直接启发

Apollo 现在如果想做 MMO 风格分布式实体,不建议只靠通用 RPC 框架。

至少要补三层能力:

  1. 内部连接语义
  2. 聚合发送单元
  3. 显式消息声明层

Apollo 可以不照搬 KBE 的宏,但至少需要:

  • InternalChannel
  • MessageEnvelope / MessageBundle
  • MessageRegistry
  • ComponentMessageCodec

否则跨进程实体通信会长期停留在“能通就行”的阶段,很难演进成稳定运行时。

六、entitydef 是 KBEngine 最核心的壁垒

关键目录:

  • kbe/src/lib/entitydef/

关键文件:

  • entitydef.h
  • scriptdef_module.h
  • property.h
  • method.h
  • datatype.h
  • entity_call.h
  • entity_component.h
  • volatileinfo.h

如果只看 BaseApp 和 CellApp,会误以为 KBEngine 的核心是“分布式服务编排”。实际不是。

它最深的壁垒是把“实体定义”做成了统一元数据系统。

1. EntityDef

从 entitydef.h 看,EntityDef 负责:

  • 初始化实体定义系统
  • 加载全部脚本模块
  • 加载 property / method / interface / component 描述
  • 建立 script module -> utype 映射
  • 维护 defs 的 MD5

这是实体系统的全局注册中心。

它不是只做配置加载

EntityDef 的意义在于把下面几个问题绑定在一起:

  • 这个实体有哪些属性
  • 属性属于 base 还是 cell 还是 client
  • 哪些属性持久化
  • 哪些方法可远程调用
  • 哪些属性支持 alias 压缩
  • 哪些数据是 volatile 的

这就是它为什么重,但也为什么强。

2. ScriptDefModule

ScriptDefModule 是单个实体脚本模块的运行时描述对象。

从 scriptdef_module.h 能看到它维护:

  • PROPERTYDESCRIPTION_MAP
  • METHODDESCRIPTION_MAP
  • component 描述
  • detail level
  • volatile info
  • base / cell / client 三侧能力标记

这意味着一个实体定义,不是“一张表”,而是一整套跨边界描述:

  • 本地对象结构
  • 远程方法表
  • 客户端同步裁剪规则
  • 组件化扩展点

3. EntityCall

entity_call.h 很关键。

它不是普通 RPC 代理,而是“分布式实体引用”。

它携带的信息包括:

  • 实体类型
  • 目标地址
  • 组件 ID
  • 实体 ID
  • call 类型

也就是说,KBE 里的远程调用模型不是“请求某个服务上的某个方法”,而是“拿着实体引用去访问实体侧的方法”。

这和传统微服务完全不是一套语义。

4. entity_component

源码里确实有 entity_component.h/.cpp,但不能把它误读成 ECS。

更准确地说,它解决的是:

  • 实体定义的复用
  • 子组件级别的 property/method 描述
  • 组件内属性同步与脚本描述整合

它不是为了数据导向批处理,也不是为了高频系统遍历。

对 Apollo 的直接启发

Apollo 如果想走 KBE/BigWorld 路线,最该补的不是 API facade,而是统一的实体定义层。

建议补出下面这些结构:

  • EntitySchema
  • PropertySchema
  • MethodSchema
  • ReplicationSchema
  • PersistenceSchema
  • RemoteEntityRef

Apollo 现在如果协议、实体字段、持久化字段、脚本接口分散在不同地方,后面一定会漂。

七、脚本桥接是主路径,不是插件

关键目录:

  • kbe/src/lib/pyscript/
  • kbe/src/lib/python/

以及:

  • entitydef/py_entitydef.*
  • entitydef/scriptdef_module.*

从 EntityApp 的初始化流程也能看出来:

  • 先装 Python 环境
  • 再装 Python 模块
  • 再装 EntityDef

这个顺序很说明问题。

设计含义

KBEngine 的业务对象不是“C++ 里创建完了,再顺便给 Python 一份映射”。

它更接近:

  • C++ 提供宿主、网络、定时器、持久化和实体运行时
  • Python 提供业务实体的脚本定义和行为扩展

这就是为什么实体定义、脚本模块、远程方法、属性同步会绑在一起。

对 Apollo 的启发

Apollo 不一定要接 Python,但必须尽早决定:

  • 业务对象定义到底由什么驱动
  • 是纯 C++ 静态类型
  • 还是 DSL / 脚本 / 数据定义驱动

这个问题不能拖。

因为一旦跨进程实体、属性同步、存储映射都长出来后,再回头统一定义系统,代价会非常高。

八、BaseApp:它不是数据库层,而是玩家会话锚点

关键文件:

  • kbe/src/server/baseapp/baseapp.h
  • kbe/src/server/baseapp/entity.h
  • kbe/src/server/baseapp/proxy.h

1. Baseapp

从 baseapp.h 可以直接看出,它负责:

  • 创建实体
  • 从 DB 创建实体
  • createEntityAnywhere
  • createEntityRemotely
  • 创建 cell entity
  • 管理客户端 proxy
  • 转发消息
  • 处理备份与归档

这已经说明它绝不是单纯的数据服务。

2. Proxy

proxy.h 非常关键,它基本暴露了会话锚点的真实语义:

  • 保存客户端地址
  • 向客户端发送 bundle
  • 统计 RTT
  • 获取 timeSinceHeardFromClient
  • 是否有客户端附着
  • giveClientTo
  • onGetWitness
  • kick

这说明 KBEngine 把“玩家连接”和“玩家实体”之间放了一个明确的代理对象。

这个代理对象负责:

  • 连接状态
  • 带宽与加密
  • 登录数据
  • 客户端转移
  • 与 witness 的衔接

为什么这点重要

很多项目里会把“玩家在线状态”“socket 连接”“角色实体”揉成一个对象。前期简单,后期会非常难扩展。

KBEngine 明确把这些概念拆开:

  • Proxy: 会话与客户端承载
  • BaseEntity: 玩家长期权威逻辑
  • CellEntity: 空间内实时权威逻辑

对 Apollo 的启发

Apollo 当前如果要补 BigWorld/KBE 风格能力,必须尽快明确三种对象:

  • Session/Proxy
  • BaseEntity
  • CellEntity

不要让网关 session 直接等于逻辑实体,也不要让场景实体直接绑定 socket。

九、CellApp:它不是逻辑服,而是空间权威节点

关键文件:

  • kbe/src/server/cellapp/cellapp.h
  • kbe/src/server/cellapp/entity.h
  • kbe/src/server/cellapp/spacememory.h
  • kbe/src/server/cellapp/witness.h
  • kbe/src/server/cellapp/ghost_manager.h
  • kbe/src/server/cellapp/view_trigger.h

1. Cellapp

从 cellapp.h 里能直接看到它承担的能力:

  • 创建和销毁 cell entity
  • 处理 entity call
  • 处理 client -> cell 的转发消息
  • 处理 ghost property / volatile data 更新
  • 处理 reqTeleportToCellApp
  • 持有 GhostManager
  • 管理 Updatable

所以 CellApp 的本质是:

空间内实时权威模拟节点

2. cellapp::Entity

cellapp/entity.h 是 MMO 语义最浓的文件之一。

从头文件能直接看到:

  • isReal
  • hasGhost
  • realCell
  • ghostCell
  • baseEntityCall
  • clientEntityCall
  • controlledBy
  • position
  • direction
  • teleport
  • setViewRadius
  • getWitnesses
  • backupCellData

这说明 Cell 侧实体同时承载:

  • 权威状态
  • 客户端控制关系
  • 空间位置
  • 传送与迁移
  • AOI 可视范围
  • 持久化前的 cell 数据回写

这已经不是普通 ECS 或普通 scene entity 的概念了,而是“分布式空间实体”。

十、SpaceMemory:空间不是 ID,而是运行时世界容器

关键文件:

  • kbe/src/server/cellapp/spacememory.h

从头文件可以看出 SpaceMemory 至少管理:

  • 当前 space 的 SPACE_ID
  • 实体集合
  • 当前所属 Cell
  • CoordinateSystem
  • NavigationHandle
  • spaceData

它负责什么

1. 空间内实体生命周期

  • addEntity
  • removeEntity
  • onEnterWorld
  • onLeaveWorld

2. 空间几何和导航

  • loadSpaceGeometry
  • unLoadSpaceGeometry
  • NavigationHandle

3. 空间级共享数据

  • setSpaceData
  • getSpaceData
  • delSpaceData

4. 空间更新

  • update()

设计结论

KBE 的 Space 不是“地图 ID”,而是一个实际参与运行时调度的对象。

这对 Apollo 很关键。Apollo 后面如果做跨服场景,不应该把 spaceId 只当一个数据库字段,而是要有真正的 WorldSpace 运行时对象。

十一、Witness:客户端看到的世界不是直接从实体表里扫出来的

关键文件:

  • kbe/src/server/cellapp/witness.h
  • kbe/src/server/cellapp/view_trigger.h
  • kbe/src/server/cellapp/entityref.h

1. Witness 是什么

从 witness.h 看,它维护:

  • viewRadius
  • viewHysteresisArea
  • ViewTrigger
  • viewEntities
  • clientViewSize
  • 发送 bundle 缓冲

它还提供:

  • onEnterView
  • onLeaveView
  • sendToClient
  • addUpdateToStream
  • resetViewEntities

所以 Witness 本质上是:

客户端视野上下文

它不是实体本身,而是“这个实体对应的客户端应该看见什么”的运行时状态机。

2. 为什么有 ViewTrigger

view_trigger.h 显示 ViewTrigger 继承自 RangeTrigger,负责:

  • 坐标节点进入范围
  • 坐标节点离开范围

这说明 AOI 在 KBE 里不是简单遍历,而是:

  • 坐标系统负责空间组织
  • Trigger 负责边界事件
  • Witness 负责客户端视图集合

3. EntityRef 的价值

KBE 没有直接把 view 集合做成 Entity* 列表,而是有显式的 EntityRef。

这通常意味着它需要在“实体存在”“别名 ID”“同步状态”“延迟移除”之间做更多管理。

对 Apollo 的启发

Apollo 现在如果只把 AOI 理解成“附近玩家查询算法”,是不够的。

需要明确拆成四层:

  • CoordinateSystem
  • Trigger
  • Witness/ViewContext
  • Replication

这才是 KBE 真正的做法。

十二、GhostManager:分布式空间里最容易被忽视但最关键的设计

关键文件:

  • kbe/src/server/cellapp/ghost_manager.h

从头文件可以直接读出它的职责:

  • 管理 realEntities_
  • 管理 ghost_route_
  • 暂存待广播 messages_
  • 同步 ghost
  • 维护路由

为什么需要它

当一个实体迁移、切 Cell 或临时跨边界协作时,会出现这样的问题:

  • 某些消息还在飞
  • 权威实体位置已经切换
  • 某些 Cell 上还保留 ghost 投影

如果没有一层专门的 ghost 路由和转发管理,跨 Cell 世界会非常脆弱。

这也是 KBE 和普通场景服最大的分水岭之一

普通房间制服务端里,实体通常只在一个进程里活着。

KBE 里不是。

KBE 假设实体可能:

  • 在一个 Cell 上是 real
  • 在另一个 Cell 上是 ghost
  • 在迁移窗口里还需要 route

这才是真正意义上的分布式世界。

对 Apollo 的启发

Apollo 如果想做连续大世界,不能只做:

  • 区域划分
  • 玩家跨区迁移

还要补:

  • GhostReplica
  • MigrationRoute
  • EntityAuthorityTransfer
  • CrossCellMessageForwarding

否则看起来像 BigWorld,实际上只能做到“切片地图 + 硬切换”。

十三、CellAppMgr:它管理的不是进程列表,而是空间拓扑

关键文件:

  • kbe/src/server/cellappmgr/cellappmgr.h
  • kbe/src/server/cellappmgr/cellapp.h
  • kbe/src/server/cellappmgr/space.h

1. Cellappmgr

从头文件能直接看到它负责:

  • findFreeCellapp
  • updateBestCellapp
  • reqCreateCellEntityInNewSpace
  • reqRestoreSpaceInCell
  • updateCellapp
  • querySpaces
  • updateSpaceData
  • setSpaceViewer

这说明 CellAppMgr 不是简单注册中心,而是空间管理者。

2. 管理视角里的 Cellapp

cellappmgr/cellapp.h 里每个 cellapp 记录:

  • 当前 load
  • entity 数量
  • space 集合
  • global/group order

这是一个很典型的调度视图。

3. 管理视角里的 Space

cellappmgr/space.h 记录:

  • SPACE_ID
  • Cells
  • 几何映射路径
  • 脚本模块名

注意,这里管理层关心的是:

  • 这个 space 在哪些 cell 上展开
  • 它的脚本和几何定义是什么

这说明 KBE 的空间系统不是只在 Cell 本地存在,管理层也持有一份空间拓扑视图。

对 Apollo 的启发

Apollo 后面如果要做 CellAppMgr,不要做成“只记录 cellapp 心跳”的薄注册中心。

它应该至少管理:

  • 空间元数据
  • 空间到 cell 的映射
  • cell 负载
  • 跨 cell 迁移协调
  • 空间可视化/调试接口

十四、BaseAppMgr:它管的是玩家入口和非空间负载

关键文件:

  • kbe/src/server/baseappmgr/baseappmgr.h
  • kbe/src/server/baseappmgr/baseapp.h

1. Baseappmgr

从头文件能直接看到它负责:

  • findFreeBaseapp
  • updateBestBaseapp
  • registerPendingAccountToBaseapp
  • sendAllocatedBaseappAddr
  • reqCreateEntityAnywhere
  • reqCreateEntityRemotely
  • queryAppsLoads

这说明 BaseAppMgr 至少承担了两类职责:

  • 玩家接入分配
  • base 侧负载调度

2. 管理视角里的 Baseapp

baseappmgr/baseapp.h 记录:

  • numEntitys
  • numProxices
  • load
  • initProgress
  • flags

很明显,Base 侧调度重点不是空间,而是:

  • 会话数
  • entity 数
  • 负载

对 Apollo 的启发

Apollo 后续如果要做 BaseAppMgr,建议它主要管两类东西:

  • 登录/会话路由
  • Base 实例负载与分配

不要把 Base 和 Cell 用同一种调度维度去看待。

十五、Machine:KBE 从一开始就把运维体系内建了

关键文件:

  • kbe/src/server/machine/machine.h

从头文件能直接看到它负责:

  • 广播接口发现
  • 查询全部接口信息
  • 查询机器
  • 启动进程
  • 停止进程
  • 杀进程
  • 设置 flags

而且它同时维护:

  • 广播地址
  • 多个 EndPoint
  • UDP packet receiver
  • 组件 ID 记录

设计意义

这表明 KBE 把“集群部署与进程管理”当作引擎内建能力,而不是完全依赖外部脚本。

现代系统当然可以把这些工作交给 k8s、nomad 或 systemd,但 KBE 这个设计提醒了一点:

运维面不是额外的,它也是架构的一部分

对 Apollo 的启发

Apollo 不一定需要复刻 machine,但必须尽早明确:

  • 组件如何被发现
  • 进程如何被观测
  • 负载如何被汇总
  • 调试接口如何暴露

如果这些全部留到后期,前面的分布式架构会很难真正跑稳。

十六、Watcher:KBE 不是只有日志,它有结构化观测树

关键文件:

  • kbe/src/lib/helper/watcher.h
  • kbe/src/lib/helper/watcher.cpp

从 watcher.h 可以直接看出它提供了:

  • WatcherObject
  • WatcherValue
  • WatcherFunction
  • WatcherMethod
  • Watchers
  • WatcherPaths

并且有统一的:

  • addToInitStream
  • addToStream
  • path 树组织
  • 远程 query 机制

这套东西为什么重要

它意味着 KBE 的观测不是“打日志 + grep”,而是:

  • 运行时指标可挂到路径树
  • 这些路径可远程查询
  • 值、函数、方法都能挂进去

在 ServerApp::initializeWatcher() 里也能看到它统一挂了:

  • stats/stampsPerSecond
  • uid
  • username
  • componentType
  • componentID
  • globalOrder
  • groupOrder
  • gametime

这是一套很完整的自省体系。

对 Apollo 的启发

Apollo 应该尽快补一个统一观测树,而不是只依赖日志。

建议最少有:

  • watcher 风格的路径型指标树
  • HTTP / RPC 查询接口
  • 统一挂载点,比如 stats/*、components/*、spaces/*

这类系统对 MMO 服务端非常重要,因为你经常需要在线看:

  • 当前空间负载
  • 当前实体数
  • 某个 app 的 tick load
  • 某类消息吞吐

十七、KBE 的主循环本质上是什么

把 ServerApp、EntityApp、NetworkInterface、Watcher、各进程组件放到一起看,KBE 的运行模型可以概括成:

  1. kbeMainT 构建统一宿主
  2. ServerApp 初始化通用运行时
  3. EntityApp 初始化脚本与实体系统
  4. EventDispatcher 驱动主循环
  5. NetworkInterface 驱动网络事件
  6. Timer 驱动 game tick
  7. BaseApp/CellApp 在 tick 和消息之间推进实体状态
  8. Watcher/Machine/Tools 暴露运维入口

所以它不是“多进程 + 网络”这么简单,而是一套完整的:

实时世界宿主

十八、Apollo 应该优先借鉴什么

第一优先级:统一宿主与运行时

先补:

  • ProcessHost
  • WorldHost
  • 统一初始化阶段
  • 统一事件循环与 timer

这是所有后续能力的基础。

第二优先级:实体定义与远程引用

先补:

  • EntitySchema
  • RemoteEntityRef
  • MethodSchema
  • ReplicationSchema

否则 Apollo 的分布式实体最终会沦为一堆 ad-hoc 消息。

第三优先级:Base / Cell 分权

先明确:

  • 会话锚点归谁
  • 空间权威归谁
  • 登录路由归谁

不要先写一堆功能,再想怎么拆。

第四优先级:AOI 不只做查询算法

至少要形成:

  • CoordinateSystem
  • Witness
  • ViewTrigger
  • Replication

第五优先级:观测与管理面

补:

  • watcher tree
  • component registry
  • load query
  • space query

十九、哪些地方不建议直接照搬

1. 宏和老式接口声明

KBE 的消息接口和脚本绑定大量依赖宏,这在当年很常见,但 Apollo 没必要原样复制。

可以保留思想,换成更现代的注册方式。

2. 过重的全局单例依赖

KBE 里全局单例很多,工程上可用,但会增加测试和替换成本。

Apollo 可以保留统一宿主,但尽量通过上下文对象或 service locator 控制依赖边界。

3. Python 强绑定

Apollo 不一定要走 Python 主路径,但必须从一开始就决定实体定义来源。

不照搬 Python 不代表可以不解决“统一定义系统”问题。

二十、不是所有 MMO 都需要 BigWorld/KBE 这一整套

这里有一个很容易被误解的点:

KBEngine 是一种 MMO 服务器架构,但不是 MMO 的唯一架构。

更准确地说,KBEngine 是针对 BigWorld 这一类“分布式连续大世界”问题的答案,而不是所有 MMO 的标准答案。

1. 普通 MMO 也可以完全不需要 CellApp

如果游戏满足下面这些特征,通常不需要完整的 BigWorld/KBE 架构:

  • 地图是分线、分房间、分副本
  • 切场景时允许 loading
  • 一个地图实例绑定一个逻辑进程就够
  • 玩家数量压力主要在连接和业务逻辑,不在连续空间扩容
  • 不追求跨区无缝迁移

这类项目更接近常见的“普通 MMO”或“实例型 MMO”。

这时一个更常见的架构就足够:

  • Login
  • Gateway
  • Game/World
  • DB/Cache
  • 若干独立业务服,例如聊天、公会、匹配

这种架构也完全可以支撑 MMORPG。

2. BigWorld/KBE 额外解决的是哪些问题

只有当你明确需要处理下面这些问题时,CellApp 才开始变得有必要:

  • 超大连续世界
  • 世界按空间切片后仍然无缝移动
  • 一个地图热点区域需要独立扩容
  • 实体跨进程迁移后仍然保持连续观察关系
  • 空间边界附近需要 ghost 和 witness 协作
  • 场景权威不再固定在单一地图进程

也就是说,BigWorld/KBE 解决的不是“有没有 MMO 逻辑”,而是:

MMO 逻辑在分布式空间里怎么维持权威与连续性

3. 可以把 KBE 架构看成普通 MMO 架构的增强层

从工程视角看,更合理的理解方式是:

  • 普通 MMO 架构是基础层
  • BigWorld/KBE 架构是空间分布式增强层

它不是非此即彼,而是逐步增强。

二十一、如果不启动 CellApp,会退化成什么

这是一个很现实的问题。

如果 Apollo 将来吸收 KBE 思想,但不启动 CellApp / CellAppMgr,那么整个系统可以自然退化成更普通的 MMO 形态。

1. 可保留的部分

即使没有 CellApp,下面这些能力依然有价值:

  • LoginApp 或登录入口
  • Gateway 或连接接入层
  • BaseApp 风格的玩家会话锚点
  • 统一实体定义系统
  • 统一远程调用/消息系统
  • 统一 watcher / registry / 运维接口

这部分能力并不依赖分布式空间。

2. 可以暂时不用的部分

如果没有连续大世界需求,下面这些能力可以先不启用:

  • CellApp
  • CellAppMgr
  • GhostManager
  • Witness
  • 跨 Cell 迁移
  • 空间切片负载均衡

3. 退化后的实际形态

这时系统更像:

  • LoginApp
  • GatewayApp
  • BaseApp 或 WorldApp
  • DBMgr

其中:

  • BaseApp 既承接玩家会话,也承接地图/实例逻辑
  • 一个实例或地图可以由一个逻辑进程独立管理
  • 场景切换通过普通切图或实例迁移完成

这其实就是非常典型的 MMO 服务器架构。

4. 这种退化不是“阉割版”,而是合理分层

很多项目并不是“做不到 BigWorld”,而是根本不需要承担 BigWorld 那种复杂度。

如果地图切换允许加载、单地图实例可以固定绑定单进程,那么直接上 CellApp + Ghost + Witness + 分布式空间 往往是过度设计。

二十二、Apollo 更适合做成可裁剪架构,而不是默认全量 BigWorld 化

这也是 Apollo 现在最现实的方向。

不应该一开始就假设所有项目都必须启动:

  • BaseApp
  • CellApp
  • BaseAppMgr
  • CellAppMgr
  • Machine

更好的做法是把它拆成可选层。

第一层:普通 MMO 基础层

这一层默认启用,解决大多数项目的核心问题:

  • Login
  • Gateway
  • World/Game
  • DB/Cache
  • 统一配置、日志、生命周期
  • 统一实体定义和消息系统

这层就已经足够支撑大部分 MMORPG。

第二层:会话与逻辑分权层

如果项目需要更强的玩家锚点语义,可以再引入:

  • BaseApp
  • BaseAppMgr

这时先解决:

  • 玩家会话锚点
  • 登录后路由
  • 非空间逻辑归属
  • 玩家跨地图或跨实例的长期状态归属

但仍然不必引入 CellApp。

第三层:分布式空间层

只有在明确需要连续大世界时,再引入:

  • CellApp
  • CellAppMgr
  • Ghost
  • Witness
  • 空间分片
  • 跨 Cell 迁移

这时 Apollo 才真正进入 BigWorld/KBE 风格。

这三层的好处

这样设计后:

  • 小中型 MMO 可以只用第一层
  • 需要更强玩家归属语义的项目用到第二层
  • 真正的大世界项目再用第三层

这比“一上来全量 BigWorld 化”更稳,也更符合 Apollo 现在的成熟度。

二十三、一个更适合 Apollo 的落地路线

如果 Apollo 参考 KBE,我建议按下面顺序落:

阶段 1:统一宿主

  • 抽象 AppMain<T>
  • 抽象 ServerHost
  • 抽象 WorldHost

阶段 2:统一实体定义

  • 增加 EntitySchema
  • 增加 schema 驱动的属性/方法描述
  • 增加远程引用 EntityRef

阶段 3:Base / Cell 模型成型

  • BaseService
  • CellService
  • BaseAppMgr
  • CellAppMgr

阶段 4:空间系统

  • WorldSpace
  • SpacePartition
  • Witness
  • GhostReplica

阶段 5:运维体系

  • Watcher
  • ComponentRegistry
  • SpaceView
  • LoadReport

这样 Apollo 就是在吸收 KBE 的结构,而不是只做 BigWorld 风格 API 外壳。

二十四、Apollo 可以怎样定义“普通 MMO 模式”和“BigWorld 模式”

为了避免后续架构混乱,Apollo 最好在概念上就明确两种运行模式。

1. 普通 MMO 模式

建议默认模式就是这个。

特点:

  • 不启用 CellApp
  • 地图实例绑定单逻辑服
  • 切图允许中断或切实例
  • 无需 ghost / witness / 跨 cell 迁移

适用:

  • 大多数 MMORPG
  • 副本型或分线型世界
  • 中小规模开放世界

2. BigWorld 模式

特点:

  • 启用 BaseApp + CellApp
  • 启用 BaseAppMgr + CellAppMgr
  • 启用 distributed space 相关运行时
  • 启用 witness / ghost / teleport / authority transfer

适用:

  • 连续无缝大世界
  • 热点区域需要独立水平扩容
  • 跨区迁移不可接受 loading

3. 两种模式应该共享什么

两种模式最好共享同一套底层:

  • 宿主框架
  • 消息系统
  • 实体定义系统
  • 日志和配置
  • watcher / registry

这样 Apollo 才不会分叉成两套完全不同的代码库。

二十五、结论

KBEngine 真正厉害的地方,不是它把 MMO 服务拆成了很多进程,而是它把下面这些东西做成了同一套系统:

  • 统一宿主
  • 统一网络语义
  • 统一实体定义
  • 统一空间权威模型
  • 统一观测与管理面

Apollo 现在要参考它,最值得抄的是这种“系统化收口”的能力。

最不值得抄的是表层接口形式。

如果只学它的进程名,Apollo 会得到一套名字很像 KBE 的工程。

如果学到它的运行时骨架、实体元数据、空间权威和运维体系,Apollo 才会真正长出 KBE/BigWorld 这一类架构的核心能力。

参考入口

  • C:\Users\cui\Workspaces\kbengine\kbe\src\lib\server\kbemain.h
  • C:\Users\cui\Workspaces\kbengine\kbe\src\lib\server\serverapp.h
  • C:\Users\cui\Workspaces\kbengine\kbe\src\lib\server\entity_app.h
  • C:\Users\cui\Workspaces\kbengine\kbe\src\lib\network\network_interface.h
  • C:\Users\cui\Workspaces\kbengine\kbe\src\lib\entitydef\entitydef.h
  • C:\Users\cui\Workspaces\kbengine\kbe\src\lib\entitydef\scriptdef_module.h
  • C:\Users\cui\Workspaces\kbengine\kbe\src\lib\entitydef\entity_call.h
  • C:\Users\cui\Workspaces\kbengine\kbe\src\server\baseapp\baseapp.h
  • C:\Users\cui\Workspaces\kbengine\kbe\src\server\baseapp\proxy.h
  • C:\Users\cui\Workspaces\kbengine\kbe\src\server\cellapp\cellapp.h
  • C:\Users\cui\Workspaces\kbengine\kbe\src\server\cellapp\entity.h
  • C:\Users\cui\Workspaces\kbengine\kbe\src\server\cellapp\spacememory.h
  • C:\Users\cui\Workspaces\kbengine\kbe\src\server\cellapp\witness.h
  • C:\Users\cui\Workspaces\kbengine\kbe\src\server\cellapp\ghost_manager.h
  • C:\Users\cui\Workspaces\kbengine\kbe\src\server\cellappmgr\cellappmgr.h
  • C:\Users\cui\Workspaces\kbengine\kbe\src\server\baseappmgr\baseappmgr.h
  • C:\Users\cui\Workspaces\kbengine\kbe\src\server\machine\machine.h
  • C:\Users\cui\Workspaces\kbengine\kbe\src\lib\helper\watcher.h
在 GitHub 上编辑此页
最后更新: 4/27/26, 7:56 AM
贡献者: cuihairu