客户端工程决策手册:从问题到契约与证据

自动关联目录:客户端工程决策手册:从问题到契约与证据

这不是另一篇架构总览,而是一张入口表:先把症状归类,再找到必须建立的契约和可以证明结果的证据。具体实现、反例和取舍仍以链接文章为准。

一、先定位问题属于哪一层

客户端问题
├─ 数据不一致:谁是真相?身份和版本是什么?
├─ 生命周期错位:事实应该活过 Session、World、Actor 还是一次请求?
├─ 异步竞态:回调回来时,它是否仍有提交权?
├─ 线程边界:对象存活、数据同步和业务有效性是否被混为一谈?
├─ 半次变更:哪些写入必须作为一次事务提交?
├─ 多端分歧:Client、DS、编辑器分别拥有哪部分权威?
├─ 规模失控:是否把逻辑对象过早物化成 Actor、组件或重资源?
├─ 结果不可证:用什么确定性证据说明功能、性能和恢复正确?
└─ AI 自动化:模型能提出什么,宿主允许什么,谁负责验收?

不要从“该用哪个 UE 类”开始。先回答真相、身份、生命周期、提交边界、权威和证据,再选择 Actor、Subsystem、GAS、Mass、ISM/HISM、Slate 或工具框架。

二、症状到决策的路由表

现场症状首要问题优先契约深入阅读
同一个 ID 在不同 World、VM 或重建后串台身份是否包含运行域和代次稳定内容身份 + 本地句柄 + Domain + Generation对象身份、角色重建
初始化完成却偶发不可用内部构建完成与外部可用是否混为一谈Readiness DAG + Session/World Generation启动就绪、可玩就绪
异步加载后对象已销毁或需求已变化回调是否仍有提交权Operation Scope + Request/Owner/Generation 校验异步提交、动画生命周期
后台归档偶发错乱,关闭时又与任务竞争保活是否被误当成线程安全与操作所有权Pure C++ Job + Serial Executor + Commit Permit归档异步
退出时偶发丢记录,Pause 后线程无法恢复队列是否缺少关闭、排空和 Join 协议Close + Drain/Abort + Event + Backpressure后台写盘
编辑器插件重载后回调重复或打开资产崩溃全局注册是否仍能调用旧模块实例或代码Delegate Handle + Registration Ledger编辑器模块卸载
系统选择器的旧结果回到了新页面平台回调是否携带本次请求身份RequestId + Caller Scope + Single Flight原生回调
脚本超时保护本身出现竞态或随机崩溃监测线程是否越权修改 VMAtomic Deadline Signal + Same-Thread Safepoint看门狗线程、脚本预算
一次编辑或导入失败后留下半成品哪些写入属于同一原子意图ChangeSet + Preconditions + Prepare/Commit/Compensate编辑事务、写入回滚
超时后不知道远端动作是否发生“没有响应”是否被错误当成“没有发生”Effect Handle + Idempotency Key + Reconcile发布恢复、取消语义
Client 看着正确,DS 却不同意表现、预测和权威是否分层Command/Receipt/State/Event + Authority Epoch交互同步、角色对称
某类 Actor 偶发不出现或换 Owner 后仍发错连接路由与依赖边是否随实例语义迁移Route Revision + Owned Replication Edge复制路由、复制依赖
玩家高速移动、瞬移或跨区后短暂看不到对象空间 Cell 的陈旧窗口是否有运动包络Conservative Cell + Event Fast Path + Overflow空间Cell
每帧复制数量稳定,DS 网络耗时仍尖峰数量是否被误当成字节和序列化预算Per-Connection Cost Budget + Initial Quota + Age复制预算
退组影响其他玩家,或晚加入组看不到既有对象连接订阅是否误用了 Actor 全局生命周期Group Snapshot Revision + Connection Channel Close组相关性
Fast Array 偶发读错 Key、漏变化或重复触发脚本容器地址、Dirty 与事件语义是否混用Stable Key/Index + Typed State + Separate Event LogFastArray
对象数量增长后 Actor、Tick、复制一起爆炸逻辑身份是否被绑定到最重表示数据事实 + 选择性物化 + 空间工作集海量对象、Actor迁移
Draw Call 降了但 CPU、内存或编辑能力恶化优化是否只看单一渲染指标Representation Contract + 能力与成本矩阵网格合批、渲染伸缩
AI/NPC 数量增长后 DS 帧时间失控表现 LOD 是否被误当成模拟 LOD模拟分级 + 共享调度预算 + 玩法不变量AI规模化、AI选敌
预览看起来正确,正式内容却不可用预览是否越过导入、平台和预算门禁Candidate -> Validate -> Build -> Promote生成资产、资产全生命周期
Agent 能调用工具,但影响范围不可控Schema 是否被误当成授权Capability Grant + Canonical Resource + Effect Set工具权限、脚本隔离
自动化“完成率”提高,人工返工却更多是否只评价最终回复冻结任务 + 确定性 Gate + 修复/审查成本Agent评测、自动化验证

