统一安全落位设计(延期重构)
先准备目标,再提交位移;有遮罩的普通地面传送,必须站稳且模型复位后才显露。统一的是安全落位能力,不是把坠落处罚、死亡复活、跨图和 Seq 演出揉成一个业务流程。
当前不做大重构。局部补丁仅调整普通黑屏消失条件:现有落位 → 站稳/逻辑 Idle/动画 Idle/Mesh 就绪观察 → 撤罩;不改网络接管、目标预加载、跨图、Seq3 或 V2。
新增等待只观察就绪条件,不再落位;这不等于全链已做到单次权威提交。上游提前移动、重复落位与统一提交仍留待以后重构,不能宣称已解决。
当前 Mesh 判据是在一次 Reset 后,观察多个不同 PoseTick 保持 ground、逻辑 Idle、动画 Idle 和静止的表现更新;不是严格的本次 OBR 最终求值回执,仍需实机验收。
原生 GroundFacts.SampleFrame / Revision 已确认可从 UE.GameMovementMiddlewareComponent.GetActorGroundFacts 读取;接地采样可读不等于 Mesh/OBR 复位已有严格完成回执。
以下第 1–13 节完整保留原设计供以后重构参考,其中“本次”“计划”“待确认”均指延期的完整方案,不是当前补丁范围或实施授权;未来重启仍须审阅和技术验证。
1. 本次范围与结论
- 本次纳入:同图无缝、范围内黑屏、UI load;坠落/边界调用的技术落位段;本地 ServerMove 接管、取消、上行隔离;跨图的安全交接和实体就绪门。
- 保持不变:处罚规则、扣血、复活点选择、地图/副本协议、演出资源、Seq1/2/3 叙事时序、相机设计。死亡复活保留原业务 owner。
- 不做:服务端代码/协议改动、全项目移动系统重写、V2 与普通 Binder 合并、空类和注释草稿顺手清理。
- 跨图建议一起处理,但分阶段落地:共享目标查询、角色落位验证、事务身份和资源交接;不共享整条命令链,也不统一所有 LoadDone 的发送时机。
2. 三种同图模式:只允许 UI load 主动加载
| 模式 | 目标准备 | 遮挡与落位 | 移动模式 |
|---|---|---|---|
| 无缝 | 只检查现有目标碰撞/落点;不创建目标流送源,不启动加载命令 | 不主动黑屏、不打开 Loading;目标已可用才瞬移并请求模型复位 | 不主动调用 GameSetMovementMode,不以传送名义统一锁重力 |
| 范围内黑屏 | 与无缝一致:只查询已加载的目标 | 等黑屏真正遮住 → 单次落位 → 站稳、Idle、Mesh 就绪 → 撤罩 | 保持前轮要求:普通黑屏不设置/恢复 MovementMode |
| UI load | 玩家留在原区域;独立目标源加载目标 Cell/DataLayer,并验证碰撞和可站立位置 | Loading 已遮挡 → 目标准备好 → 单次落位 → 站稳与模型就绪 → 关闭 Loading | 允许必要的临时移动保护,但不默认全程 Walking+零重力 |
“不加载”指不主动启动目标预加载,不关闭 World Partition 跟随玩家的正常流送。距离只能作为表现偏好,不能证明目标可站;现有 100m 阈值不再作为安全判断。
无缝/黑屏的即时预检若发现地形不具备落位条件,显式切换为 UI load,记录升格原因,再启动目标流送。这不算“黑屏模式偷偷加载”。若产品要求绝不升格,则保持不移动并走故障等待,不能强行传送。目标已加载但位置非法,不能靠重复加载假装修复,应由业务选择合法备用点或保持失败保护。
模式、业务、表现分开表达
- 业务来源:普通传送/坠落处罚/边界/跨图/复活。
- 落位策略:无缝/范围内黑屏/UI load。
- 表现参数:黑白遮罩、材质淡变、飞天遁地、Seq 演出。
现有 ETeleportType 是业务枚举,不替换为三种策略。BLACK/WHITE 是遮罩颜色,FADE_IN_OUT 是表现,均不直接代表 UI load。普通无演出入口先按既有意图选无缝/黑屏,再作就绪检查;已有明确 Loading 意图保留 UI load。配置到通知的完整映射需在最小验证中确认,不能猜测默认字段的含义。
3. 整体扫描发现的关键缺口
| 已读代码事实 | 影响 | 本次处理 |
|---|---|---|
| CombatMessage 位移前发 LocalPlayerServerMoveApplied,BaseMoveSync 后续还会再发;传送接管后 Base 分支仍可直接移动 | “目标加载完再移动”在上游就被打破,同一请求可能落位两次 | 区分收到目标与真正提交;在任何本地 Actor 写位置之前决定接管;Applied 只表示实际提交 |
| SameScene 当前在流送成功后先 SafeMoveTeleport,再 WaitForGroundReady | 没有提交前碰撞门,也没有实际角色 Idle 门 | 拆成准备、提交、收敛三个阶段 |
| 无缝/Fade 当前仍调用快照流送;SceneLoadingTeleportCmd 还含事件、场景信息和源交接 | 与新三模式语义不一致;旧命令不是纯等待工具 | 拆开纯目标准备与必要业务通知,不能整条旧命令照搬 |
| CommandBail 会进入结果回调;坠落和 WithStreaming 的部分回调未检查 result | 旧请求取消后仍可能继续传送、锁源、关新请求遮罩 | 结果状态+请求代次+资源 owner 三重检查 |
| 九点射线不检查完整胶囊空间、实际接地、姿态;窄平台可能永远不通过 | 射线全命中不是“已站稳” | 保留必要诊断,安全契约改为角色尺寸相关的落点查询与实际接地证据 |
| ResetOffsetRootBone 原生只置请求标志,动画 NativeUpdateAnimation 才消费 | 调用返回/固定 200ms 不是模型复位完成证明 | 动画实际更新与模型位置验证先做最小实测,禁止业务读取消费接口抢走请求 |
| 主流送源会跟随玩家;目标源被场景预加载和旧保活共同使用 | 玩家未移动时提前交接,主源可能又跟回旧区域 | 目标源保活至主源确认覆盖目标,旧 owner 不能释放新 owner 的源 |
| 跨图 After 先 LoadDone 握手,后处理/创建角色 | 把 LoadDone 移到“新角色 Idle 后”会产生等待环 | 协议握手保持原位,角色安全与显露另设阶段 |
以上是静态代码核查结论,不是原始 Mesh 故障的完整运行时因果证明。前轮二次 Reset+200ms 补丁尚无游戏复测,不能作为新架构已验证的依据。
4. 统一时序:准备 → 唯一提交 → 站稳 → 显露
“单次落位”是每个已接管请求只有一次权威传送提交,普通物理贴地修正不是第二次传送。无缝没有遮罩可等,必须先满足可立即显示的条件;若动画复位首帧仍会露出拖尾,就不能宣称无缝质量已达标,须先完成动画验证或按审阅后的规则升格。
共享能力的最小结构
保留 TeleportController 门面、SameSceneFlow 编排、Presentation 表现和 GroundProbe 查询。计划新增一个轻量
TeleportPlacementFlow(暂定名,待验证后确定)承接请求身份、提交与
Settling,供同图和坠落复用;跨图调用其查询/验证能力,不强行接管整条场景链。不另建一套通用任务框架或全局服务注册体系。
5. “站在地面且 Idle”如何判断
| 检查层 | 普通地面传送的要求 | 不能替代它的东西 |
|---|---|---|
| 提交前目标 | 可用碰撞、可走支撑面、角色胶囊不穿透、落点修正量在业务许可内 | 只有 WP Activated;远处一条射线命中 |
| 提交结果 | 原生位移成功且实际位置符合候选落点;请求/角色身份仍一致 | SafeMoveTeleport 返回 void;FixBornLocation 返回候选成功 |
| 实际接地 | 提交后新鲜物理 GroundFacts 表明接地,胶囊脚底与支撑面合理 | 旧位置缓存;强设 MOVE_Walking |
| 逻辑姿态 | 地面 Phase+NotMoving,速度在验证确定的容差内;无技能/演出继续驱动 | 单独 NotMoving:受击、技能、死亡阶段也可能映射到它 |
| 模型 | 复位请求经过实际动画更新,渲染相关 Mesh/根骨偏移回到角色合法基准附近,连续采样稳定 | 调用 Reset 就当完成;固定 200ms;仅 Mesh 组件 transform(根骨偏移可能仍在) |
不硬编码“Mesh 和 Capsule 原点相同”:角色合法相对偏移、体型和动画根骨均不同。采样接口、帧数、容差通过 Demo 确定。现有 TS 没找到完整复位回执;若无法可靠观测,需要动画模块提供支持建议,在获得授权前不修改 Native 或生成 Typing。
普通黑屏不强设 MovementMode。落位后给原生物理正常刷新机会,由实际接地推动 Grounded/NotMoving。如果必须改模式才走得通,应报告验证失败并重新审阅,不能偷偷加回 Walking。
原位置安全不等于永远不需要保护
- 原地站稳:停止输入/残余移动,保留地面物理;验证后移除无必要的重力锁。
- 空中/坠落:禁输入不能阻止继续下落,需独立姿态保护;不能承诺全流程零保护。该保护不应借“黑屏表现”偷偷修改 MovementMode。
- 技能/Root Motion/座位/载具/水中:先由所属业务退出或交还控制;本次不把它们全部改造成站立 Idle。无法安全接管时不并发写位置,保留原专属流程。
- Settling:解除会阻止接地和动画更新的准备期冻结,但继续保留输入、上行和遮挡屏障;禁止关掉全部 Tick 再等 Idle。
6. 网络、取消和流送源:必须同时补齐
6.1 本地位移接管与上行屏障
- CombatMessage 在本地 Actor 的 Enabled/非 Enabled 直写分支之前完成路由;未创建角色交给创建流程。不得用两个 undefined 相等判断本地玩家。
- 只接管已审定的传送 reason。ROLLBACK 是权威纠正,REVIVE 是复活业务,不用“除 ROLLBACK 外全部都是传送”的黑名单。
- 收到权威目标可以更新协议所需的接收版本,但必须区分 pending 与 committed,不能让“新版本+旧位置”形成上行样本。
- 屏障覆盖普通 Tick 采样、技能强制采样、最终打包;单独 SetEnableMovementSync(false) 不够。恢复前清旧队列、速度和 Reporter 缓存,恢复时的即时采样必须使用已提交目标。
- 本次所有者覆盖的模拟移动、DeferredMoveTo、演出和座位写位置必须协调;不直接全局停掉所有实体。遇到新的权威纠正/重连/切场,作废旧事务后交接。
6.2 通知乱序的真实能力边界
WorldTeleportSelfSCNotify 只有表现/方向等字段,没有共同 requestId 或 serverMoveVersion。客户端 generation 可以阻止旧异步回调,但不能凭空证明表现通知属于哪个 ServerMove。设计以权威目标包建立事务,表现通知只在业务上下文唯一、关联条件成立时使用;不能拿“最近一份快照”无条件拼接。模糊关联时走保守安全表现,不承诺演出配对绝对正确。最小验证必须覆盖倒序和连续通知;若产品要求并发请求严格配对,需另提服务端协议建议,本次不改服务端。
6.3 取消、失败、交接是不同结果
- 新请求取代旧请求:先增加 generation,再取消监听/计时器/执行器;所有 Promise、result、黑屏回调、延迟回调复核 owner。
- Bail/Fail 不能进入成功落位;终态区分 Completed、Superseded、Cancelled、Failed、HandedOver。
- 只释放自己持有的遮罩、输入、缓存、目标源和移动保护。新请求仍需要的资源显式交接,不先全开再重新锁。
- 实体销毁后不对 Global 当前的新角色恢复旧请求保存的状态;进入新世界后旧 version 不参与当前会话比较。
6.4 目标流送源的所有权
UI load 获取目标源 owner → 设置目标位置/层并激活 → 等 Cell 和碰撞 → 提交玩家 → 主源跟随并确认覆盖目标 → 释放目标源。主源未接住时不按固定 30 秒认定交接成功。目标源是全局共享资源,场景预加载、跨图和传送保活也必须遵循同一 owner 规则;不同时新增第二套互不知情的锁。复制 StreamingSourceInfo 不等于复制锁状态,也不等于主源已覆盖目标。
7. 坠落重生:处罚入口不变,复用落位
触发/规则/请求 → 获得合法重生点 → 选择黑屏或 UI load → 共享安全落位 → 业务完成。
- 保留安全点记录、坠落/边界触发、处罚请求、扣血语义、个人复活点与备用点选择、朝向及业务事件。
- 重生点已经加载且可站:黑屏模式,不再无条件启动流送。
- 重生点地形未就绪:按待确认规则显式升格 UI load,先准备再移动。已在坠落中的原地保护单独验证。
- 备用出生点同样经过完整目标查询;不能第一次探地失败后直接跳过备用点验证。
- IsInRebornProcess 覆盖从请求接受到真正业务收尾,不能安排 timer 后就提前设 false。
- 现有 500ms 可保留为表现最短黑屏时间;实际关闭取“最短表现已满足”与“落位安全已满足”的交集。新增 200ms 不再作为安全证明,待替代验证通过后去掉其安全依赖。
- 不把坠落改成 ETeleportType.Respawn,不凭空补普通同图 LoadDone;沿用本业务的协议和完成事件。
死亡复活仍由 PlayerDeath/RoleRevive 负责生命状态、溶解和 Montage。边界入口若本就转坠落,可随该适配获益;其提示和触发条件不改。
8. 跨图:共享安全门,不改握手含义
| 协议/事件 | 保持的责任 |
|---|---|
| 同图 LoadDone | 关联 serverMoveVersion;普通同图路径安全条件满足后按原栅栏规则发送/重试 |
| 跨图 LoadDone | 关联 world/level;场景准备后握手,之后可能才获得角色实体,不能后移到新角色 Idle |
| SceneLoadAnimDone | 保持演出完成语义,不当作物理落位回执 |
Seq3/V2 是受控例外:演出可能需要降落,不强制在起播前普通站立 Idle。起播要求资产/绑定/首帧姿态和目标环境就绪;演出归还控制时再验证普通角色恢复。普通 Binder 与 V2 私有 Binder 各自保留,只统一请求身份和交接约束。恢复同步前先清速度/旧样本,避免即时采样带走演出残留。
9. 等待失败策略:不伪成功,也不擅自退登
沿用 既有“异常只记录、不自动退登”决策。安全未成立不得因计时到点而移动、撤罩或发同图成功;跨图握手不属于这类物理完成回执。每个异步阶段都必须可取消并有诊断截止点,截止后进入明确的故障保持状态或交还 owner,而不是静默挂住 Promise。
- 提交前失败:不移动到未准备好的目标,不自动恢复上行报告旧位置;记录目标/失败原因,等待可验证的恢复、新权威请求或既有退出流程。
- 提交后失败:继续保护并告警,不擅自回滚到可能已卸载、且服务端不认可的旧点。
- UI 打开失败/超时:不把它当“已遮住”;只在真正获得有效遮挡后继续提交。
- 旧协议缓存 15 秒 TTL 与安全等待不是同一时钟;回放入口必须仍受事务校验约束,不能靠缓存一直挡住所有竞态。
- 现行每 100 秒安全告警可保留;是否新增“重试/返回登录”交互另行审批,不在本轮暗加。
代价明确:若目标永远不可用且没有新请求或外部恢复,用户仍可能长时间被遮挡。这个产品取舍未因本方案自动改变。
10. 文件职责与建议改动边界
工作区:D:\zhaozibo_071\Source\Client\TypeScript。以下为本轮读取时的职责快照;全部是计划,不是已提交的代码改动。
| 传送目录文件(15 个) | 处置 |
|---|---|
| TeleportController.ts | 保留门面;提供本地请求接管和跨图交接接口 |
| TeleportDefine.ts / TeleportModel.ts | 独立三策略定义;不可变请求快照;pending 与 committed 区分 |
| TeleportSameSceneFlow.ts | 编排三模式,不再一律启动流送;接入共享提交和收敛 |
| TeleportGroundProbe.ts | 准备期纯查询;实际角色接地/候选位置契约分离 |
| TeleportPresentation.ts | 只管理遮罩/Loading/淡变的 own token 与真正显露条件 |
| TeleportTransitionFlow.ts | 删除无缝无条件流送路径;表现升格独立于安全完成 |
| TeleportSequenceFlow.ts / TeleportSeq3Binder.ts | 只适配目标事务/角色控制权交接,不改演出资源和叙事时序 |
| TeleportV2Flow.ts | 保留独立实现,只适配共享边界;本轮为职责与关键路径扫描,非逐行演出审计 |
| TeleportEntityBufferController.ts | 明确缓存所有权和 TTL 后回放仍须校验,不充当上行屏障 |
| LevelEnterConfig.ts | 保留配置适配,不顺手改配置语义 |
| TsTeleportLibrary.ts / TeleprotLoadScene.ts / TeleportView.ts | 空壳或注释草稿,不纳入现行运行链,不顺手清理 |
| 关联路径组 | 必须处理的边界 |
|---|---|
| Module/CombatMessage/CombatMessageController.ts;World/Entity/Common/BaseMoveSyncComponent.ts | 提前移动、非 Enabled 直写、事件语义、WithStreaming 取消与提交结果 |
| World/Entity/Common/ComplexMoveSyncComponent.ts;Common/Move/MoveSyncReporter.ts | 普通/强制采样/最终打包屏障;调用点只在必要时最小适配 |
| World/Entity/MoveSync/MoveSyncController.ts | 与当前事务冲突的延迟位置队列清理或作废 |
| World/Entity/Character/SceneItem/FallingRebornController.ts | 只迁移技术落位,修正防重入生命周期与备用点验证 |
| World/SceneLoading/Commands/SceneLoadingCommandHelper.ts;SceneLoadingTeleportCmd.ts | 分离纯准备与场景副作用;结果和 Reset 仅作用于自身 executor |
| World/SceneLoading/StreamingSourceInfo.ts;相关 NewScene/Move/WaitWp 命令 | 目标源 owner、保活和主源覆盖确认;协调现有场景预加载 |
| SceneLoadingAfterCmd.ts;SceneLoadingWpMapCmd.ts;实体创建/激活链 | 保留跨图握手;创建前保护、创建后绑定与验证;不阻断 EndClientLoading→Seq3 |
| BaseActorComponent.ts;BaseCharacterComponent.ts;MovementMiddlewareComponent.ts;CharacterPlacementQuery.ts | 优先复用查询与原生结果;必要适配限定本次落位调用,不改变所有普通移动语义 |
关键证据定位(本次读取行号)
- TeleportSameSceneFlow.ts:197–228,先 SafeMoveTeleport 后 WaitForGroundReady;575–595,generation 先递增的取消模式。
- TeleportTransitionFlow.ts:447–501,无缝路径与距离升格;TeleportGroundProbe.ts:36–127,九点判据、纯查询和无限轮询。
- CombatMessageController.ts:805–882,目标快照/前置 Applied/本地分支;BaseMoveSyncComponent.ts:392–479、570–769,二次事件、WithStreaming 和保活。
- FallingRebornController.ts:456–591,处罚、目标选择、流送、timer;405–411,结束事件和解除输入。
- SceneLoadingAfterCmd.ts:261–275,LoadDone;419–495,实体和无缝换点;SceneLoadingWpMapCmd.ts:55–124,命令顺序。
- CommandExecutor.ts:542–594,Bail 触发结果回调。
- 客户端插件 KuroGameRuntime/Private/Movement/CharacterLocomotionComponent.cpp:2350–2359,Reset 置标志;KuroAnim/Private/GameAnimInstanceRoleBase.cpp:553–569,动画更新消费。
11. 先做最小实测,再展开实现
以下均为未验证。先用一个同图 UI load+一个坠落黑屏代表链路跑通,再测跨图创建与 Seq3 交接;没有证据不把勾选项标为通过。
| 难点/返工源 | 验证方式 | 通过标准 |
|---|---|---|
| 先流送后移动 | 慢速目标流送,记录 Actor、主源、目标源和提交帧 | 准备期 Actor 留在原区域;碰撞准备好才提交;目标源直到主源接住才释放 |
| 普通黑屏不改 MovementMode | 站立、走动、坠落入口分别记录模式调用、保护源、物理 Tick | 无普通黑屏主动模式设置;落位后自然获得新接地/Grounded/NotMoving |
| Idle 和 Mesh 不是同一完成信号 | 低帧率、角色差异下记录逻辑态、骨骼/模型偏移和遮罩首个可见帧 | 首个显露帧已站稳且模型无从高处落下;Reset 只置标志不能被误记为完成 |
| 乱序/重复/取消 | 交换通知顺序,连续 A→B,Bail A,延迟触发 A 的 timer/Promise | A 不再位移、不关 B 遮罩、不释放 B 目标源;每个事务提交/完成至多一次 |
| 上行漏口 | 准备期触发普通移动、技能强制采样、最终组包和回放 | 没有“新 version+旧位置”;恢复首包是目标位置,无残留速度/旧队列 |
| 源点在空中 | 坠落请求延迟响应、目标未加载、技能/Root Motion 冲突 | 等待期不继续失控下坠、不重复处罚、不死锁;保护能按 owner 释放 |
| 跨图创建窗口 | 新建/复用角色、慢资产、无缝换落点、创建失败 | 首个物理 Tick 不掉入空场景;绑定后再验证;LoadDone 与实体创建无等待环 |
| Seq3/V2 交接 | EndClientLoading 触发、演出取消、换装、归还角色 | 共享层不抢 Binder 的位置;Seq3 能启动;恢复同步前样本已清理 |
日志统一带“传送”前缀,记录 generation、version、world/level、entity、策略、阶段、源/目标/实际位置、ground/pose/mesh 证据、资源 owner 和失败原因;只在阶段变化或聚合告警时输出,避免每帧刷屏。模型验证若缺 Native 可观测接口,先交动画团队确认,不扩大客户端 TS 改动范围去猜。
12. 通过验证后的落地顺序
- 事务基础:收包接管、单次提交、取消校验、上行屏障与资源 owner 一起落地,先堵住旧旁路。
- 三种同图:拆纯查询/目标流送/Settling,接入表现显露条件;普通黑屏不改模式,无缝不默认遮挡。
- 坠落与边界:迁移技术段,保留处罚请求/安全点/完成事件,去掉“固定时长代表安全”的依赖。
- 跨图:创建前保护和创建后验证,目标源交接,Seq3/V2 边界适配;握手含义不变。
- 回归与收口:逐项实机证据达标后删除被替代路径,不长期保留两套并行落位实现。
每批改动前记录用户已有差异,只回退本批内容,不整文件覆盖他人修改。代码 CRLF、短注释;精准 eslint --fix 后执行 npm run tsc-native 并报告结果。静态检查通过不等于实机通过。
最低回归矩阵
同图三模式 × 已加载/未加载/非法点;走动/站立/空中/技能;窄平台/斜坡/动态支撑;不同角色与低帧率;坠落本地安全点/个人复活点/备用点;连续请求/倒序/取消/重连/销毁;同 WP 换落点/跨地图新角色;普通 Seq 与 V2;目标源预加载争用。未支持的特殊姿态必须明确交还 owner,不得静默当作普通 Idle。
13. 本次审阅只需确认的产品规则
- 目标未加载时是否允许升格:建议无缝/黑屏显式切 UI load,坠落也遵循该规则;不允许则保留故障等待而不强移。
- “范围内”的定义:建议“目标已加载且可落位”为硬前提,距离只决定表现偏好,不沿用 100m 作安全保证;如有策划距离限制,作为额外规则明确。
- 跨图纳入本次整体方案、后阶段实施:建议同意;不重写 Seq3/V2 演出,不更改服务端握手。
已有“不自动退登、不超时伪成功”和“普通黑屏不改 MovementMode”要求继续有效。审阅通过后先执行第 11 节技术验证,关键难点全部获得证据再展开实现;当前交付仅为方案和站点文档。