294 lines
10 KiB
C#
294 lines
10 KiB
C#
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<Assembly> m_HotfixAssemblys;
|
||
|
||
protected override void OnEnter(IFsm<IProcedureManager> procedureOwner)
|
||
{
|
||
base.OnEnter(procedureOwner);
|
||
Log.Info(" ProcedureLoadAssembly OnEnter");
|
||
m_LoadAssemblyComplete = false;
|
||
m_HotfixAssemblys = new List<Assembly>();
|
||
|
||
//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<TextAsset>(assetLocation, LoadAssetSuccess);
|
||
}
|
||
|
||
m_LoadAssemblyWait = true;
|
||
}
|
||
else
|
||
{
|
||
m_MainLogicAssembly = GetMainLogicAssembly();
|
||
}
|
||
}
|
||
|
||
|
||
if (m_LoadAssetCount == 0)
|
||
{
|
||
m_LoadAssemblyComplete = true;
|
||
}
|
||
}
|
||
|
||
|
||
protected override void OnUpdate(IFsm<IProcedureManager> procedureOwner, float elapseSeconds,
|
||
float realElapseSeconds)
|
||
{
|
||
if (!m_LoadAssemblyComplete)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (!m_LoadMetadataAssemblyComplete)
|
||
{
|
||
return;
|
||
}
|
||
|
||
AllAssemblyLoadComplete(procedureOwner);
|
||
}
|
||
|
||
private void AllAssemblyLoadComplete(IFsm<IProcedureManager> procedureOwner)
|
||
{
|
||
ChangeState<ProcedureGameLauncherState>(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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载代码资源成功回调。
|
||
/// </summary>
|
||
/// <param name="textAsset">代码资产。</param>
|
||
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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 为Aot Assembly加载原始metadata, 这个代码放Aot或者热更新都行。
|
||
/// 一旦加载后,如果AOT泛型函数对应native实现不存在,则自动替换为解释模式执行。
|
||
/// </summary>
|
||
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<TextAsset>(assetLocation, LoadMetadataAssetSuccess);
|
||
}
|
||
|
||
m_LoadMetadataAssemblyWait = true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载元数据资源成功回调。
|
||
/// </summary>
|
||
/// <param name="textAsset">代码资产。</param>
|
||
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);
|
||
}
|
||
}
|
||
}
|