From 0b0d22d0094af5075e1f8ae34d392036b9ba06f7 Mon Sep 17 00:00:00 2001 From: Ding Yuntian <1491671119@qq.com> Date: Thu, 18 Jun 2026 14:34:29 +0800 Subject: [PATCH] =?UTF-8?q?feat(save):=20=E9=AA=8C=E8=AF=81=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E5=A2=9E=E5=8A=A0=E8=BF=90=E8=A1=8C=E6=97=B6=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E5=AD=98=E6=A1=A3=E5=8E=86=E5=8F=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- .../SaveSystemValidationWindow.cs | 163 +++++++++++++++++- 1 file changed, 160 insertions(+), 3 deletions(-) diff --git a/Assets/Editor/SaveSystemValidation/SaveSystemValidationWindow.cs b/Assets/Editor/SaveSystemValidation/SaveSystemValidationWindow.cs index 2e7cefdcc..9c591c438 100644 --- a/Assets/Editor/SaveSystemValidation/SaveSystemValidationWindow.cs +++ b/Assets/Editor/SaveSystemValidation/SaveSystemValidationWindow.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -20,6 +20,8 @@ namespace AibisDream.SaveSystem.Editor private const string MenuPath = "AIBIS/存档系统验证工具"; private const string WindowTitle = "SaveSystem Validator"; + private const float SaveHistoryBoxMinHeight = 400f; + private Vector2 _scrollPosition; private string _lastLog = string.Empty; private int _manualSlotIndex = 1; @@ -27,10 +29,109 @@ namespace AibisDream.SaveSystem.Editor private string _testNodeName = "SomeNode"; private string _testTags = ""; + private readonly List _saveHistory = new(); + private string _lastObservedSavedAt; + + /// 运行时自动存档历史条目,仅内存缓存。 + private class SaveHistoryEntry + { + public string SavedAt; + public string NodeName; + public string SceneName; + } + [MenuItem(MenuPath)] public static void Open() { - GetWindow(WindowTitle); + var window = GetWindow(WindowTitle); + window.minSize = new Vector2(420, 640); + } + + private void OnEnable() + { + EditorApplication.update += OnEditorUpdate; + EditorApplication.playModeStateChanged += OnPlayModeStateChanged; + InitializeLastObservedSavedAt(); + } + + private void OnDisable() + { + EditorApplication.update -= OnEditorUpdate; + EditorApplication.playModeStateChanged -= OnPlayModeStateChanged; + } + + private void OnPlayModeStateChanged(PlayModeStateChange state) + { + if (state == PlayModeStateChange.EnteredPlayMode) + { + _saveHistory.Clear(); + InitializeLastObservedSavedAt(); + TrySeedHistoryFromCurrentAutoSlot(); + Repaint(); + return; + } + + if (state == PlayModeStateChange.ExitingPlayMode) + { + _saveHistory.Clear(); + _lastObservedSavedAt = null; + } + } + + private void TrySeedHistoryFromCurrentAutoSlot() + { + var meta = SlotManager.LoadMeta(SlotIndex.Auto); + if (meta == null) + { + return; + } + + _saveHistory.Add(CreateHistoryEntry(meta)); + _lastObservedSavedAt = meta.savedAt; + } + + private static SaveHistoryEntry CreateHistoryEntry(SlotMeta meta) + { + return new SaveHistoryEntry + { + SavedAt = meta.savedAt, + NodeName = string.IsNullOrEmpty(meta.nodeName) ? "(无节点)" : meta.nodeName, + SceneName = meta.sceneName + }; + } + + private void InitializeLastObservedSavedAt() + { + var meta = SlotManager.LoadMeta(SlotIndex.Auto); + _lastObservedSavedAt = meta?.savedAt; + } + + /// + /// 监听 slot_0 的 meta 变化,运行时每次自动存档都会记录到内存列表中。 + /// 纯 Editor 行为,不侵入运行时代码。 + /// + private void OnEditorUpdate() + { + if (!Application.isPlaying) + { + return; + } + + var meta = SlotManager.LoadMeta(SlotIndex.Auto); + if (meta == null) + { + return; + } + + if (!string.IsNullOrEmpty(_lastObservedSavedAt) && _lastObservedSavedAt == meta.savedAt) + { + return; + } + + _lastObservedSavedAt = meta.savedAt; + _saveHistory.Add(CreateHistoryEntry(meta)); + + Repaint(); } private void OnGUI() @@ -40,6 +141,8 @@ namespace AibisDream.SaveSystem.Editor DrawHeader(); DrawSlotOverview(); GUILayout.Space(12); + DrawSaveHistorySection(); + GUILayout.Space(12); DrawDirectCaptureSection(); GUILayout.Space(12); DrawAutoSaveSimulationSection(); @@ -131,6 +234,51 @@ namespace AibisDream.SaveSystem.Editor } } + private void DrawSaveHistorySection() + { + EditorGUILayout.LabelField("自动存档历史 (运行时)", EditorStyles.boldLabel); + + using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox, GUILayout.MinHeight(SaveHistoryBoxMinHeight))) + { + if (!Application.isPlaying) + { + EditorGUILayout.HelpBox( + "进入 Play Mode 后,每次 slot_0 自动存档都会在此记录节点名与时间。\n" + + "数据仅保存在当前窗口内存中,退出 Play Mode 后清空。", + MessageType.Info); + } + else if (_saveHistory.Count == 0) + { + EditorGUILayout.LabelField("等待自动存档…", EditorStyles.wordWrappedLabel); + EditorGUILayout.LabelField( + "触发 content / hub / linear 节点,或点击下方 TryAutoSave。", + EditorStyles.miniLabel); + } + else + { + EditorGUILayout.LabelField($"共 {_saveHistory.Count} 条记录", EditorStyles.miniLabel); + + for (var i = 0; i < _saveHistory.Count; i++) + { + var entry = _saveHistory[i]; + EditorGUILayout.LabelField( + $"{i + 1}. [{entry.SavedAt}] {entry.NodeName}", + EditorStyles.wordWrappedLabel); + EditorGUILayout.LabelField( + $" 场景: {entry.SceneName ?? "N/A"}", + EditorStyles.miniLabel); + } + + GUILayout.FlexibleSpace(); + + if (GUILayout.Button("清空历史记录")) + { + _saveHistory.Clear(); + } + } + } + } + private void DrawManualSlotSection() { EditorGUILayout.LabelField("手动档操作", EditorStyles.boldLabel); @@ -243,7 +391,16 @@ namespace AibisDream.SaveSystem.Editor var beforeLatest = SlotManager.GetLatestSlotIndex(); var beforeSnapshot = SlotManager.LoadSnapshot(0); - SaveRestoreOrchestrator.TryAutoSave(); + if (!SavePointEvaluator.CanAutoSave(out var rejectReason)) + { + Log($"CanAutoSave 拒绝: {rejectReason}"); + return; + } + + var routine = SaveRestoreOrchestrator.AutoSaveRoutine(); + while (routine.MoveNext()) + { + } var afterSnapshot = SlotManager.LoadSnapshot(0); var afterLatest = SlotManager.GetLatestSlotIndex();