页面加载中
博客快捷键
按住 Shift 键查看可用快捷键
ShiftK
开启/关闭快捷键功能
ShiftA
打开/关闭中控台
ShiftD
深色/浅色显示模式
ShiftS
站内搜索
ShiftR
随机访问
ShiftH
返回首页
ShiftL
友链页面
ShiftP
关于本站
ShiftI
原版/本站右键菜单
松开 Shift 键或点击外部区域关闭
互动
最近评论
暂无评论
标签
寻找感兴趣的领域
暂无标签
    0
    文章
    0
    标签
    8
    分类
    10
    评论
    128
    功能
    深色模式
    标签
    JavaScript12TypeScript8React15Next.js6Vue10Node.js7CSS5前端20
    互动
    最近评论
    暂无评论
    标签
    寻找感兴趣的领域
    暂无标签
      0
      文章
      0
      标签
      8
      分类
      10
      评论
      128
      功能
      深色模式
      标签
      JavaScript12TypeScript8React15Next.js6Vue10Node.js7CSS5前端20
      随便逛逛
      博客分类
      文章标签
      复制地址
      深色模式
      AnHeYuAnHeYu
      Search⌘K
      博客
        暂无其他文档

        Iris 网络复制系统技术分析 - 第四部分:核心组件详解

        December 17, 202558 分钟 阅读445 次阅读

        🎯 Iris 网络复制系统技术分析 - 第四部分:核心组件详解

        如果说 Iris 是一个高效的物流公司,那么本章介绍的五大核心组件就是这家公司的关键部门。让我们一起认识这些"部门经理"们!


        📋 组件关系总览

        在深入每个组件之前,先来看看它们之间的关系:

        PLAINTEXT
        ┌─────────────────────────────────────────────────────────────────────┐
        │                        🎮 游戏世界                                   │
        │                   (AActor, UActorComponent)                         │
        └─────────────────────────────────────────────────────────────────────┘
                                            │
                                            ▼
        ┌─────────────────────────────────────────────────────────────────────┐
        │              🌉 UEngineReplicationBridge                             │
        │                   "UE引擎的翻译官"                                    │
        │         负责将 Actor/Component 转换为 Iris 能理解的格式               │
        └─────────────────────────────────────────────────────────────────────┘
                                            │
                                            ▼
        ┌─────────────────────────────────────────────────────────────────────┐
        │              🔗 UObjectReplicationBridge                             │
        │                   "对象管理专家"                                      │
        │           处理休眠、轮询频率、依赖关系等通用对象逻辑                    │
        └─────────────────────────────────────────────────────────────────────┘
                                            │
                                            ▼
        ┌─────────────────────────────────────────────────────────────────────┐
        │                 🌐 UReplicationBridge                                │
        │                    "桥接基础设施"                                     │
        │              提供网络对象创建、销毁、关卡组管理等基础能力               │
        └─────────────────────────────────────────────────────────────────────┘
                                            │
                                            ▼
        ┌─────────────────────────────────────────────────────────────────────┐
        │                 🧠 UReplicationSystem                                │
        │                    "总指挥中心"                                       │
        │           协调所有子系统:过滤、优先级、连接管理、数据发送              │
        └─────────────────────────────────────────────────────────────────────┘
                                            │
                                            ▼
        ┌─────────────────────────────────────────────────────────────────────┐
        │                 🎫 FNetRefHandleManager                              │
        │                    "身份证管理局"                                     │
        │              为每个网络对象分配唯一ID,管理对象索引                     │
        └─────────────────────────────────────────────────────────────────────┘

        🏠 生活化类比

        想象你经营一家国际快递公司:

        组件

        类比角色

        职责

        UReplicationSystem

        📊 总经理办公室

        制定策略、协调各部门、做最终决策

        UReplicationBridge

        🌉 物流中转站

        接收包裹、分拣、打包、发送

        UObjectReplicationBridge

        📦 包裹处理中心

        决定包裹优先级、是否暂存、多久检查一次

        UEngineReplicationBridge

        🏭 工厂对接部

        与生产线(游戏引擎)对接,收取原材料(Actor)

        FNetRefHandleManager

        🎫 快递单号系统

        给每个包裹分配唯一追踪号


        🧠 4.1 UReplicationSystem - 总指挥中心

        💡 它是什么?

        UReplicationSystem 是 Iris 的大脑,负责协调整个网络复制系统的运作。就像一个交响乐团的指挥,它不亲自演奏任何乐器,但确保所有乐手在正确的时间演奏正确的音符。

        PLAINTEXT
        ┌────────────────────────────────────────────────────────────────┐
        │                    🧠 UReplicationSystem                        │
        │                        "总指挥中心"                              │
        ├────────────────────────────────────────────────────────────────┤
        │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐         │
        │  │ 🔗 连接管理   │  │ 🎯 过滤系统   │  │ ⚡ 优先级系统 │         │
        │  │ Connections  │  │  Filtering   │  │Prioritization│         │
        │  └──────────────┘  └──────────────┘  └──────────────┘         │
        │                                                                │
        │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐         │
        │  │ 📁 组管理     │  │ 📡 RPC处理   │  │ 📊 脏数据追踪 │         │
        │  │   Groups     │  │     RPC      │  │ DirtyTracker │         │
        │  └──────────────┘  └──────────────┘  └──────────────┘         │
        │                                                                │
        │  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐         │
        │  │ 🗜️ 增量压缩  │  │ 📋 条件复制   │  │ 🎫 句柄管理   │         │
        │  │   Delta     │  │ Conditionals │  │  HandleMgr  │         │
        │  └──────────────┘  └──────────────┘  └──────────────┘         │
        └────────────────────────────────────────────────────────────────┘

        🏗️ 内部架构:FReplicationSystemImpl

        UReplicationSystem 采用 Pimpl(Pointer to Implementation) 设计模式,真正的"大脑"是 FReplicationSystemImpl,它管理着所有子系统。

        📖 源码位置: ReplicationSystem.cpp:55-91

        CPP
        // ReplicationSystem.cpp:55-91 - FReplicationSystemImpl 核心成员class FReplicationSystemImpl
        {
        public:
            // 附件发送策略映射
            TMap<FObjectKey, ENetObjectAttachmentSendPolicyFlags> AttachmentSendPolicyFlags;
            
            // 指向公开接口的指针
            UReplicationSystem* ReplicationSystem;
            
            // Token 存储(用于名称/字符串复制)
            FNetTokenStore* NetTokenStore;
            
            // 核心内部系统 - 包含所有子系统
            FReplicationSystemInternal ReplicationSystemInternal;
            
            // 特殊过滤组句柄
            FNetObjectGroupHandle NotReplicatedNetObjectGroupHandle;    // 不复制组
            FNetObjectGroupHandle NetGroupOwnerNetObjectGroupHandle;    // 所有者组
            FNetObjectGroupHandle NetGroupReplayNetObjectGroupHandle;   // 回放组
            
            // 待发送连接的位数组
            FNetBitArray ConnectionsPendingPostTickDispatchSend;
            
            // 当前发送阶段
            EReplicationSystemSendPass CurrentSendPass = EReplicationSystemSendPass::Invalid;
        };

        🔧 初始化流程深度剖析

        当创建 UReplicationSystem 时,会触发一系列精密的初始化操作。

        📖 源码位置: ReplicationSystem.cpp:106-293

        CPP
        // ReplicationSystem.cpp:106-293 - Init 方法核心流程void FReplicationSystemImpl::Init(const UReplicationSystem::FReplicationSystemParams& Params){
            // 1️⃣ 验证 NetTokenStore(必须有效)
            bool bHasValidNetTokenStore = ensureAlwaysMsgf(Params.NetTokenStore, 
                TEXT("ReplicationSystem cannot be initialized without a valid NetTokenStore"));
            
            // 2️⃣ 初始化 NetRefHandleManager - 对象句柄管理器
            FNetRefHandleManager& NetRefHandleManager = ReplicationSystemInternal.GetNetRefHandleManager();
            {
                FNetRefHandleManager::FInitParams NetRefHandleManagerInitParams;
                NetRefHandleManagerInitParams.ReplicationSystemId = ReplicationSystemId;
                NetRefHandleManagerInitParams.MaxActiveObjectCount = Params.MaxReplicatedObjectCount;
                NetRefHandleManagerInitParams.InternalNetRefIndexInitSize = Params.InitialNetObjectListCount;
                NetRefHandleManagerInitParams.InternalNetRefIndexGrowSize = Params.NetObjectListGrowCount;
                NetRefHandleManager.Init(NetRefHandleManagerInitParams);
                
                // 订阅索引增长事件 - 当对象数量超过当前容量时触发
                NetRefHandleManager.GetOnMaxInternalNetRefIndexIncreasedDelegate()
                    .AddRaw(this, &FReplicationSystemImpl::OnMaxInternalNetRefIndexIncreased);
            }
            
            // 3️⃣ 初始化脏对象追踪器(仅服务器需要)
            if (Params.bAllowObjectReplication)
            {
                FDirtyNetObjectTrackerInitParams DirtyNetObjectTrackerInitParams;
                DirtyNetObjectTrackerInitParams.NetRefHandleManager = &NetRefHandleManager;
                DirtyNetObjectTrackerInitParams.MaxInternalNetRefIndex = CurrentMaxInternalNetRefIndex;
                ReplicationSystemInternal.InitDirtyNetObjectTracker(DirtyNetObjectTrackerInitParams);
            }
            
            // 4️⃣ 初始化状态存储系统
            {
                FReplicationStateStorage& StateStorage = ReplicationSystemInternal.GetReplicationStateStorage();
                FReplicationStateStorageInitParams InitParams;
                InitParams.MaxObjectCount = AbsoluteMaxObjectCount;
                InitParams.MaxDeltaCompressedObjectCount = Params.MaxDeltaCompressedObjectCount;
                StateStorage.Init(InitParams);
            }
            
            // 5️⃣ 初始化对象组系统
            {
                FNetObjectGroups& Groups = ReplicationSystemInternal.GetGroups();
                FNetObjectGroupInitParams InitParams;
                InitParams.MaxGroupCount = Params.MaxNetObjectGroupCount;
                Groups.Init(InitParams);
            }
            
            // 6️⃣ 初始化过滤系统
            {
                FReplicationFiltering& ReplicationFiltering = ReplicationSystemInternal.GetFiltering();
                FReplicationFilteringInitParams InitParams;
                InitParams.ReplicationSystem = ReplicationSystem;
                InitParams.MaxGroupCount = Params.MaxNetObjectGroupCount;
                ReplicationFiltering.Init(InitParams);
            }
            
            // 7️⃣ 初始化默认过滤组
            InitDefaultFilteringGroups();
            
            // 8️⃣ 初始化条件复制系统 & 优先级系统
            ReplicationSystemInternal.GetConditionals().Init(InitParams);
            ReplicationSystemInternal.GetPrioritization().Init(InitParams);
            
            // 9️⃣ 初始化 ReplicationBridge
            ReplicationSystemInternal.SetReplicationBridge(Params.ReplicationBridge);
            ReplicationSystemInternal.GetReplicationBridge()->Initialize(ReplicationSystem);
            
            // 🔟 初始化 NetBlobManager(处理 RPC 和附件)
            {
                FNetBlobManager& BlobManager = ReplicationSystemInternal.GetNetBlobManager();
                FNetBlobManagerInitParams InitParams;
                InitParams.bSendAttachmentsWithObject = ReplicationSystem->IsServer();
                BlobManager.Init(InitParams);
            }
        }

        ⚡ 主更新循环 (NetUpdate)

        这是 Iris 的心跳!每一帧,服务器都会执行这个更新循环,决定哪些数据需要发送给哪些客户端。

        PLAINTEXT
        ┌─────────────────────────────────────────────────────────────────────┐
        │                    🔄 NetUpdate 主循环                               │
        │                   "每帧的工作流程"                                    │
        └─────────────────────────────────────────────────────────────────────┘
        
          ┌─────────────────┐
          │ 🚀 PreSendUpdate │ ◄── 发送前准备(最复杂的阶段)
          └────────┬────────┘
                   │
                   ▼
          ┌─────────────────────────────────────────────────────────────────┐
          │  1️⃣ 更新脏对象列表                                               │
          │     "检查哪些包裹内容变了"                                         │
          │                                                                  │
          │  2️⃣ 更新世界位置                                                  │
          │     "更新所有包裹的当前位置"                                        │
          │                                                                  │
          │  3️⃣ 更新过滤                                                      │
          │     "决定哪些包裹该发给哪些客户"                                    │
          │                                                                  │
          │  4️⃣ 更新条件                                                      │
          │     "检查特殊发送条件"                                             │
          │                                                                  │
          │  5️⃣ 量化脏状态数据                                                │
          │     "把数据压缩成网络友好格式"                                      │
          │                                                                  │
          │  6️⃣ 处理附件发送队列                                              │
          │     "处理大文件分片发送"                                           │
          │                                                                  │
          │  7️⃣ 更新对象作用域                                                │
          │     "确定每个连接能看到哪些对象"                                    │
          │                                                                  │
          │  8️⃣ 传播脏变更                                                    │
          │     "标记需要发送的数据"                                           │
          │                                                                  │
          │  9️⃣ 更新优先级                                                    │
          │     "决定发送顺序"                                                 │
          └─────────────────────────────────────────────────────────────────┘
                   │
                   ▼
          ┌─────────────────┐
          │ 📤 SendUpdate    │ ◄── 实际发送数据
          └────────┬────────┘
                   │
                   ▼
          ┌─────────────────┐
          │ 🧹 PostSendUpdate│ ◄── 发送后清理
          └─────────────────┘

        🔍 PreSendUpdate 详解

        CPP
        void UReplicationSystem::PreSendUpdate(const FSendUpdateParams& Params){
            // 🏁 启动预发送更新
            Impl->StartPreSendUpdate();
            
            // 1️⃣ 更新脏对象列表 - "哪些数据变化了?"
            Impl->UpdateDirtyObjectList();
            
            // 2️⃣ 更新世界位置 - "对象现在在哪里?"
            Impl->UpdateWorldLocations();
            
            // 3️⃣ 更新过滤 - "谁应该看到什么?"
            Impl->UpdateFiltering();
            
            // 4️⃣ 调用预发送回调
            Impl->CallPreSendUpdate(Params.DeltaSeconds);
            
            // 5️⃣ 完成脏对象列表
            Impl->UpdateDirtyListPostPoll();
            
            // 6️⃣ 更新条件 - "特殊条件检查"
            Impl->UpdateConditionals();
            
            // 7️⃣ 量化脏状态数据 - "压缩数据"
            Impl->QuantizeDirtyStateData();
            
            // 8️⃣ 处理附件发送队列
            Impl->ProcessNetObjectAttachmentSendQueue(...);
            
            // 9️⃣ 更新对象作用域
            Impl->UpdateObjectScopes();
            
            // 🔟 传播脏变更
            Impl->PropagateDirtyChanges();
            
            // 1️⃣1️⃣ 更新优先级
            Impl->UpdatePrioritization(ReplicatingConnections);
        }

        📖 源码位置: Engine/Source/Runtime/Experimental/Iris/Core/Private/Iris/ReplicationSystem/ReplicationSystem.cpp:738-838

        🎬 生活化比喻:快递公司的一天

        想象你是快递公司的调度员,每天早上(每帧)你需要:

        1. 检查包裹变化 📦 - 看看哪些包裹内容更新了

        2. 更新位置信息 📍 - 确认所有包裹现在在哪个仓库

        3. 分配配送区域 🗺️ - 决定哪个快递员负责哪个区域

        4. 检查特殊要求 ⚠️ - 有些包裹只能本人签收

        5. 打包压缩 📦 - 把包裹打包得更紧凑

        6. 处理超大件 🚛 - 大包裹需要分批发送

        7. 确定可见范围 👀 - 每个客户能查看哪些包裹状态

        8. 标记待发送 ✅ - 确定今天要发的包裹

        9. 排定优先级 🔢 - 急件先发,普通件后发

        🔌 连接管理接口

        管理客户端连接,就像管理快递公司的各个配送站点:

        CPP
        // 📥 添加新连接 - "新开了一个配送站"void AddConnection(uint32 ConnectionId);
        
        // 📤 移除连接 - "关闭一个配送站"void RemoveConnection(uint32 ConnectionId);
        
        // ✅ 验证连接是否有效bool IsValidConnection(uint32 ConnectionId) const;
        
        // 👋 优雅关闭连接 - "通知配送站准备关门"void SetConnectionGracefullyClosing(uint32 ConnectionId);
        
        // 🔛 启用/禁用连接的复制功能void SetReplicationEnabledForConnection(uint32 ConnectionId, bool bEnabled);

        📦 对象管理接口

        管理需要复制的游戏对象:

        CPP
        // ═══════════════════════════════════════════════════════════════════//                          📁 组管理// ═══════════════════════════════════════════════════════════════════
        
        // 创建一个新组 - 就像创建一个"分类标签"FNetObjectGroupHandle CreateGroup(FName GroupName);
        
        // 销毁组void DestroyGroup(FNetObjectGroupHandle GroupHandle);
        
        // 将对象添加到组 - "给包裹贴标签"void AddToGroup(FNetObjectGroupHandle GroupHandle, FNetRefHandle Handle);
        
        // 从组中移除对象void RemoveFromGroup(FNetObjectGroupHandle GroupHandle, FNetRefHandle Handle);
        
        // ═══════════════════════════════════════════════════════════════════//                          🎯 过滤设置// ═══════════════════════════════════════════════════════════════════
        
        // 设置对象的过滤器 - "决定谁能收到这个包裹"bool SetFilter(FNetRefHandle Handle, 
                       FNetObjectFilterHandle FilterHandle, 
                       FName FilterConfigProfile);
        
        // 设置对象的所有者连接 - "指定包裹的收件人"void SetOwningNetConnection(FNetRefHandle Handle, uint32 ConnectionId);
        
        // ═══════════════════════════════════════════════════════════════════//                          ⚡ 优先级设置// ═══════════════════════════════════════════════════════════════════
        
        // 设置静态优先级 - "固定的发送优先级"void SetStaticPriority(FNetRefHandle Handle, float Priority);
        
        // 设置动态优先级器 - "根据情况动态计算优先级"bool SetPrioritizer(FNetRefHandle Handle, FNetObjectPrioritizerHandle Prioritizer);
        
        // ═══════════════════════════════════════════════════════════════════//                          🔄 状态控制// ═══════════════════════════════════════════════════════════════════
        
        // 强制立即更新 - "紧急包裹,马上发!"void ForceNetUpdate(FNetRefHandle Handle);
        
        // 标记为脏 - "包裹内容变了,需要重新发送"void MarkDirty(FNetRefHandle Handle);
        
        // 下一次更新时断开复制 - "最后一次发送后断开"void TearOffNextUpdate(FNetRefHandle Handle);

        📡 RPC 发送接口

        远程过程调用(RPC)是网络游戏中调用远端函数的方式:

        CPP
        // 发送 RPC 到指定连接// 就像"给特定客户打电话通知"bool SendRPC(UObject* Object, 
                     const UFunction* Function, 
                     const void* Parameters,
                     uint32 ConnectionId);
        
        // 多播 RPC - "群发通知"bool MulticastRPC(UObject* Object,
                          const UFunction* Function,
                          const void* Parameters);

        🌉 4.2 UReplicationBridge - 桥接基础设施

        💡 它是什么?

        UReplicationBridge 是连接游戏世界和网络复制系统的桥梁。它就像一个翻译官,把游戏对象"翻译"成网络系统能理解的格式。

        PLAINTEXT
        ┌─────────────────────────────────────────────────────────────────────┐
        │                                                                     │
        │    🎮 游戏世界                    🌐 网络世界                        │
        │    ┌─────────┐                   ┌─────────┐                       │
        │    │ AActor  │                   │ NetObj  │                       │
        │    │ UObject │ ═══════════════►  │ Handle  │                       │
        │    │Component│    🌉 Bridge      │ State   │                       │
        │    └─────────┘                   └─────────┘                       │
        │                                                                     │
        │              UReplicationBridge 负责这个转换过程                     │
        │                                                                     │
        └─────────────────────────────────────────────────────────────────────┘

        🏠 类继承关系

        PLAINTEXT
        UObject
           └── UReplicationBridge          ◄── 基类:提供核心桥接功能
                 └── UObjectReplicationBridge  ◄── 中间层:添加 UObject 特有功能
                       └── UEngineReplicationBridge  ◄── 最终层:处理 Actor/Component
        

        🔧 初始化流程源码分析

        📖 源码位置: ReplicationBridge.cpp:348-415

        CPP
        // ReplicationBridge.cpp:348-415 - Initialize 方法void UReplicationBridge::Initialize(UReplicationSystem* InReplicationSystem){
            using namespace UE::Net;
            using namespace UE::Net::Private;
        
            // 获取 ReplicationSystem 的内部实现
            Private::FReplicationSystemInternal* ReplicationSystemInternal = 
                InReplicationSystem->GetReplicationSystemInternal();
        
            // 1️⃣ 保存核心引用
            ReplicationSystem = InReplicationSystem;
            ReplicationProtocolManager = &ReplicationSystemInternal->GetReplicationProtocolManager();
            ReplicationStateDescriptorRegistry = &ReplicationSystemInternal->GetReplicationStateDescriptorRegistry();
            NetRefHandleManager = &ReplicationSystemInternal->GetNetRefHandleManager();
            ObjectReferenceCache = &ReplicationSystemInternal->GetObjectReferenceCache();
            Groups = &ReplicationSystemInternal->GetGroups();
        
            // 2️⃣ 创建销毁信息协议(用于通知客户端对象被销毁)
            {
                const FReplicationFragments RegisteredFragments;
                FCreateReplicationProtocolParameters CreateProtocolParams {
                    .bValidateProtocolId = false, 
                    .TypeStatsIndex = GetReplicationSystem()->GetReplicationSystemInternal()
                        ->GetNetTypeStats().GetOrCreateTypeStats(FName("DestructionInfo"))
                };
                DestructionInfoProtocol = ReplicationProtocolManager->CreateReplicationProtocol(
                    FReplicationProtocolManager::CalculateProtocolIdentifier(RegisteredFragments), 
                    RegisteredFragments, 
                    TEXT("InternalDestructionInfo"), 
                    CreateProtocolParams
                );
                // 显式引用计数
                DestructionInfoProtocol->AddRef();
            }
        }

        🔧 核心职责

        PLAINTEXT
        ┌─────────────────────────────────────────────────────────────────────┐
        │                    🌉 UReplicationBridge 职责                        │
        ├─────────────────────────────────────────────────────────────────────┤
        │                                                                     │
        │  ┌─────────────────────────────────────────────────────────────┐   │
        │  │ 1️⃣ 网络对象生命周期管理                                       │   │
        │  │    • 创建网络对象 (CreateNetRefHandleFromRemote)              │   │
        │  │    • 销毁网络对象 (StopReplicatingNetRefHandle)               │   │
        │  │    • 分离实例 (DetachInstanceFromNetRefHandle)                │   │
        │  └─────────────────────────────────────────────────────────────┘   │
        │                                                                     │
        │  ┌─────────────────────────────────────────────────────────────┐   │
        │  │ 2️⃣ 子对象管理                                                 │   │
        │  │    • 注册子对象                                               │   │
        │  │    • 维护父子关系                                             │   │
        │  │    • 子对象生命周期跟随父对象                                  │   │
        │  └─────────────────────────────────────────────────────────────┘   │
        │                                                                     │
        │  ┌─────────────────────────────────────────────────────────────┐   │
        │  │ 3️⃣ 关卡组管理                                                 │   │
        │  │    • 创建关卡组 (CreateLevelGroup)                            │   │
        │  │    • 将对象添加到关卡组                                        │   │
        │  │    • 支持关卡流送                                             │   │
        │  └─────────────────────────────────────────────────────────────┘   │
        │                                                                     │
        │  ┌─────────────────────────────────────────────────────────────┐   │
        │  │ 4️⃣ 协议管理                                                   │   │
        │  │    • 管理复制协议                                             │   │
        │  │    • 协议匹配和版本兼容                                        │   │
        │  └─────────────────────────────────────────────────────────────┘   │
        │                                                                     │
        └─────────────────────────────────────────────────────────────────────┘

        🔬 网络对象生命周期管理 - 深入源码分析

        网络对象的生命周期管理是 UReplicationBridge 最核心的职责之一。让我们从源码层面深入理解三个关键方法:

        📊 生命周期状态机

        PLAINTEXT
        ┌─────────────────────────────────────────────────────────────────────────────┐
        │                      🔄 网络对象生命周期状态机                                │
        └─────────────────────────────────────────────────────────────────────────────┘
        
          ┌──────────────┐                                    ┌──────────────┐
          │  🆕 未创建    │                                    │  💀 已销毁    │
          │  (NotExist)  │                                    │ (Destroyed)  │
          └──────┬───────┘                                    └──────────────┘
                 │                                                   ▲
                 │ CreateNetRefHandleFromRemote()                   │
                 │ (客户端) 或                                       │
                 │ InternalCreateNetObject()                        │
                 │ (服务器)                                          │
                 ▼                                                   │
          ┌──────────────┐      AttachInstance()      ┌──────────────┐
          │  📋 已注册    │ ─────────────────────────► │  🔗 已绑定    │
          │ (Registered) │                            │  (Attached)  │
          │              │ ◄───────────────────────── │              │
          │ • 有 Handle  │      DetachInstance()      │ • 有 Handle  │
          │ • 无实例绑定  │                            │ • 有游戏对象 │
          └──────┬───────┘                            └──────┬───────┘
                 │                                           │
                 │ InternalDestroyNetObject()                │ StopReplicating() /
                 │ (无实例时直接销毁)                          │ DestroyLocalNetHandle()
                 │                                           │
                 ▼                                           ▼
          ┌─────────────────────────────────────────────────────────────────┐
          │                          💀 已销毁 (Destroyed)                   │
          │  • Handle 被回收到空闲池                                         │
          │  • 实例协议被销毁                                                │
          │  • 子对象递归销毁                                                │
          │  • 从所有组中移除                                                │
          └─────────────────────────────────────────────────────────────────┘
        

        1️⃣ CreateNetRefHandleFromRemote - 客户端创建网络对象

        📖 源码位置: ObjectReplicationBridge.cpp:813-948

        CPP
        // ═══════════════════════════════════════════════════════════════════════════════// CreateNetRefHandleFromRemote - 客户端从服务器数据创建网络对象的核心函数// 参数说明://   - RootObjectOfSubObject: 如果是子对象,这是其根对象的句柄;否则无效//   - WantedNetHandle: 服务器分配的期望网络句柄//   - Context: 序列化上下文,包含网络流和连接信息// ═══════════════════════════════════════════════════════════════════════════════FReplicationBridgeCreateNetRefHandleResult UObjectReplicationBridge::CreateNetRefHandleFromRemote(
            FNetRefHandle RootObjectOfSubObject, 
            FNetRefHandle WantedNetHandle, 
            FReplicationBridgeSerializationContext& Context){
            LLM_SCOPE_BYTAG(IrisState);  // 内存追踪标记,用于 LLM (Low Level Memory) 分析
        
            using namespace UE::Net;
            using namespace UE::Net::Private;
        
            // ┌─────────────────────────────────────────────────────────────────────────┐
            // │ 阶段1: 读取工厂ID并获取对应的对象工厂                                      │
            // └─────────────────────────────────────────────────────────────────────────┘
            FNetBitStreamReader* Reader = Context.SerializationContext.GetBitStreamReader();
        
            // 从网络流中读取工厂ID(使用固定位数,由 GetMaxBits() 决定)
            // IntCastChecked 确保类型转换安全,溢出时会触发断言
            const FNetObjectFactoryId FactoryId = IntCastChecked<FNetObjectFactoryId>(
                Reader->ReadBits(FNetObjectFactoryRegistry::GetMaxBits()));
            
            // 通过工厂ID获取对应的 UNetObjectFactory 实例
            // 不同类型的对象(Actor、ActorComponent等)使用不同的工厂
            UNetObjectFactory* Factory = GetNetFactory(FactoryId);
            if (Factory == nullptr)
            {
                // 【关键检查】工厂必须有效,无效说明数据流可能损坏
                // 设置错误标记后返回,上层会处理断开连接
                Context.SerializationContext.SetError(GNetError_InvalidValue);
                return FReplicationBridgeCreateNetRefHandleResult();
            }
        
            // ┌─────────────────────────────────────────────────────────────────────────┐
            // │ 阶段2: 读取并解析对象创建头(Header)                                      │
            // └─────────────────────────────────────────────────────────────────────────┘
            // Header 包含创建对象所需的所有元数据(类信息、Archetype路径、协议ID等)
            TUniquePtr<FNetObjectCreationHeader> Header = Factory->ReadHeader(
                WantedNetHandle, Context.SerializationContext);
                
            // 验证 Header 有效性和流状态
            if (!Header.IsValid() || Context.SerializationContext.HasErrorOrOverflow())
            {
                return FReplicationBridgeCreateNetRefHandleResult();
            }
        
            // ┌─────────────────────────────────────────────────────────────────────────┐
            // │ 阶段3: 提取协议ID并准备Fragment注册上下文                                  │
            // └─────────────────────────────────────────────────────────────────────────┘
            // 协议ID用于验证客户端和服务器的复制协议是否匹配
            const FReplicationProtocolIdentifier ReceivedProtocolId = Header->GetProtocolId();    
        
            // 创建 Fragment 注册上下文
            // 【重要】CanReceive 标记表明远端对象只能接收数据,不能发送
            // 这决定了后续创建的 Fragment 只包含接收相关的状态
            FFragmentRegistrationContext FragmentRegistrationContext(
                GetReplicationStateDescriptorRegistry(), 
                GetReplicationSystem(), 
                EReplicationFragmentTraits::CanReceive);  // ← 仅接收模式
            
            FReplicationProtocolManager* ProtocolManager = GetReplicationProtocolManager();
        
            // 日志记录(仅在 Verbose 级别启用时)
            if (UE_LOG_ACTIVE(LogIrisBridge, Verbose))
            {
                if (RootObjectOfSubObject.IsValid())
                {
                    // 子对象日志
                    UE_LOG(LogIrisBridge, Verbose, 
                        TEXT("CreateNetRefHandleFromRemote: SubObject: %s of RootObject: %s using header: %s"), 
                        *WantedNetHandle.ToString(), *RootObjectOfSubObject.ToString(), ToCStr(Header->ToString()));
                }
                else
                {
                    // 根对象日志
                    UE_LOG(LogIrisBridge, Verbose, 
                        TEXT("CreateNetRefHandleFromRemote: RootObject: %s using header: %s"), 
                        *WantedNetHandle.ToString(), ToCStr(Header->ToString()));
                }
            }
            
            // ┌─────────────────────────────────────────────────────────────────────────┐
            // │ 阶段4: 实例化对象                                                         │
            // └─────────────────────────────────────────────────────────────────────────┘
            // 【设计说明】当前总是立即实例化远端对象
            // 未来计划支持延迟实例化,等到需要应用状态数据时再创建
            // https://jira.it.epicgames.com/browse/UE-127369    
        
            UObject* InstancePtr = nullptr;
            FReplicationBridgeCreateNetRefHandleResult CreateResult;
            
            // 构建实例化上下文,包含:
            // - WantedNetHandle: 期望的网络句柄
            // - ResolveContext: 对象引用解析上下文
            // - RootObjectOfSubObject: 父对象句柄(如果是子对象)
            const UNetObjectFactory::FInstantiateContext InstantiateContext(
                WantedNetHandle, 
                Context.SerializationContext.GetInternalContext()->ResolveContext, 
                RootObjectOfSubObject);
            
            // 【核心调用】通过工厂从 Header 创建实际的 UObject 实例
            // 工厂会根据 Header 中的类信息、Archetype 等创建正确类型的对象
            const UNetObjectFactory::FInstantiateResult Result = 
                Factory->InstantiateReplicatedObjectFromHeader(InstantiateContext, Header.Get());
        
            // ┌─────────────────────────────────────────────────────────────────────────┐
            // │ 阶段5: 实例化失败处理                                                     │
            // └─────────────────────────────────────────────────────────────────────────┘
            if (!Result.Instance)
            {
                // 实例化失败,记录警告日志
                if (UE_LOG_ACTIVE(LogIrisBridge, Warning) && !bSuppressCreateInstanceFailedEnsure)
                {
                    if (RootObjectOfSubObject.IsValid())
                    {
                        UE_LOG(LogIrisBridge, Warning, 
                            TEXT("CreateNetRefHandleFromRemote: Failed to instantiate SubObject NetHandle: %s of %s using header: %s"), 
                            *WantedNetHandle.ToString(), 
                            *PrintObjectFromNetRefHandle(RootObjectOfSubObject), 
                            ToCStr(Header->ToString()));
                    }
                    else
                    {
                        UE_LOG(LogIrisBridge, Warning, 
                            TEXT("CreateNetRefHandleFromRemote: Failed to instantiate RootObject NetHandle: %s using header: %s"), 
                            *WantedNetHandle.ToString(), ToCStr(Header->ToString()));
                    }
                }
                
                // ensureMsgf 在开发版本中会触发断点,便于调试
                // bSuppressCreateInstanceFailedEnsure 可以抑制此断言(某些情况下失败是预期的)
                ensureMsgf(bSuppressCreateInstanceFailedEnsure, 
                    TEXT("Failed to instantiate Handle: %s"), *WantedNetHandle.ToString());
                return FReplicationBridgeCreateNetRefHandleResult();
            }
        
            InstancePtr = Result.Instance;
            CreateResult.Flags |= Result.Flags;  // 合并工厂返回的标志位
        
            // ┌─────────────────────────────────────────────────────────────────────────┐
            // │ 阶段6: 注册复制 Fragment                                                  │
            // └─────────────────────────────────────────────────────────────────────────┘
            // 调用对象的 RegisterReplicationFragments,让对象注册其复制片段
            // 每个 Fragment 代表对象的一部分可复制状态
            CallRegisterReplicationFragments(InstancePtr, FragmentRegistrationContext, 
                EFragmentRegistrationFlags::None);
        
            // 获取已注册的所有 Fragment
            const FReplicationFragments& RegisteredFragments = 
                FFragmentRegistrationContextPrivateAccessor::GetReplicationFragments(FragmentRegistrationContext);
        
            // ┌─────────────────────────────────────────────────────────────────────────┐
            // │ 阶段7: 协议查找与验证                                                     │
            // └─────────────────────────────────────────────────────────────────────────┘
            // 获取 Archetype 或 CDO 作为协议缓存的 Key
            // 【设计思想】同一 Archetype 的对象共享相同的复制协议,避免重复创建
            const UObject* ArchetypeOrCDOUsedAsKey = InstancePtr->GetArchetype();
        
            // 为当前实例创建 InstanceProtocol(实例级别的协议绑定)
            FReplicationInstanceProtocolPtr InstanceProtocol(
                ProtocolManager->CreateInstanceProtocol(RegisteredFragments));
        
            // 尝试查找已存在的协议(使用 ProtocolId + Archetype 作为 Key)
            const FReplicationProtocol* ReplicationProtocol = 
                ProtocolManager->GetReplicationProtocol(ReceivedProtocolId, ArchetypeOrCDOUsedAsKey);
            
            if (!ReplicationProtocol)
            {
                // 协议不存在,创建新协议
                // bValidateProtocolId = true 表示需要验证服务器发来的协议ID
                FCreateReplicationProtocolParameters CreateProtocolParams {
                    .ArchetypeOrCDOUsedAsKey = ArchetypeOrCDOUsedAsKey, 
                    .bValidateProtocolId = true
                };
                ReplicationProtocol = ProtocolManager->CreateReplicationProtocol(
                    ReceivedProtocolId, 
                    RegisteredFragments, 
                    *(InstancePtr->GetClass()->GetName()), 
                    CreateProtocolParams);
            }
            else
            {
                // 协议已存在,验证本地 Fragment 是否与协议匹配
                // 【重要】这是防止客户端/服务器代码不一致的关键检查
                constexpr bool bDoNotLogErrors = false;
                bool bIsValid = FReplicationProtocolManager::ValidateReplicationProtocol(
                    ReplicationProtocol, RegisteredFragments, bDoNotLogErrors);
                if (!bIsValid)
                {
                    ReplicationProtocol = nullptr;  // 验证失败,置空触发错误处理
                }
            }
        
            // ┌─────────────────────────────────────────────────────────────────────────┐
            // │ 阶段8: 协议不匹配错误处理                                                 │
            // └─────────────────────────────────────────────────────────────────────────┘
            if (!ReplicationProtocol)
            {
                // 【严重错误】协议不匹配,无法进行复制
                // 通常是客户端/服务器版本不一致导致
                UE_LOG(LogIris, Error, 
                    TEXT("Protocol mismatch prevents binding %s to instanced object %s (CDO: %s)."), 
                    *WantedNetHandle.ToString(), *GetNameSafe(InstancePtr), *GetNameSafe(ArchetypeOrCDOUsedAsKey));
        
                // 调用调试工具输出详细的不匹配信息
                if (UE_LOG_ACTIVE(LogIris, Error))
                {
                    UE::Net::Private::ObjectBridgeDebugging::RemoteProtocolMismatchDetected(
                        ArchetypesAlreadyPrinted, 
                        ReplicationSystem, 
                        Context.ConnectionId, 
                        RegisteredFragments, 
                        ArchetypeOrCDOUsedAsKey, 
                        InstancePtr);
                }
        
                // 广播严重错误事件,通知其他系统
                FIrisDelegates::GetCriticalErrorDetectedDelegate().Broadcast(ReplicationSystem);
        
                // 调用虚函数,允许子类自定义错误处理
                OnProtocolMismatchDetected(WantedNetHandle);
            }
            else
            {
                // ┌─────────────────────────────────────────────────────────────────────┐
                // │ 阶段9: 创建网络句柄并绑定实例                                         │
                // └─────────────────────────────────────────────────────────────────────┘
                // 在内部创建远端网络对象,分配实际的句柄
                FNetRefHandle Handle = InternalCreateNetObjectFromRemote(
                    WantedNetHandle, ReplicationProtocol, FactoryId);
                CreateResult.NetRefHandle = Handle;
                
                if (Handle.IsValid())
                {
                    // 注册远端实例,建立 Handle <-> Instance <-> Protocol 的三方绑定
                    RegisterRemoteInstance(Handle, InstancePtr, ReplicationProtocol, 
                        InstanceProtocol.Get(), Context.ConnectionId);
        
                    // 【所有权转移】InstanceProtocol 的所有权已转移给系统
                    // Release() 释放 unique_ptr 的所有权但不销毁对象
                    (void)InstanceProtocol.Release();
        
                    // 调用工厂的后处理回调,允许执行额外的初始化逻辑
                    Factory->PostInstantiation(UNetObjectFactory::FPostInstantiationContext{ 
                        .Instance = InstancePtr, 
                        .Header = Header.Get(), 
                        .ConnectionId = Context.ConnectionId 
                    });
                }
            }
        
            return CreateResult;
        }

        📌 代码流程(9个阶段):

        阶段

        说明

        关键函数/操作

        1️⃣

        读取工厂ID

        Reader->ReadBits() → GetNetFactory()

        2️⃣

        读取创建头

        Factory->ReadHeader() → FNetObjectCreationHeader

        3️⃣

        准备Fragment上下文

        FFragmentRegistrationContext(CanReceive)

        4️⃣

        实例化对象

        InstantiateReplicatedObjectFromHeader()

        5️⃣

        失败处理

        ensureMsgf + 返回空结果

        6️⃣

        注册Fragment

        CallRegisterReplicationFragments()

        7️⃣

        协议查找/创建

        GetReplicationProtocol() / CreateReplicationProtocol()

        8️⃣

        协议不匹配处理

        RemoteProtocolMismatchDetected() + 广播错误

        9️⃣

        创建句柄并绑定

        InternalCreateNetObjectFromRemote() → RegisterRemoteInstance()

        🔑 关键设计要点:

        • CanReceive 标记:远端对象只能接收数据,不能主动发送

        • 协议缓存:以 ProtocolId + Archetype 为 Key 避免重复创建协议

        • 所有权转移:InstanceProtocol.Release() 将所有权交给系统管理

        • 工厂模式:不同类型对象(Actor、Component 等)使用不同工厂处理

        2️⃣ StopReplicatingNetRefHandle - 停止复制(优雅清理)

        当一个对象不再需要网络复制时(比如被销毁、或者主动停止复制):

        📖 源码位置: ReplicationBridge.cpp:486-525

        CPP
        // ReplicationBridge.cpp - StopReplicatingNetRefHandle 实现void UReplicationBridge::StopReplicatingNetRefHandle(FNetRefHandle Handle){
            // ========== 前置检查 ==========
            if (!Handle.IsValid())
            {
                return;
            }
            
            const FInternalNetRefIndex InternalIndex = NetRefHandleManager->GetInternalIndex(Handle);
            if (InternalIndex == InvalidInternalNetRefIndex)
            {
                return;  // 句柄无效或已被销毁
            }
            
            // ========== 获取对象数据 ==========
            FReplicatedObjectData* ObjectData = NetRefHandleManager->GetReplicatedObjectDataNoCheck(InternalIndex);
            
            // 检查是否是根对象
            // 💡 只有根对象才能主动停止复制,子对象跟随父对象
            if (ObjectData->SubObjectRootIndex != InvalidInternalNetRefIndex)
            {
                // 这是子对象,应该通过父对象的销毁流程来处理
                UE_LOG(LogIris, Warning, 
                    TEXT("StopReplicatingNetRefHandle called on SubObject %s, should destroy through root!"),
                    *Handle.ToString());
                return;
            }
            
            // ========== 确定销毁标志 ==========
            EEndReplicationFlags EndFlags = EEndReplicationFlags::Destroy;
            
            // 如果是本地权威对象(服务器上创建的),需要通知客户端销毁
            if (ObjectData->bHasLocalAuthority)
            {
                EndFlags |= EEndReplicationFlags::SendDestroyToClients;
            }
            
            // ========== 执行销毁 ==========
            DestroyLocalNetHandle(Handle, EndFlags);
        }

        🔍 EEndReplicationFlags 标志详解:

        CPP
        // 来自 ReplicationBridge.henum class EEndReplicationFlags : uint32
        {
            None                    = 0,
            
            // 🗑️ 销毁网络句柄(释放 ID 回到空闲池)
            DestroyNetHandle        = 1 << 0,
            
            // 🧹 清除 Push Model 的 NetPushId
            ClearNetPushId          = 1 << 1,
            
            // 📡 发送销毁通知给所有客户端
            SendDestroyToClients    = 1 << 2,
            
            // 🔄 立即刷新,不等待下一帧
            FlushDestroyImmediate   = 1 << 3,
            
            // 💨 不调用子类的清理回调
            SkipPendingDestroy      = 1 << 4,
            
            // 常用组合
            Destroy = DestroyNetHandle | ClearNetPushId,
            DestroyAndNotify = Destroy | SendDestroyToClients,
        };

        📊 停止复制的触发场景:

        场景

        触发方式

        EndFlags

        Actor 被销毁

        EndPlay → StopReplicating

        DestroyAndNotify

        手动停止复制

        直接调用 StopReplicating

        Destroy

        关卡卸载

        关卡组禁用

        批量 Destroy

        服务器关闭

        清理所有对象

        Destroy(无需通知)

        3️⃣ DetachInstanceFromNetRefHandle - 分离实例(高级用法)

        分离与销毁不同:分离只是解除游戏对象与网络句柄的绑定,句柄本身可以继续存在。

        📖 源码位置: ReplicationBridge.cpp:467-484

        CPP
        // ReplicationBridge.cpp - InternalDetachInstanceFromNetRefHandle 实现void UReplicationBridge::InternalDetachInstanceFromNetRefHandle(FNetRefHandle RefHandle){
            const FInternalNetRefIndex InternalIndex = NetRefHandleManager->GetInternalIndex(RefHandle);
            
            // ========== 分离实例协议 ==========
            // DetachInstanceProtocol 返回之前绑定的协议(如果有的话)
            FReplicationInstanceProtocol* InstanceProtocol = 
                const_cast<FReplicationInstanceProtocol*>(
                    NetRefHandleManager->DetachInstanceProtocol(InternalIndex)
                );
            
            if (InstanceProtocol)
            {
                UE_LOG_REPLICATIONBRIDGE(Verbose, 
                    TEXT("InternalDetachInstanceFromNetHandle Detached: %s from (InternalIndex: %u)"), 
                    *RefHandle.ToString(), InternalIndex);
                
                // ========== 解绑实例协议 ==========
                // 如果协议已经绑定到 ReplicationState,需要解绑
                if (EnumHasAnyFlags(InstanceProtocol->InstanceTraits, EReplicationInstanceProtocolTraits::IsBound))
                {
                    // 解除 Fragment 与游戏对象的绑定
                    // 这会调用每个 Fragment 的 Unbind 方法
                    FReplicationInstanceOperationsInternal::UnbindInstanceProtocol(
                        InstanceProtocol, 
                        NetRefHandleManager->GetReplicatedObjectDataNoCheck(InternalIndex).Protocol
                    );
                }
                
                // ========== 销毁实例协议 ==========
                // 释放 InstanceProtocol 占用的内存
                ReplicationProtocolManager->DestroyInstanceProtocol(InstanceProtocol);
            }
            
            // 注意:此时 NetRefHandle 仍然有效!
            // 只是没有游戏对象与之关联了
        }

        🤔 为什么需要"分离"而不是直接"销毁"?

        PLAINTEXT
        场景:对象池 (Object Pooling)
        ───────────────────────────────────────────────────────────────────
        
        传统做法(无对象池):
        ┌─────────┐  销毁    ┌─────────┐  创建    ┌─────────┐
        │  敌人A   │ ───────► │  空     │ ───────► │  敌人B   │
        │ Handle=1│         │         │         │ Handle=2│
        └─────────┘         └─────────┘         └─────────┘
            ↑                                        ↑
            └── 完全销毁,句柄回收         新句柄,重新同步所有属性
        
        
        对象池做法(使用 Detach):
        ┌─────────┐  分离    ┌─────────┐  重新附加  ┌─────────┐
        │  敌人A   │ ───────► │ 池对象   │ ─────────► │  敌人B   │
        │ Handle=1│         │ Handle=1│          │ Handle=1│
        │ 实例=X  │         │ 无实例   │          │ 实例=Y  │
        └─────────┘         └─────────┘          └─────────┘
            ↑                    ↑                    ↑
            │                    │                    │
            │               句柄保留,              重用句柄,
            │               等待重用               只同步变化的属性
        
        优势:
        ✅ 减少句柄分配/释放开销
        ✅ 客户端可以复用已有对象
        ✅ 减少网络流量(不需要发送完整的创建信息)

        📊 Attach vs Detach 操作对比:

        操作

        方法

        效果

        附加

        InternalAttachInstanceToNetRefHandle

        将游戏对象绑定到网络句柄,开始追踪属性变化

        分离

        InternalDetachInstanceFromNetRefHandle

        解除绑定,停止追踪,但句柄保留

        销毁

        DestroyLocalNetHandle

        分离 + 销毁句柄 + 通知客户端

        🔄 完整生命周期流程图

        PLAINTEXT
        服务器端                                          客户端
        ═══════════════════════════════════════════════════════════════════════
        
        1️⃣ 创建阶段
        ┌──────────────────┐                              
        │ SpawnActor()     │                              
        └────────┬─────────┘                              
                 │                                        
                 ▼                                        
        ┌──────────────────┐                              
        │ StartReplicating │                              
        │ • 分配 Handle    │ ─────── 📡 创建数据包 ──────►┌──────────────────┐
        │ • 创建协议       │                              │CreateNetRefHandle│
        │ • 附加实例       │                              │FromRemote        │
        └──────────────────┘                              │ • 使用服务器Handle│
                                                          │ • 创建本地对象   │
                                                          └──────────────────┘
        
        2️⃣ 运行阶段
        ┌──────────────────┐                              ┌──────────────────┐
        │ Poll 检测变化    │                              │                  │
        │ • 属性变化       │ ─────── 📡 属性数据包 ──────►│ Apply 应用状态   │
        │ • 标记脏位       │                              │ • 更新属性       │
        └──────────────────┘                              │ • 调用 RepNotify │
                                                          └──────────────────┘
        
        3️⃣ 销毁阶段
        ┌──────────────────┐                              
        │ DestroyActor()   │                              
        └────────┬─────────┘                              
                 │                                        
                 ▼                                        
        ┌──────────────────┐                              ┌──────────────────┐
        │StopReplicating   │                              │                  │
        │ • 分离实例       │ ─────── 📡 销毁数据包 ──────►│ 收到销毁通知     │
        │ • 销毁子对象     │                              │ • 分离实例       │
        │ • 释放句柄       │                              │ • 销毁本地对象   │
        │ • 发送销毁通知   │                              │ • 释放句柄       │
        └──────────────────┘                              └──────────────────┘

        👶 子对象管理

        游戏对象通常有层级关系,比如一个角色(Actor)可能有多个组件(Component):

        PLAINTEXT
        ┌─────────────────────────────────────────────────────────────────────┐
        │                    👨‍👧‍👦 父子对象关系                                  │
        └─────────────────────────────────────────────────────────────────────┘
        
                            ┌─────────────────┐
                            │   🧑 Character   │  ◄── 根对象 (RootObject)
                            │   (AActor)      │
                            └────────┬────────┘
                                     │
                   ┌─────────────────┼─────────────────┐
                   │                 │                 │
                   ▼                 ▼                 ▼
            ┌─────────────┐   ┌─────────────┐   ┌─────────────┐
            │ 🎒 Inventory │   │ 🏃 Movement  │   │ ❤️ Health   │
            │  Component  │   │  Component  │   │  Component  │
            └─────────────┘   └─────────────┘   └─────────────┘
                 ▲                 ▲                 ▲
                 │                 │                 │
                 └─────────────────┴─────────────────┘
                                   │
                            子对象 (SubObject)
                            跟随父对象一起复制

        关键规则:

        • 📌 子对象的生命周期跟随父对象

        • 📌 父对象被销毁时,子对象也会被销毁

        • 📌 子对象的休眠状态继承自父对象

        🗺️ 关卡组管理

        关卡组用于支持关卡流送(Level Streaming),让大型开放世界游戏能够动态加载/卸载区域:

        PLAINTEXT
        ┌─────────────────────────────────────────────────────────────────────┐
        │                    🗺️ 关卡组示意图                                   │
        └─────────────────────────────────────────────────────────────────────┘
        
            ┌─────────────────────────────────────────────────────────────┐
            │                      🌍 游戏世界                              │
            │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐         │
            │  │  🏔️ 区域A   │  │  🌲 区域B   │  │  🏖️ 区域C   │         │
            │  │  Level_A    │  │  Level_B    │  │  Level_C    │         │
            │  │             │  │             │  │             │         │
            │  │ • 敌人1     │  │ • 敌人3     │  │ • 敌人5     │         │
            │  │ • 敌人2     │  │ • 敌人4     │  │ • NPC1      │         │
            │  │ • 宝箱1     │  │ • 道具1     │  │ • 宝箱2     │         │
            │  └─────────────┘  └─────────────┘  └─────────────┘         │
            └─────────────────────────────────────────────────────────────┘
                                        │
                                        ▼
            ┌─────────────────────────────────────────────────────────────┐
            │                    📁 关卡组映射                              │
            │                                                             │
            │  LevelGroups = {                                            │
            │      Level_A → GroupHandle_A  (包含: 敌人1, 敌人2, 宝箱1)    │
            │      Level_B → GroupHandle_B  (包含: 敌人3, 敌人4, 道具1)    │
            │      Level_C → GroupHandle_C  (包含: 敌人5, NPC1, 宝箱2)     │
            │  }                                                          │
            └─────────────────────────────────────────────────────────────┘
        

        当玩家离开某个区域时,可以禁用该关卡组的复制,节省带宽!


        🔗 4.3 UObjectReplicationBridge - 对象管理专家

        💡 它是什么?

        UObjectReplicationBridge 在 UReplicationBridge 的基础上,添加了UObject 特有的复制功能,比如休眠管理、轮询频率控制等。

        🛏️ 休眠 (Dormancy) 管理

        休眠是一种优化技术:当对象长时间不变化时,让它"睡觉",不再占用网络带宽。

        PLAINTEXT
        ┌─────────────────────────────────────────────────────────────────────┐
        │                    🛏️ 休眠状态机                                     │
        └─────────────────────────────────────────────────────────────────────┘
        
                            ┌─────────────────┐
                            │   😴 休眠中      │
                            │   (Dormant)     │
                            └────────┬────────┘
                                     │
                              FlushDormant()
                              或属性变化
                                     │
                                     ▼
                            ┌─────────────────┐
                            │   😃 活跃中      │
                            │   (Awake)       │
                            └────────┬────────┘
                                     │
                           SetWantsToBeDormant(true)
                           且无属性变化
                                     │
                                     ▼
                            ┌─────────────────┐
                            │   😴 休眠中      │
                            │   (Dormant)     │
                            └─────────────────┘

        🎮 生活化比喻

        想象你在玩一个开放世界游戏:

        • 活跃状态 😃:你正在和一个NPC战斗,它的位置、血量每帧都在变化,需要实时同步

        • 休眠状态 😴:远处的一棵树,它几乎不会变化,没必要每帧都检查和发送

        ⏱️ 轮询频率控制源码分析

        不是所有对象都需要每帧检查变化。轮询频率控制让我们可以为不同对象设置不同的检查频率。

        📖 源码位置: ObjectReplicationBridge.cpp:61-94 - 控制变量定义

        CPP
        // ObjectReplicationBridge.cpp:61-94 - 轮询相关控制变量static bool bUseFrequencyBasedPolling = true;
        static FAutoConsoleVariableRef CVarUseFrequencyBasedPolling(
            TEXT("net.Iris.UseFrequencyBasedPolling"),
            bUseFrequencyBasedPolling,
            TEXT("Whether to use frequency based polling or not. Default is true.")
        );
        
        static bool bUseDormancyToFilterPolling = true;
        static FAutoConsoleVariableRef CVarUseDormancyToFilterPolling(
            TEXT("net.Iris.UseDormancyToFilterPolling"),
            bUseDormancyToFilterPolling,
            TEXT("Whether we should use dormancy to filter out objects that we should not poll. Default is true.")
        );
        
        static bool bAllowPollPeriodOverrides = true;
        static FAutoConsoleVariableRef CVarAllowPollPeriodOverrides(
            TEXT("net.Iris.AllowPollPeriodOverrides"),
            bAllowPollPeriodOverrides,
            TEXT("Whether we allow poll period overrides set in ObjectReplicationBridgeConfig. Default is true.")
        );
        
        static bool bEnableForceNetUpdate = false;
        static FAutoConsoleVariableRef CVarEnableForceNetUpdate(
            TEXT("net.Iris.EnableForceNetUpdate"),
            bEnableForceNetUpdate,
            TEXT("When true the system only allows ForceNetUpdate to skip the poll frequency of objects. "
                 "When false any MarkDirty object will be immediately polled.")
        );
        PLAINTEXT
        ┌─────────────────────────────────────────────────────────────────────┐
        │                    ⏱️ 轮询频率示意                                   │
        └─────────────────────────────────────────────────────────────────────┘
        
            时间轴 →  帧1   帧2   帧3   帧4   帧5   帧6   帧7   帧8
                      │     │     │     │     │     │     │     │
            ─────────────────────────────────────────────────────────────
            
            玩家角色    ✓     ✓     ✓     ✓     ✓     ✓     ✓     ✓
            (每帧轮询)  │     │     │     │     │     │     │     │
            
            敌人AI      ✓           ✓           ✓           ✓
            (每2帧轮询) │           │           │           │
            
            环境物体    ✓                       ✓
            (每4帧轮询) │                       │
            
            背景装饰    ✓
            (每8帧轮询) │

        🌳 StartReplicatingRootObject 源码分析

        这是开始复制根对象的核心方法,展示了如何配置轮询频率、优先级和过滤器。

        📖 源码位置: ObjectReplicationBridge.cpp:421-493

        CPP
        // ObjectReplicationBridge.cpp:421-493 - StartReplicatingRootObject 实现FNetRefHandle UObjectReplicationBridge::StartReplicatingRootObject(
            UObject* Instance, 
            const FRootObjectReplicationParams& Params, 
            FNetObjectFactoryId NetFactoryId){
            LLM_SCOPE_BYTAG(IrisState);
        
            // 1️⃣ 设置复制特性
            EReplicationFragmentTraits Traits = EReplicationFragmentTraits::CanReplicate;
            Traits |= Params.bNeedsPreUpdate ? EReplicationFragmentTraits::NeedsPreSendUpdate : EReplicationFragmentTraits::None;
            Traits |= Params.bNeedsWorldLocationUpdate ? EReplicationFragmentTraits::NeedsWorldLocationUpdate : EReplicationFragmentTraits::None;
        
            // 2️⃣ 注册对象
            FNetRefHandle RefHandle = StartReplicatingNetObject(Instance, Traits, NetFactoryId);
            if (!RefHandle.IsValid())
            {
                return FNetRefHandle::GetInvalid();
            }
            
            // 3️⃣ 配置根对象
            const FInternalNetRefIndex ObjectIndex = NetRefHandleManager->GetInternalIndex(RefHandle);
        
            // 4️⃣ 设置轮询帧周期
            float PollFrequency = Params.PollFrequency;
            FindOrCachePollFrequency(Instance->GetClass(), PollFrequency);
            uint8 PollFramePeriod = ConvertPollFrequencyIntoFrames(PollFrequency);
            PollFrequencyLimiter->SetPollFramePeriod(ObjectIndex, PollFramePeriod);
        
            // 5️⃣ 初始化世界位置缓存(如果需要)
            if (Params.bNeedsWorldLocationUpdate)
            {
                FWorldLocations& WorldLocations = ReplicationSystem->GetReplicationSystemInternal()->GetWorldLocations();
                WorldLocations.InitObjectInfoCache(ObjectIndex);
            }
        
            // 6️⃣ 设置优先级器
            const bool bRequireForceEnabled = Params.StaticPriority > 0.0f;
            const FNetObjectPrioritizerHandle PrioritizerHandle = GetPrioritizer(Instance->GetClass(), bRequireForceEnabled);
        
            if (Params.StaticPriority > 0.0f && PrioritizerHandle == InvalidNetObjectPrioritizerHandle)
            {
                // 使用静态优先级
                ReplicationSystem->SetStaticPriority(RefHandle, Params.StaticPriority);
            }
            else
            {
                if (PrioritizerHandle != InvalidNetObjectPrioritizerHandle)
                {
                    ReplicationSystem->SetPrioritizer(RefHandle, PrioritizerHandle);
                }
                else if (Params.bNeedsWorldLocationUpdate || HasRepTag(ReplicationSystem->GetReplicationProtocol(RefHandle), RepTag_WorldLocation))
                {
                    // 使用默认空间优先级器
                    ReplicationSystem->SetPrioritizer(RefHandle, DefaultSpatialNetObjectPrioritizerHandle);
                }
            }
        
            // 7️⃣ 设置动态过滤器
            AssignDynamicFilter(Instance, Params, RefHandle);
        
            // 8️⃣ 空间过滤的非休眠对象需要频繁更新世界位置
            if (Params.bNeedsWorldLocationUpdate && !Params.bIsDormant)
            {
                OptionallySetObjectRequiresFrequentWorldLocationUpdate(RefHandle, true);
            }
        
            return RefHandle;
        }

        👶 StartReplicatingSubObject 源码分析

        子对象的复制流程与根对象略有不同。

        📖 源码位置: ObjectReplicationBridge.cpp:531-575

        CPP
        // ObjectReplicationBridge.cpp:531-575 - StartReplicatingSubObject 实现FNetRefHandle UObjectReplicationBridge::StartReplicatingSubObject(
            UObject* Instance, 
            const FSubObjectReplicationParams& Params, 
            FNetObjectFactoryId NetFactoryId){
            LLM_SCOPE_BYTAG(IrisState);
        
            // ✅ 验证:所有者必须已经在复制
            checkf(IsReplicatedHandle(Params.RootObjectHandle), 
                TEXT("Owner %s must be replicated for subobject %s to replicate."), 
                *GetNameSafe(Owner), *GetNameSafe(Instance));
        
            // ✅ 验证:不能复制原型或默认对象
            checkf(!Instance->HasAnyFlags(RF_ArchetypeObject | RF_ClassDefaultObject), 
                TEXT("Iris cannot replicate subobject %s because it's an Archetype or DefaultObject"), 
                *GetNameSafe(Instance));
        
            // 🔍 检查是否已经在复制
            FNetRefHandle SubObjectRefHandle = GetReplicatedRefHandle(Instance);
            if (SubObjectRefHandle.IsValid())
            {
                // 验证现有对象是所有者的子对象
                check(Params.RootObjectHandle == LocalNetRefHandleManager.GetRootObjectOfSubObject(SubObjectRefHandle));
                return SubObjectRefHandle;
            }
            
            // 📦 开始复制网络对象
            const EReplicationFragmentTraits Traits = EReplicationFragmentTraits::CanReplicate;
            SubObjectRefHandle = StartReplicatingNetObject(Instance, Traits, NetFactoryId);
        
            if (!SubObjectRefHandle.IsValid())
            {
                return FNetRefHandle::GetInvalid();
            }
        
            // 🔗 添加子对象关系
            InternalAddSubObject(Params.RootObjectHandle, SubObjectRefHandle, 
                Params.InsertRelativeToSubObjectHandle, Params.InsertionOrder);
        
            UE_LOG_OBJECTREPLICATIONBRIDGE(Verbose, TEXT("StartReplicatingSubObject Added %s RelativeToSubObjectHandle %s"), 
                *PrintObjectFromNetRefHandle(SubObjectRefHandle), 
                *PrintObjectFromNetRefHandle(Params.InsertRelativeToSubObjectHandle));
        
            // ⏱️ 子对象应该总是与所有者一起轮询
            SetPollWithObject(Params.RootObjectHandle, SubObjectRefHandle);
        
            // 🛏️ 从所有者复制休眠状态
            SetSubObjectDormancyStatus(SubObjectRefHandle, Params.RootObjectHandle);
            
            return SubObjectRefHandle;
        }

        🌳 RootObject 与 SubObject

        PLAINTEXT
        ┌─────────────────────────────────────────────────────────────────────┐
        │                    🌳 对象层级结构                                   │
        └─────────────────────────────────────────────────────────────────────┘
        
            ┌─────────────────────────────────────────────────────────────┐
            │                   🌳 RootObject (根对象)                      │
            │                                                             │
            │   特点:                                                     │
            │   • 独立存在,有自己的网络句柄                                 │
            │   • 可以设置过滤器和优先级器                                   │
            │   • 控制自己和子对象的休眠状态                                 │
            │   • 例如:AActor, APawn, ACharacter                          │
            └────────────────────────┬────────────────────────────────────┘
                                     │
                      ┌──────────────┼──────────────┐
                      │              │              │
                      ▼              ▼              ▼
            ┌─────────────┐  ┌─────────────┐  ┌─────────────┐
            │ 🍃 SubObject │  │ 🍃 SubObject │  │ 🍃 SubObject │
            │   (子对象)   │  │   (子对象)   │  │   (子对象)   │
            │             │  │             │  │             │
            │ 特点:       │  │ 特点:       │  │ 特点:       │
            │ • 依附于根   │  │ • 依附于根   │  │ • 依附于根   │
            │ • 共享过滤   │  │ • 共享过滤   │  │ • 共享过滤   │
            │ • 继承休眠   │  │ • 继承休眠   │  │ • 继承休眠   │
            │             │  │             │  │             │
            │ 例如:       │  │ 例如:       │  │ 例如:       │
            │ UComponent  │  │ UInventory  │  │ UAbility    │
            └─────────────┘  └─────────────┘  └─────────────┘

        🔗 依赖对象管理

        有时候,一个对象的复制依赖于另一个对象:

        PLAINTEXT
        ┌─────────────────────────────────────────────────────────────────────┐
        │                    🔗 依赖关系示例                                   │
        └─────────────────────────────────────────────────────────────────────┘
        
            场景:玩家拿起一把武器
            ┌─────────────┐         ┌─────────────┐
            │  🧑 Player   │ ──────► │  🗡️ Weapon  │
            │             │  持有    │             │
            └─────────────┘         └─────────────┘
                  │                       │
                  │                       │
                  ▼                       ▼
            必须先复制 Player      才能复制 Weapon
            (因为 Weapon 引用了 Player 作为持有者)
        

        依赖调度提示:

        CPP
        enum class EDependentObjectSchedulingHint : uint8
        {
            // 默认:依赖对象在同一批次中复制
            Default,
            
            // 在父对象之前复制
            ScheduleBeforeParent,
            
            // 在父对象之后复制
            ScheduleAfterParent,
        };

        🏭 4.4 UEngineReplicationBridge - UE引擎的翻译官

        💡 它是什么?

        UEngineReplicationBridge 是最接近游戏代码的桥接层,专门处理 Actor 和 Component 的复制。它知道如何与 UE 的 NetDriver 系统对接。

        🎭 Actor 复制接口

        CPP
        FNetRefHandle UEngineReplicationBridge::StartReplicatingActor(
            AActor* Actor, 
            const FActorReplicationParams& Params){
            // ✅ 验证检查
            if (!bIsNetActor || 
                Actor->GetLocalRole() != ROLE_Authority ||  // 必须是权威端
                !Actor->GetIsReplicated())                  // 必须标记为可复制
            {
                return FNetRefHandle::GetInvalid();
            }
            
            // 📋 创建根对象参数
            FRootObjectReplicationParams RootObjectParams = {
                .bNeedsPreUpdate = 1U,           // 需要预更新
                .bNeedsWorldLocationUpdate = 1U, // 需要位置更新
                .bIsDormant = Actor->NetDormancy > DORM_Awake,  // 初始休眠状态
                .StaticPriority = (Actor->bAlwaysRelevant || Actor->bOnlyRelevantToOwner) 
                                  ? Actor->NetPriority : 0.0f,
                .PollFrequency = Actor->GetNetUpdateFrequency()  // 轮询频率
            };
            
            // 🎫 开始复制根对象
            FNetRefHandle ActorRefHandle = StartReplicatingRootObject(
                Actor, 
                RootObjectParams, 
                ActorFactoryId
            );
            
            // 🎯 设置所有者过滤(如果只对所有者可见)
            if (Actor->bOnlyRelevantToOwner && !Actor->bAlwaysRelevant)
            {
                GetReplicationSystem()->SetFilter(ActorRefHandle, ToOwnerFilterHandle);
            }
            
            // 🛏️ 设置休眠状态
            if (Dormancy > DORM_Awake)
            {
                SetObjectWantsToBeDormant(ActorRefHandle, true);
            }
            
            // 🗺️ 添加到关卡组
            AddActorToLevelGroup(Actor);
            
            // 🔧 复制子对象和组件
            // ...
            
            return ActorRefHandle;
        }

        📖 源码位置: Engine/Source/Runtime/Engine/Private/Net/Iris/ReplicationSystem/EngineReplicationBridge.cpp:204-381

        🔧 Component 复制接口

        组件作为 Actor 的子对象进行复制:

        PLAINTEXT
        ┌─────────────────────────────────────────────────────────────────────┐
        │                    🔧 Component 复制流程                             │
        └─────────────────────────────────────────────────────────────────────┘
        
            ┌─────────────────┐
            │   🎭 AActor     │
            │   (已复制)      │
            └────────┬────────┘
                     │
                     │ GetReplicatedComponents()
                     │ 获取需要复制的组件列表
                     ▼
            ┌─────────────────────────────────────────────────────────────┐
            │  遍历每个组件                                                │
            │                                                             │
            │  for (UActorComponent* Component : ReplicatedComponents)    │
            │  {                                                          │
            │      // 作为子对象开始复制                                    │
            │      StartReplicatingComponent(ActorHandle, Component);     │
            │  }                                                          │
            └─────────────────────────────────────────────────────────────┘

        🔌 与 NetDriver 的集成

        UEngineReplicationBridge 需要与传统的 UNetDriver 系统协同工作:

        CPP
        void UEngineReplicationBridge::SetNetDriver(UNetDriver* const InNetDriver){
            // 🔌 断开旧连接
            if (NetDriver)
            {
                NetDriver->OnNetServerMaxTickRateChanged.RemoveAll(this);
                NetDriver->GetOnNetUpdateFrequencyChanged().RemoveAll(this);
            }
            
            NetDriver = InNetDriver;
            
            if (InNetDriver != nullptr)
            {
                // ⏱️ 同步 Tick 速率
                SetMaxTickRate(static_cast<float>(
                    FPlatformMath::Max(InNetDriver->GetNetServerMaxTickRate(), 0)
                ));
                
                // 📡 监听速率变化事件
                InNetDriver->OnNetServerMaxTickRateChanged.AddUObject(
                    this, 
                    &UEngineReplicationBridge::OnMaxTickRateChanged
                );
                
                InNetDriver->GetOnNetUpdateFrequencyChanged().AddUObject(
                    this, 
                    &UEngineReplicationBridge::OnNetUpdateFrequencyChanged
                );
            }
        }

        📖 源码位置: Engine/Source/Runtime/Engine/Private/Net/Iris/ReplicationSystem/EngineReplicationBridge.cpp:649-677

        🗺️ 关卡流送支持

        当关卡被加载或卸载时,需要相应地管理网络对象:

        PLAINTEXT
        ┌─────────────────────────────────────────────────────────────────────┐
        │                    🗺️ 关卡流送与网络复制                             │
        └─────────────────────────────────────────────────────────────────────┘
        
            玩家移动方向 →
            
            ┌─────────────┐  ┌─────────────┐  ┌─────────────┐
            │ 🌲 Level_A  │  │ 🏔️ Level_B  │  │ 🏖️ Level_C  │
            │   (卸载)    │  │  (当前)     │  │   (加载)    │
            │             │  │             │  │             │
            │ 停止复制    │  │ 正常复制    │  │ 开始复制    │
            │ 组内对象    │  │ 组内对象    │  │ 组内对象    │
            └─────────────┘  └─────────────┘  └─────────────┘
                 ▲                                   │
                 │                                   │
                 └───────────────────────────────────┘
                         关卡组控制复制状态

        🎫 4.5 FNetRefHandleManager - 身份证管理局

        💡 它是什么?

        FNetRefHandleManager 负责为每个网络对象分配唯一标识符(句柄),并管理对象的内部索引。就像身份证管理局给每个公民发放身份证号。

        🏗️ 核心数据结构与初始化

        📖 源码位置: NetRefHandleManager.cpp:31-80

        CPP
        // NetRefHandleManager.cpp:31-80 - Init 方法完整实现void FNetRefHandleManager::Init(const FInitParams& InitParams){
            // 1️⃣ 配置最大对象数(向上取整到字边界)
            MaxActiveObjectCount = FNetBitArray::RoundUpToMaxWordBitCount(InitParams.MaxActiveObjectCount);
            InternalNetRefIndexGrowSize = InitParams.InternalNetRefIndexGrowSize > 0 
                ? FNetBitArray::RoundUpToMaxWordBitCount(InitParams.InternalNetRefIndexGrowSize) 
                : MaxActiveObjectCount;
        
            ReplicationSystemId = InitParams.ReplicationSystemId;
        
            // 2️⃣ 预分配分块数组数量(最少为 1,用于 InvalidInternalIndex)
            uint32 PreAllocatedNetChunkedArrayCount = FMath::Clamp(InitParams.NetChunkedArrayCount, 1U, MaxActiveObjectCount);
            HighestNetChunkedArrayInternalIndex = PreAllocatedNetChunkedArrayCount - 1;
        
            // 3️⃣ 初始化内部索引配置
            CurrentMaxInternalNetRefIndex = InitParams.InternalNetRefIndexInitSize > 0 
                ? FMath::Min(InitParams.InternalNetRefIndexInitSize, MaxActiveObjectCount) 
                : MaxActiveObjectCount;
            CurrentMaxInternalNetRefIndex = FNetBitArray::RoundUpToMaxWordBitCount(CurrentMaxInternalNetRefIndex);
        
            UE_LOG(LogIris, Log, 
                TEXT("NetRefHandleManager: Configured with MaxActiveObjectCount=%d, MaxInternalNetRefIndex: %u, Grow=%u"),
                MaxActiveObjectCount, CurrentMaxInternalNetRefIndex, InternalNetRefIndexGrowSize);
        
            // 4️⃣ 初始化分块数组(TNetChunkedArray)
            ReplicatedObjectData = TNetChunkedArray<FReplicatedObjectData>(PreAllocatedNetChunkedArrayCount, EInitMemory::Constructor);
            ReplicatedObjectRefCount = TNetChunkedArray<uint16>(PreAllocatedNetChunkedArrayCount, EInitMemory::Zero);
            ReplicatedObjectStateBuffers = TNetChunkedArray<uint8*>(PreAllocatedNetChunkedArrayCount, EInitMemory::Zero);
            ReplicatedInstances = TNetChunkedArray<TObjectPtr<UObject>>(PreAllocatedNetChunkedArrayCount, EInitMemory::Zero);
        
            // 5️⃣ 为 InvalidInternalIndex 初始化默认数据
            ReplicatedObjectData[InvalidInternalIndex] = FReplicatedObjectData();
        
            // 6️⃣ 初始化所有位数组
            {
                InitNetBitArray(&ScopeFrameData.CurrentFrameScopableInternalIndices);
                InitNetBitArray(&ScopeFrameData.PrevFrameScopableInternalIndices);
                InitNetBitArray(&GlobalScopableInternalIndices);
                InitNetBitArray(&RelevantObjectsInternalIndices);
                InitNetBitArray(&PolledObjectsInternalIndices);
                InitNetBitArray(&DirtyObjectsToQuantize);
                InitNetBitArray(&AssignedInternalIndices);
                InitNetBitArray(&SubObjectInternalIndices);
                InitNetBitArray(&DependentObjectInternalIndices);
                InitNetBitArray(&ObjectsWithDependentObjectsInternalIndices);
                InitNetBitArray(&DestroyedStartupObjectInternalIndices);
                InitNetBitArray(&WantToBeDormantInternalIndices);
                InitNetBitArray(&ObjectsWithPreUpdate);
                InitNetBitArray(&DormantObjectsPendingFlushNet);
            }
        
            // 7️⃣ 标记无效索引为已使用
            AssignedInternalIndices.SetBit(0);
        }
        PLAINTEXT
        ┌─────────────────────────────────────────────────────────────────────┐│                    🎫 FNetRefHandleManager 内部结构                  │└─────────────────────────────────────────────────────────────────────┘
        
            ┌─────────────────────────────────────────────────────────────┐
            │                      📊 计数器                               │
            │  MaxActiveObjectCount = 65536    // 最大对象数               │
            │  ActiveObjectCount = 1234        // 当前活跃对象数           │
            │  NextStaticHandleIndex = 100     // 下一个静态句柄ID         │
            │  NextDynamicHandleIndex = 5000   // 下一个动态句柄ID         │
            └─────────────────────────────────────────────────────────────┘
        
            ┌─────────────────────────────────────────────────────────────┐
            │                      🗺️ 映射表                               │
            │                                                             │
            │  RefHandleToInternalIndex:                                  │
            │  ┌──────────────┬─────────────────┐                        │
            │  │ NetRefHandle │ InternalIndex   │                        │
            │  ├──────────────┼─────────────────┤                        │
            │  │ Handle_001   │      0          │                        │
            │  │ Handle_002   │      1          │                        │
            │  │ Handle_003   │      2          │                        │
            │  │ ...          │     ...         │                        │
            │  └──────────────┴─────────────────┘                        │
            └─────────────────────────────────────────────────────────────┘
        
            ┌─────────────────────────────────────────────────────────────┐
            │                      📝 位数组 (BitArrays)                   │
            │                                                             │
            │  AssignedInternalIndices:     [1,1,1,0,0,1,1,0,...]        │
            │                                ↑ 已分配的索引                │
            │                                                             │
            │  WantToBeDormantIndices:      [0,0,1,0,0,0,1,0,...]        │
            │                                ↑ 想要休眠的对象              │
            │                                                             │
            │  SubObjectInternalIndices:    [0,1,0,0,1,1,0,0,...]        │
            │                                ↑ 子对象标记                  │
            │                                                             │
            │  DirtyObjectsToQuantize:      [1,0,1,0,0,1,0,0,...]        │
            │                                ↑ 待量化的脏对象              │
            └─────────────────────────────────────────────────────────────┘
        
            ┌─────────────────────────────────────────────────────────────┐
            │                      📦 分块数组 (Chunked Arrays)            │
            │                                                             │
            │  ReplicatedObjectData[]:      对象元数据                     │
            │  ReplicatedObjectStateBuffers[]: 状态缓冲区指针              │
            │  ReplicatedObjectRefCount[]:  引用计数                       │
            │  ReplicatedInstances[]:       UObject 实例指针               │
            └─────────────────────────────────────────────────────────────┘

        🎫 句柄分配机制源码分析

        📖 源码位置: NetRefHandleManager.cpp:169-186

        CPP
        // NetRefHandleManager.cpp:169-186 - 获取下一个句柄IDuint64 FNetRefHandleManager::GetNextNetRefHandleId(uint64 HandleId) const{
            // 由于我们使用索引中的最低位来指示句柄是静态还是动态的,
            // 所以不能使用所有位作为索引
            constexpr uint64 NetHandleIdIndexBitMask = (1ULL << (FNetRefHandle::IdBits - 1)) - 1;
        
            uint64 NextHandleId = (HandleId + 1) & NetHandleIdIndexBitMask;
            if (NextHandleId == 0)
            {
                ++NextHandleId;  // 跳过 0,因为 0 表示无效句柄
            }
            return NextHandleId;
        }
        
        // NetRefHandleManager.cpp:182-186 - 获取下一个空闲内部索引FInternalNetRefIndex FNetRefHandleManager::GetNextFreeInternalIndex() const{
            // 在位数组中找到第一个为 0 的位(表示未分配)
            const uint32 NextFreeIndex = AssignedInternalIndices.FindFirstZero();
            return NextFreeIndex != FNetBitArray::InvalidIndex ? NextFreeIndex : InvalidInternalIndex;
        }

        📦 创建网络对象源码分析

        📖 源码位置: NetRefHandleManager.cpp:188-268

        CPP
        // NetRefHandleManager.cpp:188-268 - InternalCreateNetObject 完整实现FInternalNetRefIndex FNetRefHandleManager::InternalCreateNetObject(
            const FNetRefHandle NetRefHandle, 
            const FNetHandle GlobalHandle, 
            const FReplicationProtocol* ReplicationProtocol){
            // 1️⃣ 检查是否达到最大对象数
            if (ActiveObjectCount >= MaxActiveObjectCount)
            {
                UE_LOG(LogIris, Error, TEXT("NetRefHandleManager: Maximum active object count reached (%d/%d)."), 
                    ActiveObjectCount, MaxActiveObjectCount);
                ensureMsgf(false, TEXT("NetRefHandleManager: Maximum active object count reached (%d/%d)."), 
                    ActiveObjectCount, MaxActiveObjectCount);
                return InvalidInternalIndex;
            }
        
            // 2️⃣ 验证句柄是否已存在
            if (RefHandleToInternalIndex.Contains(NetRefHandle))
            {
                ensureMsgf(false, TEXT("NetRefHandleManager::InternalCreateNetObject %s already exists"), 
                    *NetRefHandle.ToString());
                return InvalidInternalIndex;
            }
        
            // 3️⃣ 获取下一个空闲索引
            uint32 InternalIndex = GetNextFreeInternalIndex();
        
            // 4️⃣ 如果没有空闲索引,尝试增长列表
            if (InternalIndex == InvalidInternalIndex)
            {
                InternalIndex = GrowNetObjectLists();
        
                // 如果无法继续增长,终止进程
                if (InternalIndex == InvalidInternalIndex)
                {
                    UE_LOG(LogIris, Fatal, 
                        TEXT("NetRefHandleManager: Hit the maximum limit of active replicated objects: %u. "
                             "Aborting since we cannot replicate %s"), 
                        MaxActiveObjectCount, ReplicationProtocol->DebugName->Name);
                    return InvalidInternalIndex;
                }
            }
        
            UE_LOG(LogIris, Verbose, TEXT("FNetRefHandleManager::InternalCreateNetObject: (InternalIndex: %u) (%s)"), 
                InternalIndex, *NetRefHandle.ToString());
        
            // 5️⃣ 如果需要,增长分块数组缓冲区
            if (InternalIndex > HighestNetChunkedArrayInternalIndex)
            {
                GrowNetChunkedArrayBuffers(InternalIndex);
            }
        
            // 6️⃣ 存储对象数据
            FReplicatedObjectData& Data = ReplicatedObjectData[InternalIndex];
            Data = FReplicatedObjectData();  // 重置为默认值
            Data.RefHandle = NetRefHandle;
            Data.NetHandle = GlobalHandle;
            Data.Protocol = ReplicationProtocol;
            Data.InstanceProtocol = nullptr;
            Data.ReceiveStateBuffer = nullptr;
            Data.bShouldPropagateChangedStates = 1U;  // 应该传播变更
            Data.bNeedsFullCopyAndQuantize = 1U;      // 需要完整复制
            Data.bWantsFullPoll = 1U;                 // 需要完整轮询
        
            // 7️⃣ 清除相关位数组
            ObjectsWithPreUpdate.ClearBit(InternalIndex);
            ReplicatedObjectStateBuffers[InternalIndex] = nullptr;
        
            ++ActiveObjectCount;
        
            // 8️⃣ 添加映射:NetRefHandle -> InternalIndex
            RefHandleToInternalIndex.Add(NetRefHandle, InternalIndex);
            
            // 9️⃣ 添加映射:GlobalHandle -> InternalIndex(加速公共 API 查找)
            if (GlobalHandle.IsValid())
            {
                NetHandleToInternalIndex.Add(GlobalHandle, InternalIndex);
            }
        
            // 🔟 标记索引为已分配和可作用域
            AssignedInternalIndices.SetBit(InternalIndex);
            GlobalScopableInternalIndices.SetBit(InternalIndex);
        
            // 1️⃣1️⃣ 新创建的句柄不是子对象
            SubObjectInternalIndices.ClearBit(InternalIndex);
        
            // 1️⃣2️⃣ 重置引用计数
            ReplicatedObjectRefCount[InternalIndex] = 0;
        
            return InternalIndex;
        }

        🔗 附加实例协议源码分析

        📖 源码位置: NetRefHandleManager.cpp:270-282

        CPP
        // NetRefHandleManager.cpp:270-282 - AttachInstanceProtocol 实现void FNetRefHandleManager::AttachInstanceProtocol(
            FInternalNetRefIndex InternalIndex, 
            const FReplicationInstanceProtocol* InstanceProtocol, 
            UObject* Instance){
            if (ensure((InternalIndex != InvalidInternalIndex) && InstanceProtocol))
            {
                FReplicatedObjectData& Data = ReplicatedObjectData[InternalIndex];
                Data.InstanceProtocol = InstanceProtocol;
        
                // 确保之前没有实例
                check(ReplicatedInstances[InternalIndex] == nullptr);
                ReplicatedInstances[InternalIndex] = Instance;
        
                // 根据实例协议特性设置是否需要预发送更新
                ObjectsWithPreUpdate.SetBitValue(InternalIndex, 
                    EnumHasAnyFlags(InstanceProtocol->InstanceTraits, 
                                   EReplicationInstanceProtocolTraits::NeedsPreSendUpdate));
            }
        }

        📈 列表增长机制源码分析

        当对象数量超过当前容量时,系统会自动增长列表。

        📖 源码位置: NetRefHandleManager.cpp:101-127

        CPP
        // NetRefHandleManager.cpp:101-127 - GrowNetObjectLists 实现FInternalNetRefIndex FNetRefHandleManager::GrowNetObjectLists(){
            check(AssignedInternalIndices.GetNumBits() == CurrentMaxInternalNetRefIndex);
        
            // 旧的最大值就是下一个可用索引
            const FInternalNetRefIndex NextFreeIndex = CurrentMaxInternalNetRefIndex;
        
            // 如果已经达到最大值,返回无效索引
            if (CurrentMaxInternalNetRefIndex >= MaxActiveObjectCount)
            {
                return InvalidInternalIndex;
            }
        
            // 增长列表
            CurrentMaxInternalNetRefIndex += InternalNetRefIndexGrowSize;
        
            if (CurrentMaxInternalNetRefIndex > MaxActiveObjectCount)
            {
                // 最后一次增长机会
                CurrentMaxInternalNetRefIndex = MaxActiveObjectCount;
            }
        
            UE_LOG(LogIris, Log, TEXT("FNetRefHandleManager::GrowNetObjectLists grew MaxInternalIndex from %u to %u (+%u)"), 
                NextFreeIndex, CurrentMaxInternalNetRefIndex, CurrentMaxInternalNetRefIndex - NextFreeIndex);
        
            // 通知所有订阅者列表已增长
            MaxInternalNetRefIndexIncreased(CurrentMaxInternalNetRefIndex);
        
            return NextFreeIndex;
        }
        
        // NetRefHandleManager.cpp:129-142 - 通知订阅者void FNetRefHandleManager::MaxInternalNetRefIndexIncreased(FInternalNetRefIndex NewMaxInternalNetRefIndex){
            QUICK_SCOPE_CYCLE_COUNTER(STAT_FNetRefHandleManager_MaxInternalNetRefIndexIncreased);
            CSV_CUSTOM_STAT(IrisCommon, MaxInternalIndexIncreasedCount, 1, ECsvCustomStatOp::Accumulate);
        
            // 首先重新分配所有我们拥有的 NetBitArrays
            for (FNetBitArray* NetBitArray : OwnedNetBitArrays)
            {
                NetBitArray->SetNumBits(NewMaxInternalNetRefIndex);
            }
        
            // 告诉其他系统也增长它们的列表
            OnMaxInternalNetRefIndexIncreased.Broadcast(NewMaxInternalNetRefIndex);
        }

        🎯 句柄与索引的关系

        PLAINTEXT
        ┌─────────────────────────────────────────────────────────────────────┐
        │                    🎫 句柄 vs 索引                                   │
        └─────────────────────────────────────────────────────────────────────┘
        
            ┌─────────────────────────────────────────────────────────────┐
            │                   FNetRefHandle (句柄)                       │
            │                                                             │
            │   • 对外使用的唯一标识符                                      │
            │   • 包含 ReplicationSystemId(支持 PIE 多实例)              │
            │   • 区分静态/动态句柄                                        │
            │   • 可以安全地在网络上传输                                    │
            │                                                             │
            │   结构: [ReplicationSystemId | Static/Dynamic | HandleId]   │
            └─────────────────────────────────────────────────────────────┘
                                        │
                                        │ 映射
                                        ▼
            ┌─────────────────────────────────────────────────────────────┐
            │               FInternalNetRefIndex (内部索引)                │
            │                                                             │
            │   • 内部使用的数组索引                                        │
            │   • 用于快速访问对象数据                                      │
            │   • 不在网络上传输                                           │
            │   • 可以被回收重用                                           │
            │                                                             │
            │   用途: ReplicatedObjectData[InternalIndex]                 │
            └─────────────────────────────────────────────────────────────┘

        🏠 生活化类比

        句柄 (Handle) 就像你的身份证号:

        • 全国唯一

        • 终身不变

        • 可以在任何地方使用

        内部索引 (InternalIndex) 就像你在公司的工位号:

        • 只在公司内部有意义

        • 你离职后,工位可以分给别人

        • 访问速度快(直接走到那个位置)


        📊 组件协作流程图

        让我们看看这五个组件是如何协同工作的:

        PLAINTEXT
        ┌─────────────────────────────────────────────────────────────────────┐
        │                    🎬 Actor 开始复制的完整流程                        │
        └─────────────────────────────────────────────────────────────────────┘
        
            游戏代码
               │
               │ Actor->SetReplicates(true)
               ▼
        ┌──────────────────────────────────────────────────────────────────┐
        │ 🏭 UEngineReplicationBridge::StartReplicatingActor()              │
        │                                                                  │
        │   1. 验证 Actor 是否可以复制                                       │
        │   2. 准备复制参数(休眠状态、优先级、轮询频率)                       │
        └────────────────────────────┬─────────────────────────────────────┘
                                     │
                                     ▼
        ┌──────────────────────────────────────────────────────────────────┐
        │ 🔗 UObjectReplicationBridge::StartReplicatingRootObject()         │
        │                                                                  │
        │   1. 获取/创建复制协议                                             │
        │   2. 设置轮询周期                                                  │
        │   3. 配置过滤器和优先级器                                          │
        └────────────────────────────┬─────────────────────────────────────┘
                                     │
                                     ▼
        ┌──────────────────────────────────────────────────────────────────┐
        │ 🌉 UReplicationBridge::StartReplicatingNetObject()                │
        │                                                                  │
        │   1. 分配网络句柄                                                  │
        │   2. 创建网络对象                                                  │
        │   3. 绑定实例                                                      │
        └────────────────────────────┬─────────────────────────────────────┘
                                     │
                                     ▼
        ┌──────────────────────────────────────────────────────────────────┐
        │ 🎫 FNetRefHandleManager::CreateNetObject()                        │
        │                                                                  │
        │   1. 分配内部索引                                                  │
        │   2. 分配状态缓冲区                                                │
        │   3. 建立句柄到索引的映射                                          │
        │   4. 标记为已分配                                                  │
        └────────────────────────────┬─────────────────────────────────────┘
                                     │
                                     ▼
        ┌──────────────────────────────────────────────────────────────────┐
        │ 🧠 UReplicationSystem (注册完成)                                   │
        │                                                                  │
        │   对象现在可以参与:                                                │
        │   • 过滤系统 - 决定谁能看到                                        │
        │   • 优先级系统 - 决定发送顺序                                       │
        │   • 脏数据追踪 - 检测变化                                          │
        │   • 增量压缩 - 优化带宽                                            │
        └──────────────────────────────────────────────────────────────────┘

        📁 源文件索引

        组件

        头文件

        实现文件

        关键行号

        UReplicationSystem

        Iris/Core/Public/Iris/ReplicationSystem/ReplicationSystem.h

        Iris/Core/Private/Iris/ReplicationSystem/ReplicationSystem.cpp

        Init: 106-293, PreSendUpdate: 738-838

        UReplicationBridge

        Iris/Core/Public/Iris/ReplicationSystem/ReplicationBridge.h

        Iris/Core/Private/Iris/ReplicationSystem/ReplicationBridge.cpp

        Initialize: 348-415, DestroyLocalNetHandle: 527-560

        UObjectReplicationBridge

        Iris/Core/Public/Iris/ReplicationSystem/ObjectReplicationBridge.h

        Iris/Core/Private/Iris/ReplicationSystem/ObjectReplicationBridge.cpp

        StartReplicatingRootObject: 421-493, StartReplicatingSubObject: 531-575

        UEngineReplicationBridge

        Engine/Public/Net/Iris/ReplicationSystem/EngineReplicationBridge.h

        Engine/Private/Net/Iris/ReplicationSystem/EngineReplicationBridge.cpp

        StartReplicatingActor: 204-381, SetNetDriver: 649-677

        FNetRefHandleManager

        Iris/Core/Private/Iris/ReplicationSystem/NetRefHandleManager.h

        Iris/Core/Private/Iris/ReplicationSystem/NetRefHandleManager.cpp

        Init: 31-80, InternalCreateNetObject: 188-268, GrowNetObjectLists: 101-127


        🎯 本章小结

        组件

        一句话总结

        关键职责

        🧠 UReplicationSystem

        总指挥中心

        协调所有子系统,执行主更新循环

        🌉 UReplicationBridge

        桥接基础设施

        对象生命周期管理,关卡组管理

        🔗 UObjectReplicationBridge

        对象管理专家

        休眠管理,轮询频率控制

        🏭 UEngineReplicationBridge

        UE引擎翻译官

        Actor/Component 复制,NetDriver 集成

        🎫 FNetRefHandleManager

        身份证管理局

        句柄分配,对象索引管理


        💡 下一步

        理解了这五大核心组件后,你可以继续学习:

        • 📖 第五部分:过滤系统 - 了解如何决定"谁能看到什么"

        • 📖 第六部分:优先级系统 - 了解如何决定"先发送什么"

        • 📖 第七部分:序列化系统 - 了解数据如何被打包传输


        本文档基于 Unreal Engine 5.5.0 Iris 源代码分析(源码目录:Engine/Source/Runtime/Experimental/Iris/)

        最后更新于 April 13, 2026
        On this page
        暂无目录