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广播与消息去重

BigWorld 架构深度解析

目录

  • 什么是 BigWorld
  • BigWorld 核心概念
  • BigWorld vs 传统架构对比
  • BigWorld 核心组件
  • BigWorld 的特点
  • 适用场景
  • BigWorld 在 Apollo 中的兼容层

一、什么是 BigWorld?

BigWorld 是一个专为 MMO 设计的服务器引擎技术,最著名的应用是 Wargaming 的《坦克世界》、《战舰世界》。

与传统游戏服务器对比:

传统MMO架构:           BigWorld架构:
┌─────────┐            ┌─────────────────────────┐
│  Gate   │            │      CellApp (单元格)   │
├─────────┤            ├─────────────────────────┤
│  Logic  │            │      BaseApp (数据库)   │
├─────────┤            ├─────────────────────────┤
│  DB     │            │     LoginApp (登录)     │
└─────────┘            │     ChatApp (聊天)      │
                        │      ... (专用服务)      │
                        └─────────────────────────┘

二、BigWorld 核心概念

1. 空间分割架构 (CellApps)

BigWorld 将游戏世界划分为空间单元,每个单元由独立的 CellApp 进程管理。

世界地图按区域划分:
┌────────┬────────┬────────┬────────┐
│CellApp0│CellApp1│CellApp2│CellApp3│
│  区域A  │  区域B  │  区域C  │  区域D  │
├────────┼────────┼────────┼────────┤
│CellApp4│CellApp5│CellApp6│CellApp7│
│  区域E  │  区域F  │  区域G  │  区域H  │
└────────┴────────┴────────┴────────┘

玩家跨区域移动时:
Zone A ──(无缝迁移)──> Zone B

特点:

  • 每个区域独立进程,故障隔离
  • 负载均衡:热点区域可独立扩展
  • 无缝世界:玩家感觉不到边界

2. 实体-组件分离 (Entity + Real/Shadow)

BigWorld 引入了 Real 和 Shadow 实体的概念:

// Real Entity - 真实的、有状态的服务器端实体
class RealEntity {
    Position position;
    int health;
    std::vector<Spell> activeSpells;
    void update(float dt);  // 每帧更新逻辑
};

// Shadow Entity - 影子实体,只做状态同步
class ShadowEntity {
    EntityID id;
    Position position;     // 只同步位置
    int health;            // 只同步血量
    // 没有逻辑,只是数据容器
};

设计理念:

在本地区域的玩家 → Real Entity (有逻辑)
在远处区域看到的玩家 → Shadow Entity (只同步)

3. 唯一ID系统

BigWorld 使用 64位唯一ID 标识所有实体:

struct EntityID {
    uint64_t id;

    // 格式: [类型(8位) | 服务器ID(16位) | 实体索引(40位)]
    uint8_t  type;      // Entity/Player/Monster等
    uint16_t serverId;  // 来源服务器
    uint64_t index;     // 本地索引
};

三、BigWorld vs 传统架构对比

对比表

特性传统MMO架构BigWorld架构
世界划分单一世界/分线空间分割 CellApps
负载均衡玩家数量分线按区域动态迁移
扩展方式垂直扩展水平扩展
故障影响整个世界/分线只影响单个区域
无缝切换需要切换场景真正的无缝世界
实体管理集中式分布式 + Ghost模式
位置同步AOI九宫格CellApps间协商
数据持久化定时保存BaseApp 专用服务

四、BigWorld 核心组件

1. CellApp - 游戏逻辑服务器

class CellApp {
    // 管理一个空间区域
    Rect bounds;
    std::vector<RealEntity*> entities;

    // 跨区域通信
    void onEntityEnter(Entity* e, CellApp* from);
    void onEntityLeave(Entity* e, CellApp* to);

    // 与相邻 CellApp 同步边界实体
    void syncGhosts();
};

// 关键:区域交接
void CellApp::handoverEntity(Entity* e, Position newPos) {
    if (!myBounds.contains(newPos)) {
        CellApp* neighbor = findNeighbor(newPos);
        neighbor->receiveEntity(e, this);  // 迁移实体
        entities.erase(e->id);
    }
}

2. BaseApp - 数据库服务器

class BaseApp {
    // 专门处理数据库操作
    DatabaseConnection db;

