Files
aibis-dream/Assets/Scripts/SaveSystem/SnapshotRestore.cs
T
dingyuntian cc3e1dd138 refactor(save): 合并 SaveSystem 过度拆分的文件
将以下小文件合并到相关核心类中,减少文件数量并保持职责清晰:
- SaveSnapshotSummary -> SaveSnapshot
- SnapshotProviderIds + SnapshotBootstrap -> SnapshotRegistry
- SnapshotSectionDeserializer -> SnapshotRestore
- SnapshotSerializer -> SnapshotPersistence

同步更新 SnapshotService 与 YarnVariableStorage 的调用点。
2026-06-12 17:40:36 +08:00

158 lines
5.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections;
using Newtonsoft.Json.Linq;
using UnityEngine;
namespace AibisDream.SaveSystem
{
/// <summary>
/// 将 <see cref="SaveSnapshot"/> 逐项写回运行时(纯内存,不读盘)。
/// <para>
/// 顺序:Yarn 变量 → 场景加载 → 设置章节 → 各 Provider(按 RestoreOrder)→ 锚点加载对话并重进节点。
/// 完整淡入淡出时序由 P4 在 <see cref="SaveRestoreOrchestrator"/> 扩展。
/// </para>
/// </summary>
public static class SnapshotRestore
{
/// <summary>
/// 还原 Yarn 变量、场景、章节与各 section;不包含锚点重进(由 <see cref="RestoreAnchor"/> 或上层编排)。
/// </summary>
public static IEnumerator Restore(YarnVariableStorage storage, SaveSnapshot snapshot)
{
if (snapshot == null)
{
Debug.LogError("[SnapshotRestore] snapshot 为 null");
yield break;
}
RestoreYarnVariables(storage, snapshot);
yield return RestoreScene(snapshot);
RestoreSceneSo(snapshot);
foreach (var provider in SnapshotRegistry.GetOrderedProviders())
{
if (!snapshot.sections.TryGetValue(provider.SaveId, out var dto) || dto == null)
{
continue;
}
dto = CoerceSectionDto(provider.SaveId, dto);
yield return provider.Restore(dto);
}
if (snapshot.deepRepair != null && snapshot.deepRepair.sections != null
&& snapshot.deepRepair.sections.Count > 0)
{
Debug.LogWarning("[SnapshotRestore] DeepRepair 段尚未实现,已跳过(P5)。");
}
}
/// <summary>
/// 按 <see cref="AnchorSnapshot"/> 加载对话工程并重新进入 Yarn 节点,实现「所见即所存」。
/// 调用前应已完成场景加载与各 provider 还原。
/// </summary>
public static IEnumerator RestoreAnchor(SaveSnapshot snapshot)
{
if (snapshot?.anchor == null || string.IsNullOrEmpty(snapshot.anchor.nodeName))
{
yield break;
}
var dialog = DialogController.Instance;
if (dialog == null) yield break;
var runner = dialog.DialogueRunner;
if (runner == null) yield break;
// 若当前 Runner 未加载对应 YarnProject,则先加载
if (!string.IsNullOrEmpty(snapshot.anchor.yarnProjectId))
{
var sceneSo = GameManager.Instance?.GetCurrentTalkSceneSo();
if (sceneSo?.yarnProject != null && sceneSo.yarnProject.name == snapshot.anchor.yarnProjectId)
{
if (runner.YarnProject == null || runner.YarnProject.name != snapshot.anchor.yarnProjectId)
{
dialog.LoadDialog(sceneSo.yarnProject);
}
}
else if (runner.YarnProject != null && runner.YarnProject.name != snapshot.anchor.yarnProjectId)
{
Debug.LogWarning(
$"[SnapshotRestore] YarnProject 不匹配:当前 {runner.YarnProject.name},存档 {snapshot.anchor.yarnProjectId}");
}
}
if (runner.IsDialogueRunning)
{
yield return runner.Stop();
}
yield return runner.StartDialogue(snapshot.anchor.nodeName);
}
private static void RestoreYarnVariables(YarnVariableStorage storage, SaveSnapshot snapshot)
{
var vars = snapshot.yarnVariables;
if (vars == null) return;
storage.SetAllVariables(vars.floats, vars.strings, vars.bools);
}
private static IEnumerator RestoreScene(SaveSnapshot snapshot)
{
if (snapshot.scene == null || string.IsNullOrEmpty(snapshot.scene.sceneName))
{
yield break;
}
var sceneLoader = SceneLoader.Instance;
if (sceneLoader == null)
{
Debug.LogError("[SnapshotRestore] SceneLoader 未初始化,无法加载场景");
yield break;
}
yield return sceneLoader.LoadSceneAsync(snapshot.scene.sceneName);
}
private static void RestoreSceneSo(SaveSnapshot snapshot)
{
if (snapshot.anchor == null || string.IsNullOrEmpty(snapshot.anchor.sceneSoName))
{
return;
}
var gameManager = GameManager.Instance;
if (gameManager == null)
{
Debug.LogWarning("[SnapshotRestore] GameManager 未初始化,无法设置章节 SO");
return;
}
gameManager.SetSceneSoByName(snapshot.anchor.sceneSoName);
}
/// <summary>
/// 读盘后 <see cref="SaveSnapshot.sections"/> 中的值常为 <see cref="JObject"/>
/// 按 SaveId 转回各 Provider 所需的强类型 DTO。
/// </summary>
private static object CoerceSectionDto(string saveId, object dto)
{
if (dto is not JObject jobj)
{
return dto;
}
return saveId switch
{
SnapshotProviderIds.Environment => jobj.ToObject<EnvironmentSnapshotDto>(),
SnapshotProviderIds.Actor => jobj.ToObject<ActorSnapshotDto>(),
SnapshotProviderIds.Audio => jobj.ToObject<AudioSnapshotDto>(),
SnapshotProviderIds.Timeline => jobj.ToObject<TimelineSnapshotDto>(),
SnapshotProviderIds.Fix => jobj.ToObject<FixSnapshotDto>(),
SnapshotProviderIds.Screen => jobj.ToObject<ScreenSnapshotDto>(),
_ => jobj
};
}
}
}