using HybridCLR; using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using HotMain; using UnityEngine; using UnityEngine.Networking; using YooAsset; /// /// 脚本工作流程: /// 1.下载资源,用YooAsset资源框架进行下载 /// 1)资源文件,ab包等 /// 2)热更新dll /// 3) AOT泛型补充元数据dll /// 2.给AOT dll补充元数据,通过RuntimeApi.LoadMetadataForAOTAssembly() /// 3.通过实例化一个prefab,运行热更新代码 /// public class LoadDll : MonoBehaviour { /// /// 资源系统运行模式 /// public EPlayMode PlayMode = EPlayMode.OfflinePlayMode; //补充元数据dll的列表, //通过RuntimeApi.LoadMetadataForAOTAssembly()函数来补充AOT泛型的原始元数据 public static List AOTMetaAssemblyNames { get; } = new List() { "mscorlib.dll", "System.dll", "System.Core.dll", }; void Start() { //StartCoroutine(DownLoadAssets(this.StartGame)); StartCoroutine(DownLoadAssetsByYooAssets(this.StartGame)); } private static Dictionary s_assetDatas = new Dictionary(); public static byte[] GetAssetData(string dllName) { return s_assetDatas[dllName]; } private string GetWebRequestPath(string asset) { var path = $"{Application.streamingAssetsPath}/{asset}"; if (!path.Contains("://")) { path = "file://" + path; } if (path.EndsWith(".dll")) { path += ".bytes"; } return path; } IEnumerator DownLoadAssetsByYooAssets(Action onDownloadComplete) { // 1.初始化资源系统 YooAssets.Initialize(); string packageName = "DefaultPackage"; // 创建默认的资源包 var package = YooAssets.TryGetPackage(packageName) ?? YooAssets.CreatePackage(packageName); // 设置该资源包为默认的资源包,可以使用YooAssets相关加载接口加载该资源包内容。 YooAssets.SetDefaultPackage(package); if (PlayMode == EPlayMode.EditorSimulateMode) { Debug.Log("编辑器模式"); //编辑器模拟模式 var initParameters = new EditorSimulateModeParameters(); initParameters.EditorFileSystemParameters = FileSystemParameters.CreateDefaultEditorFileSystemParameters( EditorSimulateModeHelper.SimulateBuild(EDefaultBuildPipeline.ScriptableBuildPipeline, "DefaultPackage") ); yield return package.InitializeAsync(initParameters); } else if (PlayMode == EPlayMode.HostPlayMode) { Debug.Log("在线模式"); // 注意:GameQueryServices.cs 太空战机的脚本类,详细见StreamingAssetsHelper.cs string defaultHostServer = "http://127.0.0.1/CDN/Android/v1.0"; string fallbackHostServer = "http://127.0.0.1/CDN/Android/v1.0"; IRemoteServices remoteServices = new RemoteServices(defaultHostServer, fallbackHostServer); var cacheFileSystem = FileSystemParameters.CreateDefaultCacheFileSystemParameters(remoteServices); var buildinFileSystem = FileSystemParameters.CreateDefaultBuildinFileSystemParameters(); var initParameters = new HostPlayModeParameters(); initParameters.BuildinFileSystemParameters = buildinFileSystem; initParameters.CacheFileSystemParameters = cacheFileSystem; yield return package.InitializeAsync(initParameters); } else if (PlayMode == EPlayMode.OfflinePlayMode) { Debug.Log("离线模式"); // var buildinFileSystem = FileSystemParameters.CreateDefaultBuildinRawFileSystemParameters(); // var initParameters = new OfflinePlayModeParameters(); // initParameters.BuildinFileSystemParameters = buildinFileSystem; // yield return package.InitializeAsync(initParameters); // var initParametersOfflinePlayMode = new OfflinePlayModeParameters(); // yield return package.InitializeAsync(initParametersOfflinePlayMode); var createParameters = new OfflinePlayModeParameters(); createParameters.BuildinFileSystemParameters = FileSystemParameters.CreateDefaultBuildinFileSystemParameters(new FileStreamDecryption()); yield return package.InitializeAsync(createParameters); } //2.获取资源版本 var operation = package.RequestPackageVersionAsync(); yield return operation; if (operation.Status != EOperationStatus.Succeed) { //更新失败 Debug.LogError(operation.Error); //TODO yield break; } string PackageVersion = operation.PackageVersion; //3.更新补丁清单 var operation2 = package.UpdatePackageManifestAsync(PackageVersion); yield return operation2; if (operation2.Status != EOperationStatus.Succeed) { //更新失败 Debug.LogError(operation2.Error); //TODO: yield break; } //4.下载补丁包 yield return Download(); //TODO:判断是否下载成功... var assets = new List { "HotSamples.dll", }.Concat(AOTMetaAssemblyNames); foreach (var asset in assets) { var handle = package.LoadRawFileAsync(asset); yield return handle; byte[] fileData = handle.GetRawFileData(); s_assetDatas[asset] = fileData; Debug.Log($"dll:{asset} size:{fileData.Length}"); } onDownloadComplete(); } IEnumerator Download() { int downloadingMaxNum = 10; int failedTryAgain = 3; int timeout = 60; var package = YooAssets.GetPackage("DefaultPackage"); var downloader = package.CreateResourceDownloader(downloadingMaxNum, failedTryAgain, timeout); //没有需要下载的资源 if (downloader.TotalDownloadCount == 0) { yield break; } //需要下载的文件总数和总大小 int totalDownloadCount = downloader.TotalDownloadCount; long totalDownloadBytes = downloader.TotalDownloadBytes; //注册回调方法 downloader.OnDownloadErrorCallback = OnDownloadErrorFunction; downloader.OnDownloadProgressCallback = OnDownloadProgressUpdateFunction; downloader.OnDownloadOverCallback = OnDownloadOverFunction; downloader.OnStartDownloadFileCallback = OnStartDownloadFileFunction; //开启下载 downloader.BeginDownload(); yield return downloader; //检测下载结果 if (downloader.Status == EOperationStatus.Succeed) { //下载成功 Debug.Log("更新完成!"); //TODO: } else { //下载失败 Debug.LogError("更新失败!"); //TODO: } } /// /// 开始下载 /// /// /// /// private void OnStartDownloadFileFunction(string fileName, long sizeBytes) { Debug.Log(string.Format("开始下载:文件名:{0}, 文件大小:{1}", fileName, sizeBytes)); } /// /// 下载完成 /// /// /// private void OnDownloadOverFunction(bool isSucceed) { Debug.Log("下载" + (isSucceed ? "成功" : "失败")); } /// /// 更新中 /// /// /// /// /// /// private void OnDownloadProgressUpdateFunction(int totalDownloadCount, int currentDownloadCount, long totalDownloadBytes, long currentDownloadBytes) { Debug.Log(string.Format("文件总数:{0}, 已下载文件数:{1}, 下载总大小:{2}, 已下载大小:{3}", totalDownloadCount, currentDownloadCount, totalDownloadBytes, currentDownloadBytes)); } /// /// 下载出错 /// /// /// /// private void OnDownloadErrorFunction(string fileName, string error) { Debug.LogError(string.Format("下载出错:文件名:{0}, 错误信息:{1}", fileName, error)); } void StartGame() { LoadMetadataForAOTAssemblies(); #if !UNITY_EDITOR System.Reflection.Assembly.Load(GetAssetData("HotSamples.dll")); #endif //委托加载方式,加载prefab var package = YooAssets.GetPackage("DefaultPackage"); AssetHandle handle = package.LoadAssetAsync("HotPrefab"); handle.Completed += Handle_Completed; //AssetBundle prefabAb = AssetBundle.LoadFromMemory(GetAssetData("prefabs")); //GameObject testPrefab = Instantiate(prefabAb.LoadAsset("HotUpdatePrefab.prefab")); } private void Handle_Completed(AssetHandle obj) { GameObject go = obj.InstantiateSync(); Debug.Log($"Prefab name is {go.name}"); } /// /// 为aot assembly加载原始metadata, 这个代码放aot或者热更新都行。 /// 一旦加载后,如果AOT泛型函数对应native实现不存在,则自动替换为解释模式执行 /// private static void LoadMetadataForAOTAssemblies() { /// 注意,补充元数据是给AOT dll补充元数据,而不是给热更新dll补充元数据。 /// 热更新dll不缺元数据,不需要补充,如果调用LoadMetadataForAOTAssembly会返回错误 /// HomologousImageMode mode = HomologousImageMode.SuperSet; foreach (var aotDllName in AOTMetaAssemblyNames) { byte[] dllBytes = GetAssetData(aotDllName); // 加载assembly对应的dll,会自动为它hook。一旦aot泛型函数的native函数不存在,用解释器版本代码 LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(dllBytes, mode); Debug.Log($"LoadMetadataForAOTAssembly:{aotDllName}. mode:{mode} ret:{err}"); } } }