247 lines
8.7 KiB
C#
247 lines
8.7 KiB
C#
|
#if USE_HCLR
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Linq;
|
|||
|
using System.Reflection;
|
|||
|
using Cysharp.Threading.Tasks;
|
|||
|
using HybridCLR;
|
|||
|
using PhxhSDK.AOT.SimpleLoader;
|
|||
|
using UnityEngine;
|
|||
|
|
|||
|
namespace PhxhSDK.AOT
|
|||
|
{
|
|||
|
public class HybridCLRHelper
|
|||
|
{
|
|||
|
const string META_DATA_DLL_PATH = "Assets/HotUpdateDlls/MetaDataDll/";
|
|||
|
const string HOT_UPDATE_DLL_PATH = "Assets/HotUpdateDlls/HotUpdateDll/";
|
|||
|
const string PHXH_DLL_NAME = "Phxh.dll";
|
|||
|
const string FRAMEWORK_DLL_NAME = "FrameWork.dll";
|
|||
|
const string GAMEPLAY_DLL_NAME = "GamePlay.dll";
|
|||
|
|
|||
|
private byte[] _dllBytes;
|
|||
|
|
|||
|
private Dictionary<string, Assembly> _allHotUpdateAssemblies = new();
|
|||
|
|
|||
|
private readonly List<string> _phxhDependencyDlls = new List<string>()
|
|||
|
{
|
|||
|
};
|
|||
|
|
|||
|
private readonly List<string> _frameWorkDependencyDlls = new List<string>()
|
|||
|
{
|
|||
|
//不要随意改顺序!
|
|||
|
};
|
|||
|
|
|||
|
private readonly List<string> _gamePlayDependencyDlls = new List<string>()
|
|||
|
{
|
|||
|
//不要随意改顺序!
|
|||
|
"BehaviorDesigner.Runtime.dll",
|
|||
|
//"BestHTTP.dll",
|
|||
|
"DOTweenCSharp.dll",
|
|||
|
};
|
|||
|
|
|||
|
//所有使用了RuntimeInitializeOnLoadMethod attribute的程序集
|
|||
|
private readonly List<string> _hasRuntimeInitializeOnLoadMethodAssemblies = new List<string>()
|
|||
|
{
|
|||
|
"BehaviorDesigner.Runtime",
|
|||
|
//"BestHTTP",
|
|||
|
"GamePlay",
|
|||
|
};
|
|||
|
|
|||
|
private List<string> _aotAssemblyList = new List<string>();
|
|||
|
|
|||
|
private ISimpleLoader _loader;
|
|||
|
|
|||
|
public async UniTask LoadAssemblies()
|
|||
|
{
|
|||
|
await LoadPhxhDependencyAssemblies();
|
|||
|
await LoadPhxhAssembies();
|
|||
|
await LoadFrameworkDependencyAssemblies();
|
|||
|
await LoadFrameWorkAssemblies();
|
|||
|
await LoadMetadataForAOTAssemblies();
|
|||
|
await LoadGamePlayDependencyAssemblies();
|
|||
|
await LoadGamePlayAssemblies();
|
|||
|
await ReloadAddressableCatalog();
|
|||
|
_ExecuteRuntimeInitializeOnLoadMethodAttribute();
|
|||
|
await UniTask.NextFrame();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Addressable初始化时热更新代码所对应的ScriptableObject的类型会被识别为System.Object,需要在热更新dll加载完后重新加载一下Addressable的Catalog
|
|||
|
/// https://hybridclr.doc.code-philosophy.com/docs/help/commonerrors
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
private async UniTask ReloadAddressableCatalog()
|
|||
|
{
|
|||
|
#if !USE_YOO
|
|||
|
await SimpleLoader_Addressables.ReloadAddressableCatalog();
|
|||
|
#endif
|
|||
|
await UniTask.NextFrame();
|
|||
|
}
|
|||
|
|
|||
|
private void _TryHandleMethod(MethodInfo method, List<MethodExecutionInfo> runtimeMethods)
|
|||
|
{
|
|||
|
if (!method.IsStatic)
|
|||
|
return;
|
|||
|
var runtimeInitializedAttribute = typeof(RuntimeInitializeOnLoadMethodAttribute);
|
|||
|
var attri = method.GetCustomAttribute(runtimeInitializedAttribute);
|
|||
|
if (attri == null)
|
|||
|
return;
|
|||
|
var sequence = (int)((RuntimeInitializeOnLoadMethodAttribute)attri).loadType;
|
|||
|
var methodInfo = new MethodExecutionInfo(method, sequence);
|
|||
|
runtimeMethods.Add(methodInfo);
|
|||
|
}
|
|||
|
|
|||
|
private void _ExecuteRuntimeInitializeOnLoadMethodAttribute()
|
|||
|
{
|
|||
|
List<MethodExecutionInfo> runtimeMethods = new();
|
|||
|
foreach (var assemblyName in _hasRuntimeInitializeOnLoadMethodAssemblies)
|
|||
|
{
|
|||
|
var assembly = GetAssembly(assemblyName);
|
|||
|
if (assembly == null)
|
|||
|
{
|
|||
|
Debug.LogError($"找不到使用过RuntimeInitializeOnLoadMethod的assembly,name:{assemblyName}");
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
foreach (var type in assembly.GetTypes())
|
|||
|
{
|
|||
|
var methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
|||
|
foreach (var method in methods)
|
|||
|
{
|
|||
|
_TryHandleMethod(method, runtimeMethods);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
runtimeMethods.Sort((a, b) => b.sequnence.CompareTo(a.sequnence));
|
|||
|
foreach (var methodInfo in runtimeMethods)
|
|||
|
{
|
|||
|
Debug.Log($"call method methodName:{methodInfo.method.Name} sequnence:{methodInfo.sequnence}");
|
|||
|
methodInfo.method.Invoke(null, null);
|
|||
|
}
|
|||
|
|
|||
|
Debug.Log("调用RuntimeInitializeOnLoadMethod结束!");
|
|||
|
}
|
|||
|
|
|||
|
private Assembly GetAssembly(string assemblyName)
|
|||
|
{
|
|||
|
assemblyName = assemblyName.Replace(".dll", "");
|
|||
|
IEnumerable<Assembly> allAssemblies = _allHotUpdateAssemblies.Values;
|
|||
|
return allAssemblies.First(assembly => assembly.FullName.Contains(assemblyName));
|
|||
|
}
|
|||
|
|
|||
|
private async UniTask LoadSingleHotUpdateAssembly(string dllName)
|
|||
|
{
|
|||
|
var path = $"{HOT_UPDATE_DLL_PATH}{dllName}.bytes";
|
|||
|
await ReadDllBytes(path);
|
|||
|
if (_dllBytes != null)
|
|||
|
{
|
|||
|
var assembly = Assembly.Load(_dllBytes);
|
|||
|
_allHotUpdateAssemblies.Add(assembly.FullName, assembly);
|
|||
|
Debug.Log($"Load Assembly success,assembly Name:{assembly.FullName}");
|
|||
|
// foreach (var type in GetAssembly(dllName).GetTypes())
|
|||
|
// {
|
|||
|
// Debug.Log($"type:{type} in assembly:{GAMEPLAY_DLL_NAME}");
|
|||
|
// }
|
|||
|
}
|
|||
|
await UniTask.NextFrame();
|
|||
|
}
|
|||
|
|
|||
|
//加载GamePlay程序集
|
|||
|
private async UniTask LoadGamePlayAssemblies()
|
|||
|
{
|
|||
|
await LoadSingleHotUpdateAssembly(GAMEPLAY_DLL_NAME);
|
|||
|
Debug.Log("加载GamePlay程序集结束!");
|
|||
|
}
|
|||
|
|
|||
|
//加载GamePlay依赖的第三方程序集
|
|||
|
private async UniTask LoadGamePlayDependencyAssemblies()
|
|||
|
{
|
|||
|
foreach (var dllName in _gamePlayDependencyDlls)
|
|||
|
{
|
|||
|
await LoadSingleHotUpdateAssembly(dllName);
|
|||
|
}
|
|||
|
|
|||
|
Debug.Log("加载GamePlay依赖程序集结束!");
|
|||
|
}
|
|||
|
|
|||
|
//加载Framework程序集
|
|||
|
private async UniTask LoadFrameWorkAssemblies()
|
|||
|
{
|
|||
|
await LoadSingleHotUpdateAssembly(FRAMEWORK_DLL_NAME);
|
|||
|
Debug.Log("加载Framework程序集结束!");
|
|||
|
}
|
|||
|
|
|||
|
//加载FrameWork依赖的第三方热更序集
|
|||
|
private async UniTask LoadFrameworkDependencyAssemblies()
|
|||
|
{
|
|||
|
foreach (var dllName in _frameWorkDependencyDlls)
|
|||
|
{
|
|||
|
await LoadSingleHotUpdateAssembly(dllName);
|
|||
|
}
|
|||
|
|
|||
|
Debug.Log("加载Framework依赖程序集结束!");
|
|||
|
}
|
|||
|
|
|||
|
private async UniTask LoadPhxhAssembies()
|
|||
|
{
|
|||
|
await LoadSingleHotUpdateAssembly(PHXH_DLL_NAME);
|
|||
|
Debug.Log("加载Phxh程序集结束!");
|
|||
|
}
|
|||
|
|
|||
|
private async UniTask LoadPhxhDependencyAssemblies()
|
|||
|
{
|
|||
|
foreach (var dllName in _phxhDependencyDlls)
|
|||
|
{
|
|||
|
await LoadSingleHotUpdateAssembly(dllName);
|
|||
|
}
|
|||
|
|
|||
|
Debug.Log("加载Phxh依赖程序集结束!");
|
|||
|
}
|
|||
|
|
|||
|
//补充元数据
|
|||
|
private async UniTask LoadMetadataForAOTAssemblies()
|
|||
|
{
|
|||
|
var aotAssemblies = _aotAssemblyList;
|
|||
|
if (aotAssemblies == null)
|
|||
|
{
|
|||
|
Debug.LogError($"LoadMetadataForAOTAssemblies failed, PatchedAOTAssemblyList is null!");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
foreach (var aotDllName in aotAssemblies)
|
|||
|
{
|
|||
|
var path = $"{META_DATA_DLL_PATH}{aotDllName}.bytes";
|
|||
|
await ReadDllBytes(path);
|
|||
|
if (_dllBytes != null)
|
|||
|
{
|
|||
|
var err = RuntimeApi.LoadMetadataForAOTAssembly(_dllBytes, HomologousImageMode.SuperSet);
|
|||
|
Debug.Log($"LoadMetadataForAOTAssembly:{aotDllName}. ret:{err}");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Debug.Log("补充元数据结束!");
|
|||
|
}
|
|||
|
|
|||
|
private async UniTask ReadDllBytes(string path)
|
|||
|
{
|
|||
|
_dllBytes = await _loader.LoadDllBytes(path);
|
|||
|
if (_dllBytes == null)
|
|||
|
{
|
|||
|
Debug.LogError($"cant load dll,path:{path}");
|
|||
|
return;
|
|||
|
}
|
|||
|
await _loader.UnloadDll(path);
|
|||
|
}
|
|||
|
|
|||
|
public void RegistLoader(ISimpleLoader loader)
|
|||
|
{
|
|||
|
_loader = loader;
|
|||
|
}
|
|||
|
|
|||
|
public void SetDllList(IReadOnlyList<string> dllList)
|
|||
|
{
|
|||
|
_aotAssemblyList.AddRange(dllList);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
#endif
|