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 ResourceContentSource(int priority) {
Priority = priority;
}
///
/// 资源路径列表
///
public List ResourcePaths { get; } = new();
///
/// 优先级
///
public int Priority { get; }
///
/// 加载所有指定类型的对象
///
/// 对象类型
/// 对象集合
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 {
private readonly JsonSerializerOptions _options;
///
/// 构造函数
///
/// 优先级
public JsonContentSource(int priority) {
Priority = priority;
_options = new JsonSerializerOptions {
PropertyNameCaseInsensitive = true
};
_options.Converters.Add(new JsonStringEnumConverter());
}
///
/// 数据路径列表
///
public List DataPaths { get; } = new();
///
/// 优先级
///
public int Priority { get; }
///
/// 加载所有指定类型的对象
///
/// 对象类型
/// 对象集合
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; }
}
}