Skip to content

KBEngine Nex 引擎概览

KBEngine Nex 是一个以 Entity 为业务单元、以多进程组件为运行边界的分布式服务端。理解它的关键不是记住进程名称,而是掌握三条数据流:客户端会话如何进入 BaseApp、Entity 如何在 Base/Cell/Client 之间通信、持久化请求如何经 DBMgr 到达数据库。

架构图

KBEngine 服务端架构图

图中的交换网络代表部署环境提供的局域网或虚拟网络,不属于引擎组件。正式环境应让内部组件网络、客户端入口、数据库网络和管理入口处于清晰的安全边界中。

核心架构原则

以 Entity 组织状态与行为

EntityDef 描述 Entity 的组成、属性、远程方法、数据类型、同步范围和持久化策略。Python 脚本实现具体业务行为,C++ 底层负责对象生命周期、协议编解码、远程路由和调度。

Base 与 Cell 分离职责

  • Base 负责客户端会话、持久化和不依赖位置的长期状态;
  • Cell 负责 Space 中的位置、移动、AOI、战斗、AI 和实时交互;
  • Client 只包含客户端可见的数据与调用契约。

这种拆分让连接业务和实时空间业务可以分别扩展。它不是强制每个 Entity 都具备三部分,Entity 可以按用途只有 Base、只有 Cell,或同时具备 Base/Cell/Client。

管理组件与工作组件分离

BaseAppMgr、CellAppMgr 负责协调和分配,BaseApp、CellApp 承担实际业务负载。一个服务组通常只有一个对应管理组件,但可以运行多个工作组件并部署到不同机器。

异步跨进程通信

组件之间通过网络消息和 EntityCall 传递请求。远程调用不是本地函数调用:它存在序列化、排队、网络延迟、目标生命周期变化和失败的可能。业务不应依赖跨进程调用“立即执行”,也不应在高频 Tick 中制造不必要的消息往返。

组件拓扑

组件主要职责典型扩展方式
Machine组件发现、本机进程管理、机器资源与组件状态上报每台运行引擎组件的机器一个
Logger汇聚各组件日志通常一个,可按部署规划日志流量
Interfaces第三方账号、计费、运营或外部服务接入按外部请求量和可用性规划
DBMgrEntityDef 数据模型、数据库任务、持久化结果路由服务组核心组件,数据库侧独立扩展
BaseAppMgrBaseApp 注册、状态协调和负载分配服务组通常一个
BaseApp客户端连接、Proxy、Base Entity、非空间业务、写库入口增加进程或机器
CellAppMgrCellApp 注册、Space 创建位置与负载协调服务组通常一个
CellAppSpace、Cell Entity、AOI、移动、导航和实时逻辑增加进程或机器
LoginApp客户端登录入口、鉴权流程和 BaseApp 地址分配增加实例并由入口层分流
Bots模拟客户端,用于功能验证和压力测试按测试规模增加进程

启动与组件发现

组件启动时首先初始化资源路径、配置、日志、网络接口和脚本运行时,然后通过 Machine 发现同一服务组中的其他组件。管理组件和工作组件完成注册后,才具备处理后续业务请求的条件。

典型依赖关系包括:

  1. Machine 提供组件发现和本机管理入口;
  2. Logger 接收组件日志,但业务组件也需要能够在本地记录启动失败;
  3. DBMgr 初始化 EntityDef、脚本和数据库接口;
  4. BaseAppMgr、CellAppMgr 建立对应工作组件的管理关系;
  5. BaseApp、CellApp 完成注册并进入可服务状态;
  6. LoginApp 获得可用 BaseApp 信息后接受客户端登录;
  7. Bots 和管理工具按需接入。

启动日志反复出现 finding dbmgr 或其他组件名称,表示当前组件仍未发现或未能连接目标组件,不应只处理这条表面日志。应继续检查目标进程是否存活、目标进程更早的初始化错误、UID/服务组、广播地址、内部网卡和端口配置。详见 常见错误

客户端登录与会话建立

典型登录流程如下:

  1. 客户端 SDK 连接 LoginApp,提交登录或注册请求;
  2. LoginApp 执行内置验证,或通过 Interfaces 与第三方账号系统交互;
  3. 验证通过后,LoginApp 请求 BaseAppMgr 选择合适的 BaseApp;
  4. LoginApp 向客户端返回目标 BaseApp 的外部地址、端口和会话信息;
  5. 客户端断开 LoginApp,并连接目标 BaseApp;
  6. BaseApp 验证会话后创建或恢复与客户端关联的 Proxy Entity;
  7. 服务端向客户端同步初始 Entity 状态,客户端进入业务流程。

