01

备份与隔离

先建立可回退边界,不在旧项目上原地覆盖。

现在只做这一件事停止旧服务,备份 Assets 和数据库,并为 Nex 准备一个全新迁移目录。
  • 生产环境使用安全关闭流程,保留首个异常前的完整日志。
  • 备份旧 Assets、数据库、客户端 SDK、配置和引擎 Commit。
  • 旧 Assets 迁移期间保持只读,不在其中清理或重构。
  • 第一次启动 Nex 使用新测试库;随后再用生产库副本演练。
停止条件没有数据库备份,或无法明确当前旧引擎版本时,不进入下一步。
02

生成 Nex 新骨架

让目标版本自己给出正确目录和启动脚本。

现在只做这一件事打开 Nex 根目录,运行 new_assets.bat,然后重命名新生成的 server_assets
  1. 打开 D:\KBELAB\kbengine\KBEngine-Nex
  2. 双击根目录下的 new_assets.bat
  3. 等待 server_assets 生成完成,不要在生成过程中移动目录。
  4. server_assets 重命名为项目 Assets 名称,例如 mygame_nex_assets

推荐把新 Assets 放在 Nex 引擎根目录,与 `kbe` 同级。放在其他位置时,稍后必须显式设置 `KBE_ROOT`。

03

展开旧业务脚本

复制的是旧 `scripts` 里面的内容,不是 `scripts` 文件夹本身。

现在只做这一件事把旧 `scripts/base` 映射为新 `base`,其余业务目录同理;保留新 `scripts` 里的启动文件。
旧位置新位置判断
scripts/basebase复制内容
scripts/cellcell复制内容
scripts/entity_defsentity_defs复制内容
scripts/entities.xmlentities.xml复制并校验
scripts/data 等扩展目录Assets 根目录同名目录按项目保留
根目录 start_server.*scripts/start_server.*不复制旧文件
  1. 打开旧 Assets 的 scripts 目录。
  2. 选择其中的业务目录和根级 entities.xml,把它们复制到新 Assets 根目录。
  3. 保留新 Assets 的 scripts 目录及其中的 Nex 启动脚本,不用旧根目录启动脚本覆盖它。
  4. 在新 Assets 中搜索并删除全部 __pycache__ 目录和 .pyc 文件。
目录验收
mygame_nex_assets/
├─ base/
│  └─ Account.py
├─ bots/
├─ cell/
│  └─ Avatar.py
├─ common/
├─ data/
├─ db/
├─ entity_defs/
│  └─ Avatar.def
├─ interface/
├─ logger/
├─ login/
├─ server_common/
├─ user_type/
├─ entities.xml
├─ res/
└─ scripts/
   └─ start_server.bat
立即纠正若看到 scripts/base/Account.py,复制层级错了;不要带着错误层级继续修 import。
04

迁移资源与配置

Nex 配置兼容原版,旧 res 内容可以直接覆盖同名文件。

现在只做这一件事把旧 Assets 的 res 内容复制到新 Assets 的 res,同名文件直接覆盖。
  1. 复制旧 res 中的全部项目资源和配置。
  2. 粘贴到新 Assets 的 res,出现同名文件时选择覆盖。
  3. 确认数据库、网络、日志、Tick 和账号系统配置仍与部署环境一致。
  4. 记录所有使用中的地图;旧导航二进制仍要在下一阶段重建。

资源路径新规则

KBE_ROOT/kbe/res ; PROJECT_PATH ; PROJECT_PATH/res

entities.xml 不再写成 scripts/entities.xml;地图仍可写 spaces/xxx

配置可直接覆盖

Nex 兼容原版配置,kbengine.xmlserver_errors.xml 和日志配置可直接覆盖同名文件。

覆盖后仍要核对部署参数;导航二进制兼容性不在此范围内。

重点:代码里的资源路径也要迁移业务目录提升到 Assets 根级后,所有传给 KBE 资源 API 的 scripts/... 路径都要去掉 scripts/ 前缀。只移动文件而不修改代码,KBEngine.hasRes() 仍会返回 False

createSpawnPointDatas 前后对照

原版:Nex 中路径错误

res = r"scripts\data\spawnpoints\%s_spawnpoints.xml" % space_name

KBEngine.hasRes(res)

Nex:根级 data

res = "data/spawnpoints/%s_spawnpoints.xml" % space_name

KBEngine.hasRes(res)
  • scripts/data/... 改为 data/...
  • scripts/entities.xml 改为 entities.xml
  • scripts/base/...scripts/cell/...scripts/entity_defs/... 去掉 scripts/
  • spaces/...server/... 保持不变,不要增加 res/ 前缀。
  • 资源逻辑路径统一使用 /,并严格匹配文件名大小写。

如果 Assets 不在引擎根目录,应在系统环境变量或项目启动配置中把 KBE_ROOT 指向 D:\KBELAB\kbengine\KBEngine-Nex,再从新 Assets 的 scripts 目录启动。

