diff --git a/Assets/GameScripts/Main/Procedure/ProcedureLoadAssembly.cs b/Assets/GameScripts/Main/Procedure/ProcedureLoadAssembly.cs index 7ab2c23b..e3b55206 100644 --- a/Assets/GameScripts/Main/Procedure/ProcedureLoadAssembly.cs +++ b/Assets/GameScripts/Main/Procedure/ProcedureLoadAssembly.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; using System.IO; -#if HybridCLR_Enable using HybridCLR; -#endif using UnityEngine; using TEngine; using System.Reflection; @@ -21,287 +19,264 @@ namespace GameMain /// public bool NeedLoadDll => GameModule.Resource.playMode == EPlayMode.HostPlayMode || GameModule.Resource.playMode == EPlayMode.OfflinePlayMode; - public override bool UseNativeDialog => true; -// private int m_LoadAssetCount; -// private int m_LoadMetadataAssetCount; -// private int m_FailureAssetCount; -// private int m_FailureMetadataAssetCount; -// private bool m_LoadAssemblyComplete; -// private bool m_LoadMetadataAssemblyComplete; -// private bool m_LoadAssemblyWait; -// private bool m_LoadMetadataAssemblyWait; -// private System.Reflection.Assembly m_MainLogicAssembly; -// private List m_HotfixAssemblys; -// private IFsm m_procedureOwner; -// -// protected override void OnEnter(IFsm procedureOwner) -// { -// base.OnEnter(procedureOwner); -// Log.Debug("HyBridCLR ProcedureLoadAssembly OnEnter"); -// m_procedureOwner = procedureOwner; -// m_LoadAssemblyComplete = false; -// m_HotfixAssemblys = new List(); -// -// if (!NeedLoadDll || GameModule.Resource.playMode == EPlayMode.EditorSimulateMode) -// { -// m_MainLogicAssembly = GetMainLogicAssembly(); -// } -// else -// { -// if (SettingsUtils.HybridCLRCustomGlobalSettings.Enable) -// { -// m_LoadAssetCallbacks ??= new LoadAssetCallbacks(LoadAssetSuccess, LoadAssetFailure); -// foreach (var hotUpdateDllName in SettingsUtils.HybridCLRCustomGlobalSettings.HotUpdateAssemblies) -// { -// var assetPath = Utility.Path.GetRegularPath( -// Path.Combine( -// "Assets", -// SettingsUtils.HybridCLRCustomGlobalSettings.AssemblyTextAssetPath, -// $"{hotUpdateDllName}{SettingsUtils.HybridCLRCustomGlobalSettings.AssemblyTextAssetExtension}")); -// Log.Debug($"LoadAsset: [ {assetPath} ]"); -// m_LoadAssetCount++; -// GameModule.Resource.LoadAssetAsync(assetPath,typeof(UnityEngine.TextAsset), m_LoadAssetCallbacks, hotUpdateDllName); -// } -// -// m_LoadAssemblyWait = true; -// } -// else -// { -// m_MainLogicAssembly = GetMainLogicAssembly(); -// } -// } -// -// if (SettingsUtils.HybridCLRCustomGlobalSettings.Enable) -// { -// #if !UNITY_EDITOR -// m_LoadMetadataAssemblyComplete = false; -// LoadMetadataForAOTAssembly(); -// #else -// m_LoadMetadataAssemblyComplete = true; -// #endif -// } -// else -// { -// m_LoadMetadataAssemblyComplete = true; -// } -// -// if (m_LoadAssetCount == 0) -// { -// m_LoadAssemblyComplete = true; -// } -// } -// -// protected override void OnUpdate(IFsm procedureOwner, float elapseSeconds, float realElapseSeconds) -// { -// base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds); -// if (!m_LoadAssemblyComplete) -// { -// return; -// } -// if (!m_LoadMetadataAssemblyComplete) -// { -// return; -// } -// AllAssemblyLoadComplete(); -// } -// -// private void AllAssemblyLoadComplete() -// { -// if (GameModule.Resource.playMode == EPlayMode.EditorSimulateMode) -// { -// ChangeState(m_procedureOwner); -// return; -// } -// if (m_MainLogicAssembly == null) -// { -// Log.Fatal($"Main logic assembly missing."); -// return; -// } -// var appType = m_MainLogicAssembly.GetType("GameMain"); -// if (appType == null) -// { -// Log.Fatal($"Main logic type 'GameMain' missing."); -// return; -// } -// var entryMethod = appType.GetMethod("Entrance"); -// if (entryMethod == null) -// { -// Log.Fatal($"Main logic entry method 'Entrance' missing."); -// return; -// } -// object[] objects = new object[] { new object[] { m_HotfixAssemblys } }; -// entryMethod.Invoke(appType, objects); -// ChangeState(m_procedureOwner); -// } -// -// private Assembly GetMainLogicAssembly() -// { -// Assembly mainLogicAssembly = null; -// foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) -// { -// if (string.Compare(SettingsUtils.HybridCLRCustomGlobalSettings.LogicMainDllName, $"{assembly.GetName().Name}.dll", -// StringComparison.Ordinal) == 0) -// { -// mainLogicAssembly = assembly; -// } -// -// foreach (var hotUpdateDllName in SettingsUtils.HybridCLRCustomGlobalSettings.HotUpdateAssemblies) -// { -// if (hotUpdateDllName == $"{assembly.GetName().Name}.dll") -// { -// m_HotfixAssemblys.Add(assembly); -// } -// } -// -// if (mainLogicAssembly != null && m_HotfixAssemblys.Count == SettingsUtils.HybridCLRCustomGlobalSettings.HotUpdateAssemblies.Count) -// { -// break; -// } -// } -// -// return mainLogicAssembly; -// } -// -// /// -// /// 加载代码资源成功回调。 -// /// -// /// 资源名称。 -// /// 资源实例。 -// /// 加载耗时。 -// /// 用户数据。 -// private void LoadAssetSuccess(string assetName, object asset, float duration, object userData) -// { -// m_LoadAssetCount--; -// Log.Debug($"LoadAssetSuccess, assetName: [ {assetName} ], duration: [ {duration} ], userData: [ {userData} ]"); -// var textAsset = asset as TextAsset; -// if (textAsset == null) -// { -// Log.Warning($"Load text asset [ {assetName} ] failed."); -// return; -// } -// -// try -// { -// var assembly = Assembly.Load(textAsset.bytes); -// if (string.Compare(SettingsUtils.HybridCLRCustomGlobalSettings.LogicMainDllName, userData as string, StringComparison.Ordinal) == 0) -// { -// m_MainLogicAssembly = assembly; -// } -// m_HotfixAssemblys.Add(assembly); -// Log.Debug($"Assembly [ {assembly.GetName().Name} ] loaded"); -// } -// catch (Exception e) -// { -// m_FailureAssetCount++; -// Log.Fatal(e); -// throw; -// } -// finally -// { -// m_LoadAssemblyComplete = m_LoadAssemblyWait && 0 == m_LoadAssetCount; -// } -// } -// -// /// -// /// 加载代码资源失败回调。 -// /// -// /// 资源名称。 -// /// 加载状态。 -// /// 错误信息。 -// /// 自定义数据。 -// private void LoadAssetFailure(string assetName, LoadResourceStatus status, string errorMessage, object userData) -// { -// Log.Fatal($"LoadAssetFailure, assetName: [ {assetName} ], status: [ {status} ], errorMessage: [ {errorMessage} ], userData: [ {userData} ]"); -// m_LoadAssetCount--; -// m_FailureAssetCount++; -// } -// -// /// -// /// 为Aot Assembly加载原始metadata, 这个代码放Aot或者热更新都行。 -// /// 一旦加载后,如果AOT泛型函数对应native实现不存在,则自动替换为解释模式执行。 -// /// -// public void LoadMetadataForAOTAssembly() -// { -// // 可以加载任意aot assembly的对应的dll。但要求dll必须与unity build过程中生成的裁剪后的dll一致,而不能直接使用原始dll。 -// // 我们在BuildProcessor_xxx里添加了处理代码,这些裁剪后的dll在打包时自动被复制到 {项目目录}/HybridCLRData/AssembliesPostIl2CppStrip/{Target} 目录。 -// -// // 注意,补充元数据是给AOT dll补充元数据,而不是给热更新dll补充元数据。 -// // 热更新dll不缺元数据,不需要补充,如果调用LoadMetadataForAOTAssembly会返回错误 -// if (SettingsUtils.HybridCLRCustomGlobalSettings.AOTMetaAssemblies.Count == 0) -// { -// m_LoadMetadataAssemblyComplete = true; -// return; -// } -// m_LoadMetadataAssetCallbacks ??= new LoadAssetCallbacks(LoadMetadataAssetSuccess, LoadMetadataAssetFailure); -// foreach (var aotDllName in SettingsUtils.HybridCLRCustomGlobalSettings.AOTMetaAssemblies) -// { -// var assetPath = Utility.Path.GetRegularPath( -// Path.Combine( -// "Assets", -// SettingsUtils.HybridCLRCustomGlobalSettings.AssemblyTextAssetPath, -// $"{aotDllName}{SettingsUtils.HybridCLRCustomGlobalSettings.AssemblyTextAssetExtension}")); -// Log.Debug($"LoadMetadataAsset: [ {assetPath} ]"); -// m_LoadMetadataAssetCount++; -// GameModule.Resource.LoadAssetAsync(assetPath,typeof(UnityEngine.TextAsset), m_LoadMetadataAssetCallbacks, aotDllName); -// } -// m_LoadMetadataAssemblyWait = true; -// } -// -// /// -// /// 加载元数据资源成功回调。 -// /// -// /// 资源名称。 -// /// 资源实例。 -// /// 加载耗时。 -// /// 用户数据。 -// private unsafe void LoadMetadataAssetSuccess(string assetName, object asset, float duration, object userData) -// { -// m_LoadMetadataAssetCount--; -// Log.Debug($"LoadMetadataAssetSuccess, assetName: [ {assetName} ], duration: [ {duration} ], userData: [ {userData} ]"); -// var textAsset = asset as TextAsset; -// if (null == textAsset) -// { -// Log.Debug($"LoadMetadataAssetSuccess:Load text asset [ {assetName} ] failed."); -// return; -// } -// -// try -// { -// byte[] dllBytes = textAsset.bytes; -// fixed (byte* ptr = dllBytes) -// { -// #if HybridCLR_Enable -// // 加载assembly对应的dll,会自动为它hook。一旦Aot泛型函数的native函数不存在,用解释器版本代码 -// HomologousImageMode mode = HomologousImageMode.SuperSet; -// LoadImageErrorCode err = (LoadImageErrorCode)HybridCLR.RuntimeApi.LoadMetadataForAOTAssembly(dllBytes,mode); -// Log.Warning($"LoadMetadataForAOTAssembly:{userData as string}. mode:{mode} ret:{err}"); -// #endif -// } -// } -// catch (Exception e) -// { -// m_FailureMetadataAssetCount++; -// Log.Fatal(e.Message); -// throw; -// } -// finally -// { -// m_LoadMetadataAssemblyComplete = m_LoadMetadataAssemblyWait && 0 == m_LoadMetadataAssetCount; -// } -// } -// -// /// -// /// 加载元数据资源失败回调。 -// /// -// /// 资源名称。 -// /// 加载状态。 -// /// 错误信息。 -// /// 自定义数据。 -// private void LoadMetadataAssetFailure(string assetName, LoadResourceStatus status, string errorMessage, object userData) -// { -// Log.Warning($"LoadAssetFailure, assetName: [ {assetName} ], status: [ {status} ], errorMessage: [ {errorMessage} ], userData: [ {userData} ]"); -// m_LoadMetadataAssetCount--; -// m_FailureMetadataAssetCount++; -// } + private bool m_enableAddressable; + public override bool UseNativeDialog => true; + private int m_LoadAssetCount; + private int m_LoadMetadataAssetCount; + private int m_FailureAssetCount; + private int m_FailureMetadataAssetCount; + private bool m_LoadAssemblyComplete; + private bool m_LoadMetadataAssemblyComplete; + private bool m_LoadAssemblyWait; + private bool m_LoadMetadataAssemblyWait; + private Assembly m_MainLogicAssembly; + private List m_HotfixAssemblys; + private IFsm m_procedureOwner; + + protected override void OnEnter(IFsm procedureOwner) + { + base.OnEnter(procedureOwner); + Log.Debug("HyBridCLR ProcedureLoadAssembly OnEnter"); + m_procedureOwner = procedureOwner; + m_LoadAssemblyComplete = false; + m_HotfixAssemblys = new List(); + + if (!NeedLoadDll || GameModule.Resource.playMode == EPlayMode.EditorSimulateMode) + { + m_MainLogicAssembly = GetMainLogicAssembly(); + } + else + { + if (SettingsUtils.HybridCLRCustomGlobalSettings.Enable) + { + foreach (string hotUpdateDllName in SettingsUtils.HybridCLRCustomGlobalSettings.HotUpdateAssemblies) + { + var assetLocation = hotUpdateDllName; + if (!m_enableAddressable) + { + assetLocation = Utility.Path.GetRegularPath( + Path.Combine( + "Assets", + SettingsUtils.HybridCLRCustomGlobalSettings.AssemblyTextAssetPath, + $"{hotUpdateDllName}{SettingsUtils.HybridCLRCustomGlobalSettings.AssemblyTextAssetExtension}")); + } + + Log.Debug($"LoadAsset: [ {assetLocation} ]"); + m_LoadAssetCount++; + GameModule.Resource.LoadAssetAsync(assetLocation,LoadAssetSuccess); + } + + m_LoadAssemblyWait = true; + } + else + { + m_MainLogicAssembly = GetMainLogicAssembly(); + } + } + + if (SettingsUtils.HybridCLRCustomGlobalSettings.Enable) + { +#if !UNITY_EDITOR + m_LoadMetadataAssemblyComplete = false; + LoadMetadataForAOTAssembly(); +#else + m_LoadMetadataAssemblyComplete = true; +#endif + } + else + { + m_LoadMetadataAssemblyComplete = true; + } + + if (m_LoadAssetCount == 0) + { + m_LoadAssemblyComplete = true; + } + } + + protected override void OnUpdate(IFsm procedureOwner, float elapseSeconds, float realElapseSeconds) + { + base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds); + if (!m_LoadAssemblyComplete) + { + return; + } + if (!m_LoadMetadataAssemblyComplete) + { + return; + } + AllAssemblyLoadComplete(); + } + + private void AllAssemblyLoadComplete() + { + if (GameModule.Resource.playMode == EPlayMode.EditorSimulateMode) + { + ChangeState(m_procedureOwner); + return; + } + if (m_MainLogicAssembly == null) + { + Log.Fatal($"Main logic assembly missing."); + return; + } + var appType = m_MainLogicAssembly.GetType("GameMain"); + if (appType == null) + { + Log.Fatal($"Main logic type 'GameMain' missing."); + return; + } + var entryMethod = appType.GetMethod("Entrance"); + if (entryMethod == null) + { + Log.Fatal($"Main logic entry method 'Entrance' missing."); + return; + } + object[] objects = new object[] { new object[] { m_HotfixAssemblys } }; + entryMethod.Invoke(appType, objects); + ChangeState(m_procedureOwner); + } + + private Assembly GetMainLogicAssembly() + { + Assembly mainLogicAssembly = null; + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + if (string.Compare(SettingsUtils.HybridCLRCustomGlobalSettings.LogicMainDllName, $"{assembly.GetName().Name}.dll", + StringComparison.Ordinal) == 0) + { + mainLogicAssembly = assembly; + } + + foreach (var hotUpdateDllName in SettingsUtils.HybridCLRCustomGlobalSettings.HotUpdateAssemblies) + { + if (hotUpdateDllName == $"{assembly.GetName().Name}.dll") + { + m_HotfixAssemblys.Add(assembly); + } + } + + if (mainLogicAssembly != null && m_HotfixAssemblys.Count == SettingsUtils.HybridCLRCustomGlobalSettings.HotUpdateAssemblies.Count) + { + break; + } + } + + return mainLogicAssembly; + } + + /// + /// 加载代码资源成功回调。 + /// + /// 资源操作句柄。 + private void LoadAssetSuccess(AssetOperationHandle assetOperationHandle) + { + m_LoadAssetCount--; + var assetName = assetOperationHandle.AssetObject.name; + Log.Debug($"LoadAssetSuccess, assetName: [ {assetName} ]"); + + var textAsset = assetOperationHandle.AssetObject as TextAsset; + if (textAsset == null) + { + Log.Warning($"Load text asset [ {assetName} ] failed."); + return; + } + + try + { + var assembly = Assembly.Load(textAsset.bytes); + if (string.Compare(SettingsUtils.HybridCLRCustomGlobalSettings.LogicMainDllName, assetName, StringComparison.Ordinal) == 0) + { + m_MainLogicAssembly = assembly; + } + m_HotfixAssemblys.Add(assembly); + Log.Debug($"Assembly [ {assembly.GetName().Name} ] loaded"); + } + catch (Exception e) + { + m_FailureAssetCount++; + Log.Fatal(e); + throw; + } + finally + { + m_LoadAssemblyComplete = m_LoadAssemblyWait && 0 == m_LoadAssetCount; + } + } + + /// + /// 为Aot Assembly加载原始metadata, 这个代码放Aot或者热更新都行。 + /// 一旦加载后,如果AOT泛型函数对应native实现不存在,则自动替换为解释模式执行。 + /// + public void LoadMetadataForAOTAssembly() + { + // 可以加载任意aot assembly的对应的dll。但要求dll必须与unity build过程中生成的裁剪后的dll一致,而不能直接使用原始dll。 + // 我们在BuildProcessor_xxx里添加了处理代码,这些裁剪后的dll在打包时自动被复制到 {项目目录}/HybridCLRData/AssembliesPostIl2CppStrip/{Target} 目录。 + + // 注意,补充元数据是给AOT dll补充元数据,而不是给热更新dll补充元数据。 + // 热更新dll不缺元数据,不需要补充,如果调用LoadMetadataForAOTAssembly会返回错误 + if (SettingsUtils.HybridCLRCustomGlobalSettings.AOTMetaAssemblies.Count == 0) + { + m_LoadMetadataAssemblyComplete = true; + return; + } + foreach (string aotDllName in SettingsUtils.HybridCLRCustomGlobalSettings.AOTMetaAssemblies) + { + var assetLocation = aotDllName; + if (!m_enableAddressable) + { + assetLocation = Utility.Path.GetRegularPath( + Path.Combine( + "Assets", + SettingsUtils.HybridCLRCustomGlobalSettings.AssemblyTextAssetPath, + $"{aotDllName}{SettingsUtils.HybridCLRCustomGlobalSettings.AssemblyTextAssetExtension}")); + } + + + Log.Debug($"LoadMetadataAsset: [ {assetLocation} ]"); + m_LoadMetadataAssetCount++; + GameModule.Resource.LoadAssetAsync(assetLocation,LoadMetadataAssetSuccess); + } + m_LoadMetadataAssemblyWait = true; + } + + /// + /// 加载元数据资源成功回调。 + /// + /// 资源操作句柄。 + private unsafe void LoadMetadataAssetSuccess(AssetOperationHandle assetOperationHandle) + { + m_LoadMetadataAssetCount--; + string assetName = assetOperationHandle.AssetObject.name; + Log.Debug($"LoadMetadataAssetSuccess, assetName: [ {assetName} ]"); + var textAsset = assetOperationHandle.AssetObject as TextAsset; + if (null == textAsset) + { + Log.Debug($"LoadMetadataAssetSuccess:Load text asset [ {assetName} ] failed."); + return; + } + + try + { + byte[] dllBytes = textAsset.bytes; + fixed (byte* ptr = dllBytes) + { + // 加载assembly对应的dll,会自动为它hook。一旦Aot泛型函数的native函数不存在,用解释器版本代码 + HomologousImageMode mode = HomologousImageMode.SuperSet; + LoadImageErrorCode err = (LoadImageErrorCode)HybridCLR.RuntimeApi.LoadMetadataForAOTAssembly(dllBytes,mode); + Log.Warning($"LoadMetadataForAOTAssembly:{assetName}. mode:{mode} ret:{err}"); + } + } + catch (Exception e) + { + m_FailureMetadataAssetCount++; + Log.Fatal(e.Message); + throw; + } + finally + { + m_LoadMetadataAssemblyComplete = m_LoadMetadataAssemblyWait && 0 == m_LoadMetadataAssetCount; + } + } } } \ No newline at end of file