组相关性:退出一个连接的视野不等于ActorTearOff
自动关联目录:组相关性:退出一个连接的视野不等于ActorTearOff
核心判断:组相关性是“连接订阅哪些 Actor”的关系;
TearOff是“服务器永久停止该 Actor 的权威复制”的全局生命周期决定。用后者实现前者,会把局部退订扩大成全局状态变化。
一、业务冲突:同一 Actor 只对一部分连接常驻
队伍标记、私人区域实体和观察者专属信息不适合全连接 Always Relevant。连接可能同时加入多个组,也可能晚于 Actor 创建才加入;退出一个组只能影响该连接,不能让其他连接丢失同一 Actor。
二、实际实现:每连接挂组节点,方向正确
工程为连接动态创建组节点,Actor 注册时遍历现有连接,把它加入匹配节点;Gather 时直接返回节点列表。这避免每帧对所有 Actor 执行组判断,组内对象也可以对成员连接保持常驻。
但三个实际控制流破坏了这个模型:节点查找遇到第一个不匹配组就提前返回,导致多组连接只能可靠命中首个节点;连接晚加入组时只创建空节点,没有补入此前已注册 Actor;退组时遍历节点 Actor 并调用 TearOff,把单连接退订变成 Actor 全局停止复制。Actor 自身换组也缺少完整的旧组移除、新组加入事务。
三、目标设计:共享组注册表 + 每连接订阅
void JoinGroup(Connection& c, GroupId g) {
Snapshot s = Groups[g].Freeze(); // members + revision
c.Subscriptions.Add(g, s.revision);
c.GraphNode(g).ReplaceWith(s.members);
ApplyDeltasAfter(c, g, s.revision);
}
void LeaveGroup(Connection& c, GroupId g) {
ActorSet leaving = c.GraphNode(g).Members();
c.Subscriptions.Remove(g);
c.RemoveGraphNode(g);
for (ActorId a : leaving)
CloseChannelFor(c, a, RelevancyLost); // 只影响该连接
}全局注册表维护 Group -> ActorSet + Revision,Actor 换组是一次原子 Move;连接节点只表达订阅。Join 使用快照 Revision 加后续 Delta,避免快照期间增删丢失。一般情况下移出 RepGraph 候选后让 UE 相关性超时关闭 Channel;若业务要求立即消失,也必须使用连接级 Channel/销毁通知,不能调用全局 TearOff。
四、TearOff 应何时使用
TearOff 适合服务器发送最后状态后不再控制该 Actor,例如进入客户端自行模拟的最终阶段。它不是隐藏、退队、离开兴趣范围或节省某一连接带宽的 API。普通销毁又是另一种语义:服务器结束 Actor 生命周期,并由网络销毁传播。
五、必须通过的矩阵
| 场景 | A连接 | B连接 | Actor全局状态 |
|---|---|---|---|
| A加入组,B未加入 | 可见 | 不可见 | 继续复制 |
| A同时加入两个组 | 两组均可见 | 不受影响 | 继续复制 |
| Actor先存在,A后加入 | 快照后可见 | 不受影响 | 继续复制 |
| A退出,B仍在组内 | 关闭A的Channel | 仍可见 | 继续复制 |
| Actor换组 | 原组连接退出 | 新组连接进入 | NetGUID与状态连续 |
线上指标应包括组 Actor 数、订阅连接数、Join 快照耗时、快照后 Delta 堆积、连接级 Channel 关闭数、误用 TearOff 次数和多组查找失败数。