LoginApp 不是整个游戏会话的长期连接点。完成分配后,客户端主要与 BaseApp 保持连接。生产环境必须正确配置 LoginApp 和 BaseApp 的外部地址;内部网卡地址不能直接返回给公网客户端。

Proxy 与玩家 Entity

Proxy 是能够与一个客户端连接关联的特殊 Base Entity。它提供客户端消息入口、客户端 EntityCall 和会话相关能力。一个客户端在同一会话中由一个 Proxy 代表。

Proxy 不等于只有 Base 部分。当玩家进入 Space 时,对应 Entity 可以创建 Cell 部分;此后同一个玩家对象同时具有 Base、Cell 和 Client 侧语义:

  • Base 保存长期状态并持有客户端连接;
  • Cell 处理场景中的位置、移动、战斗与 AOI;
  • Client 接收可见属性和远程调用。

默认 Assets 常使用 Account 作为登录后的首个 Proxy,但具体实体名称和角色切换流程由项目定义。

Space 与 Cell 创建流程

Space 是 CellApp 内存中的实时逻辑空间。它可以是一张地图、一个副本、一个匹配房间或一局牌桌。Space 的业务含义由脚本决定,引擎负责其容器、坐标、Entity 和可见性基础能力。

创建和进入 Space 的常见流程如下:

  1. Base 业务决定创建或进入目标 Space;
  2. 如果目标 Space 尚不存在,请求 CellAppMgr 选择 CellApp;
  3. 选中的 CellApp 创建 Space Entity,并向发起方返回 EntityCall;
  4. 玩家 Base 请求在目标 Space 中创建自身 Cell 部分;
  5. Cell 创建完成后,Base 与 Cell 建立对应关系;
  6. CellApp 将移动、AOI、触发器和导航纳入该 Space 的 Tick;
  7. 玩家获得 Witness 后,服务端开始同步其视野内的 Entity。

Space 分配会参考 CellApp 状态和负载,但单个 Space 内的实时逻辑通常仍受其所在 CellApp 的执行预算约束。大型世界需要从 Entity 密度、AOI、区域拆分和迁移策略上设计,不能只依赖增加 CellApp 数量。

Witness、View 与状态同步

Witness 代表客户端在 Cell 空间中的观察者。View 是当前对该 Witness 可见的 Entity 集合。坐标系统和范围触发器检测 Entity 之间的空间关系,并驱动进入/离开视野事件。

状态同步大致经历以下过程:

  1. Entity 移动或可见属性发生变化;
  2. CellApp 标记相关空间关系或属性为待处理;
  3. Witness 根据 View、同步标志和 DetailLevel 筛选客户端应看到的数据;
  4. 数据经对应 Proxy 的客户端通道发送;
  5. 客户端 SDK 更新 Entity 并触发游戏层事件。

View 半径越大、空间越密集、属性变化越频繁,每个 Witness 维护的关系和网络消息越多。应优先减少不必要的客户端可见属性和更新频率,再考虑底层参数调优。

EntityCall 与消息路由

EntityCall 是远程 Entity 的引用。脚本可以通过它调用目标 Base、Cell 或 Client 方法,底层负责根据 Entity ID、组件类型和地址信息将消息发送到目标进程。

常见路由包括:

  • Client 调用暴露的 Base/Cell 方法;
  • Base 调用自身 Cell 或其他 Base Entity;
  • Cell 调用自身 Base、其他 Cell Entity或客户端;
  • 管理组件向工作组件发送创建、注册和状态消息;
  • DBMgr 将异步数据库结果返回请求组件。

EntityCall 只保证提供路由语义,不保证目标永远存在。Entity 销毁、Cell 迁移、断线和组件退出都可能使旧引用失效。长生命周期业务应设计超时、幂等、失败回收和状态确认,不要无限保存未经验证的远程引用。

Entity 生命周期

一个具有客户端与空间部分的玩家 Entity,生命周期通常包含:

  1. 登录验证并在 BaseApp 创建或恢复 Proxy;
  2. 从数据库加载持久化属性;
  3. 创建 Cell 或进入已有 Space;
  4. 建立 Witness 并向客户端同步可见世界;
  5. 在 Base/Cell/Client 之间执行远程调用;
  6. 离开 Space,销毁或迁移 Cell 部分;
  7. 客户端断线后进入重连、等待销毁或离线处理;
  8. 按业务时机写库并最终销毁 Base。

