//------------------------------------------------------------ // Byway Data Table Helper // 基于 DefaultDataTableHelper 重新实现 // 核心改动:ParseData 方法从 Thrift 配置加载数据 //------------------------------------------------------------ using GameFramework; using GameFramework.DataTable; using System; using UnityEngine; using UnityGameFramework.Runtime; namespace CrazyMaple { /// /// Byway 数据表辅助器 - 从 Thrift 配置加载数据 /// public class BywayDataTableHelper : DataTableHelperBase { private static readonly string BytesAssetExtension = ".bytes"; private ResourceComponent m_ResourceComponent = null; /// /// 读取数据表。 /// public override bool ReadData(DataTableBase dataTable, string dataTableAssetName, object dataTableAsset, object userData) { TextAsset dataTableTextAsset = dataTableAsset as TextAsset; if (dataTableTextAsset != null) { // Byway 系统始终使用 bytes 格式(Thrift 二进制) return dataTable.ParseData(dataTableTextAsset.bytes, userData); } Log.Warning("Data table asset '{0}' is invalid.", dataTableAssetName); return false; } /// /// 读取数据表。 /// public override bool ReadData(DataTableBase dataTable, string dataTableAssetName, byte[] dataTableBytes, int startIndex, int length, object userData) { // Byway 系统始终使用 bytes 格式(Thrift 二进制) return dataTable.ParseData(dataTableBytes, startIndex, length, userData); } /// /// 解析数据表(字符串格式 - Byway 不支持) /// public override bool ParseData(DataTableBase dataTable, string dataTableString, object userData) { Log.Error("Byway data table system does not support string format. Use Thrift bytes instead."); return false; } /// /// 解析数据表(核心方法 - 从 Thrift 配置加载) /// public override bool ParseData(DataTableBase dataTable, byte[] dataTableBytes, int startIndex, int length, object userData) { var timer = System.Diagnostics.Stopwatch.StartNew(); try { // 获取 DataRow 类型 Type dataRowType = dataTable.Type; string dataRowTypeName = dataRowType.Name; // 推断 Thrift 配置类型名(去除 DR 前缀) // 例如:DRMergeData -> MergeData string configTypeName = dataRowTypeName.StartsWith("DR") ? dataRowTypeName.Substring(2) : dataRowTypeName; // 通过反射获取 Thrift 配置类型 Type configType = FindThriftConfigType(configTypeName); if (configType == null) { Log.Error("Can not find Thrift config type '{0}' for data row type '{1}'.", configTypeName, dataRowTypeName); return false; } // 调用 ConfigManager.GetConfig() 获取配置对象 var getConfigMethod = typeof(Byway.Config.ConfigManager) .GetMethod("GetConfig") .MakeGenericMethod(configType); object configInstance = getConfigMethod.Invoke(Byway.Config.ConfigManager.Instance, null); if (configInstance == null) { Log.Error("Can not get config instance for type '{0}'.", configTypeName); return false; } // 获取配置字典 var dictData = GetConfigDictionary(configInstance, configTypeName); if (dictData == null) { Log.Error("Can not get config dictionary for type '{0}'.", configTypeName); return false; } // 性能优化:不预先创建所有 DataRow,而是缓存配置字典供按需查询 // 对于几千行的大表,这样可以避免启动时创建几千个 DataRow 对象 var dict = dictData as System.Collections.IDictionary; if (dict == null) { Log.Error("Config dictionary is not IDictionary type."); return false; } // 将配置实例缓存到 DataTable 的 userData 中,供后续按需获取 // 格式:{ "ConfigInstance": configInstance, "ConfigDict": dict } var cacheData = new System.Collections.Generic.Dictionary { { "ConfigInstance", configInstance }, { "ConfigDict", dictData }, { "OriginalUserData", userData } }; // 性能优化选项:反射路径实测反而更慢,禁用 // 标准路径(ParseDataRow + ConfigDict)已经是最优解 bool useDirectConstruction = false; // 禁用反射优化路径 int successCount = 0; int failCount = 0; // Debug.LogError($"[性能测试] 表 {dataRowTypeName} 开始加载,行数: {dict.Count}, 模式: 标准路径(ConfigDict直接获取)"); var loadTimer = System.Diagnostics.Stopwatch.StartNew(); if (useDirectConstruction) { // 【性能最优路径】直接创建 DataRow 并设置数据,完全跳过 ParseDataRow // Log.Info("Large table detected ({0} rows), using direct construction mode.", dict.Count); foreach (System.Collections.DictionaryEntry entry in dict) { try { int id = (int)entry.Key; object itemData = entry.Value; // 直接创建 DataRow 实例(不走 AddDataRow) var dataRow = Activator.CreateInstance(dataTable.Type) as DataRowBase; if (dataRow == null) { failCount++; continue; } // 调用 SetConfigData 方法直接设置数据(跳过所有查询) var setDataMethod = dataTable.Type.GetMethod("SetConfigData"); if (setDataMethod != null) { setDataMethod.Invoke(dataRow, new object[] { itemData }); // 使用反射调用 DataTable 的内部 AddDataRow 方法 var addMethod = dataTable.GetType().GetMethod("InternalAddDataRow", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); if (addMethod != null) { addMethod.Invoke(dataTable, new object[] { dataRow }); successCount++; } else { // 兜底:使用 AddDataRow(会触发 ParseDataRow,但数据已设置好) if (dataTable.AddDataRow(id.ToString(), null)) { successCount++; } else { failCount++; } } } else { Log.Warning("SetConfigData method not found, fallback to AddDataRow"); // 降级到标准模式 useDirectConstruction = false; break; } } catch (Exception ex) { Log.Error("Direct construction failed for entry: {0}", ex.Message); failCount++; } } } // 【标准路径】通过 AddDataRow + ParseDataRow if (!useDirectConstruction || (successCount == 0 && dict.Count > 0)) { Log.Info("Using standard AddDataRow mode ({0} rows).", dict.Count); successCount = 0; failCount = 0; foreach (System.Collections.DictionaryEntry entry in dict) { try { int id = (int)entry.Key; // 调用 DataTable.AddDataRow(int id) // DataRow 的 ParseDataRow 方法会被调用,传入 id 和配置实例 if (!dataTable.AddDataRow(id.ToString(), cacheData)) { Log.Warning("Add data row failed for id '{0}'.", id); failCount++; } else { successCount++; } } catch (Exception ex) { Log.Error("Add data row exception: {0}", ex.Message); failCount++; } } } loadTimer.Stop(); // Debug.LogError($"[性能测试] 表 {dataRowTypeName} 加载完成,成功: {successCount}, 失败: {failCount}, 耗时: {loadTimer.ElapsedMilliseconds}ms ({loadTimer.Elapsed.TotalSeconds:F3}秒)"); timer.Stop(); // Debug.LogError($"[性能测试] 表 {dataRowTypeName} 总耗时(含配置获取): {timer.ElapsedMilliseconds}ms ({timer.Elapsed.TotalSeconds:F3}秒)"); return failCount == 0; } catch (Exception exception) { Log.Error("Parse Thrift data table failed with exception: {0}\n{1}", exception.Message, exception.StackTrace); return false; } } /// /// 查找 Thrift 配置类型 /// private Type FindThriftConfigType(string typeName) { // 在 Byway.Thrift.Data 命名空间中查找 var assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (var assembly in assemblies) { Type type = assembly.GetType($"Byway.Thrift.Data.{typeName}"); if (type != null) { return type; } } return null; } /// /// 获取配置字典 /// private object GetConfigDictionary(object config, string configTypeName) { if (config == null) return null; Type configType = config.GetType(); // 尝试多种可能的字典属性名 string[] possibleNames = new string[] { configTypeName + "s", // MergeData -> MergeDatas configTypeName.ToLower() + "s", // MergeData -> mergedatas char.ToLower(configTypeName[0]) + configTypeName.Substring(1) + "s", // MergeData -> mergeDatas "Items", "Data", "Configs" }; foreach (string name in possibleNames) { var property = configType.GetProperty(name); if (property != null) { object value = property.GetValue(config); if (value != null) { return value; } } } // 查找第一个 Dictionary 类型的属性 foreach (var property in configType.GetProperties()) { Type propType = property.PropertyType; if (propType.IsGenericType && propType.GetGenericTypeDefinition() == typeof(System.Collections.Generic.Dictionary<,>)) { object value = property.GetValue(config); if (value != null) { return value; } } } return null; } /// /// 释放数据表资源 /// public override void ReleaseDataAsset(DataTableBase dataTable, object dataTableAsset) { m_ResourceComponent.UnloadAsset(dataTableAsset); } private void Start() { m_ResourceComponent = UnityGameFramework.Runtime.GameEntry.GetComponent(); if (m_ResourceComponent == null) { Log.Fatal("Resource component is invalid."); return; } } } }