using System; using System.Collections; using System.IO; using Cysharp.Threading.Tasks; using UnityEngine; using YooAsset; using ZC; namespace Unity.Loader { public class InitializePackage { [SerializeField] EPlayMode playMode; [SerializeField] private string packageName = "DefaultPackage"; [SerializeField] private string buildPipeline; string packageVersion; private Action iniFinishCallback; public InitializePackage(EPlayMode playMode, string packageName = "DefaultPackage", Action iniFinishCallback = null) { this.playMode = playMode; this.packageName = packageName; buildPipeline = EDefaultBuildPipeline.BuiltinBuildPipeline.ToString(); this.iniFinishCallback = iniFinishCallback; YooAssets.Initialize(); #if ENABLE_WEBGL || PLATFORM_WEBGL YooAssets.SetCacheSystemDisableCacheOnWebGL(); #endif EventManager.Instance.Subscribe(UpdatePackageCallbackEventArgs.EventId, UpdatePackageCallbackEvent); this.InitPackage().Forget(); } private void UpdatePackageCallbackEvent(object sender, GameEventArgs e) { var args = e as UpdatePackageCallbackEventArgs; switch (args.callbackType) { case UpdatePackageCallbackType.再次初始化资源包: this.InitPackage().Forget(); break; case UpdatePackageCallbackType.开始下载网络文件: this.BeginDownload().Forget(); break; case UpdatePackageCallbackType.再次更新静态版本: this.UpdatePackageVersion().Forget(); break; case UpdatePackageCallbackType.再次更新补丁清单: this.UpdateManifest().Forget(); break; case UpdatePackageCallbackType.再次下载网络文件: this.BeginDownload().Forget(); break; default: throw new ArgumentOutOfRangeException(); } } public async UniTask InitPackage() { await UniTask.Delay(500); // 创建资源包裹类 var package = YooAssets.TryGetPackage(packageName); if (package == null) package = YooAssets.CreatePackage(packageName); // 编辑器下的模拟模式 InitializationOperation initializationOperation = null; if (playMode == EPlayMode.EditorSimulateMode) { var createParameters = new EditorSimulateModeParameters(); createParameters.SimulateManifestFilePath = EditorSimulateModeHelper.SimulateBuild(buildPipeline, packageName); initializationOperation = package.InitializeAsync(createParameters); } // 单机运行模式 if (playMode == EPlayMode.OfflinePlayMode) { var createParameters = new OfflinePlayModeParameters(); createParameters.DecryptionServices = new FileStreamDecryption(); initializationOperation = package.InitializeAsync(createParameters); } // 联机运行模式 if (playMode == EPlayMode.HostPlayMode) { string defaultHostServer = GetHostServerURL(); string fallbackHostServer = GetHostServerURL(); var createParameters = new HostPlayModeParameters(); createParameters.DecryptionServices = new FileStreamDecryption(); createParameters.BuildinQueryServices = new GameQueryServices(); createParameters.RemoteServices = new RemoteServices(defaultHostServer, fallbackHostServer); initializationOperation = package.InitializeAsync(createParameters); } // WebGL运行模式 if (playMode == EPlayMode.WebPlayMode) { string defaultHostServer = GetHostServerURL(); string fallbackHostServer = GetHostServerURL(); var createParameters = new WebPlayModeParameters(); // createParameters.DecryptionServices = new FileStreamDecryption(); createParameters.BuildinQueryServices = new GameQueryServices(); createParameters.RemoteServices = new RemoteServices(defaultHostServer, fallbackHostServer); initializationOperation = package.InitializeAsync(createParameters); } await initializationOperation; // 如果初始化失败弹出提示界面 if (initializationOperation.Status != EOperationStatus.Succeed) { Debug.LogWarning($"{initializationOperation.Error}"); Debug.Log("初始化失败!"); // PatchEventDefine.InitializeFailed.SendEventMessage(); EventManager.Instance.FireNow(this, new InitializeFailedEventArgs()); } else { var version = initializationOperation.PackageVersion; Debug.Log($"Init resource package version : {version}"); // _machine.ChangeState(); await this.UpdatePackageVersion(); } } #region Helper /// /// 获取资源服务器地址 /// string GetHostServerURL() { //string hostServerIP = "http://10.0.2.2"; //安卓模拟器地址 string hostServerIP = "http://192.168.0.124:8080"; string appVersion = "v1.0"; #if UNITY_EDITOR if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.Android) return $"{hostServerIP}/CDN/Android/{appVersion}"; else if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.iOS) return $"{hostServerIP}/CDN/IPhone/{appVersion}"; else if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.WebGL) return $"{hostServerIP}/CDN/WebGL/{appVersion}"; else return $"{hostServerIP}/CDN/PC/{appVersion}"; #else if (Application.platform == RuntimePlatform.Android) return $"{hostServerIP}/CDN/Android/{appVersion}"; else if (Application.platform == RuntimePlatform.IPhonePlayer) return $"{hostServerIP}/CDN/IPhone/{appVersion}"; else if (Application.platform == RuntimePlatform.WebGLPlayer) return $"{hostServerIP}/CDN/WebGL/{appVersion}"; else return $"{hostServerIP}/CDN/PC/{appVersion}"; #endif } /// /// 远端资源地址查询服务类 /// class RemoteServices : IRemoteServices { private readonly string _defaultHostServer; private readonly string _fallbackHostServer; public RemoteServices(string defaultHostServer, string fallbackHostServer) { _defaultHostServer = defaultHostServer; _fallbackHostServer = fallbackHostServer; } string IRemoteServices.GetRemoteMainURL(string fileName) { return $"{_defaultHostServer}/{fileName}"; } string IRemoteServices.GetRemoteFallbackURL(string fileName) { return $"{_fallbackHostServer}/{fileName}"; } } /// /// 资源文件流加载解密类 /// class FileStreamDecryption : IDecryptionServices { /// /// 同步方式获取解密的资源包对象 /// 注意:加载流对象在资源包对象释放的时候会自动释放 /// AssetBundle IDecryptionServices.LoadAssetBundle(DecryptFileInfo fileInfo, out Stream managedStream) { BundleStream bundleStream = new BundleStream(fileInfo.FileLoadPath, FileMode.Open, FileAccess.Read, FileShare.Read); managedStream = bundleStream; return AssetBundle.LoadFromStream(bundleStream, fileInfo.ConentCRC, GetManagedReadBufferSize()); } /// /// 异步方式获取解密的资源包对象 /// 注意:加载流对象在资源包对象释放的时候会自动释放 /// AssetBundleCreateRequest IDecryptionServices.LoadAssetBundleAsync(DecryptFileInfo fileInfo, out Stream managedStream) { BundleStream bundleStream = new BundleStream(fileInfo.FileLoadPath, FileMode.Open, FileAccess.Read, FileShare.Read); managedStream = bundleStream; return AssetBundle.LoadFromStreamAsync(bundleStream, fileInfo.ConentCRC, GetManagedReadBufferSize()); } private static uint GetManagedReadBufferSize() { return 1024; } } /// /// 资源文件偏移加载解密类 /// class FileOffsetDecryption : IDecryptionServices { /// /// 同步方式获取解密的资源包对象 /// 注意:加载流对象在资源包对象释放的时候会自动释放 /// AssetBundle IDecryptionServices.LoadAssetBundle(DecryptFileInfo fileInfo, out Stream managedStream) { managedStream = null; return AssetBundle.LoadFromFile(fileInfo.FileLoadPath, fileInfo.ConentCRC, GetFileOffset()); } /// /// 异步方式获取解密的资源包对象 /// 注意:加载流对象在资源包对象释放的时候会自动释放 /// AssetBundleCreateRequest IDecryptionServices.LoadAssetBundleAsync(DecryptFileInfo fileInfo, out Stream managedStream) { managedStream = null; return AssetBundle.LoadFromFileAsync(fileInfo.FileLoadPath, fileInfo.ConentCRC, GetFileOffset()); } private static ulong GetFileOffset() { return 32; } } /// /// 资源文件解密流 /// public class BundleStream : FileStream { public const byte KEY = 64; public BundleStream(string path, FileMode mode, FileAccess access, FileShare share) : base(path, mode, access, share) { } public BundleStream(string path, FileMode mode) : base(path, mode) { } public override int Read(byte[] array, int offset, int count) { var index = base.Read(array, offset, count); for (int i = 0; i < array.Length; i++) { array[i] ^= KEY; } return index; } } #endregion public async UniTask UpdatePackageVersion() { Debug.Log("获取最新的资源版本 !"); await UniTask.Delay(100); var package = YooAssets.GetPackage(packageName); var operation = package.UpdatePackageVersionAsync(); await operation; if (operation.Status != EOperationStatus.Succeed) { Debug.LogWarning(operation.Error); Debug.Log("获取最新的资源版本失败!"); // PatchEventDefine.PackageVersionUpdateFailed.SendEventMessage(); EventManager.Instance.FireNow(this, new PackageVersionUpdateFailedEventArgs()); } else { packageVersion = operation.PackageVersion; await this.UpdateManifest(); } } public async UniTask UpdateManifest() { Debug.Log("更新资源清单!"); await UniTask.Delay(100); var package = YooAssets.GetPackage(packageName); bool savePackageVersion = true; var operation = package.UpdatePackageManifestAsync(packageVersion, savePackageVersion); await operation; if (operation.Status != EOperationStatus.Succeed) { Debug.LogWarning(operation.Error); Debug.Log("更新资源清单失败!"); // PatchEventDefine.PatchManifestUpdateFailed.SendEventMessage(); EventManager.Instance.FireNow(this, new PatchManifestUpdateFailedEventArgs()); return; } else { await this.CreateDownloader(); } } ResourceDownloaderOperation downloader; public async UniTask CreateDownloader() { Debug.Log("创建补丁下载器!"); await UniTask.Delay(100); var package = YooAssets.GetPackage(packageName); int downloadingMaxNum = 10; int failedTryAgain = 3; downloader = package.CreateResourceDownloader(downloadingMaxNum, failedTryAgain); if (downloader.TotalDownloadCount == 0) { Debug.Log("Not found any download files !"); await this.UpdaterDone(); } else { // 发现新更新文件后,挂起流程系统 // 注意:开发者需要在下载前检测磁盘空间不足 int totalDownloadCount = downloader.TotalDownloadCount; long totalDownloadBytes = downloader.TotalDownloadBytes; // PatchEventDefine.FoundUpdateFiles.SendEventMessage(totalDownloadCount, totalDownloadBytes); EventManager.Instance.FireNow(this, new FoundUpdateFilesEventArgs(totalDownloadCount, totalDownloadBytes)); // await this.BeginDownload(); } } public async UniTask BeginDownload() { Debug.Log("开始下载补丁文件!"); downloader.OnDownloadErrorCallback = DownloadErrorCallback; downloader.OnDownloadProgressCallback = DownloadProgressCallback; downloader.BeginDownload(); await downloader; // 检测下载结果 if (downloader.Status != EOperationStatus.Succeed) return; await this.DownloadFinish(); void DownloadErrorCallback(string fileName, string error) { Debug.Log($"下载补丁出错!{error}"); EventManager.Instance.FireNow(this, new WebFileDownloadFailedEventArgs(fileName, error)); } void DownloadProgressCallback(int totaldownloadcount, int currentdownloadcount, long totaldownloadbytes, long currentdownloadbytes) { Debug.Log($"下载进度:{currentdownloadcount}/{totaldownloadcount} , {currentdownloadbytes}/{totaldownloadbytes}!"); EventManager.Instance.FireNow(this, new DownloadProgressUpdateEventArgs(totaldownloadcount, currentdownloadcount, totaldownloadbytes, currentdownloadbytes)); } } public async UniTask DownloadFinish() { Debug.Log("下载补丁文件完成!"); await this.ClearPackageCache(); } public async UniTask ClearPackageCache() { Debug.Log("清理未使用的缓存文件!"); var package = YooAssets.GetPackage(packageName); var operation = package.ClearUnusedCacheFilesAsync(); operation.Completed += Operation_Completed; await UniTask.Yield(); } private void Operation_Completed(AsyncOperationBase obj) { this.UpdaterDone().Forget(); } public async UniTask UpdaterDone() { // 设置默认的资源包 var gamePackage = YooAssets.GetPackage("DefaultPackage"); YooAssets.SetDefaultPackage(gamePackage); Debug.Log("流程更新完毕!"); this.iniFinishCallback?.Invoke(); EventManager.Instance.FireNow(this, new UpdaterDoneEventArgs()); await UniTask.Yield(); } } }