# RPG 幕预览启动卡载入修复(2026-04-30) ## 背景 编辑器内点击“幕预览”后,独立预览层会一直停在“正在载入这一幕的游戏流程...”,无法进入真实 RPG 运行壳。 ## 根因 `SceneActPreviewRuntime` 先调用 `handleCustomWorldSelect(profile)`,紧接着调用 `handleCharacterSelect(previewCharacter)`。 但 `handleCharacterSelect()` 读取的是当前 render 闭包中的旧 `gameState`。此时 `handleCustomWorldSelect()` 写入的 `worldType` 还没有完成 React 状态提交,所以选角入口看到 `worldType` 为空后直接返回。随后幕预览虽然又手动写入了 `currentScene / currentScenePreset / currentEncounter`,却没有写入 `playerCharacter`,导致 `isPreviewReady` 永远不成立。 另一个隐患是:有 `currentEncounter` 时 story controller 不会主动生成普通首段剧情,而是交给 NPC 交互流接管;若预览没有显式注入一个可展示的 `currentStory`,运行面板也可能无法稳定挂载。 ## 本轮继续修复 继续复测时发现,`SceneActPreviewRuntime` 虽然已经不再调用 `handleCharacterSelect()`,但仍会调用 `handleCustomWorldSelect(profile)` 来同步 runtime 静态资料。 这个入口是正式运行态的“选择世界”入口,会排队写入“已选择世界、尚未选角”的中间 `GameState`。在幕预览本地 `setGameState()` 写入玩家、场景与故事后,这个中间态仍可能覆盖回来,导致 `currentScenePreset` 或 `playerCharacter` 被清掉,预览层重新停在“正在载入这一幕的游戏流程...”。 本轮调整后: 1. 幕预览不再调用 `handleCustomWorldSelect(profile)`。 2. 幕预览直接调用 `setRuntimeCustomWorldProfile(profile)` 与 `setRuntimeCharacterOverrides(buildCustomWorldRuntimeCharacters(profile))` 同步静态资料层。 3. `isPreviewReady` 同时校验: - `currentScene === "Story"` - `runtimeSessionId === "runtime-scene-act-preview"` - 当前玩家就是本次预览角色 - 当前场景就是本次预览场景 - 当前 story 已经完成注入 4. 这样 preview ready 只依赖本次预览自己的完整启动结果,不再被正式选世界中间态影响。 ## 修复口径 1. 幕预览不再调用 `handleCharacterSelect()` 触发后端开局副作用。 2. 幕预览不调用正式 `handleCustomWorldSelect(profile)`,而是直接同步 runtime 静态资料层。 3. 随后在同一个 `setGameState` 中一次性写入: - `playerCharacter` - `runtimeMode: "play"` - `runtimePersistenceDisabled: true` - `currentScene / currentScenePreset / currentEncounter` - 玩家血蓝、技能冷却、装备、统计、进度、队伍、任务等运行态基础字段 - 当前幕 `currentSceneActState` 4. 幕预览使用固定临时 `runtimeSessionId: "runtime-scene-act-preview"`,并通过禁持久化快照保持不写正式存档。 5. 启动时同步 `hydrateStoryState()`,注入当前幕 NPC 的本地开场 story,让 `RpgRuntimeShell` 立即满足挂载条件。 ## 约束 1. 幕预览继续复用正式 `play` 运行链,不恢复旧 `preview/test` 行为分支。 2. 幕预览只允许前端做临时运行态装配;正式游戏开局仍由 `server-rs` 裁决。 3. 后续如把幕预览也迁到后端 bootstrap,应新增专门的禁持久化 bootstrap 入口,而不是再次依赖 `handleCharacterSelect()` 的异步状态顺序。 ## 验证 新增回归覆盖: ```bash npm test -- --run src/components/CustomWorldEntityEditorModal.test.tsx ``` 断言幕预览打开后: 1. 不再显示“正在载入这一幕的游戏流程...”。 2. `RpgRuntimeShell` 已收到预览玩家角色。 3. 运行态为 `play` 且禁用持久化。 4. 当前 story 已注入为当前幕 NPC 开场内容。