124 lines
4.5 KiB
C#
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
|