Files

496 lines
22 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks; // 导入异步命名空间
using Base;
using Configs;
using Data;
using UnityEngine;
using Utils;
namespace Managers
{
/// <summary>
/// 定义管理器负责加载、管理和查询所有数据定义Define对象。
/// </summary>
/// <remarks>
/// 该管理器是一个单例,用于在应用程序中集中管理各种游戏或系统定义,
/// 包括从不同数据包Mods加载定义处理定义之间的引用以及提供多种查询方法。
/// </remarks>
public class DefineManager : Singleton<DefineManager>, ILaunchManager
{
/// <summary>
/// 数据集文件路径数组,用于指定定义包的根目录。
/// </summary>
private static readonly string[] dataSetFilePath = { "Data", "Mods" };
/// <summary>
/// 存储所有按类别索引的匿名定义列表。
/// </summary>
/// <remarks>
/// 匿名定义是没有明确 <see cref="Define.defName" /> 的定义,它们通常作为其他定义的子元素出现。
/// 外层字典的键是定义类别,值是该类别下的匿名定义列表。
/// </remarks>
public Dictionary<string, List<Define>> anonymousDefines = new();
/// <summary>
/// 存储所有按类别和定义名索引的定义。
/// </summary>
/// <remarks>
/// 外层字典的键是定义类别(例如,类的类型名),内层字典的键是定义的名称。
/// </remarks>
public Dictionary<string, Dictionary<string, Define>> defines = new();
/// <summary>
/// 存储所有按包ID索引的定义包。
/// </summary>
/// <remarks>
/// 键是定义包的唯一ID值是对应的 <see cref="DefinePack" /> 对象。
/// </remarks>
public Dictionary<string, DefinePack> packs = new();
public bool Completed { get; set; }
public string StepDescription { get; set; } = "等待加载..."; // 初始化默认描述
// 新增私有结构体,用于存储定义引用信息,提高可读性
private struct DefineRefInfo
{
public Define TargetDefine; // 被引用定义的字段所在的 Define 对象本身
public FieldInfo TargetField; // 字段信息,用于通过反射设置值
public Define ReferencePlaceholder; // 引用占位符 Define包含被引用定义的类型和名称
}
/// <summary>
/// 初始化定义管理器,加载所有定义包并构建定义字典。
/// </summary>
/// <remarks>
/// 该方法执行以下操作:
/// <list type="number">
/// <item>获取指定路径下的所有子文件夹,每个子文件夹代表一个定义包。</item>
/// <item>遍历每个定义包,尝试加载其中的定义数据(并行异步加载)。</item>
/// <item>将加载的定义数据按类型分类,并存储到定义字典中。</item>
/// <item>处理定义内部的引用关系,将引用占位符替换为实际定义对象。</item>
/// </list>
/// </remarks>
public async Task Init()
{
// 如果管理器已经初始化完成,则直接返回,避免重复加载。
if (Completed)
{
await Task.CompletedTask;
return;
}
// --- 阶段 1: 扫描并开始加载定义包 ---
StepDescription = "扫描定义包路径...";
var packFolders = ConfigProcessor.GetSubFolders(new List<string>(dataSetFilePath));
if (packFolders.Count == 0)
{
Debug.LogError("没有定义任何包,游戏注定无法正常运行,请下载数据包");
Completed = true;
await Task.CompletedTask;
return;
}
var currentOrder = Setting.Instance.CurrentSettings.loadOrder ?? Array.Empty<string>();
var isFirstLaunch = currentOrder.Length == 0;
var newOrderForFirstLaunch = new List<string>(); // 用于第一次启动时记录新的加载顺序
StepDescription = $"正在并行加载 {packFolders.Count} 个定义包...";
var loadingTasks = new List<Task<DefinePack>>();
foreach (var folder in packFolders)
{
// 将同步的 LoadPack 操作包装在 Task.Run 中,使其在线程池中并行执行,而非阻塞主线程。
// Task.WhenAll 会等待所有这些任务完成。
loadingTasks.Add(Task.Run(() =>
{
var pack = new DefinePack();
if (pack.LoadPack(folder)) // LoadPack 假设是同步操作
{
return pack;
}
Debug.LogWarning($"加载定义包失败:{folder}");
return null; // 加载失败则返回 null
}));
}
// 等待所有定义包加载任务完成。Task.WhenAll 将返回一个数组,其顺序与 inputTasks 列表中的顺序一致。
var loadedPacksInOrder = await Task.WhenAll(loadingTasks);
// --- 阶段 2: 处理加载结果,分配优先级,填充 'packs' 字典 ---
StepDescription = "处理定义包加载结果并排序...";
foreach (var pack in loadedPacksInOrder)
{
if (pack == null) continue; // 跳过加载失败的包
int priority;
if (isFirstLaunch)
{
// 第一次启动,按照包的发现顺序(即 loadedPacksInOrder 的顺序)赋予优先级
priority = newOrderForFirstLaunch.Count;
newOrderForFirstLaunch.Add(pack.packID);
}
else
{
// 非第一次启动,根据保存的加载顺序赋予优先级
var idx = Array.IndexOf(currentOrder, pack.packID);
if (idx >= 0)
priority = idx;
else
priority = currentOrder.Length; // 新发现的包优先级最低
}
pack.priority = priority;
// 这里使用索引器赋值如果存在相同packID的包后加载的会覆盖先加载的。
// 这与后面处理 defines 字典的覆盖逻辑保持一致。
if (packs.ContainsKey(pack.packID))
{
Debug.LogWarning($"DefineManager: 定义包 '{pack.packID}' (根路径: {pack.packRootPath}) 重复定义,将覆盖现有定义。");
}
packs[pack.packID] = pack;
}
// 如果是第一次启动,保存新的加载顺序到设置中
if (isFirstLaunch)
{
Setting.Instance.CurrentSettings.loadOrder = newOrderForFirstLaunch.ToArray();
// 假设存在 Setting.Instance.Save() 方法,用于将 CurrentSettings 持久化。
// Setting.Instance.Save();
}
// 字段信息缓存,用于优化反射性能。
Dictionary<Type, FieldInfo[]> fieldCache = new();
// 存储需要进行链接的定义引用信息。
List<DefineRefInfo> defineReferences = new();
// --- 阶段 3: 遍历并处理所有定义,填充定义字典和引用缓存 ---
StepDescription = "正在处理所有定义对象及其内部引用...";
string currentPackID = string.Empty; // 用于 ProcessDefineInternal 记录当前处理的包ID
// 按照优先级排序包,确保高优先级包的定义能覆盖低优先级包的同名定义
foreach (var pack in packs.Values.OrderBy(p => p.priority))
{
currentPackID = pack.packID;
foreach (var (typeName, defList) in pack.defines)
{
if (!defines.ContainsKey(typeName))
defines[typeName] = new Dictionary<string, Define>();
foreach (var def in defList)
{
// 同名定义覆盖逻辑:优先级高的包中的定义将覆盖优先级低的包中的定义
if (defines[typeName].ContainsKey(def.defName))
{
Debug.LogWarning($"DefineManager: 定义 '{def.defName}' (类型: '{typeName}') 在包 '{currentPackID}' 中重复定义,将覆盖现有定义。");
}
defines[typeName][def.defName] = def;
ProcessDefineInternal(def, currentPackID, fieldCache, defineReferences);
}
}
}
// --- 阶段 4: 解决所有定义间的引用关系 ---
StepDescription = $"正在解决 {defineReferences.Count} 条定义引用...";
foreach (var defRefInfo in defineReferences)
{
if (defRefInfo.TargetDefine == null)
{
Debug.LogError("引用目标 Define 为 null");
continue;
}
if (defRefInfo.TargetField == null)
{
Debug.LogError($"定义 '{defRefInfo.ReferencePlaceholder?.defName}' 的引用字段为 null");
continue;
}
var value = FindDefine(defRefInfo.ReferencePlaceholder.description, defRefInfo.ReferencePlaceholder.defName);
if (value == null)
{
Debug.LogError(
$"未找到引用。错误来源定义类型: '{defRefInfo.TargetDefine.GetType().Name}', 定义名: '{defRefInfo.TargetDefine.defName}'。" +
$"\n引用的类型: '{defRefInfo.ReferencePlaceholder.description}', 引用的定义名: '{defRefInfo.ReferencePlaceholder.defName}'");
continue;
}
try
{
defRefInfo.TargetField.SetValue(defRefInfo.TargetDefine, value);
}
catch (Exception ex)
{
Debug.LogError($"SetValue 出错。将值 '{value.defName}' 赋给字段 '{defRefInfo.TargetField.Name}' " +
$"在定义 '{defRefInfo.TargetDefine.defName}' (类型: '{defRefInfo.TargetDefine.GetType().Name}') 时失败: {ex.Message}");
}
}
// --- 阶段 5: 完成初始化 ---
Completed = true; // 标记为完成
StepDescription = "数据定义加载完成。";
await Task.CompletedTask;
}
/// <summary>
/// 递归处理定义对象及其内部的嵌套定义和引用。
/// 提取自 Init 方法,以提高可读性和模块化。
/// </summary>
/// <param name="def">要处理的 Define 对象。</param>
/// <param name="currentPackID">当前 Define 所属的包ID。</param>
/// <param name="fieldCache">字段信息缓存。</param>
/// <param name="defineReferences">用于收集引用信息的列表。</param>
private void ProcessDefineInternal(Define def, string currentPackID, Dictionary<Type, FieldInfo[]> fieldCache, List<DefineRefInfo> defineReferences)
{
if (def == null || def.isReferene)
return;
def.packID = currentPackID;
// 检查是否已缓存字段信息,如果已缓存则直接使用。
if (!fieldCache.TryGetValue(def.GetType(), out var defineFields))
{
// 获取所有公共实例字段。
defineFields = def.GetType()
.GetFields(BindingFlags.Public | BindingFlags.Instance);
// 缓存当前类型的字段信息。
fieldCache[def.GetType()] = defineFields;
}
foreach (var defineField in defineFields)
{
var fieldType = defineField.FieldType;
// 处理单个 Define 类型的字段
if (typeof(Define).IsAssignableFrom(fieldType))
{
var defRef = (Define)defineField.GetValue(def);
if (defRef == null)
continue;
if (defRef.isReferene)
{
defineReferences.Add(new DefineRefInfo
{
TargetDefine = def,
TargetField = defineField,
ReferencePlaceholder = defRef
});
}
else
{
if (string.IsNullOrEmpty(defRef.defName))
{
var typeName = defRef.GetType().Name;
if (!anonymousDefines.ContainsKey(typeName))
anonymousDefines.Add(typeName, new List<Define>());
anonymousDefines[typeName].Add(defRef);
}
ProcessDefineInternal(defRef, currentPackID, fieldCache, defineReferences);
}
}
// 处理 List<Define> 类型的字段
else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(List<>))
{
var elementType = fieldType.GenericTypeArguments[0];
if (typeof(Define).IsAssignableFrom(elementType))
{
var list = (IList)defineField.GetValue(def);
if (list != null)
foreach (var item in list)
if (item is Define { isReferene: false } defItem)
ProcessDefineInternal(defItem, currentPackID, fieldCache, defineReferences);
}
}
// 处理 Define[] 类型的数组字段
else if (fieldType.IsArray)
{
var elementType = fieldType.GetElementType();
if (typeof(Define).IsAssignableFrom(elementType))
{
var array = (Array)defineField.GetValue(def);
if (array != null)
foreach (var item in array)
if (item is Define { isReferene: false } defItem)
ProcessDefineInternal(defItem, currentPackID, fieldCache, defineReferences);
}
}
}
}
public void Clear()
{
defines.Clear();
packs.Clear();
anonymousDefines.Clear();
Completed = false; // 清理后,重置 Completed 状态
}
/// <summary>
/// 查找指定定义类型的定义名对应的 <see cref="Define" /> 对象。
/// </summary>
/// <param name="defineType">定义类型(通常是类的名称)。</param>
/// <param name="defineName">定义名。</param>
/// <returns>如果找到,返回 <see cref="Define" /> 对象;否则返回 null。</returns>
public Define FindDefine(string defineType, string defineName)
{
if (defines.TryGetValue(defineType, out var typeDict))
if (typeDict.TryGetValue(defineName, out var define))
return define;
return null;
}
/// <summary>
/// 使用泛型模板查找并返回指定类型的 <see cref="Define" /> 对象。
/// </summary>
/// <typeparam name="T">目标类型,必须继承自 <see cref="Define" />。</typeparam>
/// <param name="defineName">定义名。</param>
/// <returns>如果找到,返回转换为目标类型的 <see cref="Define" /> 对象;否则返回 null。</returns>
public T FindDefine<T>(string defineName) where T : Define
{
if (string.IsNullOrEmpty(defineName))
return null;
if (defines.TryGetValue(typeof(T).Name, out var typeDict))
if (typeDict.TryGetValue(defineName, out var define))
return (T)define;
return null;
}
/// <summary>
/// 获取指定 <see cref="Define" /> 对象所属的定义包。
/// </summary>
/// <param name="define">要查询的 <see cref="Define" /> 对象。</param>
/// <returns>如果找到对应的定义包,则返回 <see cref="DefinePack" /> 对象;否则返回 null。</returns>
public DefinePack GetDefinePackage(Define define)
{
if (define == null || define.packID == null)
return null;
packs.TryGetValue(define.packID, out var pack);
return pack;
}
/// <summary>
/// 获取指定 <see cref="Define" /> 对象所属定义包的名称。
/// </summary>
/// <param name="define">要查询的 <see cref="Define" /> 对象。</param>
/// <returns>如果找到对应的定义包,则返回其名称;否则返回 null。</returns>
public string GetDefinePackageName(Define define)
{
return GetDefinePackage(define)?.Name;
}
/// <summary>
/// 获取指定包ID对应定义包的根路径。
/// </summary>
/// <param name="packID">定义包的唯一标识ID。</param>
/// <returns>如果找到对应的定义包,则返回其根路径;否则返回 null。</returns>
public string GetPackagePath(string packID)
{
if (packs.TryGetValue(packID, out var pack)) return pack.packRootPath;
return null;
}
/// <summary>
/// 获取所有已加载的 <see cref="Define" /> 对象。
/// </summary>
/// <returns>包含所有命名定义和匿名定义的数组。</returns>
public Define[] GetAllDefine()
{
List<Define> defineList = new();
foreach (var define in defines.Values) defineList.AddRange(define.Values);
foreach (var anonymousDefine in anonymousDefines) defineList.AddRange(anonymousDefine.Value);
return defineList.ToArray();
}
/// <summary>
/// 查询指定类型下的所有 <see cref="Define" /> 对象(包括命名定义和匿名定义)。
/// </summary>
/// <param name="defineType">定义类型(外层字典的键,通常是类的名称)。</param>
/// <returns>该类型下的 <see cref="Define" /> 数组。如果未找到任何定义,则返回空数组。</returns>
public Define[] QueryDefinesByType(string defineType)
{
if (string.IsNullOrEmpty(defineType))
{
Debug.LogError("查询失败:定义类型参数不能为空!");
return Array.Empty<Define>();
}
var result = new List<Define>();
// 从命名定义中查询。
if (defines.TryGetValue(defineType, out var namedDefinitions)) result.AddRange(namedDefinitions.Values);
// 从匿名定义中查询。
if (anonymousDefines.TryGetValue(defineType, out var anonymousDefinitionList))
result.AddRange(anonymousDefinitionList);
// 如果结果为空,则返回空数组。
if (result.Count == 0) return Array.Empty<Define>();
return result.ToArray();
}
/// <summary>
/// 查询指定类型下的所有 <see cref="Define" /> 对象,并尝试转换为目标类型(包括命名定义和匿名定义)。
/// </summary>
/// <typeparam name="T">目标类型,必须继承自 <see cref="Define" />。</typeparam>
/// <returns>转换后的目标类型数组。如果未找到或转换失败,则返回 <see cref="Array.Empty{T}" />。</returns>
public T[] QueryDefinesByType<T>() where T : Define
{
var defineType = typeof(T).Name;
// 直接获取 Define[] 数组,避免中间的 ToList() 调用
var allDefines = QueryDefinesByType(defineType);
if (allDefines == null || allDefines.Length == 0)
return Array.Empty<T>();
try
{
// 初始化 List 时预估容量,减少不必要的内存重新分配
var result = new List<T>(allDefines.Length);
foreach (var item in allDefines)
if (item is T converted) // 使用 'is' 和模式匹配进行安全转换
result.Add(converted);
else
// 这是一个警告,因为这表示数据可能不是预期的类型
Debug.LogWarning($"类型转换失败:无法将 {item.GetType().Name} 转换为 {typeof(T).Name},已跳过该项。");
return result.ToArray();
}
catch (Exception ex)
{
Debug.LogError($"类型转换失败:从 Define 转换为 {typeof(T).Name} 时出错。错误信息:{ex.Message}");
return Array.Empty<T>();
}
}
/// <summary>
/// 返回所有加载的定义包的字符串表示。
/// </summary>
/// <returns>一个包含所有定义包信息的字符串,每个包信息占一行。</returns>
public override string ToString()
{
if (packs == null || packs.Count == 0)
// 如果集合为空或为 null返回默认信息。
return "No packs available";
var result = new StringBuilder();
foreach (var definePack in packs.Values.OrderBy(p => p.priority)) // 按优先级排序后输出
// 每个定义包对象占一行。
result.AppendLine(definePack.ToString());
return result.ToString();
}
}
}