using UnityEngine; using Cysharp.Threading; using Cysharp.Threading.Tasks; using System; using DG.Tweening; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using UnityEngine.Networking; using LaunchCommon; using HybridCLR; public class Launch : MonoBehaviour { private static Assembly _hotUpdateAss; //网络获取 private static List AOTMetaAssemblyFiles { get; } = new List(); private static Dictionary s_assetDatas = new Dictionary(); public static Launch Instance { get { return m_Instance; } } private GameObject launchExWin = null; private static Launch m_Instance; private void Awake() { if (m_Instance != null) { Debug.LogError("Launch Instance is not null"); return; } m_Instance = this; #if !UNITY_EDITOR if (File.Exists(Directory.GetParent(Application.persistentDataPath) + "/Debug") || LocalSave.GetString("#@#BrancH") != string.Empty) { Debug.unityLogger.logEnabled = true; } else { Debug.unityLogger.logEnabled = false; } #endif } // 启动入口 async void Start() { Debug.Log("Launch Start"); InitPlugins(); InitSetting(); // 1. 打开加载界面 } private void InitSetting() { System.Globalization.CultureInfo culture = System.Globalization.CultureInfo.CreateSpecificCulture("en-US"); System.Globalization.CultureInfo.CurrentCulture = culture; System.Globalization.CultureInfo.CurrentUICulture = culture; System.Globalization.CultureInfo.DefaultThreadCurrentCulture = culture; System.Globalization.CultureInfo.DefaultThreadCurrentUICulture = culture; System.Net.ServicePointManager.DefaultConnectionLimit = 100; #if UNITY_EDITOR //内网下载测试 _hotUpdateAss = System.AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "Main"); Type type = _hotUpdateAss.GetType("InGameDownTestUtility"); LocalResManager.Instance.isPCTestDownLoad = (bool)type.GetMethod("GetReadVerionEx").Invoke(null, null); #endif SDKInit(); LocalResManager.Instance.Init(); LocalResManager.Instance.InitTable(() => { LocalResManager.Instance.InitDefaultLanguage(); launchExWin = LaunchExWin.OpenWindow(); // LocalResManager.Instance.OpenWindow("LaunchExWin", m_UICanvas); #if !UNITY_EDITOR LocalResManager.step = LocalResManager.LoadDllStep.RequestVersion; #else if (LocalResManager.Instance.isPCTestDownLoad) { LocalResManager.step = LocalResManager.LoadDllStep.RequestVersion; } else { StartGame(); } #endif }); } private void InitPlugins() { DOTween.Init(); } private void SDKInit() { } /// /// 为aot assembly加载原始metadata, 这个代码放aot或者热更新都行。 /// 一旦加载后,如果AOT泛型函数对应native实现不存在,则自动替换为解释模式执行 /// private void LoadMetadataForAOTAssemblies() { /// 注意,补充元数据是给AOT dll补充元数据,而不是给热更新dll补充元数据。 /// 热更新dll不缺元数据,不需要补充,如果调用LoadMetadataForAOTAssembly会返回错误 /// HomologousImageMode mode = HomologousImageMode.SuperSet; foreach (var aotDllName in AOTMetaAssemblyFiles) { if (aotDllName == "Main.dll.bytes") continue; // 加载assembly对应的dll,会自动为它hook。一旦aot泛型函数的native函数不存在,用解释器版本代码 LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(ReadBytesFromStreamingAssets(aotDllName), mode); Debug.Log($"LoadMetadataForAOTAssembly:{aotDllName}. mode:{mode} ret:{err}"); } } public static byte[] ReadBytesFromStreamingAssets(string dllName) { return s_assetDatas[dllName]; } private void StartGame() { #if !UNITY_EDITOR LoadMetadataForAOTAssemblies(); _hotUpdateAss = Assembly.Load(ReadBytesFromStreamingAssets("Main.dll.bytes")); s_assetDatas = null; #else if (_hotUpdateAss == null) _hotUpdateAss = System.AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "Main"); #endif LocalResManager.step = LocalResManager.LoadDllStep.None; // m_UICanvas.gameObject.SetActive(false); DestroySingleton(); Type type = _hotUpdateAss.GetType("LaunchInHot").BaseType; var getInstance = type.GetMethod("get_Instance", BindingFlags.Public | BindingFlags.Static); if (getInstance != null) { getInstance.Invoke(null, null); } else { Debug.LogError("无法找到get_Instance方法"); } Debug.Log("进入游戏流程"); } private void DestroySingleton() { if (LocalResManager.IsValid()) { LocalResManager.Destroy(); } if (DownloadMgr.IsValid()) { Destroy(DownloadMgr.Instance); } if (LogicEngine.IsValid()) { Destroy(LogicEngine.Instance); } if (HttpRequest.IsValid()) { Destroy(HttpRequest.Instance); } if (DownLoadAndDiscompressTask.IsValid()) { DownLoadAndDiscompressTask.Destroy(); } if (null != launchExWin) { Destroy(launchExWin); launchExWin = null; } stop = true; } bool stop = false; void Update() { if (stop) return; if (LocalResManager.step == LocalResManager.LoadDllStep.None || LocalResManager.step == LocalResManager.LoadDllStep.Wait) return; else if (LocalResManager.step == LocalResManager.LoadDllStep.RequestVersion) { LocalResManager.step = LocalResManager.LoadDllStep.Wait; LocalResManager.Instance.RequestVersionCheck(); } else if (LocalResManager.step == LocalResManager.LoadDllStep.PrepareDownLoad) { LocalResManager.step = LocalResManager.LoadDllStep.Wait; //下载前准备,读表判断是否需要多语言不同下载路径 PrepareDownLoad(); } else if (LocalResManager.step == LocalResManager.LoadDllStep.DownLoad) { LocalResManager.step = LocalResManager.LoadDllStep.Wait; BeginDownload(); } else if (LocalResManager.step == LocalResManager.LoadDllStep.ReadBytes) { LocalResManager.step = LocalResManager.LoadDllStep.Wait; ReadDllBytes(this.StartGame); } //else if (LocalResManager.step == LocalResManager.LoadDllStep.Completed) //{ // LocalResManager.step = LocalResManager.LoadDllStep.None; // m_UICanvas.gameObject.SetActive(false); // DestroySingleton(); //} } private string GetWebRequestPath(string asset) { var path = LocalResManager.Instance.GetAssetFilePath(string.Concat(LocalResManager.bytesFolderName, asset)); if (!path.Contains("file:")) { //ExternalStorePath 路径需要添加 path = "file://" + path; } return path; } private async void ReadDllBytes(Action callback) { foreach (var assetVersion in LocalResManager.Instance.assetVersions.Values) { if (assetVersion.localValid) { AOTMetaAssemblyFiles.Add(string.Concat(assetVersion.fileName, assetVersion.extersion)); } else { Debug.LogErrorFormat("文件无效 {0}", assetVersion.fileName); } } foreach (var asset in AOTMetaAssemblyFiles) { string dllPath = GetWebRequestPath(asset); Debug.Log($"dllPath:{dllPath}"); UnityWebRequest www = UnityWebRequest.Get(dllPath); await www.SendWebRequest(); #if UNITY_2020_1_OR_NEWER if (www.result != UnityWebRequest.Result.Success) { Debug.Log(www.error); } #else if (www.isHttpError || www.isNetworkError) { Debug.Log(www.error); } #endif else { // 特殊处理 byte[] assetData; if (asset == "Main.dll.bytes") { assetData = new byte[www.downloadHandler.data.Length - 3]; Array.Copy(www.downloadHandler.data, 3, assetData, 0, assetData.Length); } else { assetData = www.downloadHandler.data; } Debug.Log($"dll:{asset} size:{assetData.Length}"); s_assetDatas[asset] = assetData; } } callback?.Invoke(); } private void PrepareDownLoad() { if (LocalResManager.downLoadCount > 3) { LocalResManager.step = LocalResManager.LoadDllStep.None; stop = true; HttpBehaviour.ConnectAllTimes = 9999; return; } LocalResManager.Instance.RequestLogicBytes(); } private void BeginDownload() { List priorDownLoadAssetVersions = new List(); foreach (var assetVersion in LocalResManager.Instance.assetVersions.Values) { AssetVersion localAssetVersion = null; LocalResManager.Instance.localAssetVersions.TryGetValue(assetVersion.relativePath, out localAssetVersion); if (!assetVersion.CheckLocalFileValid(localAssetVersion)) { priorDownLoadAssetVersions.Add(assetVersion); assetVersion.localValid = false; } else { assetVersion.localValid = true; } } Debug.LogFormat("需要下载的文件数量:{0}", priorDownLoadAssetVersions.Count); if (priorDownLoadAssetVersions.Count == 0) { DownloadComplete(); return; } var targetDirectory = LocalResManager.Instance.ExternalStorePath; if (!Directory.Exists(targetDirectory)) { Directory.CreateDirectory(targetDirectory); } DownLoadAndDiscompressTask.Instance.Prepare(priorDownLoadAssetVersions, DownloadComplete); } void DownloadComplete() { LocalResManager.step = LocalResManager.LoadDllStep.ReadBytes; } }