    // 异步加载数据
    Future<PlayerData> loadPlayer(uint64_t playerId);

    // 定时保存
    void savePlayer(PlayerData& data);

    // 不处理游戏逻辑,只做数据CRUD
};

设计理念:数据库IO与应用逻辑分离

3. LoginApp - 登录服务器

class LoginApp {
    // 处理认证
    AuthManager auth;

    // 分配玩家到合适的 CellApp
    CellApp* assignCellApp(Player* p);

    // 创建 Proxy
    Proxy* createProxy(Player* p);
};

4. Proxy - 玩家连接代理

class Proxy {
    // 代表玩家与服务器通信
    NetworkConnection connection;
    EntityID controlledEntity;  // 控制的实体ID

    // 转发客户端消息到 CellApp
    void forwardToCell(ClientMessage& msg);

    // 从 CellApp 接收更新
    void sendToClient(EntityUpdate& update);
};

五、BigWorld 的特点

✅ 优点

1. 真正的无缝世界

玩家从地图一端走到另一端:
传统: ┌────┐ ┌────┐ ┌────┐  需要加载/切换场景
      │地图1│→│地图2│→│地图3│
      └────┘ └────┘ └────┘

BigWorld: ┌─────────────────────────┐
           │   单一连续世界          │  玩家感觉不到边界
           │                         │
           └─────────────────────────┘

2. 水平扩展能力强

热点区域(如主城)压力大:
传统: 整个分线都卡
BigWorld: 只给主城区域加 CellApp
          ┌───────────────┐
          │主城:3个CellApp│  独立扩展
          └───────────────┘

3. 故障隔离

一个 CellApp 崩溃:
影响范围: 只有一个区域的玩家掉线
其他区域: 正常运行

4. 负载均衡

玩家自动迁移到低负载 CellApp:
CellApp A (1000人) ──→── CellApp B (300人)

❌ 缺点

1. 复杂度高

// 跨边界实体同步逻辑复杂
void updateEntityAtBorder(Entity* e) {
    // 1. 检查是否在边界
    // 2. 通知相邻 CellApp
    // 3. 创建 Ghost 实体
    // 4. 同步状态
    // 5. 处理边界碰撞
    // ... 100+ 行代码
}

2. 一致性问题

问题: 玩家在边界附近
       CellApp A 认为在左边
       CellApp B 认为在右边

需要: 复杂的协商机制

3. 调试困难

一个 bug 涉及多个 CellApp:
- 无法单步调试
- 日志分散在多个进程
- 时序问题难以重现

4. 边界处理特殊

边界附近的技能/碰撞需要特殊处理:
- 技能跨越边界
- 子弹跨越边界
- NPC巡逻跨越边界

六、CellApp 水平扩展详解

核心思想:按位置(空间)拆分

BigWorld 的水平扩展核心就是按位置(空间分割)。

传统 vs BigWorld 扩展方式对比

传统MMO:垂直扩展 + 分线复制

┌─────────────────────────────────────────┐
│          游戏世界 (完整副本)             │
│                                          │
│  🏰城堡──────────────🌲森林             │
│                          🏠村庄          │
│                                          │
└─────────────────────────────────────────┘
         ↓ 复制整个世界
┌─────────────────────────────────────────┐
│          分线1 (完整副本)                 │
│  🏰城堡──────────────🌲森林             │
│                          🏠村庄          │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│          分线2 (完整副本)                 │
│  🏰城堡──────────────🌲森林             │
│                          🏠村庄          │
└─────────────────────────────────────────┘

扩展方式 = 加更多服务器,每个服务器跑完整世界
问题 = 每个分线是独立的,玩家无法跨分线交互

BigWorld:空间水平切割

┌─────────────────────────────────────────────────────┐
│                  单一游戏世界                        │
│                                                     │
│   ┌─────────┬─────────┬─────────┬─────────┐        │
│   │CellApp0 │CellApp1 │CellApp2 │CellApp3 │        │
│   │ 雪山区域 │ 城堡区域│ 森林区域│ 村庄区域│        │
│   │  500人  │ 2000人  │  800人  │ 1200人  │        │
│   ├─────────┼─────────┼─────────┼─────────┤        │
│   │CellApp4 │CellApp5 │CellApp6 │CellApp7 │        │
│   │ 荒原区域│ 主城区域│ 沙漠区域│ 海域区域│        │
│   │  300人  │ 5000人⭐│  600人  │  400人  │        │
│   └─────────┴─────────┴─────────┴─────────┘        │
│                                                     │
└─────────────────────────────────────────────────────┘