三、可复用契约

契约最小字段解决什么不解决什么
StableHandlelogicalId, domain, generation跨重建引用和迟到结果拒绝不证明目标当前可用
RevisionedSnapshotrevision, lineage, digest, payload冻结一次保存、构建或恢复输入不负责外部副作用提交
OperationScopeoperationId, ownerGeneration, cancelTree, deadline统一异步所有权和取消传播不能撤销已提交效果
ClosablePipelinestate, queueBytes, shutdownPolicy, flushFence约束生产准入、Drain/Abort 与 Join不保证底层介质永不失败
RegistrationLedgerowner, removalToken, phase, removed保证模块按逆序撤销全局注册不判断回调业务逻辑正确性
NativeRequestrequestId, kind, requester, session, deadline把跨语言结果交给正确调用方不验证载荷内容安全
ReadinessGraphnode, prerequisites, generation, evidence区分 Decode、World、角色和可见就绪不替代业务权威判断
ChangeSetmutations, preconditions, verification, compensation原子表达跨文件和编辑器意图不能让不可逆外部动作变可逆
EffectReceiptidempotencyKey, handle, state, resultDigest对账远端与长任务的未知结果依赖工具提供查询能力
AuthorityLeaseowner, epoch, deadline, scope拒绝旧所有者和迟到释放不等于永久持久状态
RuntimeOverlaybaselineRevision, sessionId, overrides隔离试玩和运行时变化不自动合并回创作数据
ArtifactManifestlogicalAsset, sourceRevision, target, toolchain, digest证明制品属于哪个输入和目标路径本身仍不是身份
RepresentationBindinglogicalId, representationKind, host, generation在 Actor、实例和数据记录之间切换不保证不同表示语义一致
CapabilityGrantprincipal, effects, targets, budget, expiry把授权限定到具体资源与影响Schema 正确仍不代表语义正确
EvidenceBundlegates, receipts, runtime traces, environment让编译、运行、性能和审查结论可追溯证据缺失时不能靠解释补齐

契约之间的依赖关系见 目标架构:按事实、执行、投影与制品划分边界。

四、设计前检查单

4.1 真相与身份

  • 哪份数据是创作事实、运行时权威、派生索引和表现投影?
  • ID 在跨 World、跨进程、对象复用和重建后是否仍唯一?
  • 引用是否携带 Revision、Domain 或 Generation,而不是只存裸指针和名字?
  • 缓存、Manifest、分类表能否从权威事实重建?

参考:派生索引、资产身份、跨语言合约。

4.2 生命周期与异步

  • 事实应归 GameInstance、World、Actor、组件、播放会话还是一次操作?
  • Travel、PIE 重启、对象池复用、热重载和断线重连会改变哪个代次?
  • 每个异步结果提交前是否重验需求、所有者、Revision 和运行角色?
  • Cancel、Timeout、Failed、Unknown Outcome 和 Rolled Back 是否是不同状态?
  • UObject 保活、工作线程访问许可和结果提交权是否分别证明?
  • 后台消费者能否被唤醒,并在关闭时明确 Drain 或 Abort 后 Join?
  • 模块、页面或 World 结束时,委托、Timer、Registry 和原生请求是否失效?

参考:生命周期归属、归档异步、后台写盘、编辑器模块卸载、原生回调。

4.3 UE 表示选择

  • 该对象必须拥有 Actor 生命周期、网络通道、独立碰撞和脚本宿主吗?
  • 只有可见、可交互或进入相关性的对象,是否可以按需物化?
  • ISM/HISM、合批或代理表示失去了哪些编辑、碰撞、材质和变换能力?
  • DS 是否需要渲染资产,Client 是否被迫承载只属于权威模拟的数据?

