物理所有权:Desired Owner 不是 Committed Authority

自动关联目录:物理所有权:Desired Owner 不是 Committed Authority

核心判断:选出“应该由谁模拟”只是仲裁结果,不是权威已经转移。真正的切权必须证明目标端身份、拓扑、配置和执行帧已就绪,并经过冻结、提交、首个有效快照和旧源释放;约束连接的多个 Body 必须作为一个岛原子切换。

一、业务冲突:低延迟交互想靠近玩家模拟,一致性要求全组只有一个求解者

可抓取物体、受力机关和约束组合希望由交互玩家本地模拟,以缩短输入到运动的反馈;无人交互或 Owner 失联时,DS 又必须接管。自动所有权还要避免多人靠近时来回争抢,显式脚本指定和“正在持有”则必须压过距离策略。

早期逐物体思路容易产生两个窗口。第一,服务器宣布新 Owner 后,新客户端还没收到对象映射或创建 Body,旧端若立刻停算,物体会冻结;第二,新端先开始、旧端仍在广播,两个物理世界同时写 Transform,表现抖动。对于约束组问题更严重:若成员逐个切权,同一 Joint 的两端会短暂分属不同求解域,约束力没有共同时间步,结构会拉裂或爆炸。

因此所有权不是 OwnerKey 字段,而是一段分布式状态机。Desired Owner、Committed Owner、当前快照 Source 和本地 Solver Active 必须分别记录,不能由一个枚举隐式代表。

二、实际实现:纯仲裁器选候选,Handoff 状态机完成交接

自动仲裁被实现为无副作用策略:输入当前 Owner、候选玩家距离、最后切换时间和 Hard Hold,输出期望 Owner。默认策略有获取/释放半径滞回、最短驻留时间、抢占距离差和 Hard Hold 拒绝;显式玩家 Owner 不被自动策略覆盖。策略只做选择,不能直接修改 Body、网络状态或广播源。

单 Body 的服务器到玩家交接采用 make-before-break。授予后状态先进入 Pending,服务器继续模拟并广播,新 Owner 用 Seed 初始化本地 Body;只有收到身份、Owner 与 epoch 都合法的第一份上行样本后,才确认 Stable 并切换权威广播源。超时或玩家断开会把 Owner 收回服务器,epoch 单调递增,使旧 Owner 的迟到包永久失效。

约束岛使用更严格的屏障:Prepare 携带稳定成员身份、TopologyGeneration、ConfigRevision、目标域和每个 Body 的姿态/速度 Seed;目标端解析全部成员、Frame Provider 与配置后回 DomainReady。源端随后 Freeze 并确认,协调者才 Commit;目标 Solver 激活,成员 Owner 统一换 epoch。Commit 后观察者仍等待目标端第一份完整岛快照,避免刚切换就把尚未产出结果的目标当广播源。屏障超时、拓扑改变、目标域不可用和 Owner 失联都按整岛 Reclaim。

stateDiagram-v2
    [*] --> StableSource
    StableSource --> Preparing: desired owner changes / epoch++
    Preparing --> DomainReady: identities + config + frames ready
    DomainReady --> Frozen: source solver stops / ack
    Frozen --> Committed: target solver starts atomically
    Committed --> StableTarget: first valid island snapshot
    Preparing --> Reclaimed: timeout / owner lost / topology changed
    DomainReady --> Reclaimed: timeout / readiness lost
    Frozen --> Reclaimed: commit fails
    Committed --> Reclaimed: first snapshot timeout

三、值得保留:所有权策略、交接机制和物理解算已经解耦

仲裁器是纯函数这一点非常好。距离、滞回、驻留和抢占只是策略,Handoff、epoch、Seed 与超时是机制;未来改成基于交互焦点、输入者、服务器负载或平台能力的策略,不需要重写网络交接。显式 Owner 与 Hard Hold 独立建模,也避免自动策略破坏正在进行的交互。

Pending 阶段继续由旧权威输出,是典型的 make-before-break。它把可靠控制消息晚于对象物化、首包丢失和调度延迟都纳入协议,而不是假设 RPC 到达即完成。首个有效上行才确认新源,Owner epoch 又把玩家身份、一次具体授权与样本绑定,阻止旧授权复活。

