using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using AibisDream.Framework; using AibisDream.Utility; using Newtonsoft.Json.Linq; using UnityEngine; using Yarn.Unity; namespace AibisDream { public class StorageSystem : VariableStorageBehaviour { private const string VariablePrefix = "$"; private const string GlobalPrefix = "$global."; private const string DataPrefix = "$data."; private const int MaxSaveNum = 200; private static readonly string SaveFilePath = Application.streamingAssetsPath + "/SaveFiles"; #region 存储部分 private readonly Dictionary _variableDict = new(); private readonly DataContainer _dataContainer = new(); #endregion #region 数据类注册和注销 public void RegisterData(T data) where T : IData { _dataContainer.Register(data); } public void UnregisterData() where T : class, IData { _dataContainer.Unregister(); } private void Update() { if (Input.GetKeyDown(KeyCode.K)) { Debug.Log(_dataContainer.Count); } } #endregion #region 变量设置 public override void SetValue(string variableName, bool boolValue) { SetValue(variableName, boolValue); } public override void SetValue(string variableName, string stringValue) { SetValue(variableName, stringValue); } public override void SetValue(string variableName, float floatValue) { SetValue(variableName, floatValue); } private void SetValue(string variableName, T value) { var variableType = ValidateVariableName(variableName); if (variableType == VariableType.Illegal) { Debug.LogError($"{variableName}: 缺少$前缀"); return; } if (variableType == VariableType.DataField) { Debug.LogError($"{variableName}: 暂时不支持直接设置系统数据"); return; } // Global和Local直接设置 _variableDict[variableName] = value; // 数据设置事件广播 var variableItem = new VariableItem { variableName = variableName, value = value }; EnumEventSystem.Global.Send(StorageEvent.VariableSet, variableItem); } public override void Clear() { _variableDict.Clear(); } public void ClearLocal() { // 收集所有Local类型的变量 var locals = _variableDict.Keys.Where(item => ValidateVariableName(item) == VariableType.Local).ToArray(); // 清除 foreach (var localKey in locals) { _variableDict.Remove(localKey); } } #endregion #region 变量获取 public override bool Contains(string variableName) { var variableType = ValidateVariableName(variableName); if (variableType == VariableType.Illegal) { return false; } return variableType == VariableType.DataField ? _dataContainer.TryGetFieldValue(variableName, out _) : _variableDict.ContainsKey(variableName); } public override bool TryGetValue(string variableName, out T result) { var variableType = ValidateVariableName(variableName); // 非法的直接pass if (variableType == VariableType.Illegal) { result = default; return false; } // 在系统数据中查找 if (variableType == VariableType.DataField) { return _dataContainer.TryGetFieldValue(variableName, out result); } // 直接查找数据 if (_variableDict.TryGetValue(variableName, out var value)) { if (value is T target) { result = target; return true; } } result = default; return false; } #endregion #region 序列化 public override void SetAllVariables(Dictionary floats, Dictionary strings, Dictionary bools, bool clear = true) { if (clear) { Clear(); } foreach (var value in floats) { SetValue(value.Key, value.Value); } foreach (var value in strings) { SetValue(value.Key, value.Value); } foreach (var value in bools) { SetValue(value.Key, value.Value); } } public override (Dictionary FloatVariables, Dictionary StringVariables, Dictionary BoolVariables) GetAllVariables() { var floatDict = new Dictionary(); var stringDict = new Dictionary(); var boolDict = new Dictionary(); foreach (var variablePair in _variableDict) { switch (variablePair.Value) { case float: { var value = Convert.ToSingle(variablePair.Value); floatDict.Add(variablePair.Key, value); break; } case string: { var value = Convert.ToString(variablePair.Value); stringDict.Add(variablePair.Key, value); break; } case bool: { var value = Convert.ToBoolean(variablePair.Value); boolDict.Add(variablePair.Key, value); break; } default: Debug.Log($"{variablePair.Key} is not a valid type"); break; } } return (floatDict, stringDict, boolDict); } #endregion #region 保存/加载 public void Save() { // 先序列化Yarn变量 var saveFile = new JObject(); // 保存版本号 saveFile["version"] = $"Ver.{Application.version}"; // 保存系统数据 saveFile["systemData"] = _dataContainer.SaveAsJson(); // 保存对话变量 var (floatDict, stringDict, boolDict) = GetAllVariables(); saveFile["floatDict"] = JObject.FromObject(floatDict); saveFile["stringDict"] = JObject.FromObject(stringDict); saveFile["boolDict"] = JObject.FromObject(boolDict); // 存为Json JsonUtil.SaveJObject(saveFile, GetSavePath()); #if !UNITY_EDITOR DeleteOldSave(); #endif } private void DeleteOldSave() { var fileList = Directory.GetFiles(SaveFilePath); if (fileList.Length > MaxSaveNum) { var sorted = fileList.OrderByDescending(f => f).ToArray(); for (var i = MaxSaveNum; i < fileList.Length; i++) { File.Delete(sorted[i]); } } } public void Load(string savePath) { // 先将存档文件转为JObject var saveData = JsonUtil.ReadJObject(savePath); // 加载Yarn数据 var floatDict = saveData["floatDict"]?.ToObject>(); var stringDict = saveData["stringDict"]?.ToObject>(); var boolDict = saveData["boolDict"]?.ToObject>(); SetAllVariables(floatDict, stringDict, boolDict); // 加载系统数据 if (saveData["systemData"] is not JObject systemDataJson) { Debug.Log("维修数据读取问题"); return; } StartCoroutine(_dataContainer.LoadByJson(systemDataJson)); } private string GetSavePath() { string gameStage; if (TryGetValue("$gameStage", out float gameStageFloat)) { gameStage = gameStageFloat.ToString(CultureInfo.InvariantCulture); } else { TryGetValue("$gameStage", out string gameStageStr); gameStage = gameStageStr; } return $"{SaveFilePath}/{DateTime.Now:yyyyMMdd_HHmmss}_{GameManager.Instance.GetSceneSoName()}_{gameStage}"; } #endregion private static VariableType ValidateVariableName(string variableName) { if (string.IsNullOrWhiteSpace(variableName) || !variableName.StartsWith(VariablePrefix)) { return VariableType.Illegal; } if (variableName.StartsWith(GlobalPrefix)) { return VariableType.Global; } if (variableName.StartsWith(DataPrefix)) { return VariableType.DataField; } return VariableType.Local; } #region 单例部分 public static StorageSystem Instance { get; private set; } private void Awake() { if (Instance != null) { Destroy(gameObject); } Instance = this; } #endregion } public enum VariableType { Local, Global, DataField, Illegal } public struct VariableItem { public string variableName; public object value; } }