参考:网格合批、碰撞表示、跨目标降级。

4.4 网络与玩法权威

  • 客户端发送的是意图、预测还是事实?DS 会重新证明哪些资格?
  • 状态、瞬时事件、命令和处理回执是否使用了不同通道?
  • 晚加入、重连、丢包、乱序和所有权迁移后如何恢复?
  • 视觉、动画和音频能否被纠正或取消,而不反向决定玩法结果?
  • 路由、每连接相关性、调度优先级和实际传输预算是否分层?
  • 连接离开兴趣集合时,是否误用了 TearOff 或全局 Destroy?
  • 数量限流是否还验证了 Initial Channel、Bits、CPU 和最大等待年龄?
  • Fast Array 承载的是可恢复状态,还是必须逐次消费的业务事件?

参考:交互资格、物理所有权、复制路由、复制预算、组相关性、FastArray。

4.5 性能与证据

  • 成本是否按 Decode、Spawn、初始化、Tick、复制、渲染和回收阶段拆开?
  • 预算约束的是任务数量,还是主线程时间、队列年龄和不可抢占长尾?
  • 工作集是否绑定空间候选、活跃对象和变化集合,而不是每帧全量扫描?
  • 指标是否包含 P95/P99、连续慢帧、阶段身份和 Client/DS 真实负载?

参考:主线程预算、集中调度、性能基准。

4.6 AI 辅助开发

  • 模型输出是候选、假设、观察还是确定性证据?
  • 上下文事实是否带来源、版本、信任级别、有效期和数据策略?
  • 工具权限是否绑定资源、Effect、次数、预算和过期时间?
  • 变更是否经过 Schema、编译、Client/DS 运行、性能和人工审批门禁?
  • 评测是否记录返工、审查、回滚、取消、未知结果和资源成本?

参考:能力边界、上下文装配、Agent评测。

五、伪代码目录

需要实现的机制推荐入口
跨域对象句柄与代次校验对象身份
快照保存、代际提交和多文件 Manifest保存快照、多文件保存
多关卡切换与大世界 Cell Prepare/Commit切换关卡、多关卡不是大世界
资产获取、安装、驻留和选择性物化共享下载、缓存安装、运行时驻留
动画会话、层叠姿势与网络恢复动画生命周期、姿势合成、动画同步
物理属性重建、权威迁移和快照原子应用物理属性、物理所有权、物理快照
交互资格、Lease、预测与状态恢复交互资格、交互占用、交互同步
AI 感知、选敌、寻路、攻击和池化AI感知、AI选敌、AI寻路、AI战斗、AI池化
复制路由、空间相关性、连接预算与增量状态复制路由、空间Cell、复制预算、组相关性、FastArray
输入捕获、相机恢复和 UI 增量投影输入路由、相机生命周期、UI物化
脚本 Capability、预算与热重载清理脚本隔离、脚本预算、脚本热重载
UObject 后台任务、可关闭线程和同线程 VM 中断归档异步、后台写盘、看门狗线程
编辑器模块注册和原生平台请求关联编辑器模块卸载、原生回调
导入冻结、质量门禁和可重现工具分发导入提交、质量门禁、工具分发
Agent ChangeSet、权限、取消和评测写入回滚、工具权限、取消语义、Agent评测

六、判断方案是否真的更好

一个方案只有同时回答下面五个问题,才算架构改进,而不是换了一组类名:

  1. 守住了什么不变量:身份唯一、权威唯一、创作数据不被试玩污染,或同一 Revision 的多目标制品不互相覆盖。
  2. 缩小了什么工作集:从全部对象缩到空间候选、活跃集合、变化集合或明确相关性。
  3. 把失败变成了什么状态:可重试、可补偿、结果未知、需要对账,还是必须人工裁决。
  4. 付出了什么成本:更多元数据、状态机、存储、延迟、工具复杂度或内容约束。
  5. 拿什么证明:静态契约、单元测试、真实 Client/DS 运行、故障注入、平台构建、性能分位数和人工审查。

如果回答只能落在“更优雅”“更通用”或“模型认为正确”,就还没有完成工程决策。