diff --git a/Assets/Scripts/SaveSystem/ISnapshotProvider.cs b/Assets/Scripts/SaveSystem/ISnapshotProvider.cs index 459f708f5..1a2fad042 100644 --- a/Assets/Scripts/SaveSystem/ISnapshotProvider.cs +++ b/Assets/Scripts/SaveSystem/ISnapshotProvider.cs @@ -1,13 +1,12 @@ -using System.Collections; +using System; +using System.Collections; +using UnityEngine; namespace AibisDream.SaveSystem { /// - /// 快照提供者契约:将某一子系统的运行时状态与纯数据 DTO 互相转换。 - /// - /// 设计约定:DTO 不含事件、协程或 Unity 引用; 只读, - /// 负责逐项写回。实现类通常很薄,实际逻辑在各 Manager 中。 - /// + /// 快照提供者契约:将某一子系统的运行时状态转换为纯数据 DTO。 + /// 还原侧由同步 / 异步子接口显式声明,避免 Provider 内部 fire-and-forget。 /// public interface ISnapshotProvider { @@ -18,14 +17,62 @@ namespace AibisDream.SaveSystem string SaveId { get; } /// - /// 读档还原顺序。数值越小越先执行(如 scene=10 先于 actor=40)。 + /// 同阶段内的读档还原顺序。数值越小越先执行。 /// int RestoreOrder { get; } /// 从当前运行时捕获该子系统的快照 DTO;无可存内容时可返回 null。 object Capture(); + } - /// 将 DTO 逐项还原到运行时;可为协程以等待异步加载。 - IEnumerator Restore(object dto); + /// + /// 同步快照还原 Provider。实现中不得启动 fire-and-forget 协程。 + /// + public interface ISyncSnapshotProvider : ISnapshotProvider + { + void Restore(object dto, SnapshotRestoreContext context); + } + + /// + /// 异步快照还原 Provider。所有必须等待的加载 / 状态切换须通过此协程暴露给编排层。 + /// + public interface IAsyncSnapshotProvider : ISnapshotProvider + { + IEnumerator RestoreAsync(object dto, SnapshotRestoreContext context); + } + + /// + /// 单次读档还原流程上下文。 + /// + public sealed class SnapshotRestoreContext + { + public SnapshotRestoreContext(SaveSnapshot snapshot, bool strictMode = false, Action logStep = null) + { + Snapshot = snapshot; + StrictMode = strictMode; + LogStep = logStep; + } + + public SaveSnapshot Snapshot { get; } + public bool StrictMode { get; } + public Action LogStep { get; } + + public void Log(string message) + { + LogStep?.Invoke(message); + Debug.Log($"[SnapshotRestore] {message}"); + } + + public void Warn(string message) + { + LogStep?.Invoke($"WARN: {message}"); + Debug.LogWarning($"[SnapshotRestore] {message}"); + } + + public void Error(string message) + { + LogStep?.Invoke($"ERROR: {message}"); + Debug.LogError($"[SnapshotRestore] {message}"); + } } } diff --git a/Assets/Scripts/SaveSystem/Providers/ActorSnapshotProvider.cs b/Assets/Scripts/SaveSystem/Providers/ActorSnapshotProvider.cs index cfd8b0f49..40ab832ce 100644 --- a/Assets/Scripts/SaveSystem/Providers/ActorSnapshotProvider.cs +++ b/Assets/Scripts/SaveSystem/Providers/ActorSnapshotProvider.cs @@ -1,4 +1,3 @@ -using System.Collections; using UnityEngine; namespace AibisDream.SaveSystem @@ -7,7 +6,7 @@ namespace AibisDream.SaveSystem /// sections["actor"]:场景中各角色显隐、槽位、动画状态、透明度等(ActorManager)。 /// RestoreOrder=40,依赖 scene 与 env 已就绪。 /// - public class ActorSnapshotProvider : ISnapshotProvider + public class ActorSnapshotProvider : ISyncSnapshotProvider { public string SaveId => SnapshotProviderIds.Actor; public int RestoreOrder => 40; @@ -23,14 +22,21 @@ namespace AibisDream.SaveSystem return ActorManager.Instance.CaptureSnapshot(); } - public IEnumerator Restore(object dto) + public void Restore(object dto, SnapshotRestoreContext context) { if (dto is not ActorSnapshotDto actors) { - yield break; + context.Warn("[ActorSnapshotProvider] DTO 类型不匹配,跳过还原。"); + return; } - yield return ActorManager.Instance.RestoreSnapshot(actors); + if (ActorManager.Instance == null) + { + context.Warn("[ActorSnapshotProvider] ActorManager 未初始化,跳过还原。"); + return; + } + + ActorManager.Instance.RestoreSnapshot(actors); } } } diff --git a/Assets/Scripts/SaveSystem/Providers/AudioSnapshotProvider.cs b/Assets/Scripts/SaveSystem/Providers/AudioSnapshotProvider.cs index 984967e47..abf44e1b1 100644 --- a/Assets/Scripts/SaveSystem/Providers/AudioSnapshotProvider.cs +++ b/Assets/Scripts/SaveSystem/Providers/AudioSnapshotProvider.cs @@ -1,4 +1,3 @@ -using System.Collections; using AibisDream.Kit; using UnityEngine; @@ -8,7 +7,7 @@ namespace AibisDream.SaveSystem /// sections["audio"]:FMOD 音乐/SFX 实例路径与 AMB 状态终态(AudioManager)。 /// 不记录播放进度,读档后按路径重新 CreateInstance 并 start。 /// - public class AudioSnapshotProvider : ISnapshotProvider + public class AudioSnapshotProvider : ISyncSnapshotProvider { public string SaveId => SnapshotProviderIds.Audio; public int RestoreOrder => 50; @@ -24,14 +23,21 @@ namespace AibisDream.SaveSystem return AudioManager.Instance.CaptureSnapshot(); } - public IEnumerator Restore(object dto) + public void Restore(object dto, SnapshotRestoreContext context) { if (dto is not AudioSnapshotDto audio) { - yield break; + context.Warn("[AudioSnapshotProvider] DTO 类型不匹配,跳过还原。"); + return; } - yield return AudioManager.Instance.RestoreSnapshot(audio); + if (AudioManager.Instance == null) + { + context.Warn("[AudioSnapshotProvider] AudioManager 未初始化,跳过还原。"); + return; + } + + AudioManager.Instance.RestoreSnapshot(audio); } } } diff --git a/Assets/Scripts/SaveSystem/Providers/EnvironmentSnapshotProvider.cs b/Assets/Scripts/SaveSystem/Providers/EnvironmentSnapshotProvider.cs index 54ae7175f..49582ced3 100644 --- a/Assets/Scripts/SaveSystem/Providers/EnvironmentSnapshotProvider.cs +++ b/Assets/Scripts/SaveSystem/Providers/EnvironmentSnapshotProvider.cs @@ -1,4 +1,3 @@ -using System.Collections; using UnityEngine; namespace AibisDream.SaveSystem @@ -7,7 +6,7 @@ namespace AibisDream.SaveSystem /// sections["env"]:场景内时间/天气(EnvironmentManager)。 /// RestoreOrder=20,在 scene 加载完成后、macro 之前还原环境。 /// - public class EnvironmentSnapshotProvider : ISnapshotProvider + public class EnvironmentSnapshotProvider : ISyncSnapshotProvider { public string SaveId => SnapshotProviderIds.Environment; public int RestoreOrder => 20; @@ -23,14 +22,21 @@ namespace AibisDream.SaveSystem return EnvironmentManager.Instance.CaptureSnapshot(); } - public IEnumerator Restore(object dto) + public void Restore(object dto, SnapshotRestoreContext context) { if (dto is not EnvironmentSnapshotDto env) { - yield break; + context.Warn("[EnvironmentSnapshotProvider] DTO 类型不匹配,跳过还原。"); + return; } - yield return EnvironmentManager.Instance.RestoreSnapshot(env); + if (EnvironmentManager.Instance == null) + { + context.Warn("[EnvironmentSnapshotProvider] EnvironmentManager 未初始化,跳过还原。"); + return; + } + + EnvironmentManager.Instance.RestoreSnapshot(env); } } } diff --git a/Assets/Scripts/SaveSystem/Providers/FixSnapshotProvider.cs b/Assets/Scripts/SaveSystem/Providers/FixSnapshotProvider.cs index 163a655e5..d28467114 100644 --- a/Assets/Scripts/SaveSystem/Providers/FixSnapshotProvider.cs +++ b/Assets/Scripts/SaveSystem/Providers/FixSnapshotProvider.cs @@ -7,7 +7,7 @@ namespace AibisDream.SaveSystem /// sections["fix"]:Fix 场景宏观状态(FixStateMachine)。 /// RestoreOrder=65,在 timeline(60) 之后、screen(70) 之前。 /// - public class FixSnapshotProvider : ISnapshotProvider + public class FixSnapshotProvider : IAsyncSnapshotProvider { public string SaveId => SnapshotProviderIds.Fix; public int RestoreOrder => 65; @@ -23,16 +23,17 @@ namespace AibisDream.SaveSystem return FixSystem.FixSystemCenter.Instance.CaptureSnapshot(); } - public IEnumerator Restore(object dto) + public IEnumerator RestoreAsync(object dto, SnapshotRestoreContext context) { if (dto is not FixSnapshotDto fix) { + context.Warn("[FixSnapshotProvider] DTO 类型不匹配,跳过还原。"); yield break; } if (FixSystem.FixSystemCenter.Instance == null) { - Debug.LogWarning("[FixSnapshotProvider] FixSystemCenter 未初始化,跳过还原。"); + context.Warn("[FixSnapshotProvider] FixSystemCenter 未初始化,跳过还原。"); yield break; } diff --git a/Assets/Scripts/SaveSystem/Providers/ScreenSnapshotProvider.cs b/Assets/Scripts/SaveSystem/Providers/ScreenSnapshotProvider.cs index 6fba58af8..a3860323e 100644 --- a/Assets/Scripts/SaveSystem/Providers/ScreenSnapshotProvider.cs +++ b/Assets/Scripts/SaveSystem/Providers/ScreenSnapshotProvider.cs @@ -1,12 +1,10 @@ -using System.Collections; - namespace AibisDream.SaveSystem { /// /// sections["screen"]:屏幕饱和度与演出淡入淡出遮罩。 /// 委托 ;RestoreOrder=70,通常在表现类最后还原。 /// - public class ScreenSnapshotProvider : ISnapshotProvider + public class ScreenSnapshotProvider : ISyncSnapshotProvider { public string SaveId => SnapshotProviderIds.Screen; public int RestoreOrder => 70; @@ -16,15 +14,15 @@ namespace AibisDream.SaveSystem return ScreenSnapshotHelper.Capture(); } - public IEnumerator Restore(object dto) + public void Restore(object dto, SnapshotRestoreContext context) { if (dto is not ScreenSnapshotDto screen) { - yield break; + context.Warn("[ScreenSnapshotProvider] DTO 类型不匹配,跳过还原。"); + return; } ScreenSnapshotHelper.Restore(screen); - yield break; } } } diff --git a/Assets/Scripts/SaveSystem/Providers/TimelineSnapshotProvider.cs b/Assets/Scripts/SaveSystem/Providers/TimelineSnapshotProvider.cs index c24e038ce..20d81e2ab 100644 --- a/Assets/Scripts/SaveSystem/Providers/TimelineSnapshotProvider.cs +++ b/Assets/Scripts/SaveSystem/Providers/TimelineSnapshotProvider.cs @@ -7,7 +7,7 @@ namespace AibisDream.SaveSystem /// sections["timeline"]:各 PlayableDirector 的终/初态(TimelineCenter)。 /// 按 D1.1 只存 AtStart/AtEnd/Stopped,不存时间轴中间帧。 /// - public class TimelineSnapshotProvider : ISnapshotProvider + public class TimelineSnapshotProvider : IAsyncSnapshotProvider { public string SaveId => SnapshotProviderIds.Timeline; public int RestoreOrder => 60; @@ -23,15 +23,21 @@ namespace AibisDream.SaveSystem return TimelineCenter.Instance.CaptureSnapshot(); } - public IEnumerator Restore(object dto) + public IEnumerator RestoreAsync(object dto, SnapshotRestoreContext context) { if (dto is not TimelineSnapshotDto timeline) { + context.Warn("[TimelineSnapshotProvider] DTO 类型不匹配,跳过还原。"); yield break; } - TimelineCenter.Instance.RestoreSnapshot(timeline); - yield break; + if (TimelineCenter.Instance == null) + { + context.Warn("[TimelineSnapshotProvider] TimelineCenter 未初始化,跳过还原。"); + yield break; + } + + yield return TimelineCenter.Instance.RestoreSnapshotAsync(timeline); } } }