结构撤销:恢复子树必须携带一次性能力
自动关联目录:结构撤销:恢复子树必须携带一次性能力
核心判断:删除对象后的完整子树快照不是普通数据,而是能够重新物化对象、占用身份并改写层级的高权限能力;它必须绑定记录、会话代次和所有者,并且只能消费一次。
一、为什么结构 Undo 比属性 Undo 危险
属性撤销通常在既有对象上恢复旧值;删除撤销却要重新创建整棵子树。原对象标识可能已被占用,外部引用可能已经变化,旧脚本 VM 甚至可能已经销毁。若任何调用者都能反复提交旧快照,就能重复生成对象或把历史内容恢复到错误世界。
工程实践中的强项是:结构恢复不只携带快照,还携带与历史记录绑定的一次性凭证、会话 generation、随机 nonce、作用域和脚本状态所有者;重置会话会使旧凭证整体失效。恢复后还维护“记录标识到当前存活标识”的 lineage,承认重建对象不一定保留原运行时身份。
二、还需补齐的证明
凭证能证明谁可以恢复,却还应证明“恢复的就是当时记录的那份不可变内容”。目标记录应冻结快照摘要、父位置与引用策略;运行时对象指针、临时句柄和当前选择不应进入快照。
struct RestoreCapability {
HistoryEntryId entry;
EditSessionId session;
Generation generation;
Nonce nonce;
Hash snapshotHash;
bool consumed;
};
RestoreResult Restore(Capability& cap, Bytes snapshot) {
Require(CurrentSession(cap.session, cap.generation));
Require(!cap.consumed && HashOf(snapshot) == cap.snapshotHash);
ValidateDetachedTree(snapshot); // 身份、环、父资格、引用
cap.consumed = true;
return MaterializeAndRecordLineage(snapshot);
}三、UE 侧的提交顺序
恢复应先在脱离 World 的数据图上做拓扑与引用校验,再按稳定顺序创建 UObject/Actor,最后统一注册组件、附着、标脏和刷新编辑视图。若边创建边校验,RegisterComponent、构造脚本和委托可能在失败前已经产生外部副作用。
四、验证与反思
必须覆盖重复消费、跨文档消费、重置后消费、篡改快照、标识冲突、父节点已删除和恢复中途失败。失败后既不能留下半棵树,也不能提前消费一张仍可安全重试的凭证。
这项实践值得总结为通用原则:凡是能从历史数据重新产生当前世界对象的入口,都应按 Capability 设计,而不是按“反序列化辅助函数”设计。
许可协议:CC BY
作者:Davids
本文链接:https://hustjjd.github.io/b4b490eb.html
更新于:2026年10月14日