约束岛屏障比逐 Body 切权更成熟。它不仅检查成员是否存在,还检查拓扑、配置和 Domain Provider 的执行帧是否 Ready;Freeze Ack 保证不会双解算,整岛 Seed 保证启动状态一致,Reclaim 则恢复服务器可用性。测试覆盖 stale epoch、错误 Sender、阶段乱序、配置变化、首快照与超时,验证的是协议状态而非单个函数。

四、结构性缺陷:单体与岛级协议并存,状态维度仍容易混淆

代码同时维护单 Body Pending/Stable、岛级 Barrier、Owner State、Client Owner View、Server Active Set、Local/Remote Owned 标志和默认 Transform 通道抑制。每个字段都有理由,但组合状态数量巨大;已有故障就来自普通属性复制或默认 CFrame 通道在远端 Owner 期间被重新打开,与专用物理流同时写 Body。

单 Body 交接与岛级交接存在两套相似但不完全相同的规则。若某个独立 Body 后来进入 Constraint 岛,必须明确谁退休旧 handoff、epoch 如何继承、默认通道何时关闭;否则同一 Body 可能同时出现在按件复制与按岛复制中。当前代码依靠多个 Set 和分支协调,缺少一个可查询的统一 Authority Record。

Commit 与“观察者已看到新源的首个完整快照”仍是两个时刻。实现已经记录等待首快照,但架构上应把 SolverCommitted、SnapshotSourceCommitted 与 PresentationReleased 显式分阶段,否则后续维护者容易在 Commit 后过早清掉旧缓存。超时基于 RTT 加上下界/上界是合理工程策略,但还需要把具体等待阶段和最后进展公开给诊断。

五、目标设计:一个 Authority Lease 覆盖 Body 与 Island

把独立 Body 视为单成员岛,统一进入 AuthorityLease。Lease 包含 SimulationUnitId、epoch、TopologyGeneration、ConfigRevision、DesiredDomain、SolverDomain、SnapshotSourceDomain 和 Phase。任何参与者只能凭匹配 Lease 写物理、上行或发布快照;普通属性同步也必须查询 Lease,而不是另有“是否抑制”布尔值。

bool AdvanceHandoff(Handoff& h, Event e)
{
    if (!Matches(e, h.unitId, h.epoch, h.topology, h.config))
        return Reject(StaleOrForeign);

    switch (h.phase) {
    case Preparing:
        if (e.kind == TargetReady) return EnterFreeze(h);
        break;
    case Freezing:
        if (e.kind == SourceFrozen) return CommitTargetSolver(h);
        break;
    case AwaitingFirstSnapshot:
        if (e.kind == CompleteTargetSnapshot) return SwitchSnapshotSource(h);
        break;
    }
    if (e.kind == Timeout || e.kind == OwnerLost || e.kind == TopologyChanged)
        return ReclaimWholeUnitToServer(h);
    return Reject(InvalidPhase);
}

Authority Inspector 应直接显示 Desired、Solver、Snapshot Source、Phase、epoch、剩余 Deadline、最后有效样本、拒绝原因和每个成员状态。任何双写都应通过 Lease 冲突计数暴露,而不是等画面抖动后人工推断。

六、决策规则:选择 Owner 看收益,完成切权看证据

条件仲裁结果交接规则
玩家靠近普通动态 Body可选择玩家域单成员岛也走 Prepare/Commit/首快照
玩家正在明确持有Hard Hold 保留当前 Owner其它自动候选不得抢占
脚本显式指定 Owner显式策略优先仍需 Ready 与 epoch,不允许直接改 Solver
多个 Constraint 成员整岛选择一个可行域所有成员同 epoch、同阶段原子切换
Domain Provider 帧未就绪Desired 可保留,不能 Commit继续旧源或服务器,输出 Pending 原因
拓扑或配置在交接中改变当前 Handoff 失效退休旧 epoch,基于新 Revision 重启
Owner 超时或断开服务器域整岛 Reclaim,旧包因 epoch 被拒绝
首个目标快照未到Solver 可已提交,Snapshot Source 不切观察者继续旧源/冻结,禁止混用成员

这段实践真正体现技术深度的地方,是把“谁更适合算”与“权威何时真的完成转移”拆开,并进一步承认 Constraint 会改变所有权粒度。后续优化距离策略或带宽之前,应先把统一 Lease 做实;只有一个地方能够回答当前谁可写、谁可发、谁可展示,双模拟和双通道才不会反复出现。