AlicizaX/Client/Assets/Scripts/Startup/Procedure/ProcedureLoadAssembly.cs

243 lines
8.1 KiB
C#
Raw Normal View History

2026-04-14 11:24:09 +08:00
#if ENABLE_HYBRIDCLR
using System;
2025-01-23 19:06:48 +08:00
using System.Collections.Generic;
using System.Reflection;
2025-04-28 19:45:45 +08:00
using AlicizaX;
2025-01-23 19:06:48 +08:00
using UnityEngine;
using YooAsset;
2025-11-05 19:32:22 +08:00
using HybridCLR;
2025-01-23 19:06:48 +08:00
namespace Unity.Startup.Procedure
{
2025-11-18 16:15:26 +08:00
public sealed class ProcedureLoadAssembly : ProcedureBase
2025-01-23 19:06:48 +08:00
{
private int m_LoadAssetCount;
private int m_LoadMetadataAssetCount;
private bool m_LoadAssemblyComplete;
private bool m_LoadMetadataAssemblyComplete;
private bool m_LoadAssemblyWait;
private bool m_LoadMetadataAssemblyWait;
private Assembly m_MainLogicAssembly;
private List<Assembly> m_HotfixAssemblys;
2025-04-28 19:45:45 +08:00
protected override void OnEnter()
2025-01-23 19:06:48 +08:00
{
2025-01-26 20:55:39 +08:00
Log.Info(" ProcedureLoadAssembly OnEnter");
2025-01-23 19:06:48 +08:00
m_LoadAssemblyComplete = false;
m_HotfixAssemblys = new List<Assembly>();
//AOT Assembly加载原始metadata
2025-10-14 15:42:34 +08:00
if (GameApp.Resource.PlayMode == EPlayMode.EditorSimulateMode)
2025-01-23 19:06:48 +08:00
{
m_LoadMetadataAssemblyComplete = true;
}
else
{
2025-04-28 19:45:45 +08:00
m_LoadMetadataAssemblyComplete = false;
LoadMetadataForAOTAssembly();
2025-01-23 19:06:48 +08:00
}
2025-10-14 15:42:34 +08:00
if (GameApp.Resource.PlayMode == EPlayMode.EditorSimulateMode)
2025-01-23 19:06:48 +08:00
{
m_MainLogicAssembly = GetMainLogicAssembly();
}
2025-10-14 15:42:34 +08:00
else
2025-01-23 19:06:48 +08:00
{
2025-04-28 19:45:45 +08:00
foreach (string hotUpdateDllName in StartupSetting.HotUpdateAssemblies)
2025-01-23 19:06:48 +08:00
{
2025-04-28 19:45:45 +08:00
m_LoadAssetCount++;
GameApp.Resource.LoadAsset<TextAsset>(hotUpdateDllName, LoadAssetSuccess);
2025-01-23 19:06:48 +08:00
}
2025-04-28 19:45:45 +08:00
m_LoadAssemblyWait = true;
2025-01-23 19:06:48 +08:00
}
2025-03-04 18:40:14 +08:00
2025-01-23 19:06:48 +08:00
if (m_LoadAssetCount == 0)
{
m_LoadAssemblyComplete = true;
}
}
2025-11-18 16:15:26 +08:00
protected override void OnUpdate()
2025-01-23 19:06:48 +08:00
{
if (!m_LoadAssemblyComplete)
{
return;
}
if (!m_LoadMetadataAssemblyComplete)
{
return;
}
2025-04-28 19:45:45 +08:00
AllAssemblyLoadComplete();
2025-01-23 19:06:48 +08:00
}
2025-04-28 19:45:45 +08:00
private void AllAssemblyLoadComplete()
2025-01-23 19:06:48 +08:00
{
2025-11-18 16:15:26 +08:00
SwitchProcedure<ProcedureUpdateFinishState>();
2025-04-28 19:45:45 +08:00
2025-01-23 19:06:48 +08:00
if (m_MainLogicAssembly == null)
{
Log.Warning($"Main logic assembly missing.");
return;
}
2025-04-28 19:45:45 +08:00
var appType = m_MainLogicAssembly.GetType(StartupSetting.EntranceClass);
2025-01-23 19:06:48 +08:00
if (appType == null)
{
2025-04-28 19:45:45 +08:00
Log.Warning($"Main logic type '{StartupSetting.EntranceClass}' missing.");
2025-01-23 19:06:48 +08:00
return;
}
2025-04-28 19:45:45 +08:00
var entryMethod = appType.GetMethod(StartupSetting.EntranceMethod);
2025-01-23 19:06:48 +08:00
if (entryMethod == null)
{
2025-04-28 19:45:45 +08:00
Log.Warning($"Main logic entry method '{StartupSetting.EntranceMethod}' missing.");
2025-01-23 19:06:48 +08:00
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())
{
2025-04-28 19:45:45 +08:00
if (string.Compare(StartupSetting.EntranceDll, $"{assembly.GetName().Name}.dll",
2025-01-23 19:06:48 +08:00
StringComparison.Ordinal) == 0)
{
mainLogicAssembly = assembly;
}
2025-04-28 19:45:45 +08:00
foreach (var hotUpdateDllName in StartupSetting.HotUpdateAssemblies)
2025-01-23 19:06:48 +08:00
{
2025-04-28 19:45:45 +08:00
if (hotUpdateDllName == $"{assembly.GetName().Name}.dll" && !m_HotfixAssemblys.Contains(assembly))
2025-01-23 19:06:48 +08:00
{
m_HotfixAssemblys.Add(assembly);
}
}
2025-03-07 18:00:59 +08:00
if (mainLogicAssembly != null && m_HotfixAssemblys.Count ==
2025-04-28 19:45:45 +08:00
StartupSetting.HotUpdateAssemblies.Count)
2025-01-23 19:06:48 +08:00
{
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);
2025-04-28 19:45:45 +08:00
if (string.Compare(StartupSetting.EntranceDll, assetName, StringComparison.Ordinal) == 0)
2025-01-23 19:06:48 +08:00
{
m_MainLogicAssembly = assembly;
}
m_HotfixAssemblys.Add(assembly);
Log.Info($"Assembly [ {assembly.GetName().Name} ] loaded");
}
catch (Exception e)
{
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会返回错误
2025-04-28 19:45:45 +08:00
if (AOTGenericReferences.PatchedAOTAssemblyList.Count == 0)
2025-01-23 19:06:48 +08:00
{
m_LoadMetadataAssemblyComplete = true;
return;
}
2025-04-28 19:45:45 +08:00
foreach (string aotDllName in AOTGenericReferences.PatchedAOTAssemblyList)
2025-01-23 19:06:48 +08:00
{
m_LoadMetadataAssetCount++;
2025-04-28 19:45:45 +08:00
GameApp.Resource.LoadAsset<TextAsset>(aotDllName, LoadMetadataAssetSuccess);
2025-01-23 19:06:48 +08:00
}
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;
try
{
byte[] dllBytes = textAsset.bytes;
fixed (byte* ptr = dllBytes)
{
// 加载assembly对应的dll会自动为它hook。一旦Aot泛型函数的native函数不存在用解释器版本代码
HomologousImageMode mode = HomologousImageMode.SuperSet;
2025-03-07 18:00:59 +08:00
LoadImageErrorCode err =
2025-11-05 19:32:22 +08:00
(LoadImageErrorCode)RuntimeApi.LoadMetadataForAOTAssembly(dllBytes, mode);
2025-04-28 19:45:45 +08:00
Log.Info($"LoadMetadataForAOTAssembly:{assetName}. mode:{mode} ret:{err}");
2025-01-23 19:06:48 +08:00
}
}
catch (Exception e)
{
Log.Error(e.Message);
throw;
}
finally
{
m_LoadMetadataAssemblyComplete = m_LoadMetadataAssemblyWait && 0 == m_LoadMetadataAssetCount;
}
GameApp.Resource.UnloadAsset(textAsset);
}
}
2025-01-26 20:55:39 +08:00
}
2026-04-14 11:24:09 +08:00
#endif