启动输出必须同时核对 KBE_ROOTKBE_RES_PATHKBE_BIN_PATHKBE_VENV_PATH。业务读取资源优先使用 KBEngine.hasRes()getResFullPath()matchPath()KBEngine.open()

05

处理 Python 3.13 与 API

先消除确定性不兼容,再运行服务端。

现在只做这一件事搜索 Python 旧语法和废弃 FD API;不要一启动就同时排查数据库、导航和客户端。
运行时跨度原版引擎是 Python 3.7.3,当前 Nex 是 3.13.5。旧 `.venv`、`site-packages`、`.pyc`、`.pyd`、`.so` 都不能直接复用。
不支持代码修改
print a, bprint(a, b)
mapping.has_key(key)key in mapping
弯引号 “”“ASCII 三引号 """
Python 3.7 二进制扩展使用 CPython 3.13 重新安装或编译

旧 readiness API:停止使用

registerReadFileDescriptor registerWriteFileDescriptor deregisterReadFileDescriptor deregisterWriteFileDescriptor

新 completion API:按 FD 类型迁移

registerAcceptFileDescriptor registerReadDataFileDescriptor writeFileDescriptor deregisterAccept/ReadData...
Python:completion 回调签名
def on_accept(listener_fd, client_fd, error_code):
    """处理 accept 完成事件。 / Handle an accept completion."""

def on_read(fd, data, error_code):
    """处理读完成事件。 / Handle a read completion."""

def on_write_complete(fd, bytes_written, error_code):
    """处理写完成事件。 / Handle a write completion."""
  • 旧回调中不要再自行阻塞调用 accept() / recv()
  • 监听 FD 与客户端 FD 使用不同注册接口,同一读 FD 不重复注册。
  • 成功、断连、异常、停止四条路径都注销并关闭 FD。
  • completion 和 asyncio 回调不可做长 CPU、同步网络或同步磁盘 IO。
  • 原始 SQL 可能命中 Nex 2.8.2 黑名单,查 dbmgr WARNING,不要直接关闭保护。
  • Redis 底层持久化已移除,依赖项目必须重新选择持久化方案。
06

重建导航与坐标适配

旧 `.navmesh` 不复用,不改名;从源模型生成新 `.bin`。

现在只做这一件事对每个实际启用的 Space 重新生成 NavMesh,并按客户端引擎的坐标定义核对位置和模型朝向。
破坏性变化原版自动发现 *.navmesh,当前 Nex 自动发现 *.bin。二进制格式也已升级,重命名文件无效。
先分清位置与前向导航升级不会改变 Entity 协议位置。KBE、Cocos Creator 3.x 和 Godot 4 都使用 Y 轴表示高度,Cocos/Godot 位置直接使用 (x, y, z)。坐标系参考页中的 (-x, z, y) 不适用于当前 Nex。+Z / -Z 描述的是默认前向,yaw + 180 只在模型前向需要补偿时使用。
  1. 从原始 OBJ / GLTF / FBX 模型开始,不从旧二进制 NavMesh 转换。
  2. 使用 NavMesh Generator 设置角色半径、高度、坡度、台阶、Tile 和体素参数。
  3. 用 TestAgent 验证门、坡道、楼层、障碍边界。
  4. 导出到 res/spaces/<mapping>/navmesh.bin
  5. 加载完成回调触发后,才调用随机点、导航或射线 API。
案例约定getRenderObject(...) 表示按实体 ID 获取客户端场景对象,是需要替换的伪代码;上传代码中的 player 表示当前 KBE 玩家实体。
Unity C# 案例

KBE → Unity:(-x, y, z),-yaw

public override void onPositionChanged(KBVector3 oldValue)
{
    base.onPositionChanged(oldValue);
    Transform renderObj = GetRenderObject(id); // 伪代码 / Pseudocode
    if (renderObj == null) return;
    renderObj.position = new Vector3(
        -position.x, position.y, position.z);
}

public override void onSmoothPositionChanged(KBVector3 oldValue)
{
    base.onSmoothPositionChanged(oldValue);
    Transform renderObj = GetRenderObject(id); // 伪代码 / Pseudocode
    if (renderObj == null) return;
    renderObj.position = new Vector3(
        -position.x, position.y, position.z);
}

public override void onDirectionChanged(KBVector3 oldValue)
{
    base.onDirectionChanged(oldValue);
    Transform renderObj = GetRenderObject(id); // 伪代码 / Pseudocode
    if (renderObj == null) return;
    renderObj.eulerAngles = new Vector3(
        direction.y, -direction.z, direction.x);
}

本地玩家上传:

Vector3 p = renderObj.position;
Vector3 r = renderObj.eulerAngles;
player.position = new KBVector3(-p.x, p.y, p.z);
player.direction = new KBVector3(r.z, r.x, -r.y);
接入边界回调写在业务 Entity 类中,不修改自动生成的 SDK。出生、普通位置、平滑位置、服务端纠正和上传必须使用同一套映射。
统一输入UnityCocos / GodotUnreal
KBE 位置 (2, 3, 4)(-2, 3, 4)(2, 3, 4)(200, 400, 300)
KBE yaw 30°330°210°120°
双向验收把上表每个客户端结果反向转换,必须精确回到位置 (2, 3, 4) 和 yaw 30°;再测试出生、平滑移动、传送、点击寻路和重连。
两个 API 共同存在navigate()navigateToDetour() 是性能和导航能力的取舍,不是迁移前后的替代关系。

navigate()

更轻量,适合单层地图、平面移动和大量轻量 AI。服务端不负责沿 NavMesh 高度移动,客户端必须自行贴合地形,并且无法处理多层建筑导航。

navigateToDetour()

使用 Detour 路径并由服务端贴合 NavMesh 高度,适合坡道、台阶和多层建筑。开销高于 navigate(),并且不支持 2D 导航。

实体 layer 必须与 getRandomPointsnavigate*raycast 的 layer 一致。角度偏移要按客户端的度/弧度单位换算并归一化。

07

最小启动并重生成 SDK

从首个错误开始修,服务端稳定后再接客户端。

现在只做这一件事打开新 Assets 的 scripts 目录,双击 start_server.bat,验证 App 链和空数据库;之后生成同版本客户端 SDK。

按以下顺序观察首个 ERROR:machine → logger → interfaces → dbmgr → baseappmgr/cellappmgr → baseapp/cellapp → loginapp

  • 启动输出的四个路径变量正确。
  • 根级 `entities.xml` 和全部实体定义加载成功。
  • 测试库表初始化完成,Bootstrap BaseApp 和首个 Space 正常。
  • 几何加载完成回调触发,导航调用没有抢跑。
  • Interfaces completion socket 能连接、收发、断连并清理。
  • 正常安全关闭后没有残留 App 或未写入数据。
再接客户端使用新 Assets 的 scripts/gensdk.* 生成当前 Nex SDK。任何 entities.xml、`.def`、Flags、UType、属性或方法变化都要求更新客户端。

不要混用旧 SDK。至少回归登录、角色、进入空间、实体创建/销毁、属性、远程方法、移动、传送和重连。

08

验收、压测与上线

功能通过不是终点,还要验证数据、Tick 和回滚。

现在只做这一件事按五层验收:静态、空库、业务、客户端导航、生产演练。任何一层失败都不向后带病推进。
A. 静态目录、Python 3.13、旧 FD API、资源路径、Entity 定义全部通过。
B. 空库全 App 冷启动、建表、Space 创建、几何加载、安全关闭通过。
C. 业务账号、角色、持久化、Timer、HTTP、DB、Interfaces、Bots 通过。
D. 客户端同版本 SDK、协议、双向坐标、导航、射线、传送、重连通过。
E. 性能目标 Entity/AOI/Bots 下 Tick、CPU、内存、GC、包量、DB 可接受。
F. 上线旧库副本升级、备份恢复、安全关闭、崩溃恢复、回滚演练通过。
上线红线没有回滚演练、仍复用旧 NavMesh、客户端仍是旧 SDK、生产库未备份、或 completion 回调有无界积压时,不切流量。
迁移完成定义新目录、新运行时、新 IO、新导航、同版本 SDK、数据库副本演练和目标负载压测全部通过,并保留可执行回滚路径。
?

按日志找根因

先对照现象,不要同时改多个模块。

`ModuleNotFoundError` 或找不到 `entities.xml`
检查业务代码是否仍位于 assets/scripts/base。Nex 应是 assets/base,实体声明应是根级 assets/entities.xml;同时核对启动输出中的 KBE_RES_PATH
出生点未创建,`KBEngine.hasRes()` 返回 `False`
检查代码是否仍使用 scripts/data/spawnpoints/...。Nex 的 data 已提升到 Assets 根目录,应改为 data/spawnpoints/...,并用 KBEngine.getResFullPath() 确认最终命中的是新 Assets 下的文件。
`RuntimeError` 提到 `registerReadFileDescriptor`
旧 readiness API 已废弃。按监听 FD、客户端读 FD、异步写三个职责迁到 completion API,并补齐注销、断连和异常路径。
找不到 NavMesh 或提示版本不匹配
.navmesh 不兼容。用当前 NavMesh Generator 从源模型重生成 .bin,放在实际 mapping 目录,不能只改扩展名。
客户端镜像、朝向相反或位置放大错误
跟踪“KBE 网络原值 → SDK 适配值 → 引擎场景值”。判断是没转换还是双重转换;UE 还要确认米/厘米 100 倍换算。
Python 包导入失败
确认没有复制 3.7 的 `.venv`、字节码或二进制包。用与 Nex 一致的 CPython 3.13 新建 `.venv`,重新安装并固化 `requirements.txt`。
SQL 在 Nex 中突然被拒绝
查看 dbmgr WARNING 是否命中 raw database command 黑名单。优先重构危险命令和权限边界,不要为了临时跑通直接关闭保护。
R

核对入口

版本变化时优先重新检查这些来源。