同图无缝传送(blackScreenType = NONE)· 客户端 TypeScript 0.7.0 · 关注点:SetGravityScale(0) 什么时候锁、什么时候解
SetGravityScale(0) + MOVE_None 把人钉在空中托着。
地形流送完 + 3×3 贴地预检通过,才解锁放人落地。
位置是瞬间改的,不等客户端。先发 15512 TeleportSelf,紧接着发 15511 ServerEntityMove(带 B 坐标)。客户端回 15513 LoadDone 才认为你"到位了"。
每帧跟随角色位置(Tick())。角色被挪到 B 的下一帧,它就跟到 B —— A 脚下的 cell 随即失去覆盖,开始卸载。
被 SetStreamingLockStateAndPosition(true, B) 锁死在 B,不跟人。负责在交接完成前把 B 的地形先拉出来。
TeleportController.LockLocalPlayerMovementForTeleport():存下原 MovementMode → SetGravityScale(0) → MOVE_None。解锁时原样恢复。
服务端此刻已经认为你在 B 了。客户端还什么都没做。
收到 15512 立刻做四件事:① 瞬时盖遮罩 OpenSeamlessTransitionMask;② 启 15s 兜底 timer;③ 注册 LocalPlayerServerMoveApplied 监听;④ 加重力锁。
注意加锁在收到位置包之前——因为 15511 可能同帧就到,SafeMoveTeleport 是同步的,慢一步人就已经在 B 的空气里开始掉了。
最危险的一帧:角色 Actor 已在 B 的坐标上,B 的 cell 一格未加载,脚下是纯空气。此刻唯一托着人的就是第 1 步的重力锁。
同时玩家灯下一帧 Tick 就跟到 B,A 脚下的 cell 从这一刻起失去覆盖,开始卸载(这是正常回收,不是 bug)。
非重度走 OnSeamlessLevel,用快照里的 B 坐标构建 SceneLoadingTeleportCmd 命令链。链里 SceneLoadingNewSceneCmd 把落点灯锁在 B 并置 LoadAndVisible。同时开启协议缓存(AOI/实体增删挂起)。
IsStreamingCompleted(Activated, radius=7000) 每 100ms 查一次,直到 B 周围 cell 全部进入 Activated。正常 约 100–300ms。
⚠ 这个 Cmd 内部没有超时,executor 超时是 TimerSystemConstants.MaxTime(等于不限)。某些 DataLayer 会永久返回 false,一旦命中就只能等第 1 步那个 15s 兜底。
先 CopyStreamingSourceInfo(玩家灯 ← 落点灯)(抄位置 + LevelLayerName + 状态),然后才把落点灯 Unload。顺序颠倒 = B 的 cell 瞬间无源 = 地形消失。
cell 到 Activated 不等于物理碰撞已 build 完。以 B 为中心做 3×3 = 9 条向下 LineTrace(水平 132cm、向下 200cm),9 点全命中才算地能托住人。
首次同步检查命中就直接放行;不命中每 50ms 重试,最多 20 次(1s 窗口),超时也强制放行并打 warning。
按顺序:UnlockLocalPlayerMovementForTeleport()(ResetGravityScale() + 恢复原 MovementMode)→ 关遮罩 → 结束协议缓存 → Emit TeleportSteamingComplete。15513 LoadDone 在这之前已发(:1249)。
落点灯会被重新点亮做兜底,直到玩家水平移动 > 500cm 或 30s 超时才关。这时人已经解锁能走了,地形由玩家灯正常维持。
| 出口 | 位置 | 触发条件 |
|---|---|---|
| ✅ 正常 finalize | TeleportController.ts:1225 | 流送 success + 3×3 贴地预检通过(或预检 1s 超时兜底) |
| ⏱ 15s 兜底 | :945 OnTeleportSelfTimeout | 15s 内没走完整套流程,强制解锁并回报 LoadDone |
| ↪ 跨图交还 | :872 | ServerMove 应用时发现已进入跨图加载,同步瞬移不接管 |
| ↪ REVIVE 交还 | :898 | reason = REVIVE,交回复活链路自己处理 |
| ⚠ 快照为空 | :1208 | StartLevelStreamingFromSnapshot 拿不到快照,直接放人 |
| ⚠ LoadDone 全部重试失败 | :1081 | 15513 重试 5 次仍失败,不能把人永远锁着 |
Cube563)。
地形其实在,只是托不住。第 6 步的 3×3 多点检测就是防它——单点命中不算数。传送-解锁前贴地预检完成 [result: ready-on-first-check] 之后紧跟 [坠落重生] 玩家Z坐标低于关卡允许的最小Z值
CopyStreamingSourceInfo 从未执行 → 玩家灯没拿到 LevelLayerName;
等第 8 步保活 30s 到点把落点灯一关,B 的 cell 无源 → 地形肉眼可见消失,人直接掉下去(典型现象:传送成功约 1 分钟后突然坠落)。等待WP场景流送完成(开始) 没有 (结束),之后出现 传送-保活守护结束,关闭 TargetStreamingSource
等待WP场景流送完成(结束) 有没有打印。| 常量 | 值 | 含义 |
|---|---|---|
TeleportSelfFallbackTimeoutMs | 15000 ms | 整套同步瞬移的兜底上限,也是重力锁最长时间 |
GroundReadyCheckIntervalMs × MaxAttempts | 50 × 20 | 贴地预检重试,最多多锁 1s |
GroundReadyTraceHorizontalDist / Vertical | 132 / 200 | 3×3 射线的水平间距与向下长度(cm) |
CheckStreamingInterval | 100 ms | 流送完成轮询间隔 |
StreamingSourceRadius | 7000 cm | 流送完成检查半径 |
TeleportStreamingMaxAliveMs | 30000 ms | 落点灯保活上限(解锁之后的事) |
TeleportKeepAliveMoveThreshold | 500 cm | 玩家走出这么远就关落点灯 |
Src/Game/Module/Teleport/TeleportController.ts ·
Src/Game/World/Entity/Common/BaseMoveSyncComponent.ts ·
Src/Game/World/SceneLoading/StreamingSourceInfo.ts ·
Src/Game/World/SceneLoading/Commands/SceneLoadingCmds/