Files
aibis-dream/Assets/Scripts/SaveSystem/Development/TestSaveRecorder.cs
T

124 lines
4.5 KiB
C#

#if UNITY_EDITOR || DEVELOPMENT_BUILD
using System;
using System.Linq;
using System.Threading.Tasks;
using AibisDream.Utility;
using UnityEngine;
namespace AibisDream.SaveSystem
{
/// <summary>正式自动档成功后使用的非阻塞测试存档旁路。</summary>
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,
string saveTrigger,
string resumeMode)
{
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 expectedResumeMode = anchor.startDialogueOnRestore
? nameof(SaveResumeMode.RestartNode)
: nameof(SaveResumeMode.StateOnly);
if (!string.Equals(resumeMode, expectedResumeMode, StringComparison.Ordinal))
{
throw new InvalidOperationException(
$"测试存档恢复模式与 snapshot anchor 不一致: meta={resumeMode}, " +
$"anchor={expectedResumeMode}");
}
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,
resumeMode);
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,
SaveTrigger = saveTrigger,
ResumeMode = resumeMode,
StartDialogueOnRestore = anchor.startDialogueOnRestore,
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,
string resumeMode = null)
{
var baseKey =
$"{sceneSoName ?? string.Empty}\n{yarnProjectId ?? string.Empty}\n{nodeName ?? string.Empty}";
return string.IsNullOrEmpty(resumeMode)
|| string.Equals(
resumeMode,
nameof(SaveResumeMode.RestartNode),
StringComparison.Ordinal)
? baseKey
: $"{baseKey}\n{resumeMode}";
}
}
}
#endif