feat: 增强存档管理,优化异步存档逻辑,添加错误处理机制
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using BaseGames.Core.Events;
|
using BaseGames.Core.Events;
|
||||||
@@ -32,7 +33,16 @@ namespace BaseGames.Core.Save
|
|||||||
if (_timer >= _intervalSeconds)
|
if (_timer >= _intervalSeconds)
|
||||||
{
|
{
|
||||||
_timer = 0f;
|
_timer = 0f;
|
||||||
_ = _saveManager.SaveAsync(EmergencySlot);
|
RunFireAndForget(_saveManager.SaveAsync(EmergencySlot), "EmergencySave");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async void RunFireAndForget(System.Threading.Tasks.Task task, string context)
|
||||||
|
{
|
||||||
|
try { await task; }
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.LogError($"[EmergencySave] {context} 失败: {e.Message}\n{e.StackTrace}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,12 +88,13 @@ namespace BaseGames.Core.Save
|
|||||||
_current.Meta.SlotIndex = targetSlot;
|
_current.Meta.SlotIndex = targetSlot;
|
||||||
_current.Meta.SaveCount++;
|
_current.Meta.SaveCount++;
|
||||||
|
|
||||||
// 先清空 checksum,序列化并计算,再序列化含 checksum 的最终版本
|
// 清空 checksum 后序列化,计算 HMAC,然后直接注入序列化字符串——避免第二次完整序列化。
|
||||||
// 使用 Formatting.None 减少序列化字符串体积和 GC 分配
|
// Base64 字符集(A-Z a-z 0-9 + / =)不含需要 JSON 转义的字符,string.Replace 是安全替换。
|
||||||
_current.Meta.Checksum = null;
|
_current.Meta.Checksum = null;
|
||||||
string jsonForChecksum = JsonConvert.SerializeObject(_current, Formatting.None);
|
string jsonForChecksum = JsonConvert.SerializeObject(_current, Formatting.None);
|
||||||
_current.Meta.Checksum = ComputeChecksum(jsonForChecksum);
|
string checksum = ComputeChecksum(jsonForChecksum);
|
||||||
string finalJson = JsonConvert.SerializeObject(_current, Formatting.None);
|
string finalJson = jsonForChecksum.Replace("\"Checksum\":null", $"\"Checksum\":\"{checksum}\"");
|
||||||
|
_current.Meta.Checksum = checksum; // 同步回内存,保持对象一致
|
||||||
|
|
||||||
await _storage.WriteAsync(targetSlot, finalJson);
|
await _storage.WriteAsync(targetSlot, finalJson);
|
||||||
|
|
||||||
|
|||||||
@@ -128,8 +128,7 @@ namespace BaseGames.Core.Save
|
|||||||
[Serializable]
|
[Serializable]
|
||||||
public class QuestSaveData
|
public class QuestSaveData
|
||||||
{
|
{
|
||||||
public Dictionary<string, QuestState> QuestStates = new();
|
public Dictionary<string, QuestState> QuestStates = new();
|
||||||
public List<string> AvailableQuestIds = new();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
|
|||||||
@@ -50,10 +50,7 @@ namespace BaseGames.Core.Save
|
|||||||
// 2.2 删除 PlayerSaveData.ShieldHP / ShieldIsBroken(护盾在存档点始终全满,无需持久化)。
|
// 2.2 删除 PlayerSaveData.ShieldHP / ShieldIsBroken(护盾在存档点始终全满,无需持久化)。
|
||||||
// SettingsSaveData.Language 字段保留(由 LocalizationManager 负责按存档槽读写)。
|
// SettingsSaveData.Language 字段保留(由 LocalizationManager 负责按存档槽读写)。
|
||||||
// 旧存档中已删除的字段由 Newtonsoft.Json 的 [JsonExtensionData] 忽略,无需额外处理。
|
// 旧存档中已删除的字段由 Newtonsoft.Json 的 [JsonExtensionData] 忽略,无需额外处理。
|
||||||
// Equipment.MaxNotches:旧存档若为 0,EquipmentManager.OnLoad 回退到初始 Notch 数量。
|
// Equipment.MaxNotches:旧存档若为 0,EquipmentManager.OnLoad 回退到 config.initialNotchCount,无需额外处理。
|
||||||
if (data.Equipment != null && data.Equipment.MaxNotches == 0)
|
|
||||||
data.Equipment.MaxNotches = 0; // 保持 0,OnLoad 回退到 config.initialNotchCount
|
|
||||||
|
|
||||||
Debug.Log("[SaveMigrator] 从 '2.1' 迁移至 '2.2'。");
|
Debug.Log("[SaveMigrator] 从 '2.1' 迁移至 '2.2'。");
|
||||||
v = "2.2";
|
v = "2.2";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,7 +129,6 @@ namespace BaseGames.Quest
|
|||||||
public void OnSave(SaveData data)
|
public void OnSave(SaveData data)
|
||||||
{
|
{
|
||||||
data.Quests.QuestStates.Clear();
|
data.Quests.QuestStates.Clear();
|
||||||
data.Quests.AvailableQuestIds.Clear();
|
|
||||||
foreach (var (id, state) in _questStates)
|
foreach (var (id, state) in _questStates)
|
||||||
{
|
{
|
||||||
data.Quests.QuestStates[id] = new BaseGames.Core.Save.QuestState
|
data.Quests.QuestStates[id] = new BaseGames.Core.Save.QuestState
|
||||||
@@ -138,7 +137,6 @@ namespace BaseGames.Quest
|
|||||||
ObjectiveIndex = 0,
|
ObjectiveIndex = 0,
|
||||||
ProgressCounts = BuildProgressList(id),
|
ProgressCounts = BuildProgressList(id),
|
||||||
};
|
};
|
||||||
if (state == QuestStateEnum.Available) data.Quests.AvailableQuestIds.Add(id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using BaseGames.Core;
|
using BaseGames.Core;
|
||||||
using BaseGames.Core.Events;
|
using BaseGames.Core.Events;
|
||||||
@@ -68,7 +70,7 @@ namespace BaseGames.World
|
|||||||
// 触发存档:OnSave() 由 SaveAsync 回调所有 ISaveable,包含本组件
|
// 触发存档:OnSave() 由 SaveAsync 回调所有 ISaveable,包含本组件
|
||||||
var svc = ServiceLocator.GetOrDefault<ISaveService>();
|
var svc = ServiceLocator.GetOrDefault<ISaveService>();
|
||||||
if (svc != null)
|
if (svc != null)
|
||||||
_ = svc.SaveAsync(svc.ActiveSlot);
|
RunFireAndForget(svc.SaveAsync(svc.ActiveSlot));
|
||||||
else
|
else
|
||||||
Debug.LogWarning("[SavePoint] ISaveService 未注册,跳过存档。", this);
|
Debug.LogWarning("[SavePoint] ISaveService 未注册,跳过存档。", this);
|
||||||
}
|
}
|
||||||
@@ -101,6 +103,14 @@ namespace BaseGames.World
|
|||||||
_isActivated = !string.IsNullOrEmpty(_savePointId)
|
_isActivated = !string.IsNullOrEmpty(_savePointId)
|
||||||
&& data.World.ActivatedSavePoints.Contains(_savePointId);
|
&& data.World.ActivatedSavePoints.Contains(_savePointId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async void RunFireAndForget(Task task)
|
||||||
|
{
|
||||||
|
try { await task; }
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[SavePoint] 存档失败: {e.Message}\n{e.StackTrace}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user