using System; using System.Collections; using System.Collections.Generic; using System.Threading.Tasks; using AibisDream.Framework; using AibisDream.UI; using UnityEngine; namespace AibisDream.SaveSystem { /// /// 存读档流程编排层:连接 UI、 与槽位系统。 /// public static class SaveRestoreOrchestrator { /// 是否正在读档还原中;供 UI / 日志 / 外部系统观察。 public static bool IsRestoring { get; private set; } /// 是否处于自动存档抑制作用域;真正用于防止读档期间覆盖 slot_0。 public static bool IsAutoSaveSuppressed => _autoSaveSuppressDepth > 0; public static IReadOnlyList LastRestoreLog => _lastRestoreLog; private static bool _isAutoSaving; private static int _autoSaveSuppressDepth; private static readonly List _lastRestoreLog = new(); private const float RestoreFadeDuration = 0.2f; /// 启动自动存档协程(手动/调试入口)。 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 → 后台序列化写盘(不阻塞主线程)。 /// ;显式 save 传 null。 /// 为 true 时不写入 Yarn 节点 anchor。 public static IEnumerator AutoSaveRoutine(string triggerNodeName = null, bool omitAnchor = false) { if (_isAutoSaving) { Debug.LogWarning("[SaveRestoreOrchestrator] 存档进行中,跳过重复请求。"); yield break; } yield return null; if (_isAutoSaving) { Debug.LogWarning("[SaveRestoreOrchestrator] 存档进行中,跳过重复请求。"); yield break; } _isAutoSaving = true; var infoPanel = UIManager.Instance.GetPanel(); infoPanel?.ShowSaveLoading(); SaveSnapshot snapshot; byte[] thumbnail; try { using (new CodeTimer("SaveSnapshot")) { snapshot = SnapshotService.Capture(triggerNodeName, omitAnchor); thumbnail = SlotThumbnailCapture.CapturePng(); } } catch (Exception ex) { Debug.LogError($"[SaveRestoreOrchestrator] 自动存档 Capture 失败: {ex}"); infoPanel?.HideSaveLoading(); _isAutoSaving = false; yield break; } infoPanel?.HideSaveLoading(); if (TestSaveArchive.IsEnabled) { TestSaveArchive.Archive(snapshot, thumbnail); Debug.Log("[SaveRestoreOrchestrator] 测试存档模式:已写入 testsavs(不覆盖 slot_0)。"); _isAutoSaving = false; } else { _ = SlotManager.SaveToAutoSlotAsync(snapshot, thumbnail).ContinueWith( writeTask => { if (writeTask.IsFaulted) { Debug.LogError( $"[SaveRestoreOrchestrator] 自动存档写盘失败: {writeTask.Exception?.GetBaseException()}"); } _isAutoSaving = false; }, TaskContinuationOptions.ExecuteSynchronously); } if (omitAnchor || (snapshot?.anchor != null && string.IsNullOrEmpty(snapshot.anchor.nodeName))) { Debug.Log("[SaveRestoreOrchestrator] 已保存无 Yarn 节点 anchor 的状态。"); } } /// /// Yarn <<save>> 显式存档:默认 omit anchor,调用方需已通过 。 /// public static IEnumerator ExplicitSaveRoutine() { yield return AutoSaveRoutine(triggerNodeName: null, omitAnchor: true); } /// 将当前自动档复制到指定手动档。 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) { var snapshot = SlotManager.LoadSnapshot(slotIndex); if (snapshot == null) { Debug.LogError($"[SaveRestoreOrchestrator] 槽位 {slotIndex} 不存在或读取失败"); yield break; } yield return RestoreSnapshot(snapshot, $"slot_{slotIndex}"); } /// 从文件读档并还原;自动区分新快照格式与 legacy 格式。 public static IEnumerator RestoreFromFile(string savePath) { if (SnapshotPersistence.IsLegacyFormat(savePath)) { Debug.LogWarning("[SaveRestoreOrchestrator] 检测到旧格式存档,请使用新快照格式重新保存。"); yield return RestoreLegacyWithFlow(savePath); yield break; } SaveSnapshot snapshot; using (new CodeTimer("LoadSnapshot")) { snapshot = SnapshotPersistence.Load(savePath); } yield return RestoreSnapshot(snapshot, savePath); } public static IDisposable SuppressAutoSaveScope(string reason) { _autoSaveSuppressDepth++; Debug.Log($"[SaveRestoreOrchestrator] SuppressAutoSave begin: {reason}, depth={_autoSaveSuppressDepth}"); return new AutoSaveSuppressScope(reason); } /// 旧档:仅恢复 Yarn 变量,再 Capture 写入 slot_0 完成格式迁移。 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"] != null) { Debug.LogWarning( "[SaveRestoreOrchestrator] 旧档 systemData 段已无法还原(IData 已移除);" + "仅恢复 Yarn 变量并迁移为新快照格式。"); } var snapshot = SnapshotService.Capture(); SlotManager.SaveToAutoSlot(snapshot, null); yield break; } private static IEnumerator RestoreLegacyWithFlow(string savePath) { using (SuppressAutoSaveScope("RestoreLegacy")) { IsRestoring = true; ResetRestoreLog("legacy"); try { yield return FadeInForRestore(); PrepareGameSessionForRestore(); yield return RestoreLegacy(savePath); yield return null; FinalizeGameSessionAfterRestore(); yield return FadeOutForRestore(); ScreenSnapshotHelper.ApplyDeferredFadeScreenIfNeeded(); } finally { IsRestoring = false; AddRestoreLog("Legacy restore finished"); } } } private static IEnumerator RestoreSnapshot(SaveSnapshot snapshot, string sourceLabel) { if (snapshot == null) { Debug.LogError("[SaveRestoreOrchestrator] snapshot 为 null,无法读档。"); yield break; } using (SuppressAutoSaveScope($"RestoreSnapshot:{sourceLabel}")) { IsRestoring = true; ResetRestoreLog(sourceLabel); var context = new SnapshotRestoreContext(snapshot, logStep: AddRestoreLog); try { yield return FadeInForRestore(); PrepareGameSessionForRestore(); SnapshotRegistry.EnsureInitialized(); yield return SnapshotRestore.RestoreState(YarnVariableStorage.Instance, snapshot, context); yield return SnapshotRestore.RestoreAnchor(snapshot, context); AddRestoreLog("Phase 3.5: settle one frame"); yield return null; FinalizeGameSessionAfterRestore(); yield return FadeOutForRestore(); ScreenSnapshotHelper.ApplyDeferredFadeScreenIfNeeded(); } finally { IsRestoring = false; AddRestoreLog("Restore finished"); } } } /// /// 读档前对齐「已进入游戏」的 UI 与输入状态。 /// 编辑器测试工具等路径可能跳过 ,导致 TerminalPanel 未关闭、 /// 为 false,从而无法推进对话。 /// private static void PrepareGameSessionForRestore() { AddRestoreLog("Prepare game session for restore"); var gameManager = GameManager.Instance; if (gameManager != null && !gameManager.state.isInGame) { EnumEventSystem.Global.Send(GameLoopEnum.GameStart); } var ui = UIManager.Instance; if (ui != null) { ui.HideTerminal(); ui.GetPanel()?.Close(); ui.ShowPanel(); ui.HidePanel(); ui.HidePanel(); } if (gameManager != null && gameManager.state.isInPause) { gameManager.ContinueGame(); } else { DialogUIManager.Instance?.OpenCanvas(); } } /// 读档完成后确保对话 Canvas 可用、退出暂停态。 private static void FinalizeGameSessionAfterRestore() { AddRestoreLog("Finalize game session after restore"); DialogUIManager.Instance?.OpenCanvas(); var gameManager = GameManager.Instance; if (gameManager != null && gameManager.state.isInPause) { gameManager.ContinueGame(); } } private static IEnumerator FadeInForRestore() { AddRestoreLog("Begin restore fade in"); var panel = UIManager.Instance?.GetPanel(); if (panel != null) { yield return panel.FadeInAsync(RestoreFadeDuration); } } private static IEnumerator FadeOutForRestore() { AddRestoreLog("End restore fade out"); var panel = UIManager.Instance?.GetPanel(); if (panel != null) { yield return panel.FadeOutAsync(RestoreFadeDuration); } } private static void ResetRestoreLog(string sourceLabel) { _lastRestoreLog.Clear(); AddRestoreLog($"Restore source: {sourceLabel}"); } private static void AddRestoreLog(string message) { _lastRestoreLog.Add($"[{DateTime.Now:HH:mm:ss}] {message}"); Debug.Log($"[SaveRestoreOrchestrator] {message}"); } private sealed class AutoSaveSuppressScope : IDisposable { private readonly string _reason; private bool _disposed; public AutoSaveSuppressScope(string reason) { _reason = reason; } public void Dispose() { if (_disposed) { return; } _disposed = true; _autoSaveSuppressDepth = Math.Max(0, _autoSaveSuppressDepth - 1); Debug.Log( $"[SaveRestoreOrchestrator] SuppressAutoSave end: {_reason}, depth={_autoSaveSuppressDepth}"); } } } }