using System.Collections;
using Newtonsoft.Json.Linq;
using UnityEngine;
namespace AibisDream.SaveSystem
{
///
/// 将 按 Phase + Barrier 写回运行时(纯内存,不读盘)。
///
public static class SnapshotRestore
{
///
/// Phase 0~2:Yarn 变量、场景、章节与所有 Provider;不包含锚点重进。
///
public static IEnumerator RestoreState(
YarnVariableStorage storage,
SaveSnapshot snapshot,
SnapshotRestoreContext context)
{
if (snapshot == null)
{
context?.Error("snapshot 为 null");
yield break;
}
context ??= new SnapshotRestoreContext(snapshot);
context.Log("Phase 0: Restore Yarn variables");
RestoreYarnVariables(storage, snapshot);
context.Log("Phase 1: Load scene");
yield return RestoreScene(snapshot, context);
context.Log("Phase 1.5: Restore scene SO");
RestoreSceneSo(snapshot, context);
context.Log("Phase 2: Restore providers (ordered by RestoreOrder)");
yield return RestoreProvidersInOrder(snapshot, context);
}
///
/// Phase 3:按 加载对话工程;若有节点名则重新进入 Yarn 节点。
///
public static IEnumerator RestoreAnchor(SaveSnapshot snapshot, SnapshotRestoreContext context = null)
{
if (snapshot?.anchor == null)
{
context?.Log("Phase 3: No anchor, skip RestoreAnchor");
yield break;
}
context ??= new SnapshotRestoreContext(snapshot);
var hasNode = !string.IsNullOrEmpty(snapshot.anchor.nodeName);
context.Log(hasNode
? $"Phase 3: Restore anchor {snapshot.anchor.nodeName}"
: "Phase 3: No anchor node, restore YarnProject only");
var dialog = DialogController.Instance;
if (dialog == null)
{
context.Warn("DialogController 未初始化,无法恢复 Yarn 状态。");
yield break;
}
var runner = dialog.DialogueRunner;
if (runner == null)
{
context.Warn("DialogueRunner 未初始化,无法恢复 Yarn 状态。");
yield break;
}
if (!string.IsNullOrEmpty(snapshot.anchor.yarnProjectId))
{
var sceneSo = GameManager.Instance?.GetCurrentTalkSceneSo();
if (sceneSo?.yarnProject == null)
{
context.Error($"章节 {snapshot.anchor.sceneSoName} 没有可用的 YarnProject。");
yield break;
}
if (sceneSo.yarnProject.name != snapshot.anchor.yarnProjectId)
{
context.Error(
$"章节 YarnProject 不匹配:章节 {sceneSo.yarnProject.name},存档 {snapshot.anchor.yarnProjectId}");
yield break;
}
if (runner.YarnProject == null || runner.YarnProject.name != snapshot.anchor.yarnProjectId)
{
dialog.LoadDialog(sceneSo.yarnProject);
}
}
if (!hasNode)
{
yield break;
}
if (runner.IsDialogueRunning)
{
yield return runner.Stop();
}
if (runner.YarnProject == null
|| !System.Array.Exists(runner.YarnProject.NodeNames, node => node == snapshot.anchor.nodeName))
{
context.Error(
$"YarnProject {runner.YarnProject?.name ?? "none"} 不包含节点 {snapshot.anchor.nodeName}。");
yield break;
}
yield return runner.StartDialogue(snapshot.anchor.nodeName);
}
private static void RestoreYarnVariables(YarnVariableStorage storage, SaveSnapshot snapshot)
{
var vars = snapshot.yarnVariables;
if (vars == null || storage == null) return;
storage.SetAllVariables(vars.floats, vars.strings, vars.bools);
}
private static IEnumerator RestoreScene(SaveSnapshot snapshot, SnapshotRestoreContext context)
{
if (snapshot.scene == null || string.IsNullOrEmpty(snapshot.scene.sceneName))
{
context.Warn("快照缺少 scene.sceneName,跳过场景加载。");
yield break;
}
var sceneLoader = SceneLoader.Instance;
if (sceneLoader == null)
{
context.Error("SceneLoader 未初始化,无法加载场景。");
yield break;
}
yield return sceneLoader.LoadSceneAsync(snapshot.scene.sceneName);
// 再等一帧,等Awake执行完
yield return null;
}
private static void RestoreSceneSo(SaveSnapshot snapshot, SnapshotRestoreContext context)
{
if (snapshot.anchor == null || string.IsNullOrEmpty(snapshot.anchor.sceneSoName))
{
context.Warn("快照缺少 anchor.sceneSoName,跳过章节 SO 设置。");
return;
}
var gameManager = GameManager.Instance;
if (gameManager == null)
{
context.Warn("GameManager 未初始化,无法设置章节 SO。");
return;
}
if (!gameManager.SetSceneSoByName(snapshot.anchor.sceneSoName))
{
context.Error($"找不到章节 SO:{snapshot.anchor.sceneSoName},无法恢复 YarnProject。");
}
}
///
/// 按 依次还原;
/// 同步 Provider 连续调用,异步 Provider 作为 Barrier 挂起,二者混排而非分两批。
///
private static IEnumerator RestoreProvidersInOrder(SaveSnapshot snapshot, SnapshotRestoreContext context)
{
foreach (var provider in SnapshotRegistry.GetOrderedProviders())
{
if (!TryGetSectionDto(snapshot, provider, context, out var dto))
{
continue;
}
switch (provider)
{
case ISyncSnapshotProvider syncProvider:
context.Log($"Sync provider [{provider.RestoreOrder}]: {provider.SaveId}");
syncProvider.Restore(dto, context);
break;
case IAsyncSnapshotProvider asyncProvider:
context.Log($"Async provider [{provider.RestoreOrder}]: {provider.SaveId}");
yield return asyncProvider.RestoreAsync(dto, context);
break;
default:
context.Warn($"Provider {provider.SaveId} 未实现同步或异步还原契约,已跳过。");
break;
}
}
}
private static bool TryGetSectionDto(
SaveSnapshot snapshot,
ISnapshotProvider provider,
SnapshotRestoreContext context,
out object dto)
{
dto = null;
if (snapshot.sections == null
|| !snapshot.sections.TryGetValue(provider.SaveId, out dto)
|| dto == null)
{
context.Log($"Provider {provider.SaveId}: no section, skip");
return false;
}
dto = CoerceSectionDto(provider.SaveId, dto);
return true;
}
///
/// 读盘后 中的值常为 ;
/// 按 SaveId 转回各 Provider 所需的强类型 DTO。
///
private static object CoerceSectionDto(string saveId, object dto)
{
if (dto is not JObject jobj)
{
return dto;
}
return saveId switch
{
SnapshotProviderIds.Environment => jobj.ToObject(),
SnapshotProviderIds.Actor => jobj.ToObject(),
SnapshotProviderIds.Audio => jobj.ToObject(),
SnapshotProviderIds.Timeline => jobj.ToObject(),
SnapshotProviderIds.Subway => jobj.ToObject(),
SnapshotProviderIds.PunchTape => jobj.ToObject(),
SnapshotProviderIds.Fix => jobj.ToObject(),
SnapshotProviderIds.FixPanel => jobj.ToObject(),
SnapshotProviderIds.BodyModule => jobj.ToObject(),
SnapshotProviderIds.Eye => jobj.ToObject(),
SnapshotProviderIds.Screen => jobj.ToObject(),
_ => jobj
};
}
}
}