using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Threading.Tasks; // 引入 Task for async/await using Newtonsoft.Json; using Newtonsoft.Json.Linq; using UnityEngine; using Utils; namespace Managers { /// /// 用于标记那些包含可存储数据的单例类。 /// 实现此接口的单例应在SaveManager初始化后注册自身。 /// public interface ISavableSingleton { } /// /// 用于标记在单例类中需要被SaveManager存储的属性。 /// [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] public class SavableAttribute : Attribute { public SavableAttribute(string key = null) { Key = key; } /// /// 获取或设置用于存储的唯一键。如果为空,则使用属性名称。 /// public string Key { get; } } public class SaveManager : Singleton, ILaunchManager { // 存档文件名 private const string SaveFileName = "game_save.json"; // 存储所有需要保存状态的单例实例 private readonly Dictionary _savableSingletons = new(); // 存档路径 private static string SaveFilePath => Path.Combine("Save", SaveFileName); /// /// 指示管理器是否已完成初始化。 /// public bool Completed { get; set; } // 新增:实现 ILaunchManager 接口的 Completed 属性 public string StepDescription => "加载存档中"; /// /// 实现ILaunchManager接口的Init方法,在启动时加载存档。 /// /// 一个表示异步操作完成的 Task。 public async Task Init() // 接口变更:方法签名变为 async Task { // 如果已经完成初始化,则直接返回,避免重复执行 if (Completed) return; LoadGame(); // 启动时加载存档 Completed = true; // 在加载逻辑完成后设置 Completed 为 true // 由于 Init 方法内部当前没有真正的异步操作,不需要显式 await Task.CompletedTask; // 编译器会为同步的 async Task 方法自动生成一个已完成的 Task。 } /// /// 实现ILaunchManager接口的Clear方法,Clear是重载不做任何事。 /// 根据用户指示,此方法不应改变管理器自身的 Completed 状态,且不执行任何清理操作。 /// public void Clear() { // 按照用户指示,此方法为空实现,不影响 SaveManager 的完成状态或数据。 } ~SaveManager() { SaveGame(); // 在销毁时保存游戏 } /// /// 注册一个需要保存状态的单例实例。 /// /// 实现ISavableSingleton接口的单例实例。 public void RegisterSavable(ISavableSingleton singleton) { var singletonType = singleton.GetType(); if (_savableSingletons.TryAdd(singletonType, singleton)) { // DEBUG: Debug.Log($"SaveManager: 已注册可保存单例: {singletonType.Name}"); } else { Debug.LogWarning($"SaveManager: 单例 {singletonType.Name} 已注册。"); } } /// /// 移除一个已注册的单例实例。 /// /// 要移除的单例实例。 public void UnregisterSavable(ISavableSingleton singleton) { var singletonType = singleton.GetType(); if (_savableSingletons.Remove(singletonType)) { // DEBUG: Debug.Log($"SaveManager: 已移除可保存单例: {singletonType.Name}"); } } /// /// 保存游戏数据到文件。 /// public void SaveGame() { var dataToSave = new Dictionary(); foreach (var singletonEntry in _savableSingletons) { var singletonType = singletonEntry.Key; var singletonInstance = singletonEntry.Value; foreach (var prop in GetSavableProperties(singletonType)) { var key = GetSavableKey(prop, singletonType); var value = prop.GetValue(singletonInstance); try { dataToSave[key] = JToken.FromObject(value); } catch (Exception ex) { Debug.LogError( $"SaveManager: 无法将属性 '{key}' (类型: {prop.PropertyType.Name}) 转换为 JToken: {ex.Message}"); } } } try { var directoryPath = Path.GetDirectoryName(SaveFilePath); if (!string.IsNullOrEmpty(directoryPath) && !Directory.Exists(directoryPath)) Directory.CreateDirectory(directoryPath); // DEBUG: Debug.Log($"SaveManager: 文件夹 '{directoryPath}' 已创建。"); var settings = new JsonSerializerSettings { Formatting = Formatting.Indented, TypeNameHandling = TypeNameHandling.Auto }; var jsonString = JsonConvert.SerializeObject(dataToSave, settings); File.WriteAllText(SaveFilePath, jsonString); // DEBUG: Debug.Log($"SaveManager: 游戏数据已保存到 '{SaveFilePath}'。"); } catch (Exception ex) { Debug.LogError($"SaveManager: 保存游戏数据到 '{SaveFilePath}' 失败: {ex.Message}"); } } /// /// 从文件加载游戏数据。 /// public void LoadGame() { if (!File.Exists(SaveFilePath)) // DEBUG: Debug.Log($"SaveManager: 未找到存档文件 '{SaveFilePath}'。将使用默认值启动。"); return; try { var jsonString = File.ReadAllText(SaveFilePath); var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto }; var savedData = JsonConvert.DeserializeObject>(jsonString, settings); if (savedData == null) { Debug.LogError("SaveManager: 存档文件为空或已损坏。将使用默认值启动。"); return; } foreach (var singletonEntry in _savableSingletons) { var singletonType = singletonEntry.Key; var singletonInstance = singletonEntry.Value; foreach (var prop in GetSavableProperties(singletonType)) { var key = GetSavableKey(prop, singletonType); if (savedData.TryGetValue(key, out var jToken)) try { var value = jToken.ToObject(prop.PropertyType, JsonSerializer.Create(settings)); prop.SetValue(singletonInstance, value); } catch (Exception innerEx) { Debug.LogError( $"SaveManager: 无法反序列化类型 '{singletonType.Name}' 的属性 '{key}' (类型: {prop.PropertyType.Name}): {innerEx.Message}"); } else Debug.LogWarning( $"SaveManager: 存档文件中未找到类型 '{singletonType.Name}' 的属性 '{key}'。将保留当前值。"); } } } catch (JsonException ex) { Debug.LogError($"SaveManager: 解析存档文件失败 (JSON 错误): {ex.Message}"); File.Delete(SaveFilePath); // 建议删除损坏的存档文件 Debug.LogWarning("SaveManager: 已删除损坏的存档文件以防止进一步问题。"); } catch (Exception ex) { Debug.LogError( $"SaveManager: 从 '{SaveFilePath}' 加载游戏数据失败 (文件 I/O 或其他错误): {ex.Message}"); } } /// /// 获取一个类型中所有带有SavableAttribute的公共实例属性。 /// private IEnumerable GetSavableProperties(Type type) { return type.GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(prop => prop.CanRead && prop.CanWrite && prop.IsDefined(typeof(SavableAttribute), true)); } /// /// 根据属性和其所属类型生成一个唯一的存储键。 /// private string GetSavableKey(PropertyInfo prop, Type singletonType) { var savableAttr = prop.GetCustomAttribute(); return savableAttr?.Key ?? $"{singletonType.Name}.{prop.Name}"; } /// /// 获取给定类型的默认值。 /// private object GetDefaultValue(Type type) { if (type.IsValueType) return Activator.CreateInstance(type); return null; } } }