Files
Genarrative/docs/technical/RPG_SCENE_ACT_PREVIEW_BOOTSTRAP_FIX_2026-04-30.md
2026-04-30 17:49:07 +08:00

3.7 KiB
Raw Blame History

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() 写入玩家、场景与故事后,这个中间态仍可能覆盖回来,导致 currentScenePresetplayerCharacter 被清掉,预览层重新停在“正在载入这一幕的游戏流程...”。

本轮调整后:

  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 的本地开场 storyRpgRuntimeShell 立即满足挂载条件。

约束

  1. 幕预览继续复用正式 play 运行链,不恢复旧 preview/test 行为分支。
  2. 幕预览只允许前端做临时运行态装配;正式游戏开局仍由 server-rs 裁决。
  3. 后续如把幕预览也迁到后端 bootstrap应新增专门的禁持久化 bootstrap 入口,而不是再次依赖 handleCharacterSelect() 的异步状态顺序。

验证

新增回归覆盖:

npm test -- --run src/components/CustomWorldEntityEditorModal.test.tsx

断言幕预览打开后:

  1. 不再显示“正在载入这一幕的游戏流程...”。
  2. RpgRuntimeShell 已收到预览玩家角色。
  3. 运行态为 play 且禁用持久化。
  4. 当前 story 已注入为当前幕 NPC 开场内容。