using System; using System.Collections.Generic; using System.IO; using System.Reflection; using AlicizaX.Fsm.Runtime; using AlicizaX.Procedure.Runtime; using AlicizaX.Runtime; using HybridCLR; using UnityEngine; using YooAsset; namespace Unity.Startup.Procedure { public sealed class ProcedureLoadAssembly : ProcedureBase { // private bool m_enableAddressable = 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; #pragma warning disable CS0414 private bool m_LoadMetadataAssemblyWait; #pragma warning restore CS0414 private Assembly m_MainLogicAssembly; private List m_HotfixAssemblys; protected override void OnEnter(IFsm procedureOwner) { base.OnEnter(procedureOwner); Log.Info(" ProcedureLoadAssembly OnEnter"); m_LoadAssemblyComplete = false; m_HotfixAssemblys = new List(); //AOT Assembly加载原始metadata if (AssemblyLoadData.Enable) { #if !UNITY_EDITOR m_LoadMetadataAssemblyComplete = false; LoadMetadataForAOTAssembly(); #else m_LoadMetadataAssemblyComplete = true; #endif } else { m_LoadMetadataAssemblyComplete = true; } if (!AssemblyLoadData.Enable || GameApp.Resource.GamePlayMode == EPlayMode.EditorSimulateMode) { m_MainLogicAssembly = GetMainLogicAssembly(); } else { if (AssemblyLoadData.Enable) { foreach (string hotUpdateDllName in AssemblyLoadData.Instance .HotUpdateAssemblies) { var assetLocation = hotUpdateDllName; // if (!m_enableAddressable) // { // assetLocation = Utility.Path.GetRegularPath( // Path.Combine( // "Assets", // FrameworkHotUpdateSettings.Instance.AssemblyTextAssetPath, // $"{hotUpdateDllName}{FrameworkHotUpdateSettings.Instance.AssemblyTextAssetExtension}")); // } Log.Info($"LoadAsset: [ {assetLocation} ]"); m_LoadAssetCount++; GameApp.Resource.LoadAsset(assetLocation, LoadAssetSuccess); } m_LoadAssemblyWait = true; } else { m_MainLogicAssembly = GetMainLogicAssembly(); } } if (m_LoadAssetCount == 0) { m_LoadAssemblyComplete = true; } } protected override void OnUpdate(IFsm procedureOwner, float elapseSeconds, float realElapseSeconds) { if (!m_LoadAssemblyComplete) { return; } if (!m_LoadMetadataAssemblyComplete) { return; } AllAssemblyLoadComplete(procedureOwner); } private void AllAssemblyLoadComplete(IFsm procedureOwner) { ChangeState(procedureOwner); #if UNITY_EDITOR m_MainLogicAssembly = GetMainLogicAssembly(); #endif if (m_MainLogicAssembly == null) { Log.Warning($"Main logic assembly missing."); return; } var appType = m_MainLogicAssembly.GetType(GlobalSetting.EntranceClass); if (appType == null) { Log.Warning($"Main logic type '{GlobalSetting.EntranceClass}' missing."); return; } var entryMethod = appType.GetMethod(GlobalSetting.EntranceMethod); if (entryMethod == null) { Log.Warning($"Main logic entry method '{GlobalSetting.EntranceMethod}' missing."); return; } object[] objects = new object[] { new object[] { m_HotfixAssemblys } }; entryMethod.Invoke(appType, objects); } private Assembly GetMainLogicAssembly() { m_HotfixAssemblys.Clear(); Assembly mainLogicAssembly = null; foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { if (string.Compare(GlobalSetting.EntranceDll, $"{assembly.GetName().Name}.dll", StringComparison.Ordinal) == 0) { mainLogicAssembly = assembly; } foreach (var hotUpdateDllName in AssemblyLoadData.Instance.HotUpdateAssemblies) { if (hotUpdateDllName == $"{assembly.GetName().Name}.dll") { m_HotfixAssemblys.Add(assembly); } } if (mainLogicAssembly != null && m_HotfixAssemblys.Count == AssemblyLoadData.Instance.HotUpdateAssemblies.Count) { break; } } return mainLogicAssembly; } /// /// 加载代码资源成功回调。 /// /// 代码资产。 private void LoadAssetSuccess(TextAsset textAsset) { m_LoadAssetCount--; if (textAsset == null) { Log.Warning($"Load Assembly failed."); return; } var assetName = textAsset.name; Log.Info($"LoadAssetSuccess, assetName: [ {assetName} ]"); try { var assembly = Assembly.Load(textAsset.bytes); if (string.Compare(GlobalSetting.EntranceDll, assetName, StringComparison.Ordinal) == 0) { m_MainLogicAssembly = assembly; } m_HotfixAssemblys.Add(assembly); Log.Info($"Assembly [ {assembly.GetName().Name} ] loaded"); } catch (Exception e) { m_FailureAssetCount++; Log.Error(e.Message); throw; } finally { m_LoadAssemblyComplete = m_LoadAssemblyWait && 0 == m_LoadAssetCount; } GameApp.Resource.UnloadAsset(textAsset); } /// /// 为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 (AssemblyLoadData.Instance.AOTMetaAssemblies.Count == 0) { m_LoadMetadataAssemblyComplete = true; return; } foreach (string aotDllName in AssemblyLoadData.Instance.AOTMetaAssemblies) { var assetLocation = aotDllName; // if (!m_enableAddressable) // { // assetLocation = Utility.Path.GetRegularPath( // Path.Combine( // "Assets", // FrameworkHotUpdateSettings.Instance.AssemblyTextAssetPath, // $"{aotDllName}{FrameworkHotUpdateSettings.Instance.AssemblyTextAssetExtension}")); // } Log.Info($"LoadMetadataAsset: [ {assetLocation} ]"); m_LoadMetadataAssetCount++; GameApp.Resource.LoadAsset(assetLocation, LoadMetadataAssetSuccess); } m_LoadMetadataAssemblyWait = true; } /// /// 加载元数据资源成功回调。 /// /// 代码资产。 private unsafe void LoadMetadataAssetSuccess(TextAsset textAsset) { m_LoadMetadataAssetCount--; if (null == textAsset) { Log.Info($"LoadMetadataAssetSuccess:Load Metadata failed."); return; } string assetName = textAsset.name; Log.Info($"LoadMetadataAssetSuccess, assetName: [ {assetName} ]"); try { byte[] dllBytes = textAsset.bytes; fixed (byte* ptr = dllBytes) { #if ENABLE_HYBRIDCLR // 加载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}"); #endif } } catch (Exception e) { m_FailureMetadataAssetCount++; Log.Error(e.Message); throw; } finally { m_LoadMetadataAssemblyComplete = m_LoadMetadataAssemblyWait && 0 == m_LoadMetadataAssetCount; } GameApp.Resource.UnloadAsset(textAsset); } } }