⭐ 主城人多 = 可以单独为主城加更多服务器资源

动态负载均衡

主城区域玩家过多:
┌────────────────────────────┐
│   CellApp5 (主城)          │
│   💥 5000玩家 = 拥挤      │
└────────────────────────────┘
          ↓ 自动分割
┌───────────────┬───────────────┐
│ CellApp5a     │ CellApp5b     │
│ 西半主城 2500 │ 东半主城 2500 │
└───────────────┴───────────────┘

关键:分割对玩家透明!玩家感觉不到自己被分配到了不同的 CellApp。


七、跨边界处理

1. 玩家跨 CellApp 移动

玩家从 CellApp0 移动到 CellApp1:

CellApp0                  CellApp1
   │                          │
   │ 1. 玩家接近边界          │
   ├──────────────────────────>│
   │ 2. 创建 Shadow 实体       │
   │                          │
   │ 3. 转移 Real 实体控制权  │
   │<──────────────────────────┤
   │ 4. 删除 Shadow 实体       │
   ▼                          ▼
完成迁移,玩家感觉无感

2. 边界交互

两个玩家在边界附近开战:

┌──────────────┬──────────────┐
│  CellApp0    │  CellApp1    │
│              │              │
│   🧙‍♂️法师 ←───→🏹射手       │  跨边界攻击
│              │              │
└──────────────┴──────────────┘

处理方式:
1. 射手在 CellApp1 创建 法师的 Shadow
2. 法师在 CellApp0 创建 射手的 Shadow
3. 伤害计算在各自 CellApp
4. 通过消息同步结果

八、适用场景

BigWorld 适合的场景 ✅

场景原因
大型开放世界MMO无缝世界体验
车辆/战车游戏载具跨区域移动
高并发热点主城可独立扩展
长期运营模块化便于维护

BigWorld 不适合的场景 ❌

场景原因
副本/闯关不需要复杂的空间分割
回合制卡牌无需位置同步
小规模游戏架构过于复杂
强一致性要求分布式一致性难保证

九、BigWorld 在 Apollo 中的兼容层

// modules/bigworld/include/apollo/bigworld/runtime.hpp

namespace BigWorld {  // 全局命名空间兼容

    // 实体ID兼容
    using EntityID = uint64_t;
    using SpaceID = uint32_t;

    // 实体接口
    struct IEntity {
        virtual SpaceID spaceID() const = 0;
        virtual Position position() const = 0;
        virtual void teleport(Position pos) = 0;
        virtual void destroy() = 0;
    };

    // 回调接口
    struct ICallback {
        virtual void onEntityEnter(IEntity*) {}
        virtual void onEntityLeave(IEntity*) {}
        virtual void onSpaceGeometryChanged(SpaceID) {}
    };

    // 运行时接口
    struct IRuntime {
        virtual IEntity* createEntity(SpaceID, const char* type) = 0;
        virtual void destroyEntity(EntityID) = 0;
        virtual void registerCallback(ICallback*) = 0;
    };

    // 全局访问点 (BigWorld 风格)
    IRuntime* Runtime();
    IEntity* getEntity(EntityID id);
}

十、总结

传统MMO架构          vs          BigWorld架构
    │                           │
    ├─ 单一世界                ├─ 空间分割
    ├─ 垂直扩展                ├─ 水平扩展
    ├─ 简单直观                ├─ 复杂但强大
    ├─ 全局故障                ├─ 故障隔离
    ├─ 场景切换                ├─ 无缝世界
    └─ 适合副本/小规模         └─ 适合大世界MMO

BigWorld 的本质:用空间换时间,通过水平扩展和区域隔离,实现超大规模无缝世界的MMO服务器。

在 Apollo 中,BigWorld 模块作为兼容层,允许现有 BigWorld 代码平滑迁移到新的模块化架构。

在 GitHub 上编辑此页
最后更新: 3/20/26, 6:06 AM
贡献者: cuihairu
Prev
架构概述
Next
BigWorld 进程架构与玩家生命周期