using System; using System.Collections.Generic; using System.IO; using System.Text.Json; using System.Text.Json.Serialization; using Godot; using Models; namespace Core; /// /// 内容加载与合并(基础 + Mod) /// 设计说明: /// 1) 通过 IContentSource 抽象读取来源,支持 res:// 与 user://mods。 /// 2) ContentRegistry 负责合并,同 Id 以后加载覆盖先加载。 /// 注意事项: /// - 真实加载逻辑应避免在主线程做大规模 IO。 /// 未来扩展: /// - 可加入“补丁合并策略”(例如列表合并/字段覆盖)。 /// public interface IContentSource { /// /// 优先级 /// int Priority { get; } /// /// 加载所有指定类型的对象 /// /// 对象类型 /// 对象集合 IEnumerable LoadAll() where T : class; } /// /// 内容合并模式 /// public enum ContentMergeMode { Override, // 覆盖 KeepFirst // 保留首次 } /// /// 内容注册表 /// public sealed class ContentRegistry { private readonly List _sources = new(); /// /// 合并模式 /// public ContentMergeMode MergeMode { get; set; } = ContentMergeMode.Override; /// /// 注册内容源 /// /// 内容源 public void RegisterSource(IContentSource source) { _sources.Add(source); _sources.Sort((a, b) => a.Priority.CompareTo(b.Priority)); } /// /// 构建游戏内容数据库 /// /// 游戏内容数据库 public GameContentDatabase BuildDatabase() { var db = new GameContentDatabase(); Merge(db.Disciplines, LoadAll(), d => d.Header.Id); Merge(db.Archetypes, LoadAll(), d => d.Header.Id); Merge(db.Roles, LoadAll(), d => d.Header.Id); Merge(db.Traits, LoadAll(), d => d.Header.Id); Merge(db.Tasks, LoadAll(), d => d.Header.Id); Merge(db.Items, LoadAll(), d => d.Header.Id); Merge(db.Papers, LoadAll(), d => d.Header.Id); Merge(db.RoguelitePerks, LoadAll(), d => d.Header.Id); return db; } /// /// 从所有源加载指定类型的所有对象 /// /// 对象类型 /// 对象集合 private IEnumerable LoadAll() where T : class { foreach (var source in _sources) { foreach (var item in source.LoadAll()) { yield return item; } } } /// /// 合并对象到目标字典 /// /// 对象类型 /// 目标字典 /// 对象集合 /// ID选择器 private void Merge(Dictionary target, IEnumerable items, Func idSelector) where T : class { foreach (var item in items) { var id = idSelector(item); if (string.IsNullOrWhiteSpace(id)) { continue; } if (target.ContainsKey(id)) { if (MergeMode == ContentMergeMode.Override) { target[id] = item; } } else { target[id] = item; } } } } /// /// 资源读取示例:res:// 中的 .tres/.res /// 这里只给出接口骨架,具体解析留给后续实现。 /// public sealed class ResourceContentSource : IContentSource { /// /// 优先级 /// public int Priority { get; } /// /// 资源路径列表 /// public List ResourcePaths { get; } = new(); /// /// 构造函数 /// /// 优先级 public ResourceContentSource(int priority) { Priority = priority; } /// /// 加载所有指定类型的对象 /// /// 对象类型 /// 对象集合 public IEnumerable LoadAll() where T : class { foreach (var path in ResourcePaths) { if (string.IsNullOrWhiteSpace(path)) { continue; } var resource = ResourceLoader.Load(path); if (resource == null) { continue; } foreach (var item in ExtractResources(resource)) { yield return item; } } } /// /// 从资源中提取对象 /// /// 对象类型 /// 资源 /// 对象集合 private IEnumerable ExtractResources(Resource resource) where T : class { if (resource is IContentResource content) { if (content.GetDefinitionType() == typeof(T)) { if (content.ToDefinition() is T typed) { yield return typed; } } yield break; } if (resource is IContentResourceCollection collection) { foreach (var item in collection.GetItems()) { if (item.GetDefinitionType() == typeof(T)) { if (item.ToDefinition() is T typed) { yield return typed; } } } } } } /// /// 资源读取示例:json/yaml/自定义格式(Mod 友好) /// public sealed class JsonContentSource : IContentSource { /// /// 优先级 /// public int Priority { get; } /// /// 数据路径列表 /// public List DataPaths { get; } = new(); private readonly JsonSerializerOptions _options; /// /// 构造函数 /// /// 优先级 public JsonContentSource(int priority) { Priority = priority; _options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; _options.Converters.Add(new JsonStringEnumConverter()); } /// /// 加载所有指定类型的对象 /// /// 对象类型 /// 对象集合 public IEnumerable LoadAll() where T : class { foreach (var path in DataPaths) { if (string.IsNullOrWhiteSpace(path)) { continue; } var resolvedPath = ResolvePath(path); if (!File.Exists(resolvedPath)) { continue; } var json = File.ReadAllText(resolvedPath); if (string.IsNullOrWhiteSpace(json)) { continue; } if (TryDeserializeList(json, out List list)) { foreach (var item in list) { yield return item; } continue; } if (TryDeserializeEnvelope(json, out JsonContentEnvelope envelope)) { if (envelope.Items != null) { foreach (var item in envelope.Items) { yield return item; } } else if (envelope.Item != null) { yield return envelope.Item; } continue; } if (TryDeserializeSingle(json, out T single)) { yield return single; } } } /// /// 解析路径 /// /// 路径 /// 解析后的绝对路径 private string ResolvePath(string path) { if (path.StartsWith("res://") || path.StartsWith("user://")) { return ProjectSettings.GlobalizePath(path); } return path; } /// /// 尝试反序列化列表 /// private bool TryDeserializeList(string json, out List list) where T : class { try { list = JsonSerializer.Deserialize>(json, _options); return list != null; } catch { list = null; return false; } } /// /// 尝试反序列化单个对象 /// private bool TryDeserializeSingle(string json, out T item) where T : class { try { item = JsonSerializer.Deserialize(json, _options); return item != null; } catch { item = null; return false; } } /// /// 尝试反序列化信封结构 /// private bool TryDeserializeEnvelope(string json, out JsonContentEnvelope envelope) where T : class { try { envelope = JsonSerializer.Deserialize>(json, _options); if (envelope == null) { return false; } return envelope.Items != null || envelope.Item != null; } catch { envelope = null; return false; } } private sealed class JsonContentEnvelope where T : class { public string Type { get; set; } public List Items { get; set; } public T Item { get; set; } } }