1
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
# 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 开场内容。
|
||||
Reference in New Issue
Block a user