using Configs; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Xml.Linq; using UnityEngine; namespace Data { /// /// 表示模组包的基本信息 /// public class PackAbout { public string Name { get; private set; } public string Description { get; private set; } public string Author { get; private set; } public string Version { get; private set; } public string PackID { get; private set; } public string[] Necessary { get; private set; } public string[] After { get; private set; } public string[] Before { get; private set; } private const string RootElementName = "About"; private const string ElementName_Name = "name"; private const string ElementName_Description = "description"; private const string ElementName_Author = "author"; private const string ElementName_Version = "version"; private const string ElementName_PackID = "packID"; private const string ElementName_Sort = "sort"; private const string ElementName_Before = "before"; private const string ElementName_After = "after"; private const string ElementName_Necessary = "necessary"; /// /// 使用静态方法从 XML 文档创建 PackAbout 实例。 /// /// XML 文档。 /// 初始化的 PackAbout 实例。 public static PackAbout FromXDocument(XDocument doc) { var aboutElement = doc.Element(RootElementName); if (aboutElement == null) throw new ArgumentException($"XML 文档无效,根节点为空或不是 '{RootElementName}'。"); PackAbout result = new(); result.Name = aboutElement.Element(ElementName_Name)?.Value ?? "Unknown"; result.Description = aboutElement.Element(ElementName_Description)?.Value ?? "Unknown"; result.Author = aboutElement.Element(ElementName_Author)?.Value ?? "Unknown"; result.Version = aboutElement.Element(ElementName_Version)?.Value ?? "Unknown"; result.PackID = aboutElement.Element(ElementName_PackID)?.Value ?? "Unknown"; var sortElement = aboutElement.Element(ElementName_Sort); if (sortElement != null) { result.Before = GetElementValues(sortElement.Element(ElementName_Before)); result.After = GetElementValues(sortElement.Element(ElementName_After)); result.Necessary = GetElementValues(sortElement.Element(ElementName_Necessary)); } else { result.Before = Array.Empty(); result.After = Array.Empty(); result.Necessary = Array.Empty(); } return result; } /// /// 获取指定 XElement 下所有子元素的值并返回为字符串数组。 /// /// 父 XElement。 /// 字符串数组。 private static string[] GetElementValues(XElement element) { if (element == null || !element.HasElements) return Array.Empty(); return element.Elements() .Select(e => e.Value.Trim()) .ToArray(); } public override string ToString() { // 定义字段标签和值的对齐格式(如左对齐,固定宽度) const int labelWidth = -12; // 负数为左对齐 const int valueWidth = -30; // 负数为左对齐 // 使用StringBuilder高效拼接 var sb = new StringBuilder(); // 基础字段(单行) sb.AppendLine($"{"Name:",labelWidth}{Name,valueWidth}"); sb.AppendLine($"{"Description:",labelWidth}{Description,valueWidth}"); sb.AppendLine($"{"Author:",labelWidth}{Author,valueWidth}"); sb.AppendLine($"{"Version:",labelWidth}{Version,valueWidth}"); sb.AppendLine($"{"PackID:",labelWidth}{PackID,valueWidth}"); // 数组字段(多行,每项缩进) sb.AppendLine( $"{"Necessary:",labelWidth}{string.Join(", ", Necessary ?? Array.Empty()),valueWidth}"); sb.AppendLine($"{"After:",labelWidth}{string.Join(", ", After ?? Array.Empty()),valueWidth}"); sb.AppendLine($"{"Before:",labelWidth}{string.Join(", ", Before ?? Array.Empty()),valueWidth}"); return sb.ToString(); } } /// /// 表示一个模组包的定义集合 /// public class DefinePack { private const string CoreNamespace = "Data."; // 优化点7:将魔法字符串转换为常量 private const string RootElementName_About = "About"; private const string RootElementName_Define = "Define"; // 优化点1和2:反射缓存和改进的类型查找 private static readonly Dictionary _typeCache = new(); private static readonly Dictionary _constructorCache = new(); private static readonly Dictionary _fieldCache = new(); private static readonly List _assembliesToSearch = new(); // 缓存要搜索的程序集 static DefinePack() { // 优化点2:一次性初始化要搜索的程序集。 // 为简单起见,我们将扫描所有当前加载的程序集。 // 在实际游戏中,您可能希望显式添加特定的程序集 // 如 Assembly.Load("YourGameLogicAssembly") 或按名称过滤。 _assembliesToSearch.AddRange(AppDomain.CurrentDomain.GetAssemblies()); // 可选:过滤或添加特定程序集: // _assembliesToSearch.Add(Assembly.GetExecutingAssembly()); // 添加当前程序集 // _assembliesToSearch.Add(Assembly.Load("AnotherAssemblyContainingDefines")); } /// /// define类别及其定义 /// public Dictionary> defines = new(); public PackAbout packAbout; public string packID; public string packRootPath; public string Name { get { // 优化点5:Name属性的空值安全性 return packAbout?.Name ?? "Unnamed Pack"; // 使用 PackAbout.Name 属性 } } public bool LoadPack(string packPath) { packRootPath = System.IO.Path.GetFullPath(packPath); var packDatas = ConfigProcessor.LoadXmlFromPath(packPath); // 优化点7:使用常量 var aboutXmls = FindDocumentsWithRootName(packDatas, RootElementName_About); if (aboutXmls == null || aboutXmls.Count < 1) { Debug.LogError("包缺少配置文件,加载跳过"); return false; } var aboutXml = aboutXmls[0]; packAbout = PackAbout.FromXDocument(aboutXml); // 优化点3:使用 PackAbout.PackID 属性 packID = packAbout.PackID; // 优化点3:使用 PackAbout.Name 属性 if (aboutXmls.Count > 1) Debug.LogWarning($"{packAbout.Name}包拥有多个配置文件,系统选择了加载序的第一个,请避免这种情况"); // 优化点7:使用常量 var defineXmls = FindDocumentsWithRootName(packDatas, RootElementName_Define); // Debug.Log($"Define文件数量{defineXmls.Count}"); foreach (var defineXml in defineXmls) LoadDefines(defineXml); return true; } private void LoadDefines(XDocument defineDoc) { var rootElement = defineDoc.Root; // 优化点7:使用常量 if (rootElement == null || rootElement.Name != RootElementName_Define) return; foreach (var element in rootElement.Elements()) { var className = element.Name.ToString(); if (string.IsNullOrEmpty(className)) continue; var def = LoadDefineClass(element, className); if (def == null) continue; def.packID = packID; if (!defines.ContainsKey(className)) defines.Add(className, new List()); defines[className].Add(def); } } /// /// 根据指定的 XML 元素 () 和类名 (), /// 动态加载并初始化一个继承自 的类实例。 /// /// 包含类定义的 XML 元素 ()。 /// 目标类的全限定名或简短名称。 /// /// 如果成功加载并初始化,则返回对应的 类实例; /// 否则返回 null。 /// /// /// 如果 为 null 或空字符串,则抛出此异常。 /// /// /// 该方法通过反射动态加载指定类,并检查其是否继承自 。 /// 如果类存在且满足条件,则尝试调用其 方法进行初始化。 /// 如果初始化失败,则使用默认初始化方法 ()。 /// public static Define LoadDefineClass(XElement defineDoc, string className) { // 优化点1和2:反射缓存和改进的类型查找 if (!_typeCache.TryGetValue(className, out var type)) { // 首先尝试使用 CoreNamespace var fullClassName = CoreNamespace + className; type = _assembliesToSearch.Select(a => a.GetType(fullClassName)).FirstOrDefault(t => t != null); // 如果未找到,尝试不使用 CoreNamespace(可能在全局命名空间中或已经是完全限定名) if (type == null) { type = _assembliesToSearch.Select(a => a.GetType(className)).FirstOrDefault(t => t != null); } if (type == null) { Debug.LogError($"未定义的类型: {className}"); return null; } _typeCache[className] = type; } // 优化点1:构造函数缓存 if (!_constructorCache.TryGetValue(type, out var constructor)) { constructor = type.GetConstructor(Type.EmptyTypes); if (constructor == null) { Debug.LogError($"{className} 必须包含无参构造函数"); return null; } _constructorCache[type] = constructor; } // 3. 创建实例 object instance; try { instance = constructor.Invoke(null); // 使用缓存的构造函数 } catch (Exception ex) { Debug.LogError($"创建 {className} 实例失败: {ex.Message}"); return null; } // 4. 检查是否继承自 Define if (instance is not Define define) { Debug.LogError($"{className} 必须继承自 Define"); return null; } if (define.Init(defineDoc)) return define; DefaultInitDefine(define, defineDoc, type); return define; } /// /// 初始化指定的 对象,根据 中的 XML 元素内容, /// 将对应的字段值赋给 对象。 /// /// 需要初始化的对象实例。 /// 包含字段定义的 XML 元素 ()。 /// 目标对象的类型 ()。 /// /// 如果 为 null,则抛出此异常。 /// /// /// 该方法会遍历 的所有字段(包括公共和非公共字段), /// 并尝试从 中找到与字段名称匹配的子元素。 /// 如果找到匹配的子元素,则将其值转换为字段的类型并赋值给字段。 /// 如果字段类型继承自 ,则递归调用 方法进行加载。 /// public static void DefaultInitDefine(Define define, XElement defineDoc, Type defineType) { // 优化点1:FieldInfo 缓存 if (!_fieldCache.TryGetValue(defineType, out var fields)) { fields = defineType.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic); _fieldCache[defineType] = fields; } // 遍历字段并尝试从 XElement 中赋值 foreach (var field in fields) { // 查找对应的 XElement 子元素 var element = defineDoc.Element(field.Name); if (element != null) try { // 优化点4:重构 ProcessArrayField 并引入通用转换辅助方法 var value = ConvertXElementValueToType(element, field.FieldType); field.SetValue(define, value); } catch (Exception ex) { Debug.LogWarning($"设置字段时出错,字段名:{field.Name}; 值: {element.Value}; 错误: {ex.Message}"); } } } // 优化点4:新的辅助方法,用于将 XElement 值转换为特定类型 private static object ConvertXElementValueToType(XElement element, Type targetType) { if (IsTypeInheritedFrom(targetType, typeof(Define))) { if (element.HasElements) { return LoadDefineClass(element, targetType.Name); } else { // 引用另一个 Define var reference = (Define)Activator.CreateInstance(targetType); reference.isReferene = true; reference.description = targetType.Name; reference.label = element.Name.LocalName; // 使用元素的名称作为标签 reference.defName = element.Value; return reference; } } else if (targetType.IsArray || typeof(IList).IsAssignableFrom(targetType)) { return ProcessArrayField(targetType, element); } else if (targetType.IsEnum) { return Enum.Parse(targetType, element.Value); } else { return Convert.ChangeType(element.Value, targetType); } } // 优化点4:修改 ProcessArrayField 以直接接受 Type private static object ProcessArrayField(Type fieldType, XElement element) { // 获取集合的元素类型 var elementType = fieldType.IsArray ? fieldType.GetElementType() : fieldType.GetGenericArguments().FirstOrDefault(); // 使用 FirstOrDefault 以确保安全 if (elementType == null) { Debug.LogWarning($"无法确定类型为 {fieldType.Name} 的集合字段的元素类型"); return null; } var arrayElements = new List(); // 遍历 XML 子元素 foreach (var liElement in element.Elements()) { // 对每个项目使用新的辅助方法 arrayElements.Add(ConvertXElementValueToType(liElement, elementType)); } // 根据目标字段的类型构造结果 if (fieldType.IsArray) { var resultArray = Array.CreateInstance(elementType, arrayElements.Count); for (var i = 0; i < arrayElements.Count; i++) { resultArray.SetValue(arrayElements[i], i); } return resultArray; } else if (typeof(IList).IsAssignableFrom(fieldType)) { var listType = typeof(List<>).MakeGenericType(elementType); var resultList = (IList)Activator.CreateInstance(listType); foreach (var item in arrayElements) { resultList.Add(item); } return resultList; } return null; } /// /// 从 ListXDocument 中查找指定根元素名称的文档。 /// /// XML 文档列表。 /// 目标根元素名称。 /// 符合条件的 XML 文档列表。 public static List FindDocumentsWithRootName(List xmlDocuments, string rootName) { // 使用 LINQ to Objects 实现更简洁的解决方案 var result = xmlDocuments .Where(doc => doc.Root != null && doc.Root.Name.LocalName == rootName) .ToList(); return result; } public override string ToString() { // 对齐格式(左对齐,固定宽度) const int labelWidth = -15; const int valueWidth = -30; var sb = new StringBuilder(); // 基础字段 sb.AppendLine($"{"PackID:",labelWidth}{packID,valueWidth}"); sb.AppendLine(); // PackAbout 对象 sb.AppendLine("=== PackAbout ==="); // 优化点3:使用 PackAbout.ToString() sb.AppendLine(packAbout?.ToString() ?? "N/A"); sb.AppendLine(); // 字典字段(defines) sb.AppendLine("=== Defines ==="); if (defines != null && defines.Count > 0) foreach (var kvp in defines) { sb.AppendLine($"【{kvp.Key}】"); // 输出字典的键(类别名) foreach (var define in kvp.Value) // 遍历该类别下的所有 Define 对象 sb.AppendLine(define.ToString()); // 调用 Define 的 ToString() sb.AppendLine(); // 每个类别后空一行 } else sb.AppendLine("未找到定义。"); return sb.ToString(); } /// /// 检查字段的类型是否继承自指定的类 (严格派生,不包括基类本身) /// /// 字段信息 /// 要检查的基类类型 /// 如果字段的类型是基类的严格派生类,则返回 true // 优化点6:为 IsFieldTypeInheritedFrom 进行语义澄清 public static bool IsFieldTypeInheritedFrom(FieldInfo field, Type baseType) { // 获取字段的类型 var fieldType = field.FieldType; // 如果字段的类型为 null 或不是基类的派生类,则返回 false // 严格派生:不包括基类本身 return fieldType != null && fieldType != baseType && baseType.IsAssignableFrom(fieldType); } /// /// 检查一个类型是否继承自指定的基类 (严格派生,不包括基类本身) /// /// 要检查的类型 /// 要检查的基类类型 /// 如果类型是基类的严格派生类,则返回 true // 在 ConvertXElementValueToType 中使用的新类型检查辅助方法 public static bool IsTypeInheritedFrom(Type type, Type baseType) { return type != null && type != baseType && baseType.IsAssignableFrom(type); } } }