#if UNITY_EDITOR || DEVELOPMENT_BUILD using System; using System.Linq; using System.Threading.Tasks; using AibisDream.Utility; using UnityEngine; namespace AibisDream.SaveSystem { /// 正式自动档成功后使用的非阻塞测试存档旁路。 public static class TestSaveRecorder { private static TestSaveRepository _repository; public static bool IsRecording { get; private set; } public static TestSaveRepository Repository => _repository ??= new TestSaveRepository(ConstRef.TestSavePath); public static event Action LibraryChanged; [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void ResetRuntimeState() { IsRecording = false; _repository = null; } public static void SetRecording(bool recording) { IsRecording = recording; } internal static TestSaveRecordRequest CreateRequest(SaveSnapshot snapshot, byte[] thumbnail) { if (!IsRecording || snapshot?.anchor == null || string.IsNullOrWhiteSpace(snapshot.anchor.sceneSoName) || string.IsNullOrWhiteSpace(snapshot.anchor.yarnProjectId) || string.IsNullOrWhiteSpace(snapshot.anchor.nodeName)) { return null; } var anchor = snapshot.anchor; var chapter = GameManager.Instance?.RuntimeChapters? .FirstOrDefault(item => item != null && string.Equals(item.name, anchor.sceneSoName, StringComparison.Ordinal) && string.Equals(item.yarnProject?.name, anchor.yarnProjectId, StringComparison.Ordinal)); var dedupeKey = BuildDedupeKey(anchor.sceneSoName, anchor.yarnProjectId, anchor.nodeName); return new TestSaveRecordRequest { Snapshot = snapshot, Thumbnail = thumbnail, DedupeKey = dedupeKey, EntryId = TestSaveRepository.StableHash(dedupeKey), ChapterId = chapter?.name ?? anchor.sceneSoName, ChapterTitle = string.IsNullOrWhiteSpace(chapter?.title) ? anchor.sceneSoName : chapter.title, SceneSoName = anchor.sceneSoName, YarnProjectId = anchor.yarnProjectId, NodeName = anchor.nodeName, SceneName = snapshot.scene?.sceneName, GameVersion = snapshot.gameVersion }; } internal static void Enqueue(TestSaveRecordRequest request) { if (request == null) return; _ = Task.Run(() => { try { Repository.Record(request); LibraryChanged?.Invoke(); } catch (Exception ex) { Debug.LogError($"[TestSaveRecorder] 测试存档旁路写入失败,不影响正式存档:{ex}"); } }); } public static string BuildDedupeKey(string sceneSoName, string yarnProjectId, string nodeName) { return $"{sceneSoName ?? string.Empty}\n{yarnProjectId ?? string.Empty}\n{nodeName ?? string.Empty}"; } } } #endif