复制协议那篇提过条件过滤发生在比较之前(FilterChangeList),这篇把 ELifetimeCondition 全集逐个过一遍——每个标志位的判定条件比文档写的细,用错的表现是"客户端收到的值忽有忽无"。基于 UE5.3 源码(RepLayout.cpp、ActorChannel.cpp)。
COND_* 全集与判定逻辑
条件在服务端序列化之前过滤:不满足条件的连接,这个属性的 changelist 里根本不会出现。判定发生在 FRepLayout::FilterChangeList,依据是每个连接的 FRepChangedPropertyTracker。逐条过:
| 标志位 | 判定 | 典型用途 |
|---|---|---|
COND_None |
无条件,发给所有相关连接 | 默认值 |
COND_InitialOnly |
只随 actor 的初始 bunch 发一次 | 静态配置、出生点 |
COND_OwnerOnly |
只发给 actor 的 owner 连接 | 私密状态(背包、弹药精确值) |
COND_SkipOwner |
除 owner 外都发 | 表现层的旁观者视角 |
COND_SimulatedOnly |
只发给 simulated proxy(非 autonomous) | 服务器校正用的缓冲状态 |
COND_AutonomousOnly |
只发给 autonomous proxy | 技能冷却等只给操作者看 |
COND_ReplayOnly |
只在回放(demo)时发 | 观战系统的独立通道 |
COND_ReplayOrOwner |
owner 或回放 | 私密状态 + 观战兼容 |
COND_Custom |
走 IsCustomConditionActive 虚函数自定义 |
按队伍/阵营过滤 |
COND_SimulatedOrPhysics |
simulated 或物理体 | 物理表现的差异化 |
COND_OrphanedOwner |
owner 连接已断开但 actor 还在 | 5.3 新增,处理断线残留 |
最容易踩的是 COND_SkipOwner 和 COND_OwnerOnly 的判定主体:它们是按 UNetConnection 判的,不是按 APlayerController。"Owner"的归属链走 AActor::GetNetConnection,中间隔着 Pawn → Controller → PlayerState 的转换,如果你手动改过 owner 或用了非标准归属(比如 AI 控制但表现上归玩家),判定结果会和直觉不一致。
COND_Custom 是被低估的一个。AActor::IsCustomConditionActive 虚函数让你写任意逻辑(按队伍、按距离、按观察者模式),条件在 Filter 阶段逐属性调用——每连接每属性每 tick 一次,逻辑要轻,别在里面做射线检测。
RepNotify 的两侧语义
OnRep_Xxx 的行为在服务端和客户端完全不同,这个差异是状态机 bug 的重灾区:
- 服务端:
Set后OnRep只在值变化且比较通过时触发(比较阶段在服务端也跑)。如果服务端自己也需要 OnRep 的副作用(比如扣血时同步刷新 UI),得手动调,或把逻辑从 OnRep 挪出来。 - 客户端:值随 bunch 到达时触发,但收到和上次相同的值默认不触发(
REPNOTIFY_OnChanged)。改成REPNOTIFY_Always后每次到达都触发,哪怕值没变。
REPNOTIFY_Always 的典型场景:一个属性是"事件计数器"(每次事件 +1),客户端要靠 OnRep 的触发次数做事。值相同不触发的默认行为会让连续两次相同值的更新漏掉一次触发。但代价是带宽——值没变也发,增量协议的"只发变化"被打破了。
组合使用的实战判断
一个属性该配什么条件,判断顺序是:
- 这个信息有没有私密性? 有 →
COND_OwnerOnly起步,观战要看得加COND_ReplayOrOwner。 - 有没有"只对操作者"和"只对旁观者"的差异? 用
COND_AutonomousOnly/COND_SkipOwner对,而不是写两个属性。 - 有没有按动态规则过滤的需求?(队伍、观察者模式)→
COND_Custom,但注意它的调用频率。 - 剩下的默认
COND_None。
验证手段:net.DebugPropertyReplications 或 LogNetTraffic,对同一 actor 的两个不同条件连接抓 bunch,对比属性出现与否;OnRep 不触发的 bug 先查条件——值可能根本没发给你这个连接。
条件过滤是每层连接独立的,所以它和 dormancy、Push Model 组合时没有额外成本:过滤在 changelist 层面做,dormant actor 根本不产生 changelist,Push Model 只登记 dirty 属性。三层各管各的,叠加使用是正交的。