using System.Collections; using System.Collections.Generic; using AibisDream.Framework; using AibisDream.UI; using Newtonsoft.Json.Linq; using UnityEngine; namespace AibisDream.SaveSystem { /// /// 存读档流程编排层:连接 UI、 与槽位系统。 /// /// 对外入口:(手动/调试触发)、 /// (节点进入事件触发)、 /// (手动存档)、 /// (读档)、 /// (旧档/调试)。 /// /// public static class SaveRestoreOrchestrator { /// 是否正在读档还原中;为 true 时禁止自动存档覆盖 slot_0。 public static bool IsRestoring { get; private set; } private static bool _isAutoSaving; /// 启动自动存档协程(手动/调试入口)。 public static void TryAutoSave() { if (YarnVariableStorage.Instance == null) { Debug.LogError("[SaveRestoreOrchestrator] YarnVariableStorage 未初始化"); return; } if (!SavePointEvaluator.CanAutoSave(out var reason)) { Debug.LogWarning($"[SaveRestoreOrchestrator] 当前不是可存点,跳过自动存档:{reason}"); return; } var (triggerNodeName, _) = DialogController.Instance.GetCurrentNodeContext(); DialogController.Instance.StartCoroutine(AutoSaveRoutine(triggerNodeName)); } /// 自动存档协程:settle 一帧 → 主线程 Capture → 异步写盘。 /// OnNodeStart 触发存档时的节点名,作为 anchor 写入快照。 public static IEnumerator AutoSaveRoutine(string triggerNodeName = null) { if (_isAutoSaving) { yield break; } yield return null; // 可能在 settle 帧内已有另一条协程开始写盘,须二次检查避免并发覆盖 slot_0。 if (_isAutoSaving) { yield break; } _isAutoSaving = true; var infoPanel = UIManager.Instance.GetPanel(); infoPanel?.ShowSaveLoading(); try { SaveSnapshot snapshot; byte[] thumbnail; using (new CodeTimer("SaveSnapshot")) { snapshot = SnapshotService.Capture(triggerNodeName); thumbnail = SlotThumbnailCapture.CapturePng(); } var writeTask = SlotManager.SaveToAutoSlotAsync(snapshot, thumbnail); while (!writeTask.IsCompleted) { yield return null; } if (writeTask.IsFaulted) { Debug.LogError( $"[SaveRestoreOrchestrator] 自动存档失败: {writeTask.Exception?.GetBaseException()}"); yield break; } if (snapshot?.anchor != null && string.IsNullOrEmpty(snapshot.anchor.nodeName)) { Debug.Log("[SaveRestoreOrchestrator] 已保存无 Yarn 节点活跃状态。"); } } finally { infoPanel?.HideSaveLoading(); _isAutoSaving = false; } } /// 将当前自动档复制到指定手动档。 public static void CreateManualSlot(int slotIndex) { if (!SavePointEvaluator.CanManualSave(out var reason)) { Debug.LogWarning($"[SaveRestoreOrchestrator] 当前不可手动存档:{reason}"); return; } SlotManager.CopyAutoToManual(slotIndex); } /// 从指定槽位读档并还原。 public static IEnumerator RestoreFromSlot(int slotIndex) { IsRestoring = true; try { var snapshot = SlotManager.LoadSnapshot(slotIndex); if (snapshot == null) { Debug.LogError($"[SaveRestoreOrchestrator] 槽位 {slotIndex} 不存在或读取失败"); yield break; } yield return RestoreSnapshot(snapshot); } finally { IsRestoring = false; } } /// 从文件读档并还原;自动区分新快照格式与 legacy 格式。 public static IEnumerator RestoreFromFile(string savePath) { IsRestoring = true; try { if (SnapshotPersistence.IsLegacyFormat(savePath)) { Debug.LogWarning("[SaveRestoreOrchestrator] 检测到旧格式存档,请使用新快照格式重新保存。"); yield return RestoreLegacy(savePath); yield break; } SaveSnapshot snapshot; using (new CodeTimer("LoadSnapshot")) { snapshot = SnapshotPersistence.Load(savePath); } yield return RestoreSnapshot(snapshot); } finally { IsRestoring = false; } } /// 旧档:Yarn 变量 + DeepRepairDataRegistry systemData。 private static IEnumerator RestoreLegacy(string savePath) { var saveData = SnapshotPersistence.ReadLegacySaveRoot(savePath); var floatDict = saveData["floatDict"]?.ToObject>(); var stringDict = saveData["stringDict"]?.ToObject>(); var boolDict = saveData["boolDict"]?.ToObject>(); YarnVariableStorage.Instance.SetAllVariables(floatDict, stringDict, boolDict); if (saveData["systemData"] is JObject systemDataJson) { yield return DeepRepairDataRegistry.LoadByJson(systemDataJson); } // 旧档还原后自动固化为新格式,方便后续使用槽位系统 var snapshot = SnapshotService.Capture(); SlotManager.SaveToAutoSlot(snapshot, null); } private static IEnumerator RestoreSnapshot(SaveSnapshot snapshot) { yield return SnapshotService.Restore(snapshot); } } }