//------------------------------------------------------------
// 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;
}
}
}
}