using System; using System.IO; using UnityEngine; using JEngine.Core; using JEngine.Helper; using System.Threading; using System.Threading.Tasks; using ILRuntime.Mono.Cecil.Pdb; using UnityEngine.Serialization; using AppDomain = ILRuntime.Runtime.Enviorment.AppDomain; public partial class InitJEngine : MonoBehaviour { //单例 public static InitJEngine Instance; //热更域 public static AppDomain Appdomain; //是否成功加载 public static bool Success; //dll名字,入口函数名字,以及周期方法名 private const string DllName = "HotUpdateScripts"; private const string HotMainType = "HotUpdateScripts.Program"; private const string SetupGameMethod = "SetupGame"; private const string RunGameMethod = "RunGame"; //加密密钥 [Tooltip("加密密钥,需要16位")] [FormerlySerializedAs("Key")] [SerializeField] public string key; //寄存器模式 [Tooltip("ILRuntime寄存器模式")] [SerializeField] private ILRuntimeJITFlag useJIT = ILRuntimeJITFlag.JITOnDemand; //是否使用pdb,真机禁止 [Tooltip("是否使用pdb,仅编辑器生效")] [FormerlySerializedAs("UsePdb")] [SerializeField] public bool usePdb; //是否允许debug(会产生log) [Tooltip("是否允许debug,勾选后产生log")] [FormerlySerializedAs("Debug")] [SerializeField] public bool debug = true; //数据流 private Stream _fs; private Stream _pdb; /// /// 初始化 /// private void Awake() { //单例 if (Instance != null) { Destroy(Instance.gameObject); } Instance = this; DontDestroyOnLoad(gameObject); } /// /// 加载热更 /// public async Task LoadHotUpdateCallback() { //替换LogHandler,让UnityEngine.Debug.LogException能定位更精准的堆栈,同时利用插件精简堆栈信息 Debug.unityLogger.logHandler = new JEngine.Core.Logger(Debug.unityLogger.logHandler); //加载热更DLL await Instance.LoadHotFixAssembly(); //初始化LifeCycle LifeCycleMgr.Initialize(); //初始化CoroutineMgr CoroutineMgr.Initialize(); //初始化ThreadMgr ThreadMgr.Initialize(); //初始化Fps监听 FpsMonitor.Initialize(); //调用SetupGame周期 Tools.InvokeHotMethod(HotMainType, SetupGameMethod); //初始化ClassBind ClassBindMgr.Instantiate(); //调用RunGame周期 Tools.InvokeHotMethod(HotMainType, RunGameMethod); //调用在主工程的热更代码加载完毕后的周期 HotUpdateLoadedHelper.Init(Appdomain); } /// /// 使用ILRuntime加载热更工程 /// private async Task LoadHotFixAssembly() { //创建新对象实例 Appdomain = new AppDomain((int)useJIT); _pdb = null; bool isEditorMode = !AssetMgr.RuntimeMode; //dll的二进制 byte[] dll = await DllMgr.GetDllBytes(DllName, isEditorMode); //pdb默认不存在 byte[] pdb = ConstMgr.NullBytes; //编辑器下模拟加密dll if (isEditorMode) { DllMgr.SimulateEncryption(ref dll, key); } if (usePdb) { var pdbFileBytes = await DllMgr.GetPdbBytes(DllName, isEditorMode); pdb = new byte[pdbFileBytes.Length]; Array.Copy(pdbFileBytes, pdb, pdbFileBytes.Length); } //尝试加载dll try { //这里默认用分块解密,JStream _fs = new JStream(dll, key); if (pdb.Length != 0) { _pdb = new MemoryStream(pdb); } //加载dll if (usePdb && pdb != null) { Appdomain.LoadAssembly(_fs, _pdb, new PdbReaderProvider()); } else { Appdomain.LoadAssembly(_fs); } } catch (Exception e) { Log.PrintError("加载热更DLL错误:\n" + e); if (!usePdb) { Log.PrintError( "加载热更DLL失败,请确保HotUpdateResources/Main/Dll里面有HotUpdateScripts.bytes文件,并且Build Bundle后将DLC传入服务器"); Log.PrintError("也有可能是密码不匹配或密码包含特殊字符导致的"); } else if (Application.isEditor && usePdb) { Log.PrintError("PDB不可用,可能是DLL和PDB版本不一致,可能DLL是Release,如果是Release出包,请取消UsePdb选项,本次已跳过使用PDB"); usePdb = false; await LoadHotFixAssembly(); } return; } //成功加载热更dll Success = true; //初始化ILRuntime InitializeILRuntime(Appdomain); } public static void InitializeILRuntime(AppDomain appdomain) { #if DEBUG && (UNITY_EDITOR || UNITY_ANDROID || UNITY_IPHONE) //由于Unity的Profiler接口只允许在主线程使用,为了避免出异常,需要告诉ILRuntime主线程的线程ID才能正确将函数运行耗时报告给Profiler appdomain.UnityMainThreadID = Thread.CurrentThread.ManagedThreadId; appdomain.DebugService.StartDebugService(56000); #endif var assemblies = System.AppDomain.CurrentDomain.GetAssemblies(); var helperInterface = typeof(IRegisterHelper); //全部程序集 foreach (var assembly in assemblies) { var types = assembly.GetTypes(); //全部类型 foreach (var type in types) { var interfaces = type.GetInterfaces(); //继承接口 foreach (var @interface in interfaces) { if (@interface == helperInterface) { //注册 var helper = (IRegisterHelper)Activator.CreateInstance(type); helper.Register(appdomain); } } } } //CLR绑定(有再去绑定),这个要在最后 Type t = Type.GetType("ILRuntime.Runtime.Generated.CLRBindings"); if (t != null) { t.GetMethod("Initialize")?.Invoke(null, new object[] { appdomain }); } } [Serializable] private enum ILRuntimeJITFlag { None = 0, /// /// Method will be JIT when method is called multiple time /// JITOnDemand = 1, /// /// Method will be JIT immediately when called, instead of progressively warm up /// JITImmediately = 2, /// /// Method will not be JIT when called /// NoJIT = 4, /// /// Method will always be inlined when called /// ForceInline = 8 } }