Timer、回调、EntityCall、数据库任务和事件订阅都必须服从 Entity 生命周期。对象销毁后继续回调旧 Entity,或在关服阶段创建新的长期任务,都会引入悬挂状态和关闭超时。

持久化数据流

持久化属性由 EntityDef 声明。典型写库流程是:

  1. Base 或 Cell 业务发起 writeToDB 等持久化请求;
  2. 引擎按 EntityDef 序列化需要保存的属性;
  3. 请求发送给 DBMgr;
  4. DBMgr 将任务交给对应数据库接口执行;
  5. 数据库线程完成操作;
  6. DBMgr 将结果异步返回原组件;
  7. 引擎在 Entity 仍有效时触发脚本回调。

数据库线程避免直接阻塞 BaseApp/CellApp 的主 Tick,但序列化、请求数量、回调处理和数据库积压仍会影响系统。高频批量写入、慢查询和超大 Entity 数据会增加 DBMgr 内存、IO 和回调延迟。

Nex 支持 MySQL、MongoDB 和 PostgreSQL。每个接口的认证、连接参数和数据模型不同,应根据项目选择并在接近生产的数据量下验证。

第三方服务接入

Interfaces 用于把账号验证、计费、邮件或其他外部系统从核心 Entity 进程中分离。LoginApp、BaseApp 等组件可以向 Interfaces 发起请求,由它与外部 HTTP 或业务系统交互。

外部服务具有不稳定延迟,调用链必须设置超时并处理重复响应、迟到响应和目标 Entity 已销毁的情况。不能让同步外部 IO 阻塞 BaseApp 或 CellApp Tick,也不能默认信任外部返回的数据。

管理、日志与诊断

Machine、Logger、Watcher 和管理工具共同提供运行观察能力:

  • Machine 展示组件与机器状态,并处理受控的管理请求;
  • Logger 汇聚各组件日志;
  • Watcher 暴露负载、队列、网络、Tick 和业务指标;
  • Bots 模拟客户端行为和压力;
  • GUIConsole、WebConsole、Space Viewer 等工具按职责连接管理或观察接口。

特权管理请求使用 management token 校验。管理网络应限制来源并与公网客户端入口隔离,token 也应按环境设置,不能依赖仓库默认值承担生产安全。

组件关闭与状态收敛

正常关闭不是直接终止所有进程。组件需要停止接收新工作、处理必要的 Entity 保存或迁移、清理回调与网络通道,并按依赖关系退出。

脚本层应配合关闭生命周期:

  • 不在关闭阶段无限创建新 Timer 或异步任务;
  • 保存回调必须允许失败、超时或 Entity 已销毁;
  • 外部服务请求不能阻止进程永久退出;
  • 清理逻辑应幂等,避免重复回调造成二次销毁。

强制结束进程只能作为异常恢复手段。它可能绕过脚本清理、未完成的数据库任务和正常连接关闭。

扩展与故障边界

增加 BaseApp 可以分担客户端连接、Base Entity 和非空间业务;增加 CellApp 可以分担不同 Space 或可迁移 Cell 的实时负载;增加 LoginApp 可以扩展登录入口。数据库、外部接口和网络入口需要独立规划高可用与容量。

需要特别关注以下边界:

  • 单 Entity 热点:一个全局管理 Entity 仍只在一个进程执行;
  • 单 Space 热点:高密度场景可能受单 CellApp Tick 限制;
  • AOI 放大:可见关系接近 Entity 数量的平方增长时,CPU 和包量会快速上升;
  • 跨进程放大:细粒度高频 RPC 会增加序列化、队列和网络成本;
  • 数据库积压:异步只改变等待方式,不会提升数据库本身吞吐;
  • Python 长任务:单次回调过长会推高整个组件的 Tick 长尾;
  • 管理组件故障:管理组件影响新分配和集群协调,需要纳入恢复方案;
  • 状态恢复语义:备份和恢复能力不能替代业务幂等、数据库备份与灾难恢复演练。

正式上线前,应使用真实 Entity 数量、AOI 密度、移动方式、RPC 频率和数据库规模进行压测,并观察延迟分位数而非只看平均值。

下一步阅读

  1. 引擎介绍:了解技术定位和适用场景。
  2. 引擎特性:查看各能力的边界与入口。
  3. Entity 基础概念:学习 EntityDef 和 Base/Cell/Client。
  4. Space 概览:理解空间、AOI 与实时世界。
  5. 创建项目:开始搭建 Assets 工程。
  6. 安装和启动:准备工具链并运行服务端。